Merge branch 'patch-turbidity' into turbidity-probe

This commit is contained in:
Russell Stanley 2022-02-08 12:26:38 +10:30
commit c4386def0f
6 changed files with 207 additions and 114 deletions

View File

@ -55,22 +55,18 @@ USAGE
package main
import (
"bytes"
"io"
"os"
"runtime/pprof"
"strconv"
"time"
"gocv.io/x/gocv"
"gonum.org/v1/gonum/stat"
"gopkg.in/natefinch/lumberjack.v2"
"bitbucket.org/ausocean/av/container/mts"
"bitbucket.org/ausocean/av/container/mts/meta"
"bitbucket.org/ausocean/av/revid"
"bitbucket.org/ausocean/av/revid/config"
"bitbucket.org/ausocean/av/turbidity"
"bitbucket.org/ausocean/iot/pi/netlogger"
"bitbucket.org/ausocean/iot/pi/netsender"
"bitbucket.org/ausocean/utils/logger"
@ -107,7 +103,6 @@ const (
profilePath = "rv.prof"
pkg = "rv: "
runPreDelay = 20 * time.Second
maxImages = 1 // Max number of images read when evaluating turbidity.
)
// Software define pin values.
@ -118,112 +113,9 @@ const (
contrastPin = "X39"
)
// Turbidity sensor constants.
const (
k1, k2 = 4, 4 // Block size, must be divisible by the size template with no remainder.
filterSize = 3 // Sobel filter size.
scale = 1.0 // Amount of scale applied to sobel filter values.
alpha = 1.0 // Paramater for contrast equation.
)
// This is set to true if the 'profile' build tag is provided on build.
var canProfile = false
type turbidityProbe struct {
sharpness, contrast float64
delay time.Duration
ticker time.Ticker
ts *turbidity.TurbiditySensor
log logger.Logger
buffer *bytes.Buffer
}
// NewTurbidityProbe returns a new turbidity probe.
func NewTurbidityProbe(log logger.Logger, delay time.Duration) (*turbidityProbe, error) {
tp := new(turbidityProbe)
tp.log = log
tp.delay = delay
tp.ticker = *time.NewTicker(delay)
tp.buffer = bytes.NewBuffer(*new([]byte))
// Create the turbidity sensor.
standard := gocv.IMRead("../../turbidity/images/template.jpg", gocv.IMReadGrayScale)
template := gocv.IMRead("../../turbidity/images/icon.jpg", gocv.IMReadGrayScale)
ts, err := turbidity.NewTurbiditySensor(template, standard, k1, k2, filterSize, scale, alpha, log)
if err != nil {
log.Error("failed create turbidity sensor", "error", err.Error())
}
tp.ts = ts
return tp, nil
}
// Write, reads input h264 frames in the form of a byte stream and writes the the sharpness and contrast
// scores of a video to the the turbidity probe.
func (tp *turbidityProbe) Write(p []byte) (int, error) {
tp.buffer.Write(p)
go func() {
select {
case <-tp.ticker.C:
tp.turbidityCalculation(p)
default:
return
}
}()
return len(p), nil
}
func (tp *turbidityProbe) Close() error {
return nil
}
func (tp *turbidityProbe) turbidityCalculation(p []byte) {
var imgs []gocv.Mat
img := gocv.NewMat()
// Write byte array to a temp file.
file, err := os.CreateTemp("temp", "video*.h264")
if err != nil {
tp.log.Error("failed to create temp file", "error", err.Error())
// TODO: Error handling.
return
}
defer os.Remove(file.Name())
_, err = file.Write(tp.buffer.Bytes())
if err != nil {
tp.log.Error("failed to write to temporary file", "error", err.Error())
// TODO: Error handling.
return
}
tp.buffer.Reset()
// Read the file and store each frame.
vc, err := gocv.VideoCaptureFile(file.Name())
if err != nil {
tp.log.Error("failed to open video file", "error", err.Error())
// TODO: Error handling.
return
}
for vc.Read(&img) && len(imgs) < maxImages {
imgs = append(imgs, img.Clone())
}
if len(imgs) <= 0 {
tp.log.Log(logger.Warning, "no frames found", "error", err.Error())
return
}
tp.log.Log(logger.Debug, "found frames", "frames", len(imgs))
// Process video data to get saturation and contrast scores.
startTime := time.Now()
res, err := tp.ts.Evaluate(imgs)
if err != nil {
tp.log.Error("evaluate failed", "error", err.Error())
// TODO: Error handling.
} else {
tp.log.Log(logger.Debug, "finished evaluation", "total duration (sec)", time.Since(startTime).Seconds())
tp.contrast = stat.Mean(res.Contrast, nil)
tp.sharpness = stat.Mean(res.Sharpness, nil)
}
return
}
func main() {
mts.Meta = meta.NewWith([][2]string{{metaPreambleKey, metaPreambleData}})

149
cmd/rv/probe.go Normal file
View File

@ -0,0 +1,149 @@
//go:build !nocv
// +build !nocv
/*
DESCRIPTION
Provides the methods for the turbidity probe using GoCV. Turbidity probe
will collect the most recent frames in a buffer and write the latest sharpness
and contrast scores to the probe.
AUTHORS
Russell Stanley <russell@ausocean.org>
LICENSE
Copyright (C) 2021-2022 the Australian Ocean Lab (AusOcean)
It is free software: you can redistribute it and/or modify them
under the terms of the GNU General Public License as published by the
Free Software Foundation, either version 3 of the License, or (at your
option) any later version.
It is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
in gpl.txt. If not, see http://www.gnu.org/licenses.
*/
package main
import (
"bytes"
"os"
"time"
"bitbucket.org/ausocean/av/turbidity"
"bitbucket.org/ausocean/utils/logger"
"gocv.io/x/gocv"
"gonum.org/v1/gonum/stat"
)
// Misc constants.
const (
maxImages = 10 // Max number of images read when evaluating turbidity.
)
// Turbidity sensor constants.
const (
k1, k2 = 4, 4 // Block size, must be divisible by the size template with no remainder.
filterSize = 3 // Sobel filter size.
scale = 1.0 // Amount of scale applied to sobel filter values.
alpha = 1.0 // Paramater for contrast equation.
)
type turbidityProbe struct {
sharpness, contrast float64
delay time.Duration
ticker time.Ticker
ts *turbidity.TurbiditySensor
log logger.Logger
buffer *bytes.Buffer
}
// NewTurbidityProbe returns a new turbidity probe.
func NewTurbidityProbe(log logger.Logger, delay time.Duration) (*turbidityProbe, error) {
tp := new(turbidityProbe)
tp.log = log
tp.delay = delay
tp.ticker = *time.NewTicker(delay)
tp.buffer = bytes.NewBuffer(*new([]byte))
// Create the turbidity sensor.
standard := gocv.IMRead("../../turbidity/images/template.jpg", gocv.IMReadGrayScale)
template := gocv.IMRead("../../turbidity/images/template.jpg", gocv.IMReadGrayScale)
ts, err := turbidity.NewTurbiditySensor(template, standard, k1, k2, filterSize, scale, alpha, log)
if err != nil {
log.Error("failed create turbidity sensor", "error", err.Error())
}
tp.ts = ts
return tp, nil
}
// Write, reads input h264 frames in the form of a byte stream and writes the the sharpness and contrast
// scores of a video to the the turbidity probe.
func (tp *turbidityProbe) Write(p []byte) (int, error) {
tp.buffer.Write(p)
go func() {
select {
case <-tp.ticker.C:
tp.turbidityCalculation(p)
default:
return
}
}()
return len(p), nil
}
func (tp *turbidityProbe) Close() error {
return nil
}
func (tp *turbidityProbe) turbidityCalculation(p []byte) {
var imgs []gocv.Mat
img := gocv.NewMat()
// Write byte array to a temp file.
file, err := os.CreateTemp("temp", "video*.h264")
if err != nil {
tp.log.Error("failed to create temp file", "error", err.Error())
// TODO: Error handling.
return
}
defer os.Remove(file.Name())
_, err = file.Write(tp.buffer.Bytes())
if err != nil {
tp.log.Error("failed to write to temporary file", "error", err.Error())
// TODO: Error handling.
return
}
tp.buffer.Reset()
// Read the file and store each frame.
vc, err := gocv.VideoCaptureFile(file.Name())
if err != nil {
tp.log.Error("failed to open video file", "error", err.Error())
// TODO: Error handling.
return
}
for vc.Read(&img) && len(imgs) < maxImages {
imgs = append(imgs, img.Clone())
}
if len(imgs) <= 0 {
tp.log.Log(logger.Warning, "no frames found", "error", err.Error())
return
}
tp.log.Log(logger.Debug, "found frames", "frames", len(imgs))
// Process video data to get saturation and contrast scores.
startTime := time.Now()
res, err := tp.ts.Evaluate(imgs)
if err != nil {
tp.log.Error("evaluate failed", "error", err.Error())
// TODO: Error handling.
} else {
tp.log.Log(logger.Debug, "finished evaluation", "total duration (sec)", time.Since(startTime).Seconds())
tp.contrast = stat.Mean(res.Contrast, nil)
tp.sharpness = stat.Mean(res.Sharpness, nil)
}
return
}

55
cmd/rv/probe_circleci.go Normal file
View File

@ -0,0 +1,55 @@
//go:build nocv
// +build nocv
/*
DESCRIPTION
Replaces turbidity probe implementation that uses the gocv package.
When Circle-CI builds revid this is needed because Circle-CI does not
have a copy of Open CV installed.
AUTHORS
Russell Stanley <russell@ausocean.org>
LICENSE
Copyright (C) 2021-2022 the Australian Ocean Lab (AusOcean)
It is free software: you can redistribute it and/or modify them
under the terms of the GNU General Public License as published by the
Free Software Foundation, either version 3 of the License, or (at your
option) any later version.
It is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
in gpl.txt. If not, see http://www.gnu.org/licenses.
*/
package main
import (
"time"
"bitbucket.org/ausocean/utils/logger"
)
type turbidityProbe struct {
sharpness, contrast float64
}
// NewTurbidityProbe returns a empty turbidity probe for CircleCI testing only.
func NewTurbidityProbe(log logger.Logger, delay time.Duration) (*turbidityProbe, error) {
tp := new(turbidityProbe)
return tp, nil
}
// Write performs no operation for CircleCI testing only.
func (tp *turbidityProbe) Write(p []byte) (int, error) {
return len(p), nil
}
func (tp *turbidityProbe) Close() error {
return nil
}

View File

@ -1,3 +1,5 @@
// +build gofuzz
/*
DESCRIPTION
fuzz.go provides a function with the required signature such that it may be
@ -24,8 +26,6 @@ LICENSE
in gpl.txt. If not, see http://www.gnu.org/licenses.
*/
// +build gofuzz
package fuzzParseLevelPrefix
/*

View File

@ -29,5 +29,5 @@ import (
)
func (r *Revid) setupAudio() error {
return errors.New("audio not implemented on OSX")
return errors.New("audio not implemented on darwin(macOS)")
}

View File

@ -1,6 +1,3 @@
//go:build !nocv
// +build !nocv
/*
DESCRIPTION
Results struct used to store results from the turbidity sensor.