-
Notifications
You must be signed in to change notification settings - Fork 129
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
0dc73b7
commit 3a3c562
Showing
3 changed files
with
245 additions
and
1 deletion.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
import cv2.cv as cv | ||
from datetime import datetime | ||
import time | ||
|
||
class MotionDetector(): | ||
|
||
def onChange(self, val): #callback when the user change the ceil | ||
self.ceil = val | ||
|
||
def __init__(self,ceil=8, 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.frame1gray = cv.CreateMat(self.frame.height, self.frame.width, cv.CV_8U) #Gray frame at t-1 | ||
cv.CvtColor(self.frame, self.frame1gray, cv.CV_RGB2GRAY) | ||
|
||
#Will hold the thresholded result | ||
self.res = cv.CreateMat(self.frame.height, self.frame.width, cv.CV_8U) | ||
|
||
self.frame2gray = cv.CreateMat(self.frame.height, self.frame.width, cv.CV_8U) #Gray frame at t | ||
|
||
self.width = self.frame.width | ||
self.height = self.frame.height | ||
self.nb_pixels = self.width * self.height | ||
self.ceil = ceil | ||
self.isRecording = False | ||
self.trigger_time = 0 #Hold timestamp of the last detection | ||
|
||
if showWindows: | ||
cv.NamedWindow("Image") | ||
cv.CreateTrackbar("Mytrack", "Image", self.ceil, 100, self.onChange) | ||
|
||
def initRecorder(self): #Create the recorder | ||
codec = cv.CV_FOURCC('D', 'I', 'V', 'X') | ||
#codec = cv.CV_FOURCC("D", "I", "B", " ") | ||
self.writer=cv.CreateVideoWriter(datetime.now().strftime("%b-%d_%H:%M:%S")+".avi", codec, 15, cv.GetSize(self.frame), 1) | ||
#FPS set at 15 because it seems to be the fps of my cam but should be ajusted to your needs | ||
self.font = cv.InitFont(cv.CV_FONT_HERSHEY_SIMPLEX, 1, 1, 0, 2, 8) #Creates a font | ||
|
||
def run(self): | ||
started = time.time() | ||
while True: | ||
|
||
curframe = cv.QueryFrame(self.capture) | ||
instant = time.time() #Get timestamp o the frame | ||
|
||
self.processImage(curframe) #Process the image | ||
|
||
if not self.isRecording: | ||
if self.somethingHasMoved(): | ||
self.trigger_time = instant #Update the trigger_time | ||
if instant > started +5:#Wait 5 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 | ||
else: | ||
if instant >= self.trigger_time +10: #Record during 10 seconds | ||
print "Stop recording" | ||
self.isRecording = False | ||
else: | ||
cv.PutText(curframe,datetime.now().strftime("%b %d, %H:%M:%S"), (25,30),self.font, 0) #Put date on the frame | ||
cv.WriteFrame(self.writer, curframe) #Write the frame | ||
|
||
if self.show: | ||
cv.ShowImage("Image", curframe) | ||
cv.ShowImage("Res", self.res) | ||
|
||
cv.Copy(self.frame2gray, self.frame1gray) | ||
c=cv.WaitKey(1) | ||
if c==27 or c == 1048603: #Break if user enters 'Esc'. | ||
break | ||
|
||
def processImage(self, frame): | ||
cv.CvtColor(frame, self.frame2gray, cv.CV_RGB2GRAY) | ||
|
||
#Absdiff to get the difference between to the frames | ||
cv.AbsDiff(self.frame1gray, self.frame2gray, self.res) | ||
|
||
#Remove the noise and do the threshold | ||
cv.Smooth(self.res, self.res, cv.CV_BLUR, 5,5) | ||
element = cv.CreateStructuringElementEx(5*2+1, 5*2+1, 5, 5, cv.CV_SHAPE_RECT) | ||
cv.MorphologyEx(self.res, self.res, None, None, cv.CV_MOP_OPEN) | ||
cv.MorphologyEx(self.res, self.res, None, None, cv.CV_MOP_CLOSE) | ||
cv.Threshold(self.res, self.res, 10, 255, cv.CV_THRESH_BINARY_INV) | ||
|
||
def somethingHasMoved(self): | ||
nb=0 #Will hold the number of black pixels | ||
|
||
for y in range(self.height): #Iterate the hole image | ||
for x in range(self.width): | ||
if self.res[y,x] == 0.0: #If the pixel is black keep it | ||
nb += 1 | ||
avg = (nb*100.0)/self.nb_pixels #Calculate the average of black pixel in the image | ||
#print "Average: ",avg, "%\r", | ||
if avg > self.ceil:#If over the ceil trigger the alarm | ||
return True | ||
else: | ||
return False | ||
|
||
if __name__=="__main__": | ||
detect = MotionDetector(doRecord=False) | ||
detect.run() |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
import cv2.cv as cv | ||
from datetime import datetime | ||
import time | ||
|
||
class MotionDetector(): | ||
|
||
def onChange(self, val): #callback when the user change the ceil | ||
self.ceil = val | ||
|
||
def __init__(self,ceil=25, 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.tmp = None | ||
|
||
self.width = self.frame.width | ||
self.height = self.frame.height | ||
self.surface = self.width * self.height | ||
self.currentsurface = 0 | ||
self.currentcontours = None | ||
self.ceil = ceil | ||
self.isRecording = False | ||
self.trigger_time = 0 #Hold timestamp of the last detection | ||
|
||
if showWindows: | ||
cv.NamedWindow("Image") | ||
cv.CreateTrackbar("Mytrack", "Image", self.ceil, 100, self.onChange) | ||
|
||
def initRecorder(self): #Create the recorder | ||
codec = cv.CV_FOURCC('D', 'I', 'V', 'X') | ||
#codec = cv.CV_FOURCC("D", "I", "B", " ") | ||
self.writer=cv.CreateVideoWriter(datetime.now().strftime("%b-%d_%H:%M:%S")+".avi", codec, 15, cv.GetSize(self.frame), 1) | ||
#FPS set at 15 because it seems to be the fps of my cam but should be ajusted to your needs | ||
self.font = cv.InitFont(cv.CV_FONT_HERSHEY_SIMPLEX, 1, 1, 0, 2, 8) #Creates a font | ||
|
||
def run(self): | ||
started = time.time() | ||
while True: | ||
|
||
currentframe = cv.QueryFrame(self.capture) | ||
instant = time.time() #Get timestamp o 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 +5:#Wait 5 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 | ||
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("%b %d, %H:%M:%S"), (25,30),self.font, 0) #Put date on the frame | ||
cv.WriteFrame(self.writer, currentframe) #Write the frame | ||
|
||
if self.show: | ||
cv.ShowImage("Image", currentframe) | ||
|
||
c=cv.WaitKey(1) % 0x100 | ||
if c==27 or c == 10: #Break if user enters 'Esc'. | ||
break | ||
|
||
def processImage(self, curframe): | ||
cv.Smooth(curframe, curframe, cv.CV_GAUSSIAN, 3, 0) #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.tmp = cv.CloneImage(curframe) | ||
cv.ConvertScale(curframe, self.average_frame, 1.0, 0.0) | ||
else: | ||
cv.RunningAvg(curframe, self.average_frame, 0.020, None) #Compute the average | ||
|
||
# Convert the scale of the moving average. | ||
cv.ConvertScale(self.average_frame, self.tmp, 1.0, 0.0) | ||
|
||
# Minus the current frame from the moving average. | ||
cv.AbsDiff(curframe, self.tmp, self.absdiff_frame) | ||
|
||
#Convert the image so that it can be thresholded | ||
cv.CvtColor(self.absdiff_frame, self.gray_frame, cv.CV_RGB2GRAY) | ||
cv.Threshold(self.gray_frame, self.gray_frame, 70, 255, cv.CV_THRESH_BINARY) | ||
|
||
cv.Dilate(self.gray_frame, self.gray_frame, None, 18) #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.ceil: | ||
return True | ||
else: | ||
return False | ||
|
||
|
||
if __name__=="__main__": | ||
detect = MotionDetector(doRecord=True) | ||
detect.run() |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,13 @@ | ||
Motion-detection-OpenCV | ||
======================= | ||
|
||
Python/OpenCV script that detection motion on webcam and allow record it to a file | ||
Python/OpenCV script that detection motion on webcam and allow record it to a file | ||
|
||
|
||
MotionDetector Features | ||
----------------------- | ||
|
||
* Detect moving object by calculating the average of changing over the hole image. | ||
* Record to a file the webcam video stream only when something moves. | ||
|
||
Note: motion-detector-contours is an alternative method to detect based on contours and area calculation. |