From 77c049aa52c240eae2f55f7dacd2ff187c692f28 Mon Sep 17 00:00:00 2001 From: Steven Polley Date: Fri, 30 Oct 2020 00:42:14 +0000 Subject: [PATCH] update comments --- main.go | 73 +++++++++++++++++++++++++++++++++------------------------ 1 file changed, 43 insertions(+), 30 deletions(-) diff --git a/main.go b/main.go index 424f356..0e8cff8 100644 --- a/main.go +++ b/main.go @@ -14,12 +14,12 @@ import ( const ( minimumMotionArea = 3000 // Motion detection minimum area needed to move recordLengthAfterMotion = 30 // Number of seconds to keep recording going after motion was last detected - motionDetectInterval = 30 //number of frames between motion detection attempts // TBD: Implement this, currently does nothing - deviceID = 0 // raspberry pi camera index + motionDetectInterval = 30 // Number of frames between motion detection algorithm running + deviceID = 0 // Raspberry Pi camera index - should be 0 if using the camera connector. Might be different if using USB webcam syncFolder = "/sync" ) -var ( +var ( // evil global variables lastMotionDetectedTime time.Time currentRecording *gocv.VideoWriter img, imgDelta, imgThresh gocv.Mat @@ -27,21 +27,8 @@ var ( osdColor color.RGBA ) -func init() { - - img = gocv.NewMat() - imgDelta = gocv.NewMat() - imgThresh = gocv.NewMat() - mog2 = gocv.NewBackgroundSubtractorMOG2() - osdColor = color.RGBA{0, 0, 255, 0} -} - func main() { - defer img.Close() - defer imgDelta.Close() - defer imgThresh.Close() - defer mog2.Close() - + // Override log output from stdout to a file on disk f, err := os.OpenFile(fmt.Sprintf("%s/storage-security.log", syncFolder), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) if err != nil { log.Fatalf("error opening log file: %v", err) @@ -50,6 +37,20 @@ func main() { log.SetOutput(f) log.Print("storage-security starting") + // LIGHTS + img = gocv.NewMat() + imgDelta = gocv.NewMat() + imgThresh = gocv.NewMat() + mog2 = gocv.NewBackgroundSubtractorMOG2() + osdColor = color.RGBA{0, 0, 255, 0} + frameCount := 0 + + defer img.Close() + defer imgDelta.Close() + defer imgThresh.Close() + defer mog2.Close() + + // CAMERA webcam, err := gocv.OpenVideoCapture(deviceID) if err != nil { log.Fatalf("error opening video capture device: %v\n", deviceID) @@ -59,7 +60,9 @@ func main() { fmt.Printf("Start reading device: %v\n", deviceID) - // This is a warm up ladies and gentlemen. + // This is a warm up ladies and gentlemen + // There will always be motion / changes in the first few frames + // this just bypasses starting a recording upon initialization for i := 0; i < 20; i++ { if ok := webcam.Read(&img); !ok { log.Fatalf("video capture device closed: %v\n", deviceID) @@ -68,9 +71,8 @@ func main() { detectMotion(img) } - frameCount := 0 - - // main loop + // ACTION + // main loop - each iteration is a different video frame for { if ok := webcam.Read(&img); !ok { log.Fatalf("video capture device closed: %v\n", deviceID) @@ -80,9 +82,12 @@ func main() { continue } + // Do not run the motion detect algorithm on every frame - it's a very expensive operation (CPU) + // While the raspberry pi hardware can keep up, it consumes a lot of unneeded power + // It will only run every motionDetectInterval frames. if frameCount >= motionDetectInterval { if detectMotion(img) { - // Determine if a new recording needs to start + // Determine if a new recording needs to start, we may already have one running if time.Now().After(lastMotionDetectedTime.Add(time.Second * recordLengthAfterMotion)) { // fileName := fmt.Sprintf("%s/storage-security-%s.avi", syncFolder, time.Now().Format("2006-01-02-15-04-05")) log.Printf("motion detected, started recording to file named %s", fileName) @@ -99,11 +104,10 @@ func main() { } frameCount++ - // Determine if we are currently recording and if so, then save the frame to the video + // Determine if we are currently recording and if so, then write the video frame to the current recording file if currentRecording != nil { - // OSD / timestamp + // OSD / timestamp in upper left of video gocv.PutText(&img, time.Now().Format(time.RFC3339), image.Pt(10, 20), gocv.FontHersheyPlain, 1.2, osdColor, 2) - currentRecording.Write(img) // Determine if we should stop recording if lastMotionDetectedTime.Add(time.Second * recordLengthAfterMotion).Before(time.Now()) { @@ -121,20 +125,29 @@ func main() { // Returns true if motion detected in current frame func detectMotion(frame gocv.Mat) bool { - // first phase of cleaning up image, obtain foreground only + // First phase of cleaning up image, obtain foreground only + // See https://docs.opencv.org/master/d1/dc5/tutorial_background_subtraction.html mog2.Apply(frame, &imgDelta) - // remaining cleanup of the image to use for finding contours. - // first use threshold + // Next the goal is to find contours in the foreground image + // But first it needs to be cleaned up + + // First use threshold + // https://docs.opencv.org/master/d7/d4d/tutorial_py_thresholding.html gocv.Threshold(imgDelta, &imgThresh, 25, 255, gocv.ThresholdBinary) - // then dilate + // Then dilate + // https://docs.opencv.org/3.4/db/df6/tutorial_erosion_dilatation.html kernel := gocv.GetStructuringElement(gocv.MorphRect, image.Pt(3, 3)) defer kernel.Close() gocv.Dilate(imgThresh, &imgThresh, kernel) - // now find contours + // Now find contours + // https://docs.opencv.org/3.4/d4/d73/tutorial_py_contours_begin.html contours := gocv.FindContours(imgThresh, gocv.RetrievalExternal, gocv.ChainApproxSimple) + + // No matter what, every camera frame will be slightly different than the subsequent frame + // Noise is a thing, so we must search the contours larger than a specified threshold for _, c := range contours { area := gocv.ContourArea(c) if area < minimumMotionArea {