diff --git a/go.mod b/go.mod index c7ff71ba..16f5c3c3 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,7 @@ require ( github.com/pkg/errors v0.9.1 github.com/yobert/alsa v0.0.0-20180630182551-d38d89fa843e gocv.io/x/gocv v0.29.0 + gonum.org/v1/gonum v0.9.3 gonum.org/v1/plot v0.10.0 gopkg.in/natefinch/lumberjack.v2 v2.0.0 ) diff --git a/go.sum b/go.sum index 7e3b4731..7c028dfa 100644 --- a/go.sum +++ b/go.sum @@ -58,10 +58,8 @@ github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+ github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/kidoman/embd v0.0.0-20170508013040-d3d8c0c5c68d h1:dPUSr0RGzXAdsUTMtiyQ/2RBLIIwkv6jGnhxrufitvQ= github.com/kidoman/embd v0.0.0-20170508013040-d3d8c0c5c68d/go.mod h1:ACKj9jnzOzj1lw2ETilpFGK7L9dtJhAzT7T1OhAGtRQ= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/mattetti/audio v0.0.0-20180912171649-01576cde1f21 h1:Hc1iKlyxNHp3CV59G2E/qabUkHvEwOIJxDK0CJ7CRjA= github.com/mattetti/audio v0.0.0-20180912171649-01576cde1f21/go.mod h1:LlQmBGkOuV/SKzEDXBPKauvN2UqCgzXO2XjecTGj40s= @@ -149,7 +147,6 @@ gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY= gonum.org/v1/plot v0.10.0 h1:ymLukg4XJlQnYUJCp+coQq5M7BsUJFk6XQE4HPflwdw= gonum.org/v1/plot v0.10.0/go.mod h1:JWIHJ7U20drSQb/aDpTetJzfC1KlAPldJLpkSy88dvQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= diff --git a/turbidity/plot.go b/turbidity/plot.go index f0e49c13..4b336d92 100644 --- a/turbidity/plot.go +++ b/turbidity/plot.go @@ -30,74 +30,35 @@ import ( "gonum.org/v1/plot" "gonum.org/v1/plot/plotter" - "gonum.org/v1/plot/plotutil" "gonum.org/v1/plot/vg" ) -// standarDeviation will return the standard deviation of a float slice -func standarDeviation(slice []float64) float64 { - mean := average(slice) - variance := 0.0 - - for _, i := range slice { - variance += math.Pow(i-mean, 2.0) - } - return math.Sqrt(variance / float64(len(slice))) -} - -// Normalize values in a slice between 0 and 1. -func normalize(slice []float64) []float64 { +// normalise normalises the values in the given slice to the range [0,1] inclusive. +func normalize(s []float64) []float64 { max := -math.MaxFloat64 min := math.MaxFloat64 - out := make([]float64, len(slice)) + out := make([]float64, len(s)) - if len(slice) <= 1 { - return slice + if len(s) <= 1 { + return s } - // Find the max and min values of the slice. - for i := range slice { - if slice[i] > max { - max = slice[i] + // Find the max and min values of the s. + for i := range s { + if s[i] > max { + max = s[i] } - if slice[i] < min { - min = slice[i] + if s[i] < min { + min = s[i] } } - for i := range slice { - out[i] = (slice[i] - min) / (max - min) + for i := range s { + out[i] = (s[i] - min) / (max - min) } return out } -// Return the average of a slice. -func average(slice []float64) float64 { - var out float64 - for i := range slice { - out += slice[i] - } - return out / float64(len(slice)) -} - -func plotResults(x, sharpness, contrast []float64) error { - err := plotToFile( - "Results", - "Almond Milk (ml)", - "Score", - func(p *plot.Plot) error { - return plotutil.AddLinePoints(p, - "Contrast", plotterXY(x, contrast), - "Sharpness", plotterXY(x, sharpness), - ) - }, - ) - if err != nil { - return fmt.Errorf("Could not plot results: %w", err) - } - return nil -} - // plotToFile creates a plot with a specified name and x&y titles using the // provided draw function, and then saves to a PNG file with filename of name. func plotToFile(name, xTitle, yTitle string, draw func(*plot.Plot) error) error { diff --git a/turbidity/results.go b/turbidity/results.go index 0cd7b928..f988eabb 100644 --- a/turbidity/results.go +++ b/turbidity/results.go @@ -36,7 +36,7 @@ type Results struct { Contrast []float64 } -// NewResults returns a new Results +// NewResults returns a new Results. func NewResults(n int) (*Results, error) { if n <= 0 { return nil, fmt.Errorf("invalid result size: %v", n) diff --git a/turbidity/turbidity_test.go b/turbidity/turbidity_test.go index 830c4866..1bb51777 100644 --- a/turbidity/turbidity_test.go +++ b/turbidity/turbidity_test.go @@ -30,12 +30,15 @@ import ( "testing" "gocv.io/x/gocv" + "gonum.org/v1/gonum/stat" + "gonum.org/v1/plot" + "gonum.org/v1/plot/plotutil" ) const ( nImages = 13 // Number of images to test. (Max 13) 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. ) func TestImages(t *testing.T) { @@ -45,7 +48,6 @@ func TestImages(t *testing.T) { scale, alpha = 1.0, 1.0 ) - // Load template and standard image. template := gocv.IMRead("images/template.jpg", gocv.IMReadGrayScale) standard := gocv.IMRead("images/default.jpg", gocv.IMReadGrayScale) @@ -59,13 +61,11 @@ func TestImages(t *testing.T) { } } - // Create turbidity sensor. ts, err := NewTurbiditySensor(template, standard, k1, k2, filterSize, scale, alpha) if err != nil { t.Fatal("could not create turbidity sensor: %w", err) } - // Create results. results, err := NewResults(nImages) if err != nil { t.Fatal("could not create results: %w", err) @@ -80,10 +80,9 @@ func TestImages(t *testing.T) { } // Add the average result from camera burst. - results.Update(average(sample_result.Sharpness), average(sample_result.Contrast), float64(i)*increment, i) + results.Update(stat.Mean(sample_result.Sharpness, nil), stat.Mean(sample_result.Contrast, nil), float64(i)*increment, i) } - // Plot the final results. err = plotResults(results.Turbidity, normalize(results.Sharpness), normalize(results.Contrast)) if err != nil { t.Fatalf("plotting Failed: %v", err) @@ -92,3 +91,21 @@ func TestImages(t *testing.T) { t.Logf("Sharpness: %v", results.Sharpness) t.Logf("Contrast: %v", results.Contrast) } + +func plotResults(x, sharpness, contrast []float64) error { + err := plotToFile( + "Results", + "Almond Milk (ml)", + "Score", + func(p *plot.Plot) error { + return plotutil.AddLinePoints(p, + "Contrast", plotterXY(x, contrast), + "Sharpness", plotterXY(x, sharpness), + ) + }, + ) + if err != nil { + return fmt.Errorf("Could not plot results: %w", err) + } + return nil +}