storage-security/main.go

137 lines
3.6 KiB
Go
Raw Normal View History

package main
import (
"fmt"
"image"
"image/color"
"log"
"os"
"time"
"gocv.io/x/gocv"
)
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 = 1 //number of seconds between motion detection attempts // TBD: Implement this, currently does nothing
)
var (
lastMotionDetectedTime time.Time
currentRecording *gocv.VideoWriter
img, imgDelta, imgThresh gocv.Mat
mog2 gocv.BackgroundSubtractorMOG2
osdColor color.RGBA
)
func init() {
if len(os.Args) < 2 {
fmt.Println("How to run:\n\tstorage-security [camera ID]")
return
}
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()
// parse args
deviceID := os.Args[1]
webcam, err := gocv.OpenVideoCapture(deviceID)
if err != nil {
fmt.Printf("Error opening video capture device: %v\n", deviceID)
return
}
defer webcam.Close()
fmt.Printf("Start reading device: %v\n", deviceID)
// This is a warm up ladies and gentlemen.
2020-10-29 03:17:11 +00:00
for i := 0; i < 20; i++ {
if ok := webcam.Read(&img); !ok {
fmt.Printf("Device closed: %v\n", deviceID)
return
}
detectMotion(img)
2020-10-29 03:17:11 +00:00
}
// main loop
for {
if ok := webcam.Read(&img); !ok {
fmt.Printf("Device closed: %v\n", deviceID)
return
}
if img.Empty() {
continue
}
if detectMotion(img) {
// Determine if a new recording needs to start
if time.Now().After(lastMotionDetectedTime.Add(time.Second * recordLengthAfterMotion)) {
fileName := fmt.Sprintf("storage-%s.avi", time.Now().Format(time.RFC3339))
log.Printf("motion detected, started recording to file named %s", fileName)
currentRecording, err = gocv.VideoWriterFile(fileName, "MJPG", 25, img.Cols(), img.Rows(), true)
if err != nil {
fmt.Printf("error opening video writer device: %v\n", err)
return
}
}
// And always update the timestamp
lastMotionDetectedTime = time.Now()
}
// Determine if we are currently recording and if so, then save the frame to the video
if currentRecording != nil {
// OSD / timestamp
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
2020-10-29 03:17:11 +00:00
if lastMotionDetectedTime.Add(time.Second * recordLengthAfterMotion).Before(time.Now()) {
log.Printf("motion has not been detected for the last %d seconds stopping recording to file", recordLengthAfterMotion)
err = currentRecording.Close()
if err != nil {
log.Printf("failed to close openCV file recording handle: %v", err)
}
currentRecording = nil
}
}
}
}
// Returns true if motion detected in current frame
func detectMotion(frame gocv.Mat) bool {
// first phase of cleaning up image, obtain foreground only
mog2.Apply(frame, &imgDelta)
// remaining cleanup of the image to use for finding contours.
// first use threshold
gocv.Threshold(imgDelta, &imgThresh, 25, 255, gocv.ThresholdBinary)
// then dilate
kernel := gocv.GetStructuringElement(gocv.MorphRect, image.Pt(3, 3))
defer kernel.Close()
gocv.Dilate(imgThresh, &imgThresh, kernel)
// now find contours
contours := gocv.FindContours(imgThresh, gocv.RetrievalExternal, gocv.ChainApproxSimple)
for _, c := range contours {
area := gocv.ContourArea(c)
if area < minimumMotionArea {
continue
}
return true
}
return false
}