From 090162c472403cd5d3fdbd1f1d33c405878644c1 Mon Sep 17 00:00:00 2001 From: Russell Stanley Date: Thu, 6 Jan 2022 14:41:35 +1030 Subject: [PATCH] improved corner detection in transform function, fixed some comments --- turbidity/results.go | 6 ++--- turbidity/turbidity.go | 46 ++++++++++++++++++++----------------- turbidity/turbidity_test.go | 2 +- 3 files changed, 29 insertions(+), 25 deletions(-) diff --git a/turbidity/results.go b/turbidity/results.go index bd2cc145..df731dc9 100644 --- a/turbidity/results.go +++ b/turbidity/results.go @@ -29,14 +29,14 @@ package main import "fmt" -// Struct to hold the results of the turbidity sensor. +// Results holds the results of the turbidity sensor. type Results struct { Turbidity []float64 Saturation []float64 Contrast []float64 } -// Results constructor +// NewResults constructs the results object. func NewResults(n int) (*Results, error) { if n <= 0 { @@ -51,7 +51,7 @@ func NewResults(n int) (*Results, error) { return r, nil } -// Update results to add new values at specified index. +// Update adds new values to slice at specified index. func (r *Results) Update(newSat, newCont, newTurb float64, index int) { r.Saturation[index] = newSat r.Contrast[index] = newCont diff --git a/turbidity/turbidity.go b/turbidity/turbidity.go index 81efb195..562e9fff 100644 --- a/turbidity/turbidity.go +++ b/turbidity/turbidity.go @@ -40,24 +40,37 @@ import ( // TurbiditySensor is a software based turbidity sensor that uses CV to determine saturation and constrast level // of a chessboard-like target submerged in water that can be correlated to turbidity/visibility values. type TurbiditySensor struct { - template, standard gocv.Mat - k1, k2, sobelFilterSize int - scale, alpha float64 + template, templateCorners gocv.Mat + standard, standardCorners gocv.Mat + k1, k2, sobelFilterSize int + scale, alpha float64 } -// Turbidity sensor constructor. +// NewTurbiditySensor constructor for a turbidity sensor. func NewTurbiditySensor(template, standard gocv.Mat, k1, k2, sobelFilterSize int, scale, alpha float64) (*TurbiditySensor, error) { ts := new(TurbiditySensor) + templateCorners := gocv.NewMat() + standardCorners := gocv.NewMat() + // Validate template image is not empty and has valid corners. if template.Empty() { return nil, errors.New("template image is empty.") } + if !gocv.FindChessboardCorners(template, image.Pt(3, 3), &templateCorners, gocv.CalibCBNormalizeImage) { + return nil, errors.New("could not find corners in template image") + } ts.template = template + ts.templateCorners = templateCorners + // Validate standard image is not empty and has valid corners. if standard.Empty() { return nil, errors.New("standard image is empty.") } + if !gocv.FindChessboardCorners(standard, image.Pt(3, 3), &standardCorners, gocv.CalibCBNormalizeImage) { + return nil, errors.New("could not find corners in standard image") + } ts.standard = standard + ts.standardCorners = standardCorners ts.k1, ts.k2, ts.sobelFilterSize = k1, k2, sobelFilterSize ts.alpha, ts.scale = alpha, scale @@ -130,6 +143,7 @@ func (ts TurbiditySensor) EvaluateImage(img, edge gocv.Mat) (float64, float64, e return sharpness, contrast, nil } +// minMax returns the max and min pixel values of an image block. func (ts TurbiditySensor) minMax(img gocv.Mat, xStart, yStart, xEnd, yEnd int) (float64, float64) { max := -math.MaxFloat64 min := math.MaxFloat64 @@ -150,7 +164,7 @@ func (ts TurbiditySensor) minMax(img gocv.Mat, xStart, yStart, xEnd, yEnd int) ( return max, min } -// Evaluate a block within an image and return the value to be added to the sharpness result. +// evaluateBlockEME will evaluate an image block and return the value to be added to the sharpness result. func (ts TurbiditySensor) evaluateBlockEME(img gocv.Mat, xStart, yStart, xEnd, yEnd int) float64 { max, min := ts.minMax(img, xStart, yStart, xEnd, yEnd) @@ -161,7 +175,7 @@ func (ts TurbiditySensor) evaluateBlockEME(img gocv.Mat, xStart, yStart, xEnd, y return 0.0 } -// Evaluate a block within an image and return the value to be added to the contrast result. +// evaluateBlockAMEE will evaluate an image block and return the value to be added to the contrast result. func (ts TurbiditySensor) evaluateBlockAMEE(img gocv.Mat, xStart, yStart, xEnd, yEnd int) float64 { max, min := ts.minMax(img, xStart, yStart, xEnd, yEnd) @@ -177,29 +191,19 @@ func (ts TurbiditySensor) evaluateBlockAMEE(img gocv.Mat, xStart, yStart, xEnd, func (ts TurbiditySensor) transform(img gocv.Mat) (gocv.Mat, error) { out := gocv.NewMat() mask := gocv.NewMat() - corners_img := gocv.NewMat() - corners_template := gocv.NewMat() + imgCorners := gocv.NewMat() // Check image is valid. if img.Empty() { return out, errors.New("image is empty, cannot transform") } - - // Find corners in image. - if !gocv.FindChessboardCorners(img, image.Pt(3, 3), &corners_img, gocv.CalibCBNormalizeImage) { - // Apply default if transformation fails. - if !gocv.FindChessboardCorners(ts.standard, image.Pt(3, 3), &corners_img, gocv.CalibCBNormalizeImage) { - return out, errors.New("Could not find corners in default image") - } - } - - // Find corners in template. - if !gocv.FindChessboardCorners(ts.template, image.Pt(3, 3), &corners_template, gocv.CalibCBNormalizeImage) { - return out, errors.New("Could not find corners in template") + // Check image for corners, if non can be found corners will be set to default value. + if !gocv.FindChessboardCorners(img, image.Pt(3, 3), &imgCorners, gocv.CalibCBFastCheck) { + imgCorners = ts.standardCorners } // Find and apply transformation. - H := gocv.FindHomography(corners_img, &corners_template, gocv.HomograpyMethodRANSAC, 3.0, &mask, 2000, 0.995) + H := gocv.FindHomography(imgCorners, &ts.templateCorners, gocv.HomograpyMethodRANSAC, 3.0, &mask, 2000, 0.995) gocv.WarpPerspective(img, &out, H, image.Pt(ts.template.Rows(), ts.template.Cols())) return out, nil diff --git a/turbidity/turbidity_test.go b/turbidity/turbidity_test.go index 9a180f1f..40791908 100644 --- a/turbidity/turbidity_test.go +++ b/turbidity/turbidity_test.go @@ -59,7 +59,7 @@ func TestImages(t *testing.T) { t.Fatal(err) } - // Create results + // Create results. results, err := NewResults(nImages) if err != nil { t.Fatal(err)