// +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" "io" "bitbucket.org/ausocean/av/revid/config" "gocv.io/x/gocv" ) const defaultDiffThreshold = 3 // 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 { debugging debugWindows dst io.WriteCloser thresh float64 prev gocv.Mat } // NewDifference returns a pointer to a new Difference struct. func NewDifference(dst io.WriteCloser, c config.Config) *Difference { // Validate parameters. if c.MotionThreshold <= 0 { c.LogInvalidField("MotionThreshold", defaultDiffThreshold) c.MotionThreshold = defaultDiffThreshold } return &Difference{ dst: dst, thresh: c.MotionThreshold, prev: gocv.NewMat(), debugging: newWindows("DIFF"), } } // 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() d.debugging.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. d.debugging.show(img, imgDelta, mean > d.thresh, nil, fmt.Sprintf("Mean: %f", mean), fmt.Sprintf("Threshold: %f", d.thresh)) // 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) }