mirror of https://bitbucket.org/ausocean/av.git
turbidity: updated naming convention and comments per comments on PR
This commit is contained in:
parent
fe0c9ffcdb
commit
445ab1d785
|
@ -226,7 +226,10 @@ func run(rv *revid.Revid, ns *netsender.Sender, l *logger.Logger, nl *netlogger.
|
||||||
l.Log(logger.Info, "revid successfully reconfigured")
|
l.Log(logger.Info, "revid successfully reconfigured")
|
||||||
|
|
||||||
// Update transform matrix based on new revid variables.
|
// Update transform matrix based on new revid variables.
|
||||||
p.Update(rv.Config())
|
err = p.Update(rv.Config().TransformMatrix)
|
||||||
|
if err != nil {
|
||||||
|
l.Log(logger.Error, "could not update turbidity probe", "error", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
l.Log(logger.Debug, "checking mode")
|
l.Log(logger.Debug, "checking mode")
|
||||||
switch ns.Mode() {
|
switch ns.Mode() {
|
||||||
|
|
|
@ -40,7 +40,6 @@ import (
|
||||||
"gonum.org/v1/gonum/stat"
|
"gonum.org/v1/gonum/stat"
|
||||||
|
|
||||||
"bitbucket.org/ausocean/av/codec/h264"
|
"bitbucket.org/ausocean/av/codec/h264"
|
||||||
"bitbucket.org/ausocean/av/revid/config"
|
|
||||||
"bitbucket.org/ausocean/av/turbidity"
|
"bitbucket.org/ausocean/av/turbidity"
|
||||||
"bitbucket.org/ausocean/utils/logger"
|
"bitbucket.org/ausocean/utils/logger"
|
||||||
)
|
)
|
||||||
|
@ -50,6 +49,7 @@ const (
|
||||||
maxImages = 1 // Max number of images read when evaluating turbidity.
|
maxImages = 1 // Max number of images read when evaluating turbidity.
|
||||||
bufferLimit = 20000 // 20KB
|
bufferLimit = 20000 // 20KB
|
||||||
trimTolerance = 200 // Number of times trim can be called where no keyframe is found.
|
trimTolerance = 200 // Number of times trim can be called where no keyframe is found.
|
||||||
|
transformSize = 9 // Size of the square projective matrix.
|
||||||
)
|
)
|
||||||
|
|
||||||
// Turbidity sensor constants.
|
// Turbidity sensor constants.
|
||||||
|
@ -81,12 +81,12 @@ func NewTurbidityProbe(log logger.Logger, delay time.Duration) (*turbidityProbe,
|
||||||
tp.ticker = *time.NewTicker(delay)
|
tp.ticker = *time.NewTicker(delay)
|
||||||
tp.buffer = bytes.NewBuffer(*new([]byte))
|
tp.buffer = bytes.NewBuffer(*new([]byte))
|
||||||
|
|
||||||
tp.transform = []float64{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}
|
tp.transform = make([]float64, transformSize)
|
||||||
H := floatToMat(tp.transform)
|
transformMatrix := floatToMat(tp.transform)
|
||||||
|
|
||||||
// Create the turbidity sensor.
|
// Create the turbidity sensor.
|
||||||
template := gocv.IMRead("../../turbidity/images/template.jpg", gocv.IMReadGrayScale)
|
template := gocv.IMRead("../../turbidity/images/template.jpg", gocv.IMReadGrayScale)
|
||||||
ts, err := turbidity.NewTurbiditySensor(template, H, k1, k2, filterSize, scale, alpha, log)
|
ts, err := turbidity.NewTurbiditySensor(template, transformMatrix, k1, k2, filterSize, scale, alpha, log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create turbidity sensor: %w", err)
|
return nil, fmt.Errorf("failed to create turbidity sensor: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -145,20 +145,21 @@ func (tp *turbidityProbe) Close() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update, will update the probe and turbidity sensor with the new transformation matrix if it has been changed.
|
// Update will update the probe and turbidity sensor with the new transformation matrix if it has been changed.
|
||||||
func (tp *turbidityProbe) Update(config config.Config) error {
|
func (tp *turbidityProbe) Update(transformMatrix []float64) error {
|
||||||
if len(config.TransformMatrix) != 9 {
|
if len(transformMatrix) != transformSize {
|
||||||
return errors.New("transformation matrix has incorrect size")
|
return errors.New("transformation matrix has incorrect size")
|
||||||
}
|
}
|
||||||
for i := range tp.transform {
|
for i := range tp.transform {
|
||||||
if tp.transform[i] != config.TransformMatrix[i] {
|
if tp.transform[i] == transformMatrix[i] {
|
||||||
// Update the turbidity sensor with new transformation
|
continue
|
||||||
tp.log.Log(logger.Debug, "updating the transformation matrix")
|
|
||||||
tp.transform = config.TransformMatrix
|
|
||||||
newTransform := floatToMat(tp.transform)
|
|
||||||
tp.ts.H = newTransform
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
// Update the turbidity sensor with new transformation.
|
||||||
|
tp.log.Log(logger.Debug, "updating the transformation matrix")
|
||||||
|
tp.transform = transformMatrix
|
||||||
|
newTransform := floatToMat(tp.transform)
|
||||||
|
tp.ts.TransformMatrix = newTransform
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
tp.log.Log(logger.Debug, "no change to the transformation matrix")
|
tp.log.Log(logger.Debug, "no change to the transformation matrix")
|
||||||
return nil
|
return nil
|
||||||
|
@ -234,11 +235,11 @@ func cleanUp(file string, vc *gocv.VideoCapture) error {
|
||||||
|
|
||||||
// floatToMat will convert a slice of 9 floats to a gocv.Mat.
|
// floatToMat will convert a slice of 9 floats to a gocv.Mat.
|
||||||
func floatToMat(array []float64) gocv.Mat {
|
func floatToMat(array []float64) gocv.Mat {
|
||||||
H := gocv.NewMatWithSize(3, 3, gocv.MatTypeCV64F)
|
mat := gocv.NewMatWithSize(3, 3, gocv.MatTypeCV64F)
|
||||||
for i := 0; i < H.Rows(); i++ {
|
for i := 0; i < mat.Rows(); i++ {
|
||||||
for j := 0; j < H.Cols(); j++ {
|
for j := 0; j < mat.Cols(); j++ {
|
||||||
H.SetDoubleAt(i, j, array[i*H.Cols()+j])
|
mat.SetDoubleAt(i, j, array[i*mat.Cols()+j])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return H
|
return mat
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,6 @@ package main
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"bitbucket.org/ausocean/av/revid/config"
|
|
||||||
"bitbucket.org/ausocean/utils/logger"
|
"bitbucket.org/ausocean/utils/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -51,7 +50,7 @@ func (tp *turbidityProbe) Write(p []byte) (int, error) {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tp *turbidityProbe) Update(config config.Config) error {
|
func (tp *turbidityProbe) Update(mat []float64) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,6 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"bitbucket.org/ausocean/av/revid/config"
|
|
||||||
"bitbucket.org/ausocean/utils/logger"
|
"bitbucket.org/ausocean/utils/logger"
|
||||||
"gopkg.in/natefinch/lumberjack.v2"
|
"gopkg.in/natefinch/lumberjack.v2"
|
||||||
)
|
)
|
||||||
|
@ -46,14 +45,13 @@ func TestProbe(t *testing.T) {
|
||||||
}
|
}
|
||||||
log := logger.New(logVerbosity, io.MultiWriter(fileLog), logSuppress)
|
log := logger.New(logVerbosity, io.MultiWriter(fileLog), logSuppress)
|
||||||
updatedMatrix := []float64{-0.2731048063, -0.0020501869, 661.0275911942, 0.0014327789, -0.2699443748, 339.3921028016, 0.0000838317, 0.0000476486, 1.0}
|
updatedMatrix := []float64{-0.2731048063, -0.0020501869, 661.0275911942, 0.0014327789, -0.2699443748, 339.3921028016, 0.0000838317, 0.0000476486, 1.0}
|
||||||
config := config.Config{TransformMatrix: updatedMatrix}
|
|
||||||
|
|
||||||
ts, err := NewTurbidityProbe(*log, time.Microsecond)
|
ts, err := NewTurbidityProbe(*log, time.Microsecond)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create turbidity probe")
|
t.Fatalf("failed to create turbidity probe")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ts.Update(config)
|
err = ts.Update(updatedMatrix)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("could not update probe: %v", err)
|
t.Fatalf("could not update probe: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -69,17 +67,3 @@ func TestProbe(t *testing.T) {
|
||||||
}
|
}
|
||||||
t.Logf("contrast: %v, sharpness: %v\n", ts.contrast, ts.sharpness)
|
t.Logf("contrast: %v, sharpness: %v\n", ts.contrast, ts.sharpness)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
func printMat(mat gocv.Mat) {
|
|
||||||
for i := 0; i < mat.Rows(); i++ {
|
|
||||||
for j := 0; j < mat.Cols(); j++ {
|
|
||||||
fmt.Printf(" %.10f", mat.GetDoubleAt(i, j))
|
|
||||||
if i < 2 || j < 2 {
|
|
||||||
fmt.Print(",")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Println()
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
|
@ -274,7 +274,9 @@ type Config struct {
|
||||||
VerticalFlip bool // VerticalFlip flips video vertically for Raspivid input.
|
VerticalFlip bool // VerticalFlip flips video vertically for Raspivid input.
|
||||||
Width uint // Width defines the input video width Raspivid input.
|
Width uint // Width defines the input video width Raspivid input.
|
||||||
|
|
||||||
TransformMatrix []float64 // Describes the transformation matrix to extract the target.
|
// TransformMatrix describes the projective transformation matrix to extract a target from the
|
||||||
|
// video data for turbidty calculations.
|
||||||
|
TransformMatrix []float64
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate checks for any errors in the config fields and defaults settings
|
// Validate checks for any errors in the config fields and defaults settings
|
||||||
|
|
|
@ -29,52 +29,45 @@ package turbidity
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"image"
|
"image"
|
||||||
|
|
||||||
"gocv.io/x/gocv"
|
"gocv.io/x/gocv"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Homography constants
|
// Perspective transformation constants.
|
||||||
const (
|
const (
|
||||||
ransacThreshold = 3.0 // Maximum allowed reprojection error to treat a point pair as an inlier.
|
ransacThreshold = 3.0 // Maximum allowed reprojection error to treat a point pair as an inlier.
|
||||||
maxIter = 2000 // The maximum number of RANSAC iterations.
|
maxIter = 2000 // The maximum number of RANSAC iterations.
|
||||||
confidence = 0.995 // Confidence level, between 0 and 1.
|
confidence = 0.995 // Confidence level, between 0 and 1.
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// FindTransform, given a template and standard image the perspetive transformation matrix will be determined.
|
||||||
|
// the matrix will be returned and logged for use in vidgrind.
|
||||||
func FindTransform(standardPath, templatePath string) (gocv.Mat, error) {
|
func FindTransform(standardPath, templatePath string) (gocv.Mat, error) {
|
||||||
mask := gocv.NewMat()
|
mask := gocv.NewMat()
|
||||||
standard := gocv.IMRead(standardPath, gocv.IMReadColor)
|
std := gocv.IMRead(standardPath, gocv.IMReadColor)
|
||||||
standardCorners := gocv.NewMat()
|
stdCorners := gocv.NewMat()
|
||||||
|
|
||||||
template := gocv.IMRead(templatePath, gocv.IMReadGrayScale)
|
template := gocv.IMRead(templatePath, gocv.IMReadGrayScale)
|
||||||
templateCorners := gocv.NewMat()
|
templateCorners := gocv.NewMat()
|
||||||
|
transformMatrix := gocv.NewMat()
|
||||||
|
|
||||||
// Validate template image is not empty and has valid corners.
|
// Validate template image is not empty and has valid corners.
|
||||||
if template.Empty() {
|
if template.Empty() {
|
||||||
return gocv.NewMat(), errors.New("template image is empty")
|
return transformMatrix, errors.New("template image is empty")
|
||||||
}
|
}
|
||||||
if !gocv.FindChessboardCorners(template, image.Pt(3, 3), &templateCorners, gocv.CalibCBNormalizeImage) {
|
if !gocv.FindChessboardCorners(template, image.Pt(3, 3), &templateCorners, gocv.CalibCBNormalizeImage) {
|
||||||
return gocv.NewMat(), errors.New("could not find corners in template image")
|
return transformMatrix, errors.New("could not find corners in template image")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate standard image is not empty and has valid corners.
|
// Validate standard image is not empty and has valid corners.
|
||||||
if standard.Empty() {
|
if std.Empty() {
|
||||||
return gocv.NewMat(), errors.New("standard image is empty")
|
return transformMatrix, errors.New("standard image is empty")
|
||||||
}
|
}
|
||||||
if !gocv.FindChessboardCorners(standard, image.Pt(3, 3), &standardCorners, gocv.CalibCBNormalizeImage) {
|
if !gocv.FindChessboardCorners(std, image.Pt(3, 3), &stdCorners, gocv.CalibCBNormalizeImage) {
|
||||||
return gocv.NewMat(), errors.New("could not find corners in standard image")
|
return transformMatrix, errors.New("could not find corners in standard image")
|
||||||
}
|
}
|
||||||
|
|
||||||
H := gocv.FindHomography(standardCorners, &templateCorners, gocv.HomograpyMethodRANSAC, ransacThreshold, &mask, maxIter, confidence)
|
transformMatrix = gocv.FindHomography(stdCorners, &templateCorners, gocv.HomograpyMethodRANSAC, ransacThreshold, &mask, maxIter, confidence)
|
||||||
for i := 0; i < H.Rows(); i++ {
|
return transformMatrix, nil
|
||||||
for j := 0; j < H.Cols(); j++ {
|
|
||||||
fmt.Printf(" %.10f", H.GetDoubleAt(i, j))
|
|
||||||
if i < 2 || j < 2 {
|
|
||||||
fmt.Print(",")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Println()
|
|
||||||
return H, nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,15 +44,15 @@ import (
|
||||||
// TurbiditySensor is a software based turbidity sensor that uses CV to determine sharpness and constrast level
|
// TurbiditySensor is a software based turbidity sensor that uses CV to determine sharpness and constrast level
|
||||||
// of a chessboard-like target submerged in water that can be correlated to turbidity/visibility values.
|
// of a chessboard-like target submerged in water that can be correlated to turbidity/visibility values.
|
||||||
type TurbiditySensor struct {
|
type TurbiditySensor struct {
|
||||||
template gocv.Mat
|
template gocv.Mat // Holds the image of the target.
|
||||||
H gocv.Mat
|
TransformMatrix gocv.Mat // The current perspective transformation matrix to extract the target from the frame.
|
||||||
k1, k2, sobelFilterSize int
|
k1, k2, sobelFilterSize int
|
||||||
scale, alpha float64
|
scale, alpha float64
|
||||||
log logger.Logger
|
log logger.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTurbiditySensor returns a new TurbiditySensor.
|
// NewTurbiditySensor returns a new TurbiditySensor.
|
||||||
func NewTurbiditySensor(template, H gocv.Mat, k1, k2, sobelFilterSize int, scale, alpha float64, log logger.Logger) (*TurbiditySensor, error) {
|
func NewTurbiditySensor(template, transformMatrix gocv.Mat, k1, k2, sobelFilterSize int, scale, alpha float64, log logger.Logger) (*TurbiditySensor, error) {
|
||||||
ts := new(TurbiditySensor)
|
ts := new(TurbiditySensor)
|
||||||
|
|
||||||
// Validate template image is not empty and has valid corners.
|
// Validate template image is not empty and has valid corners.
|
||||||
|
@ -61,7 +61,7 @@ func NewTurbiditySensor(template, H gocv.Mat, k1, k2, sobelFilterSize int, scale
|
||||||
}
|
}
|
||||||
|
|
||||||
ts.template = template
|
ts.template = template
|
||||||
ts.H = H
|
ts.TransformMatrix = transformMatrix
|
||||||
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.log = log
|
ts.log = log
|
||||||
|
@ -184,7 +184,7 @@ func (ts TurbiditySensor) transform(img gocv.Mat) (gocv.Mat, error) {
|
||||||
// if !gocv.FindChessboardCorners(img, image.Pt(3, 3), &imgCorners, gocv.CalibCBFastCheck) {}
|
// if !gocv.FindChessboardCorners(img, image.Pt(3, 3), &imgCorners, gocv.CalibCBFastCheck) {}
|
||||||
|
|
||||||
// Find and apply transformation.
|
// Find and apply transformation.
|
||||||
gocv.WarpPerspective(img, &out, ts.H, image.Pt(ts.template.Rows(), ts.template.Cols()))
|
gocv.WarpPerspective(img, &out, ts.TransformMatrix, image.Pt(ts.template.Rows(), ts.template.Cols()))
|
||||||
gocv.CvtColor(out, &out, gocv.ColorRGBToGray)
|
gocv.CvtColor(out, &out, gocv.ColorRGBToGray)
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
nImages = 12 // Number of images to test. (Max 13)
|
nImages = 13 // Number of images to test. (Max 13)
|
||||||
nSamples = 10 // Number of samples for each image. (Max 10)
|
nSamples = 10 // Number of samples for each image. (Max 10)
|
||||||
increment = 2.5 // Increment of the turbidity level.
|
increment = 2.5 // Increment of the turbidity level.
|
||||||
)
|
)
|
||||||
|
@ -77,10 +77,11 @@ func TestImages(t *testing.T) {
|
||||||
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)
|
||||||
H, err := FindTransform("images/default.jpg", "images/template.jpg")
|
transformMatrix, err := FindTransform("images/default.jpg", "images/template.jpg")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("could not find transformation: %v", err)
|
t.Fatalf("could not find transformation: %v", err)
|
||||||
}
|
}
|
||||||
|
t.Log(formatMat(transformMatrix))
|
||||||
|
|
||||||
imgs := make([][]gocv.Mat, nImages)
|
imgs := make([][]gocv.Mat, nImages)
|
||||||
|
|
||||||
|
@ -92,7 +93,7 @@ func TestImages(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ts, err := NewTurbiditySensor(template, H, k1, k2, filterSize, scale, alpha, log)
|
ts, err := NewTurbiditySensor(template, transformMatrix, 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)
|
||||||
}
|
}
|
||||||
|
@ -141,3 +142,17 @@ func plotResults(x, sharpness, contrast []float64) error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// formatMat creates a formatted transformation matrix string for use in vidgrind.
|
||||||
|
func formatMat(transformMatrix gocv.Mat) string {
|
||||||
|
var out string
|
||||||
|
for i := 0; i < transformMatrix.Rows(); i++ {
|
||||||
|
for j := 0; j < transformMatrix.Cols(); j++ {
|
||||||
|
out += fmt.Sprintf(" %.10f", transformMatrix.GetDoubleAt(i, j))
|
||||||
|
if i < 2 || j < 2 {
|
||||||
|
out += ","
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue