cmd/rv/probe.go improved error handling

This commit is contained in:
Russell Stanley 2022-03-04 17:09:50 +10:30
parent 514b5ef549
commit b97f1f465f
5 changed files with 48 additions and 40 deletions

View File

@ -148,7 +148,7 @@ func main() {
p, err := NewTurbidityProbe(*log, 60*time.Second) p, err := NewTurbidityProbe(*log, 60*time.Second)
if err != nil { if err != nil {
log.Log(logger.Error, "could not create new turbidity probe", "error", err.Error()) log.Log(logger.Fatal, "could not create new turbidity probe", "error", err.Error())
} }
log.Log(logger.Debug, "initialising netsender client") log.Log(logger.Debug, "initialising netsender client")

View File

@ -31,6 +31,8 @@ package main
import ( import (
"bytes" "bytes"
"errors"
"fmt"
"os" "os"
"time" "time"
@ -80,7 +82,7 @@ func NewTurbidityProbe(log logger.Logger, delay time.Duration) (*turbidityProbe,
template := 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) ts, err := turbidity.NewTurbiditySensor(template, standard, k1, k2, filterSize, scale, alpha, log)
if err != nil { if err != nil {
log.Error("failed create turbidity sensor", "error", err.Error()) return nil, fmt.Errorf("failed to create turbidity sensor: %w", err)
} }
tp.ts = ts tp.ts = ts
return tp, nil return tp, nil
@ -89,63 +91,58 @@ func NewTurbidityProbe(log logger.Logger, delay time.Duration) (*turbidityProbe,
// Write, reads input h264 frames in the form of a byte stream and writes the the sharpness and contrast // 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. // scores of a video to the the turbidity probe.
func (tp *turbidityProbe) Write(p []byte) (int, error) { func (tp *turbidityProbe) Write(p []byte) (int, error) {
out := len(p)
if tp.buffer.Len() == 0 { if tp.buffer.Len() == 0 {
// The first entry in the buffer must be a keyframe to speed up decoding. // The first entry in the buffer must be a keyframe to speed up decoding.
video, err := h264.Trim(p) video, err := h264.Trim(p)
if err != nil { if err != nil {
tp.log.Log(logger.Debug, "no key frame detected", "error", err.Error()) return 0, fmt.Errorf("could not trim h264: %w", err)
return 0, nil
} }
n, err := tp.buffer.Write(video) n, err := tp.buffer.Write(video)
if err != nil { if err != nil {
tp.log.Log(logger.Error, "could not write trimmed video to buffer", "error", err.Error())
tp.buffer.Reset() tp.buffer.Reset()
return 0, nil return 0, fmt.Errorf("could not write trimmed video to buffer: %w", err)
} }
tp.log.Log(logger.Debug, "video trimmed, write keyframe complete", "size(bytes)", n) tp.log.Log(logger.Debug, "video trimmed, write keyframe complete", "size(bytes)", n)
} else if tp.buffer.Len() < bufferLimit { } else if tp.buffer.Len() < bufferLimit {
// Buffer is limited to speed up decoding. // Buffer is limited to speed up decoding.
n, err := tp.buffer.Write(p) n, err := tp.buffer.Write(p)
if err != nil { if err != nil {
tp.log.Log(logger.Error, "could not write to buffer, reseting", "error", err.Error())
tp.buffer.Reset() tp.buffer.Reset()
return 0, nil return 0, fmt.Errorf("could not write to buffer: %w", err)
} }
tp.log.Log(logger.Debug, "write to video buffer complete", "size(bytes)", n) tp.log.Log(logger.Debug, "write to video buffer complete", "size(bytes)", n)
} else {
out = 0
} }
select { select {
case <-tp.ticker.C: case <-tp.ticker.C:
tp.log.Log(logger.Debug, "beginning turbidity calculation") tp.log.Log(logger.Debug, "beginning turbidity calculation")
startTime := time.Now() startTime := time.Now()
tp.turbidityCalculation() err := tp.turbidityCalculation()
if err != nil {
return 0, fmt.Errorf("could not calculate turbidity: %w", err)
}
tp.log.Log(logger.Debug, "finished turbidity calculation", "total duration (sec)", time.Since(startTime).Seconds()) tp.log.Log(logger.Debug, "finished turbidity calculation", "total duration (sec)", time.Since(startTime).Seconds())
default: default:
} }
return out, nil return len(p), nil
} }
func (tp *turbidityProbe) Close() error { func (tp *turbidityProbe) Close() error {
return nil return nil
} }
func (tp *turbidityProbe) turbidityCalculation() { func (tp *turbidityProbe) turbidityCalculation() error {
var imgs []gocv.Mat var imgs []gocv.Mat
img := gocv.NewMat() img := gocv.NewMat()
// Write byte array to a temp file. // Write byte array to a temp file.
file, err := os.CreateTemp("temp", "video*.h264") file, err := os.CreateTemp("temp", "video*.h264")
if err != nil { if err != nil {
tp.log.Error("failed to create temp file", "error", err.Error()) return fmt.Errorf("failed to create temp file: %w", err)
return
} }
tp.log.Log(logger.Debug, "writing to file", "buffer size(bytes)", tp.buffer.Len()) tp.log.Log(logger.Debug, "writing to file", "buffer size(bytes)", tp.buffer.Len())
_, err = file.Write(tp.buffer.Bytes()) _, err = file.Write(tp.buffer.Bytes())
if err != nil { if err != nil {
tp.log.Error("failed to write to temporary file", "error", err.Error()) return fmt.Errorf("failed to write to temporary file: %w", err)
return
} }
tp.log.Log(logger.Debug, "write to file success", "buffer size(bytes)", tp.buffer.Len()) tp.log.Log(logger.Debug, "write to file success", "buffer size(bytes)", tp.buffer.Len())
tp.buffer.Reset() tp.buffer.Reset()
@ -154,37 +151,48 @@ func (tp *turbidityProbe) turbidityCalculation() {
startTime := time.Now() startTime := time.Now()
vc, err := gocv.VideoCaptureFile(file.Name()) vc, err := gocv.VideoCaptureFile(file.Name())
if err != nil { if err != nil {
tp.log.Error("failed to open video file", "error", err.Error()) return fmt.Errorf("failed to open video file: %w", err)
return
} }
tp.log.Log(logger.Debug, "video capture open", "total duration (sec)", time.Since(startTime).Seconds()) tp.log.Log(logger.Debug, "video capture open", "total duration (sec)", time.Since(startTime).Seconds())
// Store each frame untill max. // Store each frame until maximum amount is reached.
startTime = time.Now() startTime = time.Now()
for vc.Read(&img) && len(imgs) < maxImages { for vc.Read(&img) && len(imgs) < maxImages {
imgs = append(imgs, img.Clone()) imgs = append(imgs, img.Clone())
} }
if len(imgs) <= 0 { if len(imgs) <= 0 {
tp.log.Log(logger.Warning, "no frames found") return errors.New("no frames found")
return
} }
tp.log.Log(logger.Debug, "read time", "total duration (sec)", time.Since(startTime).Seconds()) tp.log.Log(logger.Debug, "read time", "total duration (sec)", time.Since(startTime).Seconds())
// Process video data to get saturation and contrast scores. // Process video data to get saturation and contrast scores.
res, err := tp.ts.Evaluate(imgs) res, err := tp.ts.Evaluate(imgs)
if err != nil { if err != nil {
tp.log.Error("evaluate failed", "error", err.Error()) err_ := cleanUp(file.Name(), vc)
} else { if err_ != nil {
tp.contrast = stat.Mean(res.Contrast, nil) return fmt.Errorf("could not clean up after evaluation error: %w", err_)
tp.sharpness = stat.Mean(res.Sharpness, nil) }
return fmt.Errorf("evaluation error: %w", err)
} }
err = os.Remove(file.Name()) tp.contrast = stat.Mean(res.Contrast, nil)
tp.sharpness = stat.Mean(res.Sharpness, nil)
err = cleanUp(file.Name(), vc)
if err != nil { if err != nil {
tp.log.Error("could not remove file", "error", err.Error()) return fmt.Errorf("could not clean up: %w", err)
}
return nil
}
func cleanUp(file string, vc *gocv.VideoCapture) error {
err := os.Remove(file)
if err != nil {
return fmt.Errorf("could not remove temp file: %w", err)
} }
err = vc.Close() err = vc.Close()
if err != nil { if err != nil {
tp.log.Error("could not close video capture", "error", err.Error()) return fmt.Errorf("could not close video capture device: %w", err)
} }
return nil
} }

View File

@ -79,7 +79,7 @@ func (s *frameScanner) readByte() (b byte, ok bool) {
return b, true return b, true
} }
// Trim, will trim down a given byte stream of video data so that a key frame appears first. // Trim will trim down a given byte stream of video data so that a key frame appears first.
func Trim(n []byte) ([]byte, error) { func Trim(n []byte) ([]byte, error) {
sc := frameScanner{buf: n} sc := frameScanner{buf: n}
for { for {

View File

@ -48,11 +48,11 @@ type TurbiditySensor struct {
standard, standardCorners gocv.Mat standard, standardCorners gocv.Mat
k1, k2, sobelFilterSize int k1, k2, sobelFilterSize int
scale, alpha float64 scale, alpha float64
logger logger.Logger log logger.Logger
} }
// NewTurbiditySensor returns a new TurbiditySensor. // NewTurbiditySensor returns a new TurbiditySensor.
func NewTurbiditySensor(template, standard gocv.Mat, k1, k2, sobelFilterSize int, scale, alpha float64, logger logger.Logger) (*TurbiditySensor, error) { func NewTurbiditySensor(template, standard gocv.Mat, k1, k2, sobelFilterSize int, scale, alpha float64, log logger.Logger) (*TurbiditySensor, error) {
ts := new(TurbiditySensor) ts := new(TurbiditySensor)
templateCorners := gocv.NewMat() templateCorners := gocv.NewMat()
standardCorners := gocv.NewMat() standardCorners := gocv.NewMat()
@ -79,7 +79,7 @@ func NewTurbiditySensor(template, standard gocv.Mat, k1, k2, sobelFilterSize int
ts.k1, ts.k2, ts.sobelFilterSize = k1, k2, sobelFilterSize ts.k1, ts.k2, ts.sobelFilterSize = k1, k2, sobelFilterSize
ts.alpha, ts.scale = alpha, scale ts.alpha, ts.scale = alpha, scale
ts.logger = logger ts.log = log
return ts, nil return ts, nil
} }
@ -97,18 +97,18 @@ func (ts TurbiditySensor) Evaluate(imgs []gocv.Mat) (*Results, error) {
return nil, fmt.Errorf("could not transform image: %d: %w", i, err) return nil, fmt.Errorf("could not transform image: %d: %w", i, err)
} }
ts.logger.Log(logger.Debug, "transform successful", "transform duration (sec)", time.Since(timer).Seconds()) ts.log.Log(logger.Debug, "transform successful", "transform duration (sec)", time.Since(timer).Seconds())
timer = time.Now() timer = time.Now()
edge := ts.sobel(marker) edge := ts.sobel(marker)
ts.logger.Log(logger.Debug, "sobel filter successful", "sobel duration", time.Since(timer).Seconds()) ts.log.Log(logger.Debug, "sobel filter successful", "sobel duration", time.Since(timer).Seconds())
timer = time.Now() timer = time.Now()
sharpScore, contScore, err := ts.EvaluateImage(marker, edge) sharpScore, contScore, err := ts.EvaluateImage(marker, edge)
if err != nil { if err != nil {
return result, err return result, err
} }
ts.logger.Log(logger.Debug, "sharpness and contrast evaluation successful", "evaluation duration", time.Since(timer).Seconds()) ts.log.Log(logger.Debug, "sharpness and contrast evaluation successful", "evaluation duration", time.Since(timer).Seconds())
result.Update(sharpScore, contScore, float64(i), i) result.Update(sharpScore, contScore, float64(i), i)
} }
return result, nil return result, nil

View File

@ -74,7 +74,7 @@ func TestImages(t *testing.T) {
MaxBackups: logMaxBackup, MaxBackups: logMaxBackup,
MaxAge: logMaxAge, MaxAge: logMaxAge,
} }
log := logger.New(logVerbosity, io.MultiWriter(fileLog), logSuppress) log := *logger.New(logVerbosity, io.MultiWriter(fileLog), logSuppress)
template := gocv.IMRead("images/template.jpg", gocv.IMReadGrayScale) template := gocv.IMRead("images/template.jpg", gocv.IMReadGrayScale)
standard := gocv.IMRead("images/default.jpg", gocv.IMReadGrayScale) standard := gocv.IMRead("images/default.jpg", gocv.IMReadGrayScale)
@ -89,7 +89,7 @@ func TestImages(t *testing.T) {
} }
} }
ts, err := NewTurbiditySensor(template, standard, k1, k2, filterSize, scale, alpha, *log) ts, err := NewTurbiditySensor(template, standard, k1, k2, filterSize, scale, alpha, log)
if err != nil { if err != nil {
t.Fatalf("could not create turbidity sensor: %v", err) t.Fatalf("could not create turbidity sensor: %v", err)
} }
@ -111,7 +111,7 @@ func TestImages(t *testing.T) {
results.Update(stat.Mean(sample_result.Sharpness, nil), stat.Mean(sample_result.Contrast, nil), float64(i)*increment, i) results.Update(stat.Mean(sample_result.Sharpness, nil), stat.Mean(sample_result.Contrast, nil), float64(i)*increment, i)
} }
err = plotResults(results.Turbidity, normalize(results.Sharpness), normalize(results.Contrast)) err = plotResults(results.Turbidity, results.Sharpness, results.Contrast)
if err != nil { if err != nil {
t.Fatalf("plotting Failed: %v", err) t.Fatalf("plotting Failed: %v", err)
} }