KNN filter detects motion on an interval

This commit is contained in:
Scott 2020-01-23 12:43:09 +10:30
parent 65bc98efbf
commit 207e96b999
2 changed files with 28 additions and 11 deletions

View File

@ -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)
}

View File

@ -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")
}