// +build !circleci /* DESCRIPTION A filter that detects motion and discards frames without motion. The algorithm calculates 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. AUTHORS Scott Barnard LICENSE difference.go 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 ( "fmt" "image" "image/color" "io" "gocv.io/x/gocv" ) // Difference is a filter that provides basic motion detection. Difference calculates // 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 } // 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")} } return &Difference{dst, threshold, gocv.NewMat(), debug, windows} } // Implements io.Closer. // Close frees resources used by gocv, because it has to be done manually, due to // it using c-go. func (d *Difference) Close() error { d.prev.Close() for _, window := range d.windows { window.Close() } return nil } // Implements io.Writer. // Write applies the motion filter to the video stream. Only frames with motion // are written to the destination encoder, frames without are discarded. func (d *Difference) Write(f []byte) (int, error) { if d.prev.Empty() { var err error d.prev, err = gocv.IMDecode(f, gocv.IMReadColor) if err != nil { return 0, err } return len(f), nil } img, err := gocv.IMDecode(f, gocv.IMReadColor) defer img.Close() if err != nil { return 0, err } imgDelta := gocv.NewMat() defer imgDelta.Close() // Seperate foreground and background. gocv.AbsDiff(img, d.prev, &imgDelta) gocv.CvtColor(imgDelta, &imgDelta, gocv.ColorBGRToGray) mean := imgDelta.Mean().Val1 // Update History. 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) } // Don't write to destination if there is no motion. if mean < d.thresh { return len(f), nil } // Write to destination. return d.dst.Write(f) }