Created
September 24, 2016 15:41
-
-
Save initbrain/85f523640b2951a1719a75fed77a3cfb to your computer and use it in GitHub Desktop.
A Python/OpenCV script that detect motion on webcam and allow record it to a file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
import cv2.cv as cv | |
from datetime import datetime | |
import time | |
class MotionDetectorAdaptative(): | |
def onThresholdChange(self, val): # Callback when the user change the detection threshold | |
self.threshold = val | |
def __init__(self, threshold=1, doRecord=True, showWindows=True): | |
self.writer = None | |
self.font = None | |
self.doRecord=doRecord # Either or not record the moving object | |
self.show = showWindows # Either or not show the 2 windows | |
self.frame = None | |
self.capture=cv.CaptureFromCAM(0) | |
self.frame = cv.QueryFrame(self.capture) # Take a frame to init recorder | |
if doRecord: | |
self.initRecorder() | |
self.gray_frame = cv.CreateImage(cv.GetSize(self.frame), cv.IPL_DEPTH_8U, 1) | |
self.average_frame = cv.CreateImage(cv.GetSize(self.frame), cv.IPL_DEPTH_32F, 3) | |
self.absdiff_frame = None | |
self.previous_frame = None | |
self.surface = self.frame.width * self.frame.height | |
self.currentsurface = 0 | |
self.currentcontours = None | |
self.threshold = threshold | |
self.isRecording = False | |
self.trigger_time = 0 # Hold timestamp of the last detection | |
if showWindows: | |
cv.NamedWindow("Image") | |
cv.CreateTrackbar("Detection treshold: ", "Image", self.threshold, 100, self.onThresholdChange) | |
def initRecorder(self): # Create the recorder | |
codec = cv.CV_FOURCC('M', 'J', 'P', 'G') | |
self.writer = cv.CreateVideoWriter(datetime.now().strftime("%Y%m%d_%H%M%S")+".wmv", codec, 8, cv.GetSize(self.frame), 1) | |
# FPS set to 30 because it seems to be the fps of my cam but should be ajusted to your needs | |
self.countdownFont = cv.InitFont(cv.CV_FONT_HERSHEY_SIMPLEX, 3, 3, 0, 5, 8) # Creates a font | |
self.timeFont = cv.InitFont(cv.CV_FONT_HERSHEY_SIMPLEX, 1, 1, 0, 2, 8) # Creates a font | |
def run(self): | |
started = time.time() | |
while True: | |
c = cv.WaitKey(1) % 0x100 | |
if c==27 or c == 10: # Break if user enters 'Esc' or 'Enter'. | |
break | |
currentframe = cv.QueryFrame(self.capture) | |
instant = time.time() # Get timestamp of the frame | |
self.processImage(currentframe) # Process the image | |
if not self.isRecording: | |
if self.somethingHasMoved(): | |
self.trigger_time = instant # Update the trigger_time | |
if instant > started +10: # Wait 10 second after the webcam start for luminosity adjusting etc.. | |
print "Something is moving !" | |
if self.doRecord: # Set isRecording=True only if we record a video | |
self.isRecording = True | |
# Put a countdown on the frame | |
if instant < started +10: | |
cv.PutText(currentframe, str(int(11-(instant-started))), (self.frame.width/2-10, self.frame.height/2+15), self.countdownFont, 0) | |
else: | |
cv.DrawContours(currentframe, self.currentcontours, (0, 0, 255), (0, 255, 0), 1, 2, cv.CV_FILLED) | |
else: | |
if instant >= self.trigger_time +10: # Record during 10 seconds | |
print "Stop recording" | |
self.isRecording = False | |
else: | |
cv.PutText(currentframe, datetime.now().strftime("%Y.%m.%d %H:%M:%S"), (10, 30), self.timeFont, 0) # Put date on the frame | |
cv.WriteFrame(self.writer, currentframe) # Write the frame | |
if self.show: | |
cv.ShowImage("Image", currentframe) | |
def processImage(self, curframe): | |
cv.Smooth(curframe, curframe) # Remove false positives | |
if not self.absdiff_frame: # For the first time put values in difference, temp and moving_average | |
self.absdiff_frame = cv.CloneImage(curframe) | |
self.previous_frame = cv.CloneImage(curframe) | |
cv.Convert(curframe, self.average_frame) # Should convert because after runningavg take 32F pictures | |
else: | |
cv.RunningAvg(curframe, self.average_frame, 0.05) # Compute the average | |
cv.Convert(self.average_frame, self.previous_frame) # Convert back to 8U frame | |
cv.AbsDiff(curframe, self.previous_frame, self.absdiff_frame) # moving_average - curframe | |
cv.CvtColor(self.absdiff_frame, self.gray_frame, cv.CV_RGB2GRAY) # Convert to gray otherwise can't do threshold | |
cv.Threshold(self.gray_frame, self.gray_frame, 50, 255, cv.CV_THRESH_BINARY) | |
cv.Dilate(self.gray_frame, self.gray_frame, None, 15) # To get object blobs | |
cv.Erode(self.gray_frame, self.gray_frame, None, 10) | |
def somethingHasMoved(self): | |
# Find contours | |
storage = cv.CreateMemStorage(0) | |
contours = cv.FindContours(self.gray_frame, storage, cv.CV_RETR_EXTERNAL, cv.CV_CHAIN_APPROX_SIMPLE) | |
self.currentcontours = contours # Save contours | |
while contours: # For all contours compute the area | |
self.currentsurface += cv.ContourArea(contours) | |
contours = contours.h_next() | |
avg = (self.currentsurface*100)/self.surface # Calculate the average of contour area on the total size | |
self.currentsurface = 0 # Put back the current surface to 0 | |
if avg > self.threshold: | |
return True | |
else: | |
return False | |
if __name__=="__main__": | |
detect = MotionDetectorAdaptative(doRecord=True) | |
detect.run() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment