From 207e96b9999f213e6e657993752b4774b72f3996 Mon Sep 17 00:00:00 2001 From: Scott Date: Thu, 23 Jan 2020 12:43:09 +1030 Subject: [PATCH] KNN filter detects motion on an interval --- filter/knn.go | 37 +++++++++++++++++++++++++++---------- revid/revid.go | 2 +- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/filter/knn.go b/filter/knn.go index be733ec0..cc5a8a37 100644 --- a/filter/knn.go +++ b/filter/knn.go @@ -40,23 +40,26 @@ import ( // KNNFilter is a filter that provides basic motion detection. KNN is short for // K-Nearest Neighbours method. type KNNFilter struct { - dst io.WriteCloser - area float64 - bs *gocv.BackgroundSubtractorKNN - knl gocv.Mat - debug bool - windows []*gocv.Window + 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. } // NewKNNFilter returns a pointer to a new KNNFilter. -func NewKNNFilter(dst io.WriteCloser, area, threshold float64, history, kernelSize int, debug bool) *KNNFilter { +func NewKNNFilter(dst io.WriteCloser, area, threshold float64, history, kernelSize int, debug bool, hf int) *KNNFilter { 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")} } - return &KNNFilter{dst, area, &bs, k, debug, windows} + return &KNNFilter{dst, area, &bs, k, debug, windows, make([][]byte, hf-1), hf, 0} } // Implements io.Closer. @@ -75,6 +78,13 @@ func (m *KNNFilter) Close() error { // Write applies the motion filter to the video stream. Only frames with motion // are written to the destination encoder, frames without are discarded. func (m *KNNFilter) Write(f []byte) (int, error) { + if m.hfCount < (m.hf - 1) { + m.hold[m.hfCount] = f + m.hfCount++ + return len(f), nil + } + m.hfCount = 0 + img, err := gocv.IMDecode(f, gocv.IMReadColor) if err != nil { return 0, fmt.Errorf("can't decode image: %w", err) @@ -125,9 +135,16 @@ func (m *KNNFilter) Write(f []byte) (int, error) { // Don't write to destination if there is no motion. if len(contours) == 0 { - return -1, nil + return len(f), nil } - // Write to destination. + // Write to destination, past 4 frames then current frame. + for i, h := range m.hold { + _, err := m.dst.Write(h) + m.hold[i] = nil + if err != nil { + return len(f), fmt.Errorf("could not write previous frames: %w", err) + } + } return m.dst.Write(f) } diff --git a/revid/revid.go b/revid/revid.go index b4b83892..3ab6c704 100644 --- a/revid/revid.go +++ b/revid/revid.go @@ -341,7 +341,7 @@ func (r *Revid) setupPipeline(mtsEnc func(dst io.WriteCloser, rate float64) (io. case config.FilterVariableFPS: r.filters[i] = filter.NewVariableFPSFilter(dst, r.cfg.MinFPS, filter.NewMOGFilter(dst, r.cfg.MOGMinArea, r.cfg.MOGThreshold, int(r.cfg.MOGHistory), r.cfg.ShowWindows, r.cfg.MotionInterval)) case config.FilterKNN: - r.filters[i] = filter.NewKNNFilter(dst, r.cfg.KNNMinArea, r.cfg.KNNThreshold, int(r.cfg.KNNHistory), int(r.cfg.KNNKernel), r.cfg.ShowWindows) + r.filters[i] = filter.NewKNNFilter(dst, r.cfg.KNNMinArea, r.cfg.KNNThreshold, int(r.cfg.KNNHistory), int(r.cfg.KNNKernel), r.cfg.ShowWindows, r.cfg.MotionInterval) default: panic("Undefined Filter") }