update comments
This commit is contained in:
parent
e8ddc38159
commit
77c049aa52
73
main.go
73
main.go
@ -14,12 +14,12 @@ import (
|
|||||||
const (
|
const (
|
||||||
minimumMotionArea = 3000 // Motion detection minimum area needed to move
|
minimumMotionArea = 3000 // Motion detection minimum area needed to move
|
||||||
recordLengthAfterMotion = 30 // Number of seconds to keep recording going after motion was last detected
|
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
|
motionDetectInterval = 30 // Number of frames between motion detection algorithm running
|
||||||
deviceID = 0 // raspberry pi camera index
|
deviceID = 0 // Raspberry Pi camera index - should be 0 if using the camera connector. Might be different if using USB webcam
|
||||||
syncFolder = "/sync"
|
syncFolder = "/sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var ( // evil global variables
|
||||||
lastMotionDetectedTime time.Time
|
lastMotionDetectedTime time.Time
|
||||||
currentRecording *gocv.VideoWriter
|
currentRecording *gocv.VideoWriter
|
||||||
img, imgDelta, imgThresh gocv.Mat
|
img, imgDelta, imgThresh gocv.Mat
|
||||||
@ -27,21 +27,8 @@ var (
|
|||||||
osdColor color.RGBA
|
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() {
|
func main() {
|
||||||
defer img.Close()
|
// Override log output from stdout to a file on disk
|
||||||
defer imgDelta.Close()
|
|
||||||
defer imgThresh.Close()
|
|
||||||
defer mog2.Close()
|
|
||||||
|
|
||||||
f, err := os.OpenFile(fmt.Sprintf("%s/storage-security.log", syncFolder), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
f, err := os.OpenFile(fmt.Sprintf("%s/storage-security.log", syncFolder), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("error opening log file: %v", err)
|
log.Fatalf("error opening log file: %v", err)
|
||||||
@ -50,6 +37,20 @@ func main() {
|
|||||||
log.SetOutput(f)
|
log.SetOutput(f)
|
||||||
log.Print("storage-security starting")
|
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)
|
webcam, err := gocv.OpenVideoCapture(deviceID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("error opening video capture device: %v\n", deviceID)
|
log.Fatalf("error opening video capture device: %v\n", deviceID)
|
||||||
@ -59,7 +60,9 @@ func main() {
|
|||||||
|
|
||||||
fmt.Printf("Start reading device: %v\n", deviceID)
|
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++ {
|
for i := 0; i < 20; i++ {
|
||||||
if ok := webcam.Read(&img); !ok {
|
if ok := webcam.Read(&img); !ok {
|
||||||
log.Fatalf("video capture device closed: %v\n", deviceID)
|
log.Fatalf("video capture device closed: %v\n", deviceID)
|
||||||
@ -68,9 +71,8 @@ func main() {
|
|||||||
detectMotion(img)
|
detectMotion(img)
|
||||||
}
|
}
|
||||||
|
|
||||||
frameCount := 0
|
// ACTION
|
||||||
|
// main loop - each iteration is a different video frame
|
||||||
// main loop
|
|
||||||
for {
|
for {
|
||||||
if ok := webcam.Read(&img); !ok {
|
if ok := webcam.Read(&img); !ok {
|
||||||
log.Fatalf("video capture device closed: %v\n", deviceID)
|
log.Fatalf("video capture device closed: %v\n", deviceID)
|
||||||
@ -80,9 +82,12 @@ func main() {
|
|||||||
continue
|
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 frameCount >= motionDetectInterval {
|
||||||
if detectMotion(img) {
|
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)) { //
|
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"))
|
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)
|
log.Printf("motion detected, started recording to file named %s", fileName)
|
||||||
@ -99,11 +104,10 @@ func main() {
|
|||||||
}
|
}
|
||||||
frameCount++
|
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 {
|
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)
|
gocv.PutText(&img, time.Now().Format(time.RFC3339), image.Pt(10, 20), gocv.FontHersheyPlain, 1.2, osdColor, 2)
|
||||||
|
|
||||||
currentRecording.Write(img)
|
currentRecording.Write(img)
|
||||||
// Determine if we should stop recording
|
// Determine if we should stop recording
|
||||||
if lastMotionDetectedTime.Add(time.Second * recordLengthAfterMotion).Before(time.Now()) {
|
if lastMotionDetectedTime.Add(time.Second * recordLengthAfterMotion).Before(time.Now()) {
|
||||||
@ -121,20 +125,29 @@ func main() {
|
|||||||
|
|
||||||
// Returns true if motion detected in current frame
|
// Returns true if motion detected in current frame
|
||||||
func detectMotion(frame gocv.Mat) bool {
|
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)
|
mog2.Apply(frame, &imgDelta)
|
||||||
|
|
||||||
// remaining cleanup of the image to use for finding contours.
|
// Next the goal is to find contours in the foreground image
|
||||||
// first use threshold
|
// 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)
|
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))
|
kernel := gocv.GetStructuringElement(gocv.MorphRect, image.Pt(3, 3))
|
||||||
defer kernel.Close()
|
defer kernel.Close()
|
||||||
gocv.Dilate(imgThresh, &imgThresh, kernel)
|
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)
|
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 {
|
for _, c := range contours {
|
||||||
area := gocv.ContourArea(c)
|
area := gocv.ContourArea(c)
|
||||||
if area < minimumMotionArea {
|
if area < minimumMotionArea {
|
||||||
|
Loading…
Reference in New Issue
Block a user