diff --git a/filter/debug.go b/filter/debug.go new file mode 100644 index 00000000..a39d6883 --- /dev/null +++ b/filter/debug.go @@ -0,0 +1,70 @@ +// +build debug +// +build !circleci + +/* +DESCRIPTION + Displays debug information for the motion filters. + +AUTHORS + Scott Barnard + +LICENSE + This file is Copyright (C) 2020 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 filter + +import ( + "image" + "image/color" + + "gocv.io/x/gocv" +) + +// debugWindows is used for displaying debug information for the motion filters. +type debugWindows struct { + windows []*gocv.Window +} + +// closeWindows frees resources used by gocv. +func (d *debugWindows) closeWindows() { + for _, window := range d.windows { + window.Close() + } +} + +// newWindows creates debugging windows for the motion filter. +func (d *debugWindows) newWindows(name string) { + d.windows = []*gocv.Window{gocv.NewWindow(name + ": Bounding boxes"), gocv.NewWindow(name + ": Motion")} +} + +// showDebug displays debug information for the motion filters. +func (d *debugWindows) showDebug(img, imgDelta gocv.Mat, motion bool, contours ...[][]image.Point) { + if len(contours) > 0 { + for _, c := range contours[0] { + rect := gocv.BoundingRect(c) + gocv.Rectangle(&img, rect, color.RGBA{0, 0, 255, 0}, 1) + } + } + + if motion { + gocv.PutText(&img, "Motion", image.Pt(32, 32), gocv.FontHersheyPlain, 2.0, color.RGBA{255, 0, 0, 0}, 2) + } + + d.windows[0].IMShow(img) + d.windows[1].IMShow(imgDelta) + d.windows[0].WaitKey(1) +} diff --git a/filter/difference.go b/filter/difference.go index d01cf0a7..ff3a9751 100644 --- a/filter/difference.go +++ b/filter/difference.go @@ -30,9 +30,6 @@ LICENSE package filter import ( - "fmt" - "image" - "image/color" "io" "gocv.io/x/gocv" @@ -42,20 +39,21 @@ import ( // the absolute difference for each pixel between two frames, then finds the mean. If // the mean is above a given threshold, then it is considered motion. type Difference struct { - dst io.WriteCloser - thresh float64 - prev gocv.Mat - debug bool - windows []*gocv.Window + debugWindows + dst io.WriteCloser + thresh float64 + prev gocv.Mat } // NewDifference returns a pointer to a new Difference struct. -func NewDifference(dst io.WriteCloser, debug bool, threshold float64) *Difference { - var windows []*gocv.Window - if debug { - windows = []*gocv.Window{gocv.NewWindow("Diff: Bounding boxes"), gocv.NewWindow("Diff: Motion")} +func NewDifference(dst io.WriteCloser, threshold float64) *Difference { + d := &Difference{ + dst: dst, + thresh: threshold, + prev: gocv.NewMat(), } - return &Difference{dst, threshold, gocv.NewMat(), debug, windows} + d.newWindows("DIFF") + return d } // Implements io.Closer. @@ -63,9 +61,7 @@ func NewDifference(dst io.WriteCloser, debug bool, threshold float64) *Differenc // it using c-go. func (d *Difference) Close() error { d.prev.Close() - for _, window := range d.windows { - window.Close() - } + d.closeWindows() return nil } @@ -101,23 +97,7 @@ func (d *Difference) Write(f []byte) (int, error) { d.prev = img.Clone() // Draw debug information. - if d.debug { - if mean >= d.thresh { - gocv.PutText( - &img, - fmt.Sprintf("motion - mean:%f", mean), - image.Pt(32, 32), - gocv.FontHersheyPlain, - 2.0, - color.RGBA{255, 0, 0, 0}, - 2, - ) - } - - d.windows[0].IMShow(img) - d.windows[1].IMShow(imgDelta) - d.windows[0].WaitKey(1) - } + d.showDebug(img, imgDelta, mean > d.thresh) // Don't write to destination if there is no motion. if mean < d.thresh { diff --git a/filter/filters_circleci.go b/filter/filters_circleci.go index 2e119fe5..73fe02c1 100644 --- a/filter/filters_circleci.go +++ b/filter/filters_circleci.go @@ -32,16 +32,16 @@ import ( ) // NewMOG returns a pointer to a new NoOp struct for testing purposes only. -func NewMOG(dst io.WriteCloser, area, threshold float64, history int, debug bool, hf int, scaleFactor int) *NoOp { +func NewMOG(dst io.WriteCloser, area, threshold float64, history int, hf int, scaleFactor int) *NoOp { return &NoOp{dst: dst} } // NewKNN returns a pointer to a new NoOp struct for testing purposes only. -func NewKNN(dst io.WriteCloser, area, threshold float64, history, kernelSize int, debug bool, hf int, scaleFactor int) *NoOp { +func NewKNN(dst io.WriteCloser, area, threshold float64, history, kernelSize int, hf int, scaleFactor int) *NoOp { return &NoOp{dst: dst} } // NewDiffference returns a pointer to a new NoOp struct for testing purposes only. -func NewDifference(dst io.WriteCloser, debug bool, threshold float64) *NoOp { +func NewDifference(dst io.WriteCloser, threshold float64) *NoOp { return &NoOp{dst: dst} } diff --git a/filter/knn.go b/filter/knn.go index fdb01937..1624f6b6 100644 --- a/filter/knn.go +++ b/filter/knn.go @@ -31,7 +31,6 @@ package filter import ( "fmt" "image" - "image/color" "io" "gocv.io/x/gocv" @@ -40,12 +39,11 @@ import ( // KNN is a filter that provides basic motion detection. KNN is short for // K-Nearest Neighbours method. type KNN struct { + debugWindows dst io.WriteCloser // Destination to which motion containing frames go. area float64 // The minimum area that a contour can be found in. bs *gocv.BackgroundSubtractorKNN // Uses the KNN algorithm to find the difference between the current and background frame. knl gocv.Mat // Matrix that is used for calculations. - debug bool // If true then debug windows with the bounding boxes and difference will be shown on the screen. - windows []*gocv.Window // Holds debug windows. hold [][]byte // Will hold all frames up to hf (so only every hf frame is motion detected). hf int // The number of frames to be held. hfCount int // Counter for the hold array. @@ -53,14 +51,20 @@ type KNN struct { } // NewKNN returns a pointer to a new KNN filter struct. -func NewKNN(dst io.WriteCloser, area, threshold float64, history, kernelSize int, debug bool, hf int, scaleFactor int) *KNN { +func NewKNN(dst io.WriteCloser, area, threshold float64, history, kernelSize int, hf int, scaleFactor int) *KNN { bs := gocv.NewBackgroundSubtractorKNNWithParams(history, threshold, false) k := gocv.GetStructuringElement(gocv.MorphRect, image.Pt(kernelSize, kernelSize)) - var windows []*gocv.Window - if debug { - windows = []*gocv.Window{gocv.NewWindow("KNN: Bounding boxes"), gocv.NewWindow("KNN: Motion")} + m := &KNN{ + dst: dst, + area: area, + bs: &bs, + knl: k, + hold: make([][]byte, hf-1), + hf: hf, + scale: 1 / float64(scaleFactor), } - return &KNN{dst, area, &bs, k, debug, windows, make([][]byte, hf-1), hf, 0, 1 / float64(scaleFactor)} + m.newWindows("KNN") + return m } // Implements io.Closer. @@ -69,9 +73,7 @@ func NewKNN(dst io.WriteCloser, area, threshold float64, history, kernelSize int func (m *KNN) Close() error { m.bs.Close() m.knl.Close() - for _, window := range m.windows { - window.Close() - } + m.closeWindows() return nil } @@ -122,20 +124,7 @@ func (m *KNN) Write(f []byte) (int, error) { } // Draw debug information. - if m.debug { - for _, c := range contours { - rect := gocv.BoundingRect(c) - gocv.Rectangle(&img, rect, color.RGBA{0, 0, 255, 0}, 1) - } - - if len(contours) > 0 { - gocv.PutText(&img, "Motion", image.Pt(32, 32), gocv.FontHersheyPlain, 2.0, color.RGBA{255, 0, 0, 0}, 2) - } - - m.windows[0].IMShow(img) - m.windows[1].IMShow(imgDelta) - m.windows[0].WaitKey(1) - } + m.showDebug(img, imgDelta, len(contours) > 0, contours) // Don't write to destination if there is no motion. if len(contours) == 0 { diff --git a/filter/mog.go b/filter/mog.go index 7971452c..ebd25644 100644 --- a/filter/mog.go +++ b/filter/mog.go @@ -31,7 +31,6 @@ package filter import ( "fmt" "image" - "image/color" "io" "gocv.io/x/gocv" @@ -40,12 +39,11 @@ import ( // MOG is a filter that provides basic motion detection. MoG is short for // Mixture of Gaussians method. type MOG struct { + debugWindows dst io.WriteCloser // Destination to which motion containing frames go. area float64 // The minimum area that a contour can be found in. bs *gocv.BackgroundSubtractorMOG2 // Uses the MOG algorithm to find the difference between the current and background frame. knl gocv.Mat // Matrix that is used for calculations. - debug bool // If true then debug windows with the bounding boxes and difference will be shown on the screen. - windows []*gocv.Window // Holds debug windows. hold [][]byte // Will hold all frames up to hf (so only every hf frame is motion detected). hf int // The number of frames to be held. hfCount int // Counter for the hold array. @@ -53,14 +51,20 @@ type MOG struct { } // NewMOG returns a pointer to a new MOG filter struct. -func NewMOG(dst io.WriteCloser, area, threshold float64, history int, debug bool, hf int, scaleFactor int) *MOG { +func NewMOG(dst io.WriteCloser, area, threshold float64, history int, hf int, scaleFactor int) *MOG { bs := gocv.NewBackgroundSubtractorMOG2WithParams(history, threshold, false) k := gocv.GetStructuringElement(gocv.MorphRect, image.Pt(3, 3)) - var windows []*gocv.Window - if debug { - windows = []*gocv.Window{gocv.NewWindow("MOG: Bounding boxes"), gocv.NewWindow("MOG: Motion")} + m := &MOG{ + dst: dst, + area: area, + bs: &bs, + knl: k, + hold: make([][]byte, hf-1), + hf: hf, + scale: 1 / float64(scaleFactor), } - return &MOG{dst, area, &bs, k, debug, windows, make([][]byte, hf-1), hf, 0, 1 / float64(scaleFactor)} + m.newWindows("MOG") + return m } // Implements io.Closer. @@ -69,9 +73,7 @@ func NewMOG(dst io.WriteCloser, area, threshold float64, history int, debug bool func (m *MOG) Close() error { m.bs.Close() m.knl.Close() - for _, window := range m.windows { - window.Close() - } + m.closeWindows() return nil } @@ -122,20 +124,7 @@ func (m *MOG) Write(f []byte) (int, error) { } // Draw debug information. - if m.debug { - for _, c := range contours { - rect := gocv.BoundingRect(c) - gocv.Rectangle(&img, rect, color.RGBA{0, 0, 255, 0}, 1) - } - - if len(contours) > 0 { - gocv.PutText(&img, "Motion", image.Pt(32, 32), gocv.FontHersheyPlain, 2.0, color.RGBA{255, 0, 0, 0}, 2) - } - - m.windows[0].IMShow(img) - m.windows[1].IMShow(imgDelta) - m.windows[0].WaitKey(1) - } + m.showDebug(img, imgDelta, len(contours) > 0, contours) // Don't write to destination if there is no motion. if len(contours) == 0 { diff --git a/filter/release.go b/filter/release.go new file mode 100644 index 00000000..51b1d65e --- /dev/null +++ b/filter/release.go @@ -0,0 +1,46 @@ +// +build !debug +// +build !circleci + +/* +DESCRIPTION + Displays debug information for the motion filters. + +AUTHORS + Scott Barnard + +LICENSE + This file is Copyright (C) 2020 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 filter + +import ( + "image" + + "gocv.io/x/gocv" +) + +// debugWindows is used for displaying debug information for the motion filters. +type debugWindows struct{} + +// closeWindows frees resources used by gocv. +func (d *debugWindows) closeWindows() {} + +// newWindows creates debugging windows for the motion filter. +func (d *debugWindows) newWindows(name string) {} + +// showDebug displays debug information for the motion filters. +func (d *debugWindows) showDebug(img, imgDelta gocv.Mat, motion bool, contours ...[][]image.Point) {} diff --git a/revid/revid.go b/revid/revid.go index faac33a9..72123d6c 100644 --- a/revid/revid.go +++ b/revid/revid.go @@ -341,15 +341,15 @@ func (r *Revid) setupPipeline(mtsEnc func(dst io.WriteCloser, rate float64) (io. case config.FilterNoOp: r.filters[i] = filter.NewNoOp(dst) case config.FilterMOG: - r.filters[i] = filter.NewMOG(dst, r.cfg.MOGMinArea, r.cfg.MOGThreshold, int(r.cfg.MOGHistory), r.cfg.ShowWindows, r.cfg.MotionInterval, r.cfg.MotionDownscaling) + r.filters[i] = filter.NewMOG(dst, r.cfg.MOGMinArea, r.cfg.MOGThreshold, int(r.cfg.MOGHistory), r.cfg.MotionInterval, r.cfg.MotionDownscaling) case config.FilterVariableFPS: - r.filters[i] = filter.NewVariableFPS(dst, r.cfg.MinFPS, filter.NewMOG(dst, r.cfg.MOGMinArea, r.cfg.MOGThreshold, int(r.cfg.MOGHistory), r.cfg.ShowWindows, r.cfg.MotionInterval, r.cfg.MotionDownscaling)) + r.filters[i] = filter.NewVariableFPS(dst, r.cfg.MinFPS, filter.NewMOG(dst, r.cfg.MOGMinArea, r.cfg.MOGThreshold, int(r.cfg.MOGHistory), r.cfg.MotionInterval, r.cfg.MotionDownscaling)) case config.FilterKNN: - r.filters[i] = filter.NewKNN(dst, r.cfg.KNNMinArea, r.cfg.KNNThreshold, int(r.cfg.KNNHistory), int(r.cfg.KNNKernel), r.cfg.ShowWindows, r.cfg.MotionInterval, r.cfg.MotionDownscaling) + r.filters[i] = filter.NewKNN(dst, r.cfg.KNNMinArea, r.cfg.KNNThreshold, int(r.cfg.KNNHistory), int(r.cfg.KNNKernel), r.cfg.MotionInterval, r.cfg.MotionDownscaling) case config.FilterDifference: - r.filters[i] = filter.NewDifference(dst, r.cfg.ShowWindows, r.cfg.DiffThreshold) + r.filters[i] = filter.NewDifference(dst, r.cfg.DiffThreshold) case config.FilterBasic: - r.filters[i] = filter.NewBasic(dst, r.cfg.ShowWindows, r.cfg.BasicThreshold, r.cfg.BasicPixels) + r.filters[i] = filter.NewBasic(dst, false, r.cfg.BasicThreshold, r.cfg.BasicPixels) default: panic("Undefined Filter") }