From b39a4401051da56082d0d9e987deffc866956999 Mon Sep 17 00:00:00 2001 From: Ella Pietraroia Date: Thu, 2 Jan 2020 13:37:06 +1030 Subject: [PATCH 01/31] making mog filter that only dectects motion on every 5 frame (will make this a var) --- filter/mog.go | 118 ++++++++++++++++++++++++----------------- revid/config/config.go | 6 +++ revid/revid.go | 32 +++++++++-- 3 files changed, 103 insertions(+), 53 deletions(-) diff --git a/filter/mog.go b/filter/mog.go index 9c647bd2..5a2bb7ad 100644 --- a/filter/mog.go +++ b/filter/mog.go @@ -46,17 +46,22 @@ type MOGFilter struct { knl gocv.Mat debug bool windows []*gocv.Window + hold [][]byte + hf int } +var hfCount int = 0 + // NewMOGFilter returns a pointer to a new MOGFilter struct. -func NewMOGFilter(dst io.WriteCloser, area, threshold float64, history int, debug bool) *MOGFilter { +func NewMOGFilter(dst io.WriteCloser, area, threshold float64, history int, debug bool, hf int) *MOGFilter { 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")} } - return &MOGFilter{dst, area, &bs, k, debug, windows} + hold := make([][]byte, hf-1) + return &MOGFilter{dst, area, &bs, k, debug, windows, hold, hf} } // Implements io.Closer. @@ -75,59 +80,74 @@ func (m *MOGFilter) 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 *MOGFilter) Write(f []byte) (int, error) { - img, err := gocv.IMDecode(f, gocv.IMReadColor) - if err != nil { - return 0, fmt.Errorf("image can't be decoded: %w", err) - } - defer img.Close() - - imgDelta := gocv.NewMat() - defer imgDelta.Close() - - // Seperate foreground and background. - m.bs.Apply(img, &imgDelta) - - // Threshold imgDelta. - gocv.Threshold(imgDelta, &imgDelta, 25, 255, gocv.ThresholdBinary) - - // Remove noise. - gocv.Erode(imgDelta, &imgDelta, m.knl) - gocv.Dilate(imgDelta, &imgDelta, m.knl) - - // Fill small holes. - gocv.Dilate(imgDelta, &imgDelta, m.knl) - gocv.Erode(imgDelta, &imgDelta, m.knl) - - // Find contours and reject ones with a small area. - var contours [][]image.Point - allContours := gocv.FindContours(imgDelta, gocv.RetrievalExternal, gocv.ChainApproxSimple) - for _, c := range allContours { - if gocv.ContourArea(c) > m.area { - contours = append(contours, c) + if hfCount < (m.hf - 1) { + m.hold[hfCount] = f + hfCount++ + return -1, nil + } else { + img, err := gocv.IMDecode(f, gocv.IMReadColor) + if err != nil { + return 0, fmt.Errorf("image can't be decoded: %w", err) } - } + defer img.Close() - // 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) + imgDelta := gocv.NewMat() + defer imgDelta.Close() + + // Seperate foreground and background. + m.bs.Apply(img, &imgDelta) + + // Threshold imgDelta. + gocv.Threshold(imgDelta, &imgDelta, 25, 255, gocv.ThresholdBinary) + + // Remove noise. + gocv.Erode(imgDelta, &imgDelta, m.knl) + gocv.Dilate(imgDelta, &imgDelta, m.knl) + + // Fill small holes. + gocv.Dilate(imgDelta, &imgDelta, m.knl) + gocv.Erode(imgDelta, &imgDelta, m.knl) + + // Find contours and reject ones with a small area. + var contours [][]image.Point + allContours := gocv.FindContours(imgDelta, gocv.RetrievalExternal, gocv.ChainApproxSimple) + for _, c := range allContours { + if gocv.ContourArea(c) > m.area { + contours = append(contours, c) + } } - if len(contours) > 0 { - gocv.PutText(&img, "Motion", image.Pt(32, 32), gocv.FontHersheyPlain, 2.0, color.RGBA{255, 0, 0, 0}, 2) + // 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.windows[0].IMShow(img) - m.windows[1].IMShow(imgDelta) - m.windows[0].WaitKey(1) + // Don't write to destination if there is no motion. + if len(contours) == 0 { + return 0, nil + } + + // Write to destination, past 4 frames then current frame. + for _, h := range m.hold { + _, err := m.dst.Write(h) + if err != nil { + return 0, err + } + } + + hfCount = 0 + return m.dst.Write(f) } - // Don't write to destination if there is no motion. - if len(contours) == 0 { - return 0, nil - } - - // Write to destination. - return m.dst.Write(f) } diff --git a/revid/config/config.go b/revid/config/config.go index 8768e9db..b37fc759 100644 --- a/revid/config/config.go +++ b/revid/config/config.go @@ -85,6 +85,7 @@ const ( defaultClipDuration = 0 defaultAudioInputCodec = codecutil.ADPCM defaultPSITime = 2 + defaultFilterFrames = 1 // Ring buffer defaults. defaultRBMaxElements = 10000 @@ -275,6 +276,7 @@ type Config struct { HorizontalFlip bool // HorizontalFlip flips video horizontally for Raspivid input. VerticalFlip bool // VerticalFlip flips video vertically for Raspivid input. Filters []int // Defines the methods of filtering to be used in between lexing and encoding. + FilterFrames int // Sets the number of frames that are held before the filter is used (on the nth frame) PSITime int // Sets the time between a packet being sent. // Ring buffer parameters. @@ -460,6 +462,10 @@ func (c *Config) Validate() error { c.Logger.Log(logger.Info, pkg+"PSITime bad or unset, defaulting", "PSITime", defaultPSITime) c.PSITime = defaultPSITime } + if c.FilterFrames <= 0 { + c.Logger.Log(logger.Info, pkg+"FilterFrames bad or unset, defaulting", "FilterFrames", defaultFilterFrames) + c.FilterFrames = defaultFilterFrames + } if c.MinFPS <= 0 { c.Logger.Log(logger.Info, pkg+"MinFPS bad or unset, defaulting", "MinFPS", defaultMinFPS) diff --git a/revid/revid.go b/revid/revid.go index 07c317e4..f5398c72 100644 --- a/revid/revid.go +++ b/revid/revid.go @@ -63,6 +63,24 @@ const ( rtmpConnectionTimeout = 10 ) +// Motion filter parameters. +const minFPS = 1.0 + +// KNN specific parameters. +const ( + knnMinArea = 25.0 + knnThreshold = 300 + knnHistory = 300 + knnKernel = 9 +) + +// MOG specific parameters. +const ( + mogMinArea = 50 + mogThreshold = 100 + mogHistory = 100 +) + const pkg = "revid: " type Logger interface { @@ -337,17 +355,16 @@ 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.NewMOGFilter(dst, r.cfg.MOGMinArea, r.cfg.MOGThreshold, int(r.cfg.MOGHistory), r.cfg.ShowWindows) + r.filters[i] = filter.NewMOGFilter(dst, mogMinArea, mogThreshold, mogHistory, r.cfg.ShowWindows, r.cfg.FilterFrames) 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.filters[i] = filter.NewVariableFPSFilter(dst, minFPS, filter.NewMOGFilter(r.encoders, mogMinArea, mogThreshold, mogHistory, r.cfg.ShowWindows, r.cfg.FilterFrames)) 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, knnMinArea, knnThreshold, knnHistory, knnKernel, r.cfg.ShowWindows) default: panic("Undefined Filter") } dst = r.filters[i] } - } switch r.cfg.Input { @@ -671,6 +688,13 @@ func (r *Revid) Update(vars map[string]string) error { } r.cfg.Filters[i] = v } + case "FilterFrames": + v, err := strconv.Atoi(value) + if err != nil || v < 0 { + r.cfg.Logger.Log(logger.Warning, pkg+"invalid FilterFrames var", "value", value) + break + } + r.cfg.FilterFrames = v case "PSITime": v, err := strconv.Atoi(value) if err != nil || v < 0 { From 6f16f68611e6732303009f23a08f6650ac39f640 Mon Sep 17 00:00:00 2001 From: Ella Pietraroia Date: Thu, 2 Jan 2020 14:45:59 +1030 Subject: [PATCH 02/31] adding variable to choose how many frames to skip before filtering --- filter/mog.go | 4 ++-- revid/config/config.go | 8 ++++++++ revid/revid.go | 25 +++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/filter/mog.go b/filter/mog.go index 5a2bb7ad..cff19d3e 100644 --- a/filter/mog.go +++ b/filter/mog.go @@ -139,8 +139,9 @@ func (m *MOGFilter) Write(f []byte) (int, error) { } // Write to destination, past 4 frames then current frame. - for _, h := range m.hold { + for i, h := range m.hold { _, err := m.dst.Write(h) + m.hold[i] = nil if err != nil { return 0, err } @@ -149,5 +150,4 @@ func (m *MOGFilter) Write(f []byte) (int, error) { hfCount = 0 return m.dst.Write(f) } - } diff --git a/revid/config/config.go b/revid/config/config.go index b37fc759..ea1f3b16 100644 --- a/revid/config/config.go +++ b/revid/config/config.go @@ -273,11 +273,19 @@ type Config struct { Width uint // Width defines the input video width Raspivid input. Bitrate uint // Bitrate specifies the bitrate for constant bitrate in kbps. +<<<<<<< HEAD HorizontalFlip bool // HorizontalFlip flips video horizontally for Raspivid input. VerticalFlip bool // VerticalFlip flips video vertically for Raspivid input. Filters []int // Defines the methods of filtering to be used in between lexing and encoding. FilterFrames int // Sets the number of frames that are held before the filter is used (on the nth frame) PSITime int // Sets the time between a packet being sent. +======= + HorizontalFlip bool // HorizontalFlip flips video horizontally for Raspivid input. + VerticalFlip bool // VerticalFlip flips video vertically for Raspivid input. + Filter int // Defines the method of filtering to be used in between lexing and encoding. + FilterFrames int // Sets the number of frames that are held before the filter is used (on the nth frame) + PSITime int // Sets the time between a packet being sent +>>>>>>> d097308e... adding variable to choose how many frames to skip before filtering // Ring buffer parameters. RBMaxElements int // The maximum possible number of elements in ring buffer. diff --git a/revid/revid.go b/revid/revid.go index f5398c72..a375847e 100644 --- a/revid/revid.go +++ b/revid/revid.go @@ -344,6 +344,7 @@ func (r *Revid) setupPipeline(mtsEnc func(dst io.WriteCloser, rate float64) (io. r.encoders = multiWriter(encoders...) +<<<<<<< HEAD l := len(r.cfg.Filters) r.filters = []filter.Filter{filter.NewNoOp(r.encoders)} if l != 0 { @@ -365,6 +366,20 @@ func (r *Revid) setupPipeline(mtsEnc func(dst io.WriteCloser, rate float64) (io. } dst = r.filters[i] } +======= + switch r.cfg.Filter { + case config.FilterNoOp: + r.filter = filter.NewNoOp(r.encoders) + case config.FilterMOG: + r.filter = filter.NewMOGFilter(r.encoders, mogMinArea, mogThreshold, mogHistory, r.cfg.ShowWindows, r.cfg.FilterFrames) + case config.FilterVariableFPS: + r.filter = filter.NewVariableFPSFilter(r.encoders, minFPS, filter.NewMOGFilter(r.encoders, mogMinArea, mogThreshold, mogHistory, r.cfg.ShowWindows, r.cfg.FilterFrames)) + case config.FilterKNN: + r.filter = filter.NewKNNFilter(r.encoders, knnMinArea, knnThreshold, knnHistory, knnKernel, r.cfg.ShowWindows) + + default: + panic("Undefined Filter") +>>>>>>> d097308e... adding variable to choose how many frames to skip before filtering } switch r.cfg.Input { @@ -694,6 +709,16 @@ func (r *Revid) Update(vars map[string]string) error { r.cfg.Logger.Log(logger.Warning, pkg+"invalid FilterFrames var", "value", value) break } +<<<<<<< HEAD +======= + r.cfg.Filter = v + case "FilterFrames": + v, err := strconv.Atoi(value) + if err != nil || v < 0 { + r.cfg.Logger.Log(logger.Warning, pkg+"invalid FilterFrames var", "value", value) + break + } +>>>>>>> d097308e... adding variable to choose how many frames to skip before filtering r.cfg.FilterFrames = v case "PSITime": v, err := strconv.Atoi(value) From 0b95041d4f678d36cca2bfa798007ae4e70b3b2f Mon Sep 17 00:00:00 2001 From: Ella Pietraroia Date: Tue, 7 Jan 2020 16:48:08 +1030 Subject: [PATCH 03/31] quantization = 0 for input encode is MJPEG --- filter/mog.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filter/mog.go b/filter/mog.go index cff19d3e..bd916791 100644 --- a/filter/mog.go +++ b/filter/mog.go @@ -83,7 +83,7 @@ func (m *MOGFilter) Write(f []byte) (int, error) { if hfCount < (m.hf - 1) { m.hold[hfCount] = f hfCount++ - return -1, nil + return 0, nil } else { img, err := gocv.IMDecode(f, gocv.IMReadColor) if err != nil { From 4b285589243ffe208fb9845b10cf50272ac4c4b2 Mon Sep 17 00:00:00 2001 From: Scott Date: Wed, 8 Jan 2020 11:31:09 +1030 Subject: [PATCH 04/31] Frame counter resets on a consistent interval. --- filter/mog.go | 131 +++++++++++++++++++++++++------------------------- 1 file changed, 66 insertions(+), 65 deletions(-) diff --git a/filter/mog.go b/filter/mog.go index bd916791..243044e7 100644 --- a/filter/mog.go +++ b/filter/mog.go @@ -84,70 +84,71 @@ func (m *MOGFilter) Write(f []byte) (int, error) { m.hold[hfCount] = f hfCount++ return 0, nil - } else { - img, err := gocv.IMDecode(f, gocv.IMReadColor) - if err != nil { - return 0, fmt.Errorf("image can't be decoded: %w", err) - } - defer img.Close() - - imgDelta := gocv.NewMat() - defer imgDelta.Close() - - // Seperate foreground and background. - m.bs.Apply(img, &imgDelta) - - // Threshold imgDelta. - gocv.Threshold(imgDelta, &imgDelta, 25, 255, gocv.ThresholdBinary) - - // Remove noise. - gocv.Erode(imgDelta, &imgDelta, m.knl) - gocv.Dilate(imgDelta, &imgDelta, m.knl) - - // Fill small holes. - gocv.Dilate(imgDelta, &imgDelta, m.knl) - gocv.Erode(imgDelta, &imgDelta, m.knl) - - // Find contours and reject ones with a small area. - var contours [][]image.Point - allContours := gocv.FindContours(imgDelta, gocv.RetrievalExternal, gocv.ChainApproxSimple) - for _, c := range allContours { - if gocv.ContourArea(c) > m.area { - contours = append(contours, c) - } - } - - // 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) - } - - // Don't write to destination if there is no motion. - if len(contours) == 0 { - return 0, nil - } - - // 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 0, err - } - } - - hfCount = 0 - return m.dst.Write(f) } + + hfCount = 0 + img, err := gocv.IMDecode(f, gocv.IMReadColor) + if err != nil { + return 0, fmt.Errorf("image can't be decoded: %w", err) + } + defer img.Close() + + imgDelta := gocv.NewMat() + defer imgDelta.Close() + + // Seperate foreground and background. + m.bs.Apply(img, &imgDelta) + + // Threshold imgDelta. + gocv.Threshold(imgDelta, &imgDelta, 25, 255, gocv.ThresholdBinary) + + // Remove noise. + gocv.Erode(imgDelta, &imgDelta, m.knl) + gocv.Dilate(imgDelta, &imgDelta, m.knl) + + // Fill small holes. + gocv.Dilate(imgDelta, &imgDelta, m.knl) + gocv.Erode(imgDelta, &imgDelta, m.knl) + + // Find contours and reject ones with a small area. + var contours [][]image.Point + allContours := gocv.FindContours(imgDelta, gocv.RetrievalExternal, gocv.ChainApproxSimple) + for _, c := range allContours { + if gocv.ContourArea(c) > m.area { + contours = append(contours, c) + } + } + + // 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) + } + + // Don't write to destination if there is no motion. + if len(contours) == 0 { + return 0, nil + } + + // 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 0, err + } + } + + return m.dst.Write(f) + } From b0dd41dcf0d2feb9fc49ed30038743807ee106c3 Mon Sep 17 00:00:00 2001 From: Ella Pietraroia Date: Thu, 16 Jan 2020 14:39:44 +1030 Subject: [PATCH 05/31] back to no testing --- filter/mog.go | 13 ++++++------- revid/config/config.go | 8 -------- revid/revid.go | 24 ------------------------ 3 files changed, 6 insertions(+), 39 deletions(-) diff --git a/filter/mog.go b/filter/mog.go index 243044e7..1b8003a5 100644 --- a/filter/mog.go +++ b/filter/mog.go @@ -48,10 +48,9 @@ type MOGFilter struct { windows []*gocv.Window hold [][]byte hf int + hfCount int } -var hfCount int = 0 - // NewMOGFilter returns a pointer to a new MOGFilter struct. func NewMOGFilter(dst io.WriteCloser, area, threshold float64, history int, debug bool, hf int) *MOGFilter { bs := gocv.NewBackgroundSubtractorMOG2WithParams(history, threshold, false) @@ -61,7 +60,7 @@ func NewMOGFilter(dst io.WriteCloser, area, threshold float64, history int, debu windows = []*gocv.Window{gocv.NewWindow("MOG: Bounding boxes"), gocv.NewWindow("MOG: Motion")} } hold := make([][]byte, hf-1) - return &MOGFilter{dst, area, &bs, k, debug, windows, hold, hf} + return &MOGFilter{dst, area, &bs, k, debug, windows, hold, hf, 0} } // Implements io.Closer. @@ -80,13 +79,13 @@ func (m *MOGFilter) 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 *MOGFilter) Write(f []byte) (int, error) { - if hfCount < (m.hf - 1) { - m.hold[hfCount] = f - hfCount++ + if m.hfCount < (m.hf - 1) { + m.hold[m.hfCount] = f + m.hfCount++ return 0, nil } - hfCount = 0 + m.hfCount = 0 img, err := gocv.IMDecode(f, gocv.IMReadColor) if err != nil { return 0, fmt.Errorf("image can't be decoded: %w", err) diff --git a/revid/config/config.go b/revid/config/config.go index ea1f3b16..b37fc759 100644 --- a/revid/config/config.go +++ b/revid/config/config.go @@ -273,19 +273,11 @@ type Config struct { Width uint // Width defines the input video width Raspivid input. Bitrate uint // Bitrate specifies the bitrate for constant bitrate in kbps. -<<<<<<< HEAD HorizontalFlip bool // HorizontalFlip flips video horizontally for Raspivid input. VerticalFlip bool // VerticalFlip flips video vertically for Raspivid input. Filters []int // Defines the methods of filtering to be used in between lexing and encoding. FilterFrames int // Sets the number of frames that are held before the filter is used (on the nth frame) PSITime int // Sets the time between a packet being sent. -======= - HorizontalFlip bool // HorizontalFlip flips video horizontally for Raspivid input. - VerticalFlip bool // VerticalFlip flips video vertically for Raspivid input. - Filter int // Defines the method of filtering to be used in between lexing and encoding. - FilterFrames int // Sets the number of frames that are held before the filter is used (on the nth frame) - PSITime int // Sets the time between a packet being sent ->>>>>>> d097308e... adding variable to choose how many frames to skip before filtering // Ring buffer parameters. RBMaxElements int // The maximum possible number of elements in ring buffer. diff --git a/revid/revid.go b/revid/revid.go index a375847e..26abdee3 100644 --- a/revid/revid.go +++ b/revid/revid.go @@ -344,7 +344,6 @@ func (r *Revid) setupPipeline(mtsEnc func(dst io.WriteCloser, rate float64) (io. r.encoders = multiWriter(encoders...) -<<<<<<< HEAD l := len(r.cfg.Filters) r.filters = []filter.Filter{filter.NewNoOp(r.encoders)} if l != 0 { @@ -366,20 +365,7 @@ func (r *Revid) setupPipeline(mtsEnc func(dst io.WriteCloser, rate float64) (io. } dst = r.filters[i] } -======= - switch r.cfg.Filter { - case config.FilterNoOp: - r.filter = filter.NewNoOp(r.encoders) - case config.FilterMOG: - r.filter = filter.NewMOGFilter(r.encoders, mogMinArea, mogThreshold, mogHistory, r.cfg.ShowWindows, r.cfg.FilterFrames) - case config.FilterVariableFPS: - r.filter = filter.NewVariableFPSFilter(r.encoders, minFPS, filter.NewMOGFilter(r.encoders, mogMinArea, mogThreshold, mogHistory, r.cfg.ShowWindows, r.cfg.FilterFrames)) - case config.FilterKNN: - r.filter = filter.NewKNNFilter(r.encoders, knnMinArea, knnThreshold, knnHistory, knnKernel, r.cfg.ShowWindows) - default: - panic("Undefined Filter") ->>>>>>> d097308e... adding variable to choose how many frames to skip before filtering } switch r.cfg.Input { @@ -709,16 +695,6 @@ func (r *Revid) Update(vars map[string]string) error { r.cfg.Logger.Log(logger.Warning, pkg+"invalid FilterFrames var", "value", value) break } -<<<<<<< HEAD -======= - r.cfg.Filter = v - case "FilterFrames": - v, err := strconv.Atoi(value) - if err != nil || v < 0 { - r.cfg.Logger.Log(logger.Warning, pkg+"invalid FilterFrames var", "value", value) - break - } ->>>>>>> d097308e... adding variable to choose how many frames to skip before filtering r.cfg.FilterFrames = v case "PSITime": v, err := strconv.Atoi(value) From 61bb0b68f6396c9ca1e0fa84db8472582b50ec45 Mon Sep 17 00:00:00 2001 From: Ella Pietraroia Date: Thu, 16 Jan 2020 15:57:57 +1030 Subject: [PATCH 06/31] adding staright to file testing in, need to remove --- revid/revid.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/revid/revid.go b/revid/revid.go index 26abdee3..820d10f7 100644 --- a/revid/revid.go +++ b/revid/revid.go @@ -32,6 +32,7 @@ import ( "errors" "fmt" "io" + "os" "os/exec" "strconv" "strings" @@ -344,6 +345,15 @@ func (r *Revid) setupPipeline(mtsEnc func(dst io.WriteCloser, rate float64) (io. r.encoders = multiWriter(encoders...) + // !!! Test code + //r.encoders = multiWriter(encoders...) + f, er := os.Create("vid.mjpeg") + if er != nil { + panic("!!! TEST CODE !!!: file didnt work") + } + r.encoders = f + // !!! Test code + l := len(r.cfg.Filters) r.filters = []filter.Filter{filter.NewNoOp(r.encoders)} if l != 0 { From a26777b697df9de84619ebbb639ae3f4b579cfbc Mon Sep 17 00:00:00 2001 From: Saxon Date: Fri, 17 Jan 2020 16:02:51 +1030 Subject: [PATCH 07/31] codec/h264&h265/lex.go: returning errors even if io.EOF, and not bothering to return what's currently in buffer if error --- codec/h264/lex.go | 24 +++++------------------- codec/h265/lex.go | 2 -- 2 files changed, 5 insertions(+), 21 deletions(-) diff --git a/codec/h264/lex.go b/codec/h264/lex.go index 176c8b3b..1f169afe 100644 --- a/codec/h264/lex.go +++ b/codec/h264/lex.go @@ -31,6 +31,7 @@ LICENSE package h264 import ( + "fmt" "io" "time" @@ -66,25 +67,19 @@ func Lex(dst io.Writer, src io.Reader, delay time.Duration) error { buf := make([]byte, len(h264Prefix), bufSize) copy(buf, h264Prefix[:]) writeOut := false -outer: + for { var b byte var err error buf, b, err = c.ScanUntil(buf, 0x00) if err != nil { - if err != io.EOF { - return err - } - break + return fmt.Errorf("can't scan until: %w", err) } for n := 1; b == 0x0 && n < 4; n++ { b, err = c.ReadByte() if err != nil { - if err != io.EOF { - return err - } - break outer + return fmt.Errorf("can't read byte: %w", err) } buf = append(buf, b) @@ -106,10 +101,7 @@ outer: b, err = c.ReadByte() if err != nil { - if err != io.EOF { - return err - } - break outer + return fmt.Errorf("can't read byte: %w", err) } buf = append(buf, b) @@ -127,10 +119,4 @@ outer: } } } - if len(buf) == len(h264Prefix) { - return nil - } - <-tick - _, err := dst.Write(buf) - return err } diff --git a/codec/h265/lex.go b/codec/h265/lex.go index 55db6be6..6534e571 100644 --- a/codec/h265/lex.go +++ b/codec/h265/lex.go @@ -76,8 +76,6 @@ func (l *Lexer) Lex(dst io.Writer, src io.Reader, delay time.Duration) error { n, err := src.Read(buf) switch err { case nil: // Do nothing. - case io.EOF: - return nil default: return fmt.Errorf("source read error: %w\n", err) } From e45f51a31f912925e6991213ebe937d840345717 Mon Sep 17 00:00:00 2001 From: Saxon Date: Fri, 17 Jan 2020 16:07:37 +1030 Subject: [PATCH 08/31] codec/mjpeg/lex.go: return error if io.EOF is encountered --- codec/mjpeg/lex.go | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/codec/mjpeg/lex.go b/codec/mjpeg/lex.go index 46ab4ce8..d8b671b2 100644 --- a/codec/mjpeg/lex.go +++ b/codec/mjpeg/lex.go @@ -63,10 +63,7 @@ func Lex(dst io.Writer, src io.Reader, delay time.Duration) error { return nil } if err != nil { - if err == io.EOF { - return nil - } - return err + return fmt.Errorf("can't read source: %w", err) } if !bytes.Equal(buf, []byte{0xff, 0xd8}) { return fmt.Errorf("parser: not MJPEG frame start: %#v", buf) @@ -75,10 +72,7 @@ func Lex(dst io.Writer, src io.Reader, delay time.Duration) error { for { b, err := r.ReadByte() if err != nil { - if err == io.EOF { - return nil - } - return err + return fmt.Errorf("can't read byte: %w", err) } buf = append(buf, b) if last == 0xff && b == 0xd9 { From 399ecb88746f84955f03c3afe67d08a8a7e18f58 Mon Sep 17 00:00:00 2001 From: Saxon Date: Fri, 17 Jan 2020 16:23:28 +1030 Subject: [PATCH 09/31] revid: do more thinking about errors in processFrom --- revid/revid.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/revid/revid.go b/revid/revid.go index a05c9650..03f9bd05 100644 --- a/revid/revid.go +++ b/revid/revid.go @@ -847,7 +847,12 @@ func (r *Revid) Update(vars map[string]string) error { // processFrom is run as a routine to read from a input data source, lex and // then send individual access units to revid's encoders. func (r *Revid) processFrom(read io.Reader, delay time.Duration) { - r.err <- r.lexTo(r.filters[0], read, delay) - r.cfg.Logger.Log(logger.Info, pkg+"finished lexing") + err := r.lexTo(r.filters[0], read, delay) + switch { + case err == nil: // Do nothing. + case errors.Is(err, io.EOF): // TODO: handle this depending on loop mode. + default: + r.err <- err + } r.wg.Done() } From fdf393566a61fdfa6a6f64aadf290e77e3c6b9f4 Mon Sep 17 00:00:00 2001 From: Saxon Date: Fri, 17 Jan 2020 16:41:12 +1030 Subject: [PATCH 10/31] codec/h265/lex_test.go: adapted test for changes to error lex can return --- codec/h265/lex_test.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/codec/h265/lex_test.go b/codec/h265/lex_test.go index 1a409e4c..4ed57c12 100644 --- a/codec/h265/lex_test.go +++ b/codec/h265/lex_test.go @@ -28,6 +28,7 @@ LICENSE package h265 import ( + "errors" "io" "testing" ) @@ -247,7 +248,9 @@ func TestLex(t *testing.T) { r := &rtpReader{packets: test.packets} d := &destination{} err := NewLexer(test.donl).Lex(d, r, 0) - if err != nil { + switch { + case err == nil || errors.Is(err, io.EOF): // Do nothing + default: t.Fatalf("error lexing: %v\n", err) } From 44aac3f9fd115a6934c5a7e9e6c83b3cee027b34 Mon Sep 17 00:00:00 2001 From: Ella Pietraroia Date: Mon, 20 Jan 2020 14:09:42 +1030 Subject: [PATCH 11/31] timing for 25 frames testing --- filter/filter.go | 14 +++++++++++++- filter/mog.go | 18 +++++++++++++++++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/filter/filter.go b/filter/filter.go index ec182531..c6fec897 100644 --- a/filter/filter.go +++ b/filter/filter.go @@ -27,7 +27,9 @@ LICENSE package filter import ( + "fmt" "io" + "time" ) // Interface for all filters. @@ -44,6 +46,16 @@ type NoOp struct { func NewNoOp(dst io.Writer) *NoOp { return &NoOp{dst: dst} } -func (n *NoOp) Write(p []byte) (int, error) { return n.dst.Write(p) } +func (n *NoOp) Write(p []byte) (int, error) { + if frames == 0 { + t1 = time.Now() + } + frames++ + if frames >= 24 { + fmt.Printf("25 frames takes: %dms\n\n", time.Now().Sub(t1).Milliseconds()) + frames = 0 + } + return n.dst.Write(p) +} func (n *NoOp) Close() error { return nil } diff --git a/filter/mog.go b/filter/mog.go index 1b8003a5..7711c101 100644 --- a/filter/mog.go +++ b/filter/mog.go @@ -33,6 +33,7 @@ import ( "image" "image/color" "io" + "time" "gocv.io/x/gocv" ) @@ -51,6 +52,9 @@ type MOGFilter struct { hfCount int } +var frames int = 0 +var t1 = time.Now() + // NewMOGFilter returns a pointer to a new MOGFilter struct. func NewMOGFilter(dst io.WriteCloser, area, threshold float64, history int, debug bool, hf int) *MOGFilter { bs := gocv.NewBackgroundSubtractorMOG2WithParams(history, threshold, false) @@ -79,9 +83,17 @@ func (m *MOGFilter) 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 *MOGFilter) Write(f []byte) (int, error) { + if frames == 0 { + t1 = time.Now() + } if m.hfCount < (m.hf - 1) { m.hold[m.hfCount] = f m.hfCount++ + frames++ + if frames >= 24 { + fmt.Printf("25 frames takes: %dms\n\n", time.Now().Sub(t1).Milliseconds()) + frames = 0 + } return 0, nil } @@ -138,7 +150,11 @@ func (m *MOGFilter) Write(f []byte) (int, error) { if len(contours) == 0 { return 0, nil } - + frames++ + if frames >= 24 { + fmt.Printf("25 frames takes: %dms\n\n", time.Now().Sub(t1).Milliseconds()) + frames = 0 + } // Write to destination, past 4 frames then current frame. for i, h := range m.hold { _, err := m.dst.Write(h) From c99d3564bf980c728de56d55e68c18758f53fd22 Mon Sep 17 00:00:00 2001 From: Ella Pietraroia Date: Mon, 20 Jan 2020 14:31:40 +1030 Subject: [PATCH 12/31] more testing --- filter/mog.go | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/filter/mog.go b/filter/mog.go index 7711c101..d7223c92 100644 --- a/filter/mog.go +++ b/filter/mog.go @@ -83,6 +83,7 @@ func (m *MOGFilter) 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 *MOGFilter) Write(f []byte) (int, error) { + t2 := time.Now() if frames == 0 { t1 = time.Now() } @@ -90,6 +91,7 @@ func (m *MOGFilter) Write(f []byte) (int, error) { m.hold[m.hfCount] = f m.hfCount++ frames++ + fmt.Printf("Hold in array 1 frame:\t%dms\n", time.Now().Sub(t2).Milliseconds()) if frames >= 24 { fmt.Printf("25 frames takes: %dms\n\n", time.Now().Sub(t1).Milliseconds()) frames = 0 @@ -148,13 +150,15 @@ func (m *MOGFilter) Write(f []byte) (int, error) { // Don't write to destination if there is no motion. if len(contours) == 0 { + frames++ + fmt.Printf("No motion 1 frame:\t%dms\n", time.Now().Sub(t2).Milliseconds()) + if frames >= 24 { + fmt.Printf("25 frames takes: %dms\n\n", time.Now().Sub(t1).Milliseconds()) + frames = 0 + } return 0, nil } - frames++ - if frames >= 24 { - fmt.Printf("25 frames takes: %dms\n\n", time.Now().Sub(t1).Milliseconds()) - frames = 0 - } + // Write to destination, past 4 frames then current frame. for i, h := range m.hold { _, err := m.dst.Write(h) @@ -163,7 +167,12 @@ func (m *MOGFilter) Write(f []byte) (int, error) { return 0, err } } - + fmt.Printf("Motion 1 frame:\t\t%dms\n", time.Now().Sub(t2).Milliseconds()) + frames++ + if frames >= 24 { + fmt.Printf("25 frames takes: %dms\n\n", time.Now().Sub(t1).Milliseconds()) + frames = 0 + } return m.dst.Write(f) } From f64c986efc1096ca08515fab6a29ec2ade46dfca Mon Sep 17 00:00:00 2001 From: Saxon Date: Mon, 20 Jan 2020 15:03:49 +1030 Subject: [PATCH 13/31] codec: corrected error handling in lexers --- codec/h264/lex.go | 21 +++++++++++++++++---- codec/h265/lex.go | 10 ++++++++-- codec/mjpeg/lex.go | 9 ++++++--- 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/codec/h264/lex.go b/codec/h264/lex.go index 1f169afe..3d9fb8a4 100644 --- a/codec/h264/lex.go +++ b/codec/h264/lex.go @@ -31,7 +31,6 @@ LICENSE package h264 import ( - "fmt" "io" "time" @@ -68,18 +67,25 @@ func Lex(dst io.Writer, src io.Reader, delay time.Duration) error { copy(buf, h264Prefix[:]) writeOut := false +outer: for { var b byte var err error buf, b, err = c.ScanUntil(buf, 0x00) if err != nil { - return fmt.Errorf("can't scan until: %w", err) + if err != io.EOF { + return err + } + break } for n := 1; b == 0x0 && n < 4; n++ { b, err = c.ReadByte() if err != nil { - return fmt.Errorf("can't read byte: %w", err) + if err != io.EOF { + return err + } + break outer } buf = append(buf, b) @@ -101,7 +107,10 @@ func Lex(dst io.Writer, src io.Reader, delay time.Duration) error { b, err = c.ReadByte() if err != nil { - return fmt.Errorf("can't read byte: %w", err) + if err != io.EOF { + return err + } + break outer } buf = append(buf, b) @@ -119,4 +128,8 @@ func Lex(dst io.Writer, src io.Reader, delay time.Duration) error { } } } + if len(buf) == len(h264Prefix) { + return io.EOF + } + return io.ErrUnexpectedEOF } diff --git a/codec/h265/lex.go b/codec/h265/lex.go index 6534e571..2fb41a9d 100644 --- a/codec/h265/lex.go +++ b/codec/h265/lex.go @@ -77,7 +77,13 @@ func (l *Lexer) Lex(dst io.Writer, src io.Reader, delay time.Duration) error { switch err { case nil: // Do nothing. default: - return fmt.Errorf("source read error: %w\n", err) + if err == io.EOF { + if l.buf.Len() == 0 { + return io.EOF + } + return io.ErrUnexpectedEOF + } + return err } // Get payload from RTP packet. @@ -179,7 +185,7 @@ func (l *Lexer) handleFragmentation(d []byte) { } } -// handlePACI will handl PACI packets +// handlePACI will handle PACI packets // // TODO: complete this func (l *Lexer) handlePACI(d []byte) { diff --git a/codec/mjpeg/lex.go b/codec/mjpeg/lex.go index d8b671b2..aea4ce9f 100644 --- a/codec/mjpeg/lex.go +++ b/codec/mjpeg/lex.go @@ -60,10 +60,10 @@ func Lex(dst io.Writer, src io.Reader, delay time.Duration) error { buf := make([]byte, 2, 4<<10) n, err := r.Read(buf) if n < 2 { - return nil + return io.ErrUnexpectedEOF } if err != nil { - return fmt.Errorf("can't read source: %w", err) + return err } if !bytes.Equal(buf, []byte{0xff, 0xd8}) { return fmt.Errorf("parser: not MJPEG frame start: %#v", buf) @@ -72,7 +72,10 @@ func Lex(dst io.Writer, src io.Reader, delay time.Duration) error { for { b, err := r.ReadByte() if err != nil { - return fmt.Errorf("can't read byte: %w", err) + if err == io.EOF { + return io.ErrUnexpectedEOF + } + return err } buf = append(buf, b) if last == 0xff && b == 0xd9 { From 0c081e0b23b1746f1542340019b2d8e8524bae3b Mon Sep 17 00:00:00 2001 From: Ella Pietraroia Date: Mon, 20 Jan 2020 15:45:43 +1030 Subject: [PATCH 14/31] more testing prints now in lex --- codec/mjpeg/lex.go | 5 +++++ filter/mog.go | 6 +++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/codec/mjpeg/lex.go b/codec/mjpeg/lex.go index 46ab4ce8..1ab6db14 100644 --- a/codec/mjpeg/lex.go +++ b/codec/mjpeg/lex.go @@ -38,6 +38,8 @@ import ( ) var noDelay = make(chan time.Time) +var frameGet int = 0 +var frameSend int = 0 func init() { close(noDelay) @@ -62,6 +64,7 @@ func Lex(dst io.Writer, src io.Reader, delay time.Duration) error { if n < 2 { return nil } + frameGet++ if err != nil { if err == io.EOF { return nil @@ -88,6 +91,8 @@ func Lex(dst io.Writer, src io.Reader, delay time.Duration) error { } <-tick _, err = dst.Write(buf) + frameSend++ + fmt.Printf("frames into lex: %v frames getting written: %v\t|\t", frameGet, frameSend) if err != nil { return err } diff --git a/filter/mog.go b/filter/mog.go index d7223c92..e0cd757e 100644 --- a/filter/mog.go +++ b/filter/mog.go @@ -93,7 +93,7 @@ func (m *MOGFilter) Write(f []byte) (int, error) { frames++ fmt.Printf("Hold in array 1 frame:\t%dms\n", time.Now().Sub(t2).Milliseconds()) if frames >= 24 { - fmt.Printf("25 frames takes: %dms\n\n", time.Now().Sub(t1).Milliseconds()) + fmt.Printf("\t\t\t\t\t\t\t\t25 frames takes: %dms\n\n", time.Now().Sub(t1).Milliseconds()) frames = 0 } return 0, nil @@ -153,7 +153,7 @@ func (m *MOGFilter) Write(f []byte) (int, error) { frames++ fmt.Printf("No motion 1 frame:\t%dms\n", time.Now().Sub(t2).Milliseconds()) if frames >= 24 { - fmt.Printf("25 frames takes: %dms\n\n", time.Now().Sub(t1).Milliseconds()) + fmt.Printf("\t\t\t\t\t\t\t\t25 frames takes: %dms\n\n", time.Now().Sub(t1).Milliseconds()) frames = 0 } return 0, nil @@ -170,7 +170,7 @@ func (m *MOGFilter) Write(f []byte) (int, error) { fmt.Printf("Motion 1 frame:\t\t%dms\n", time.Now().Sub(t2).Milliseconds()) frames++ if frames >= 24 { - fmt.Printf("25 frames takes: %dms\n\n", time.Now().Sub(t1).Milliseconds()) + fmt.Printf("\t\t\t\t\t\t\t\t25 frames takes: %dms\n\n", time.Now().Sub(t1).Milliseconds()) frames = 0 } return m.dst.Write(f) From 2d2e1b0ad143b9cea6d5197f82382e71f71b0061 Mon Sep 17 00:00:00 2001 From: Saxon Date: Mon, 20 Jan 2020 16:10:10 +1030 Subject: [PATCH 15/31] revid/revid.go: added logger message back --- revid/revid.go | 1 + 1 file changed, 1 insertion(+) diff --git a/revid/revid.go b/revid/revid.go index 03f9bd05..d563cc8c 100644 --- a/revid/revid.go +++ b/revid/revid.go @@ -848,6 +848,7 @@ func (r *Revid) Update(vars map[string]string) error { // then send individual access units to revid's encoders. func (r *Revid) processFrom(read io.Reader, delay time.Duration) { err := r.lexTo(r.filters[0], read, delay) + r.cfg.Logger.Log(logger.Debug, pkg+"finished lexing") switch { case err == nil: // Do nothing. case errors.Is(err, io.EOF): // TODO: handle this depending on loop mode. From d5e0bf773107500eeba7b1f0cc7cc7734ced85a1 Mon Sep 17 00:00:00 2001 From: Ella Pietraroia Date: Mon, 20 Jan 2020 16:19:04 +1030 Subject: [PATCH 16/31] remove all tests --- codec/mjpeg/lex.go | 5 ----- filter/filter.go | 14 +------------- filter/mog.go | 26 -------------------------- revid/revid.go | 10 ---------- 4 files changed, 1 insertion(+), 54 deletions(-) diff --git a/codec/mjpeg/lex.go b/codec/mjpeg/lex.go index 1ab6db14..46ab4ce8 100644 --- a/codec/mjpeg/lex.go +++ b/codec/mjpeg/lex.go @@ -38,8 +38,6 @@ import ( ) var noDelay = make(chan time.Time) -var frameGet int = 0 -var frameSend int = 0 func init() { close(noDelay) @@ -64,7 +62,6 @@ func Lex(dst io.Writer, src io.Reader, delay time.Duration) error { if n < 2 { return nil } - frameGet++ if err != nil { if err == io.EOF { return nil @@ -91,8 +88,6 @@ func Lex(dst io.Writer, src io.Reader, delay time.Duration) error { } <-tick _, err = dst.Write(buf) - frameSend++ - fmt.Printf("frames into lex: %v frames getting written: %v\t|\t", frameGet, frameSend) if err != nil { return err } diff --git a/filter/filter.go b/filter/filter.go index c6fec897..ec182531 100644 --- a/filter/filter.go +++ b/filter/filter.go @@ -27,9 +27,7 @@ LICENSE package filter import ( - "fmt" "io" - "time" ) // Interface for all filters. @@ -46,16 +44,6 @@ type NoOp struct { func NewNoOp(dst io.Writer) *NoOp { return &NoOp{dst: dst} } -func (n *NoOp) Write(p []byte) (int, error) { - if frames == 0 { - t1 = time.Now() - } - frames++ - if frames >= 24 { - fmt.Printf("25 frames takes: %dms\n\n", time.Now().Sub(t1).Milliseconds()) - frames = 0 - } - return n.dst.Write(p) -} +func (n *NoOp) Write(p []byte) (int, error) { return n.dst.Write(p) } func (n *NoOp) Close() error { return nil } diff --git a/filter/mog.go b/filter/mog.go index e0cd757e..a14b1fe7 100644 --- a/filter/mog.go +++ b/filter/mog.go @@ -33,7 +33,6 @@ import ( "image" "image/color" "io" - "time" "gocv.io/x/gocv" ) @@ -52,9 +51,6 @@ type MOGFilter struct { hfCount int } -var frames int = 0 -var t1 = time.Now() - // NewMOGFilter returns a pointer to a new MOGFilter struct. func NewMOGFilter(dst io.WriteCloser, area, threshold float64, history int, debug bool, hf int) *MOGFilter { bs := gocv.NewBackgroundSubtractorMOG2WithParams(history, threshold, false) @@ -83,19 +79,9 @@ func (m *MOGFilter) 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 *MOGFilter) Write(f []byte) (int, error) { - t2 := time.Now() - if frames == 0 { - t1 = time.Now() - } if m.hfCount < (m.hf - 1) { m.hold[m.hfCount] = f m.hfCount++ - frames++ - fmt.Printf("Hold in array 1 frame:\t%dms\n", time.Now().Sub(t2).Milliseconds()) - if frames >= 24 { - fmt.Printf("\t\t\t\t\t\t\t\t25 frames takes: %dms\n\n", time.Now().Sub(t1).Milliseconds()) - frames = 0 - } return 0, nil } @@ -150,12 +136,6 @@ func (m *MOGFilter) Write(f []byte) (int, error) { // Don't write to destination if there is no motion. if len(contours) == 0 { - frames++ - fmt.Printf("No motion 1 frame:\t%dms\n", time.Now().Sub(t2).Milliseconds()) - if frames >= 24 { - fmt.Printf("\t\t\t\t\t\t\t\t25 frames takes: %dms\n\n", time.Now().Sub(t1).Milliseconds()) - frames = 0 - } return 0, nil } @@ -167,12 +147,6 @@ func (m *MOGFilter) Write(f []byte) (int, error) { return 0, err } } - fmt.Printf("Motion 1 frame:\t\t%dms\n", time.Now().Sub(t2).Milliseconds()) - frames++ - if frames >= 24 { - fmt.Printf("\t\t\t\t\t\t\t\t25 frames takes: %dms\n\n", time.Now().Sub(t1).Milliseconds()) - frames = 0 - } return m.dst.Write(f) } diff --git a/revid/revid.go b/revid/revid.go index 820d10f7..26abdee3 100644 --- a/revid/revid.go +++ b/revid/revid.go @@ -32,7 +32,6 @@ import ( "errors" "fmt" "io" - "os" "os/exec" "strconv" "strings" @@ -345,15 +344,6 @@ func (r *Revid) setupPipeline(mtsEnc func(dst io.WriteCloser, rate float64) (io. r.encoders = multiWriter(encoders...) - // !!! Test code - //r.encoders = multiWriter(encoders...) - f, er := os.Create("vid.mjpeg") - if er != nil { - panic("!!! TEST CODE !!!: file didnt work") - } - r.encoders = f - // !!! Test code - l := len(r.cfg.Filters) r.filters = []filter.Filter{filter.NewNoOp(r.encoders)} if l != 0 { From e278397aa3481e9f6266fab88bf21e93f6cab4f9 Mon Sep 17 00:00:00 2001 From: Trek H Date: Mon, 20 Jan 2020 21:00:24 +1030 Subject: [PATCH 17/31] mjpeg-player: append to lexer The mjpeg lexer can now have data appended to it as many times as needed instead of having the data passed at construction. --- cmd/mjpeg-player/lex-mjpeg.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cmd/mjpeg-player/lex-mjpeg.js b/cmd/mjpeg-player/lex-mjpeg.js index 4d5be7e9..222fa945 100644 --- a/cmd/mjpeg-player/lex-mjpeg.js +++ b/cmd/mjpeg-player/lex-mjpeg.js @@ -1,12 +1,9 @@ /* -NAME - lex-mjpeg.js - AUTHOR Trek Hopton LICENSE - This file is Copyright (C) 2019 the Australian Ocean Lab (AusOcean) + 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 @@ -24,11 +21,14 @@ LICENSE // MJPEGLexer lexes a byte array containing MJPEG into individual JPEGs. class MJPEGLexer { - constructor(src) { - this.src = src; + constructor() { this.off = 0; } + append(data) { + this.src = new Uint8Array(data); + } + // read returns the next single frame. read() { // Check if the src can contain at least the start and end flags (4B). From bf289221deb159f4537c5fa900b0a81041178a63 Mon Sep 17 00:00:00 2001 From: Saxon Date: Wed, 22 Jan 2020 13:08:02 +1030 Subject: [PATCH 18/31] codec/h264/lex.go: simplified return logic --- codec/h264/lex.go | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/codec/h264/lex.go b/codec/h264/lex.go index 3d9fb8a4..633d3f53 100644 --- a/codec/h264/lex.go +++ b/codec/h264/lex.go @@ -67,16 +67,12 @@ func Lex(dst io.Writer, src io.Reader, delay time.Duration) error { copy(buf, h264Prefix[:]) writeOut := false -outer: for { var b byte var err error buf, b, err = c.ScanUntil(buf, 0x00) if err != nil { - if err != io.EOF { - return err - } - break + return err } for n := 1; b == 0x0 && n < 4; n++ { @@ -85,7 +81,7 @@ outer: if err != io.EOF { return err } - break outer + return io.ErrUnexpectedEOF } buf = append(buf, b) @@ -110,7 +106,7 @@ outer: if err != io.EOF { return err } - break outer + return io.ErrUnexpectedEOF } buf = append(buf, b) @@ -128,8 +124,4 @@ outer: } } } - if len(buf) == len(h264Prefix) { - return io.EOF - } - return io.ErrUnexpectedEOF } From b588321d0015608b86f013d89c8414b04d368201 Mon Sep 17 00:00:00 2001 From: Ella Pietraroia Date: Wed, 22 Jan 2020 13:12:22 +1030 Subject: [PATCH 19/31] adding in so builds with circleci build --- filter/filters_circleci.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filter/filters_circleci.go b/filter/filters_circleci.go index f6d46082..bd9a6fee 100644 --- a/filter/filters_circleci.go +++ b/filter/filters_circleci.go @@ -32,7 +32,7 @@ import ( ) // NewMOGFilter returns a pointer to a new NoOp struct for testing purposes only. -func NewMOGFilter(dst io.WriteCloser, area, threshold float64, history int, debug bool) *NoOp { +func NewMOGFilter(dst io.WriteCloser, area, threshold float64, history int, debug bool, hf int) *NoOp { return &NoOp{dst: dst} } From 78484b5f5bc93b6ea2f554c371872eed16236d6d Mon Sep 17 00:00:00 2001 From: Saxon Date: Wed, 22 Jan 2020 13:43:08 +1030 Subject: [PATCH 20/31] codec/h264/lex.go: fixed handling of errors from ScanUntil --- codec/h264/lex.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/codec/h264/lex.go b/codec/h264/lex.go index 633d3f53..6159a15e 100644 --- a/codec/h264/lex.go +++ b/codec/h264/lex.go @@ -72,7 +72,13 @@ func Lex(dst io.Writer, src io.Reader, delay time.Duration) error { var err error buf, b, err = c.ScanUntil(buf, 0x00) if err != nil { - return err + if err != io.EOF { + return err + } + if len(buf) != 0 { + return io.ErrUnexpectedEOF + } + return io.EOF } for n := 1; b == 0x0 && n < 4; n++ { From 9b8667e56ca21f7d29ca57bb127eb6e13cdfb336 Mon Sep 17 00:00:00 2001 From: Saxon Date: Wed, 22 Jan 2020 13:45:01 +1030 Subject: [PATCH 21/31] fixed handling of errors from lex methods --- codec/h265/lex_test.go | 5 ++--- revid/revid.go | 6 +++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/codec/h265/lex_test.go b/codec/h265/lex_test.go index 4ed57c12..9f725bb9 100644 --- a/codec/h265/lex_test.go +++ b/codec/h265/lex_test.go @@ -28,7 +28,6 @@ LICENSE package h265 import ( - "errors" "io" "testing" ) @@ -248,8 +247,8 @@ func TestLex(t *testing.T) { r := &rtpReader{packets: test.packets} d := &destination{} err := NewLexer(test.donl).Lex(d, r, 0) - switch { - case err == nil || errors.Is(err, io.EOF): // Do nothing + switch err { + case nil, io.EOF: // Do nothing default: t.Fatalf("error lexing: %v\n", err) } diff --git a/revid/revid.go b/revid/revid.go index d563cc8c..aa2139fb 100644 --- a/revid/revid.go +++ b/revid/revid.go @@ -849,9 +849,9 @@ func (r *Revid) Update(vars map[string]string) error { func (r *Revid) processFrom(read io.Reader, delay time.Duration) { err := r.lexTo(r.filters[0], read, delay) r.cfg.Logger.Log(logger.Debug, pkg+"finished lexing") - switch { - case err == nil: // Do nothing. - case errors.Is(err, io.EOF): // TODO: handle this depending on loop mode. + switch err { + case nil: // Do nothing. + case io.EOF: // TODO: handle this depending on loop mode. default: r.err <- err } From b597fb9a1a21d1c9e2c2d4860f447809ff45aed0 Mon Sep 17 00:00:00 2001 From: Ella Pietraroia Date: Wed, 22 Jan 2020 14:53:03 +1030 Subject: [PATCH 22/31] commenting and refinement --- filter/mog.go | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/filter/mog.go b/filter/mog.go index a14b1fe7..1d187e96 100644 --- a/filter/mog.go +++ b/filter/mog.go @@ -40,15 +40,15 @@ import ( // MOGFilter is a filter that provides basic motion detection. MoG is short for // Mixture of Gaussians method. type MOGFilter struct { - dst io.WriteCloser - area float64 - bs *gocv.BackgroundSubtractorMOG2 - knl gocv.Mat - debug bool - windows []*gocv.Window - hold [][]byte - hf int - hfCount int + dst io.WriteCloser //writer and closer interface + area float64 //minimum area that motion + 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 } // NewMOGFilter returns a pointer to a new MOGFilter struct. @@ -59,8 +59,7 @@ func NewMOGFilter(dst io.WriteCloser, area, threshold float64, history int, debu if debug { windows = []*gocv.Window{gocv.NewWindow("MOG: Bounding boxes"), gocv.NewWindow("MOG: Motion")} } - hold := make([][]byte, hf-1) - return &MOGFilter{dst, area, &bs, k, debug, windows, hold, hf, 0} + return &MOGFilter{dst, area, &bs, k, debug, windows, make([][]byte, hf-1), hf, 0} } // Implements io.Closer. @@ -82,7 +81,7 @@ func (m *MOGFilter) Write(f []byte) (int, error) { if m.hfCount < (m.hf - 1) { m.hold[m.hfCount] = f m.hfCount++ - return 0, nil + return len(f), nil } m.hfCount = 0 From 963b74bed0767ea2d04a8296e47bc0aab56dbafb Mon Sep 17 00:00:00 2001 From: Ella Pietraroia Date: Wed, 22 Jan 2020 14:57:35 +1030 Subject: [PATCH 23/31] change variable name FilterFrames to MotionFilter --- filter/mog.go | 1 - revid/config/config.go | 10 +++++----- revid/revid.go | 10 +++++----- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/filter/mog.go b/filter/mog.go index 1d187e96..35c3046a 100644 --- a/filter/mog.go +++ b/filter/mog.go @@ -147,5 +147,4 @@ func (m *MOGFilter) Write(f []byte) (int, error) { } } return m.dst.Write(f) - } diff --git a/revid/config/config.go b/revid/config/config.go index b37fc759..19367296 100644 --- a/revid/config/config.go +++ b/revid/config/config.go @@ -85,7 +85,7 @@ const ( defaultClipDuration = 0 defaultAudioInputCodec = codecutil.ADPCM defaultPSITime = 2 - defaultFilterFrames = 1 + defaultMotionInterval = 5 // Ring buffer defaults. defaultRBMaxElements = 10000 @@ -276,7 +276,7 @@ type Config struct { HorizontalFlip bool // HorizontalFlip flips video horizontally for Raspivid input. VerticalFlip bool // VerticalFlip flips video vertically for Raspivid input. Filters []int // Defines the methods of filtering to be used in between lexing and encoding. - FilterFrames int // Sets the number of frames that are held before the filter is used (on the nth frame) + MotionInterval int // Sets the number of frames that are held before the filter is used (on the nth frame) PSITime int // Sets the time between a packet being sent. // Ring buffer parameters. @@ -462,9 +462,9 @@ func (c *Config) Validate() error { c.Logger.Log(logger.Info, pkg+"PSITime bad or unset, defaulting", "PSITime", defaultPSITime) c.PSITime = defaultPSITime } - if c.FilterFrames <= 0 { - c.Logger.Log(logger.Info, pkg+"FilterFrames bad or unset, defaulting", "FilterFrames", defaultFilterFrames) - c.FilterFrames = defaultFilterFrames + if c.MotionInterval <= 0 { + c.Logger.Log(logger.Info, pkg+"MotionInterval bad or unset, defaulting", "MotionInterval", defaultMotionInterval) + c.MotionInterval = defaultMotionInterval } if c.MinFPS <= 0 { diff --git a/revid/revid.go b/revid/revid.go index 26abdee3..d717d66f 100644 --- a/revid/revid.go +++ b/revid/revid.go @@ -355,9 +355,9 @@ 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.NewMOGFilter(dst, mogMinArea, mogThreshold, mogHistory, r.cfg.ShowWindows, r.cfg.FilterFrames) + r.filters[i] = filter.NewMOGFilter(dst, mogMinArea, mogThreshold, mogHistory, r.cfg.ShowWindows, r.cfg.MotionInterval) case config.FilterVariableFPS: - r.filters[i] = filter.NewVariableFPSFilter(dst, minFPS, filter.NewMOGFilter(r.encoders, mogMinArea, mogThreshold, mogHistory, r.cfg.ShowWindows, r.cfg.FilterFrames)) + r.filters[i] = filter.NewVariableFPSFilter(dst, minFPS, filter.NewMOGFilter(r.encoders, mogMinArea, mogThreshold, mogHistory, r.cfg.ShowWindows, r.cfg.MotionInterval)) case config.FilterKNN: r.filters[i] = filter.NewKNNFilter(dst, knnMinArea, knnThreshold, knnHistory, knnKernel, r.cfg.ShowWindows) default: @@ -689,13 +689,13 @@ func (r *Revid) Update(vars map[string]string) error { } r.cfg.Filters[i] = v } - case "FilterFrames": + case "MotionInterval": v, err := strconv.Atoi(value) if err != nil || v < 0 { - r.cfg.Logger.Log(logger.Warning, pkg+"invalid FilterFrames var", "value", value) + r.cfg.Logger.Log(logger.Warning, pkg+"invalid MotionInterval var", "value", value) break } - r.cfg.FilterFrames = v + r.cfg.MotionInterval = v case "PSITime": v, err := strconv.Atoi(value) if err != nil || v < 0 { From fd8e2e2426d972cf731e1c5c16abec2d50c34d73 Mon Sep 17 00:00:00 2001 From: Ella Pietraroia Date: Wed, 22 Jan 2020 15:25:34 +1030 Subject: [PATCH 24/31] full stops --- filter/mog.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/filter/mog.go b/filter/mog.go index 35c3046a..b83b039c 100644 --- a/filter/mog.go +++ b/filter/mog.go @@ -40,15 +40,15 @@ import ( // MOGFilter is a filter that provides basic motion detection. MoG is short for // Mixture of Gaussians method. type MOGFilter struct { - dst io.WriteCloser //writer and closer interface - area float64 //minimum area that motion - 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 + dst io.WriteCloser //writer and closer interface. + area float64 //minimum area that motion. + 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. } // NewMOGFilter returns a pointer to a new MOGFilter struct. From 2e70e3fa1bf3eaaab7609f09ec69c6cca3b6e104 Mon Sep 17 00:00:00 2001 From: Ella Pietraroia Date: Wed, 22 Jan 2020 15:34:32 +1030 Subject: [PATCH 25/31] change to error handling --- filter/mog.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/filter/mog.go b/filter/mog.go index b83b039c..9519b656 100644 --- a/filter/mog.go +++ b/filter/mog.go @@ -87,7 +87,7 @@ func (m *MOGFilter) Write(f []byte) (int, error) { m.hfCount = 0 img, err := gocv.IMDecode(f, gocv.IMReadColor) if err != nil { - return 0, fmt.Errorf("image can't be decoded: %w", err) + return len(f), fmt.Errorf("image can't be decoded: %w", err) } defer img.Close() @@ -135,7 +135,7 @@ func (m *MOGFilter) Write(f []byte) (int, error) { // Don't write to destination if there is no motion. if len(contours) == 0 { - return 0, nil + return len(f), nil } // Write to destination, past 4 frames then current frame. @@ -143,7 +143,7 @@ func (m *MOGFilter) Write(f []byte) (int, error) { _, err := m.dst.Write(h) m.hold[i] = nil if err != nil { - return 0, err + return len(f), fmt.Errorf("could not write previous frames: %w", err) } } return m.dst.Write(f) From c3f1d957e3ef147bae08d5857be73ca6305fe2b4 Mon Sep 17 00:00:00 2001 From: Ella Pietraroia Date: Wed, 22 Jan 2020 15:43:10 +1030 Subject: [PATCH 26/31] formatting --- filter/mog.go | 18 +++++++++--------- revid/config/config.go | 1 + 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/filter/mog.go b/filter/mog.go index 9519b656..b86e1cdf 100644 --- a/filter/mog.go +++ b/filter/mog.go @@ -40,15 +40,15 @@ import ( // MOGFilter is a filter that provides basic motion detection. MoG is short for // Mixture of Gaussians method. type MOGFilter struct { - dst io.WriteCloser //writer and closer interface. - area float64 //minimum area that motion. - 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. + dst io.WriteCloser // Writer and closer interface. + area float64 // Minimum area that motion. + 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. } // NewMOGFilter returns a pointer to a new MOGFilter struct. diff --git a/revid/config/config.go b/revid/config/config.go index 19367296..5f6fa4b3 100644 --- a/revid/config/config.go +++ b/revid/config/config.go @@ -328,6 +328,7 @@ var TypeData = map[string]string{ "MOGHistory": "uint", "MOGMinArea": "float", "MOGThreshold": "float", + "MotionInterval": "int", "RBCapacity": "uint", "RBMaxElements": "uint", "RBWriteTimeout": "uint", From c9dee735d43b6295d8ab711595d5efacd1dd872e Mon Sep 17 00:00:00 2001 From: Ella Pietraroia Date: Wed, 22 Jan 2020 15:44:53 +1030 Subject: [PATCH 27/31] back to old return --- filter/mog.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filter/mog.go b/filter/mog.go index b86e1cdf..ca318140 100644 --- a/filter/mog.go +++ b/filter/mog.go @@ -87,7 +87,7 @@ func (m *MOGFilter) Write(f []byte) (int, error) { m.hfCount = 0 img, err := gocv.IMDecode(f, gocv.IMReadColor) if err != nil { - return len(f), fmt.Errorf("image can't be decoded: %w", err) + return 0, fmt.Errorf("image can't be decoded: %w", err) } defer img.Close() From 3851a20aa05c94536895ff3b88c5a35af0a9a520 Mon Sep 17 00:00:00 2001 From: Ella Pietraroia Date: Wed, 22 Jan 2020 16:04:16 +1030 Subject: [PATCH 28/31] commenting --- filter/mog.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/filter/mog.go b/filter/mog.go index ca318140..20fd5807 100644 --- a/filter/mog.go +++ b/filter/mog.go @@ -40,8 +40,8 @@ import ( // MOGFilter is a filter that provides basic motion detection. MoG is short for // Mixture of Gaussians method. type MOGFilter struct { - dst io.WriteCloser // Writer and closer interface. - area float64 // Minimum area that motion. + 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. From 3c0076751e2c4ccb256adb393d9b9210d33992c9 Mon Sep 17 00:00:00 2001 From: Scott Date: Thu, 23 Jan 2020 12:15:07 +1030 Subject: [PATCH 29/31] Fixed variables --- revid/revid.go | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/revid/revid.go b/revid/revid.go index c7ea4886..b4b83892 100644 --- a/revid/revid.go +++ b/revid/revid.go @@ -63,24 +63,6 @@ const ( rtmpConnectionTimeout = 10 ) -// Motion filter parameters. -const minFPS = 1.0 - -// KNN specific parameters. -const ( - knnMinArea = 25.0 - knnThreshold = 300 - knnHistory = 300 - knnKernel = 9 -) - -// MOG specific parameters. -const ( - mogMinArea = 50 - mogThreshold = 100 - mogHistory = 100 -) - const pkg = "revid: " type Logger interface { @@ -355,11 +337,11 @@ 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.NewMOGFilter(dst, mogMinArea, mogThreshold, mogHistory, r.cfg.ShowWindows, r.cfg.MotionInterval) + r.filters[i] = filter.NewMOGFilter(dst, r.cfg.MOGMinArea, r.cfg.MOGThreshold, int(r.cfg.MOGHistory), r.cfg.ShowWindows, r.cfg.MotionInterval) case config.FilterVariableFPS: - r.filters[i] = filter.NewVariableFPSFilter(dst, minFPS, filter.NewMOGFilter(r.encoders, mogMinArea, mogThreshold, mogHistory, r.cfg.ShowWindows, r.cfg.MotionInterval)) + 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, knnMinArea, knnThreshold, knnHistory, 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) default: panic("Undefined Filter") } From 207e96b9999f213e6e657993752b4774b72f3996 Mon Sep 17 00:00:00 2001 From: Scott Date: Thu, 23 Jan 2020 12:43:09 +1030 Subject: [PATCH 30/31] 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") } From eca945339929c200182aa71b9eac8d695f6962f0 Mon Sep 17 00:00:00 2001 From: Scott Date: Thu, 23 Jan 2020 12:49:45 +1030 Subject: [PATCH 31/31] filters_circleci.go: Changed function to pass circleci builds --- filter/filters_circleci.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filter/filters_circleci.go b/filter/filters_circleci.go index bd9a6fee..374e8b0a 100644 --- a/filter/filters_circleci.go +++ b/filter/filters_circleci.go @@ -36,6 +36,6 @@ func NewMOGFilter(dst io.WriteCloser, area, threshold float64, history int, debu return &NoOp{dst: dst} } -func NewKNNFilter(dst io.WriteCloser, area, threshold float64, history, kernelSize int, debug bool) *NoOp { +func NewKNNFilter(dst io.WriteCloser, area, threshold float64, history, kernelSize int, debug bool, hf int) *NoOp { return &NoOp{dst: dst} }