Difference between revisions of "movement.py"
From Noah.org
Jump to navigationJump to searchm |
|||
(One intermediate revision by the same user not shown) | |||
Line 6: | Line 6: | ||
Under Ubuntu install "python-opencv" package. | Under Ubuntu install "python-opencv" package. | ||
− | + | Version 2 and Version 1 are a bit simpler. They follow after version 3 on this page. | |
+ | |||
+ | == Version 3 == | ||
+ | |||
<pre> | <pre> | ||
#!/usr/bin/env python | #!/usr/bin/env python | ||
Line 22: | Line 25: | ||
During period of movement the individual camera frames will be saved. | During period of movement the individual camera frames will be saved. | ||
− | THIS IS A ROUGH DRAFT | + | THIS IS A ROUGH DRAFT, BUT EVERYTHING WORKS. |
+ | |||
+ | On OS X: | ||
+ | brew tap homebrew/science | ||
+ | brew install opencv # or, "brew install opencv --env=std" | ||
+ | export PYTHONPATH=/usr/local/lib/python2.7/site-packages:${PYTHONPATH} | ||
+ | |||
+ | Video playback on OS X: | ||
+ | brew install mplayer # takes a long time (~ 5 minutes) | ||
+ | brew install mencoder | ||
+ | mplayer -vo corevideo "mf://movement*.png" -mf type=png:fps=30 -loop 0 | ||
+ | |||
+ | Video encoding on OS X: | ||
+ | mencoder "mf://*.png" -mf type=png:fps=25 -ovc lavc -lavcopts vcodec=mpeg4 -o output.mov | ||
This docstring will be printed by the script if there is an error or | This docstring will be printed by the script if there is an error or | ||
Line 314: | Line 330: | ||
</pre> | </pre> | ||
+ | == Version 1 == | ||
<pre> | <pre> | ||
#!/usr/bin/env python | #!/usr/bin/env python |
Latest revision as of 19:59, 6 May 2015
This is a simple movement detector using a video camera and OpenCV (Python cv2).
Under Ubuntu install "python-opencv" package.
Version 2 and Version 1 are a bit simpler. They follow after version 3 on this page.
Version 3
#!/usr/bin/env python ''' SYNOPSIS movement.py [-h,--help] [-v,--verbose] [--version] DESCRIPTION This is a tool that will watch the output of a video camera. It will highlight any movement that it sees. It also detects the relative amount of motion and stillnes and indicates significant changes on stdout. During period of movement the individual camera frames will be saved. THIS IS A ROUGH DRAFT, BUT EVERYTHING WORKS. On OS X: brew tap homebrew/science brew install opencv # or, "brew install opencv --env=std" export PYTHONPATH=/usr/local/lib/python2.7/site-packages:${PYTHONPATH} Video playback on OS X: brew install mplayer # takes a long time (~ 5 minutes) brew install mencoder mplayer -vo corevideo "mf://movement*.png" -mf type=png:fps=30 -loop 0 Video encoding on OS X: mencoder "mf://*.png" -mf type=png:fps=25 -ovc lavc -lavcopts vcodec=mpeg4 -o output.mov This docstring will be printed by the script if there is an error or if the user requests help (-h or --help). EXAMPLES The following are some examples of how to use this script. $ movement.py --version 1 EXIT STATUS This exits with status 0 on success and 1 otherwise. This exits with a status greater than 1 if there was an unexpected run-time error. AUTHOR Noah Spurrier <noah@noah.org> LICENSE This license is approved by the OSI and FSF as GPL-compatible. http://opensource.org/licenses/isc-license.txt Copyright (c) 2015, Noah Spurrier PERMISSION TO USE, COPY, MODIFY, AND/OR DISTRIBUTE THIS SOFTWARE FOR ANY PURPOSE WITH OR WITHOUT FEE IS HEREBY GRANTED, PROVIDED THAT THE ABOVE COPYRIGHT NOTICE AND THIS PERMISSION NOTICE APPEAR IN ALL COPIES. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. VERSION Version 3 ''' __version__ = 'Version 3' __date__ = '2015-05-01 15:46:55:z' __author__ = 'Noah Spurrier <noah@noah.org>' import sys import os import traceback import optparse import time import logging import cv2 import numpy import sys import time #from pexpect import run, spawn DELTA_COUNT_THRESHOLD = 1000 def delta_images(t0, t1, t2): d1 = cv2.absdiff(t2, t0) return d1 # d1 = cv2.absdiff(t2, t1) # return d1 # d2 = cv2.absdiff(t1, t0) # return cv2.bitwise_and(d1, d2) #for cn in range(2,-1,-1): for cn in range(0,3): cam = cv2.VideoCapture(cn) if cam.isOpened(): break if not cam.isOpened(): sys.stderr.write('ERROR: Did not open a camera.\n') sys.exit(1) print ("Running with camera number %d." % cn) print type(cam) print str(cam) #time.sleep(20) # 76800 pixels #cam.set(3,640) #cam.set(4,480) # 307200 pixels cam.set(3,640) cam.set(4,480) #cam.set(3,1024) #cam.set(4,768) winName = "image diff" cv2.namedWindow(winName, cv2.CV_WINDOW_AUTOSIZE) # Fill the queue. #t_minus = cv2.cvtColor(cam.read()[1], cv2.COLOR_RGB2GRAY) #t_now = cv2.cvtColor(cam.read()[1], cv2.COLOR_RGB2GRAY) #t_plus = cv2.cvtColor(cam.read()[1], cv2.COLOR_RGB2GRAY) #t_minus = cv2.cvtColor(cam.read()[1], cv2.COLOR_RGB2BGR) #t_now = cv2.cvtColor(cam.read()[1], cv2.COLOR_RGB2BGR) #t_plus = cv2.cvtColor(cam.read()[1], cv2.COLOR_RGB2BGR) t_minus = cam.read()[1] t_now = cam.read()[1] t_plus = cam.read()[1] t_now = cv2.resize(t_now, (640, 480)) t_minus = cv2.resize(t_minus, (640, 480)) t_plus = cv2.resize(t_plus, (640, 480)) #kernel = numpy.ones((5,5), numpy.uint8) delta_count_last = 1 #HYSTERESIS = try: os.mkdir("MOVEMENT_FRAMES") except: pass start_time = time.time() record_video_state = False while True: delta_view = delta_images(t_minus, t_now, t_plus) # cv2.morphologyEx(delta_view, cv2.MORPH_OPEN, kernel) # cv2.morphologyEx(delta_view, cv2.MORPH_CLOSE, kernel) retval, delta_view = cv2.threshold(delta_view, 16, 255, 3) cv2.normalize(delta_view, delta_view, 0, 255, cv2.NORM_MINMAX) img_count_view = cv2.cvtColor(delta_view, cv2.COLOR_RGB2GRAY) delta_count = cv2.countNonZero(img_count_view) delta_view = cv2.flip(delta_view, 1) cv2.putText(delta_view, "DELTA: %d"%(delta_count), (5, 15), cv2.FONT_HERSHEY_PLAIN, 0.8, (255,255,255)) cv2.imshow(winName, delta_view) # if delta_count_last != 0 or delta_count != 0: # sys.stdout.write("%d\n"%(delta_count)) if (delta_count_last < DELTA_COUNT_THRESHOLD and delta_count >= DELTA_COUNT_THRESHOLD): record_video_state = True sys.stdout.write("MOVEMENT %f\n" % time.time()) sys.stdout.flush() elif delta_count_last >= DELTA_COUNT_THRESHOLD and delta_count < DELTA_COUNT_THRESHOLD: record_video_state = False sys.stdout.write("STILL %f\n" % time.time()) sys.stdout.flush() now=time.time() if record_video_state == True: cv2.imwrite('MOVEMENT_FRAMES/movement-pong-%f.png' % (now-start_time),delta_view) delta_count_last = delta_count # move images through the queue. t_minus = t_now t_now = t_plus t_plus = cam.read()[1] t_plus = cv2.blur(t_plus,(8,8)) t_plus = cv2.resize(t_plus, (640, 480)) #t_plus = cv2.cvtColor(cam.read()[1], cv2.COLOR_RGB2BGR) #t_plus = cv2.cvtColor(cam.read()[1], cv2.COLOR_RGB2GRAY) #t_plus = cv2.bilateralFilter(t_plus,9,75,75) # Wait up to 10ms for a key press. # If the key is the ESC or 'q' then quit. key = cv2.waitKey(10) if key == 0x1b or key == ord('q'): cv2.destroyWindow(winName) break # vim: set ft=python fileencoding=utf-8 sr et ts=4 sw=4 : See help 'modeline'
Version 2
#!/usr/bin/env python '''This is a video motion detector. This uses about 5% CPU running with continuous motion detection on a i5-3320M 2.6GHz with 16 GB RAM under no other load. AUTHOR Noah Spurrier <noah@noah.org> LICENSE This license is approved by the OSI and FSF as GPL-compatible. http://opensource.org/licenses/isc-license.txt Copyright (c) 2014, Noah Spurrier PERMISSION TO USE, COPY, MODIFY, AND/OR DISTRIBUTE THIS SOFTWARE FOR ANY PURPOSE WITH OR WITHOUT FEE IS HEREBY GRANTED, PROVIDED THAT THE ABOVE COPYRIGHT NOTICE AND THIS PERMISSION NOTICE APPEAR IN ALL COPIES. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. VERSION Version 2 ''' import cv2 import sys import time # The two main parameters that affect movement detection sensitivity # are BLUR_SIZE and NOISE_CUTOFF. Both have little direct effect on # CPU usage. In theory a smaller BLUR_SIZE should use less CPU, but # for the range of values that are effective the difference is # negligible. The default values are effective with on most light # conditions with the cameras I have tested. At these levels the # detectory can easily trigger on eye blinks, yet not trigger if the # subject remains still without blinking. These levels will likely be # useless outdoors. BLUR_SIZE = 3 NOISE_CUTOFF = 12 # Ah, but the third main parameter that affects movement detection # sensitivity is the time between frames. I like about 10 frames per # second. Even 4 FPS is fine. #FRAMES_PER_SECOND = 10 cam = cv2.VideoCapture(0) # 320*240 = 76800 pixels #cam.set(3, 320) #cam.set(4, 240) # 640*480 = 307200 pixels cam.set(3,640) cam.set(4,480) window_name = "delta view" cv2.namedWindow(window_name, cv2.CV_WINDOW_AUTOSIZE) window_name_now = "now view" cv2.namedWindow(window_name_now, cv2.CV_WINDOW_AUTOSIZE) # Stabilize the detector by letting the camera warm up and # seeding the first frames. frame_now = cam.read()[1] frame_now = cam.read()[1] frame_now = cv2.cvtColor(frame_now, cv2.COLOR_RGB2GRAY) frame_now = cv2.blur(frame_now, (BLUR_SIZE, BLUR_SIZE)) frame_prior = frame_now delta_count_last = 1 while True: frame_delta = cv2.absdiff(frame_prior, frame_now) frame_delta = cv2.threshold(frame_delta, NOISE_CUTOFF, 255, 3)[1] delta_count = cv2.countNonZero(frame_delta) # Visual detection statistics output. # Normalize improves brightness and contrast. # Mirror view makes self display more intuitive. cv2.normalize(frame_delta, frame_delta, 0, 255, cv2.NORM_MINMAX) frame_delta = cv2.flip(frame_delta, 1) cv2.putText(frame_delta, "DELTA: %d" % (delta_count), (5, 15), cv2.FONT_HERSHEY_PLAIN, 0.8, (255, 255, 255)) cv2.imshow(window_name, frame_delta) #frame_delta = cv2.threshold(frame_delta, 92, 255, 0)[1] dst = cv2.flip(frame_now, 1) dst = cv2.addWeighted(dst,1.0, frame_delta,0.9,0) cv2.imshow(window_name_now, dst) # Stdout output. # Only output when there is new movement or when movement stops. # Time codes are in epoch time format. if (delta_count_last == 0 and delta_count != 0): sys.stdout.write("MOVEMENT %f\n" % time.time()) sys.stdout.flush() elif delta_count_last != 0 and delta_count == 0: sys.stdout.write("STILL %f\n" % time.time()) sys.stdout.flush() delta_count_last = delta_count # Advance the frames. frame_prior = frame_now frame_now = cam.read()[1] frame_now = cv2.cvtColor(frame_now, cv2.COLOR_RGB2GRAY) frame_now = cv2.blur(frame_now, (BLUR_SIZE, BLUR_SIZE)) # Wait up to 10ms for a key press. Quit if the key is either ESC or 'q'. key = cv2.waitKey(10) if key == 0x1b or key == ord('q'): cv2.destroyWindow(window_name) break # # Morphology noise filters. They work, but really don't help much. # # A simple noise cutoff and blur is good enough. #kernel = numpy.ones((5,5), numpy.uint8) # cv2.morphologyEx(frame_delta, cv2.MORPH_OPEN, kernel) # cv2.morphologyEx(frame_delta, cv2.MORPH_CLOSE, kernel) # # A bilateral filter also seems pointless. # #frame_now = cv2.bilateralFilter(frame_now,9,75,75) # vim: set ft=python fileencoding=utf-8 sr et ts=4 sw=4 : See help 'modeline'
Version 1
#!/usr/bin/env python '''This is a simple video movement detector. This uses about 5% CPU running with continuous motion detection on a i5-3320M 2.6GHz with 16 GB RAM under no other load. AUTHOR Noah Spurrier <noah@noah.org> LICENSE This license is approved by the OSI and FSF as GPL-compatible. http://opensource.org/licenses/isc-license.txt Copyright (c) 2014, Noah Spurrier PERMISSION TO USE, COPY, MODIFY, AND/OR DISTRIBUTE THIS SOFTWARE FOR ANY PURPOSE WITH OR WITHOUT FEE IS HEREBY GRANTED, PROVIDED THAT THE ABOVE COPYRIGHT NOTICE AND THIS PERMISSION NOTICE APPEAR IN ALL COPIES. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. VERSION Version 1 ''' import cv2 import sys import time # The two main parameters that affect movement detection sensitivity # are BLUR_SIZE and NOISE_CUTOFF. Both have little direct effect on # CPU usage. In theory a smaller BLUR_SIZE should use less CPU, but # for the range of values that are effective the difference is # negligible. The default values are effective with on most light # conditions with the cameras I have tested. At these levels the # detector can easily trigger on eye blinks, yet not trigger if the # subject remains still without blinking. These levels will likely be # useless outdoors. BLUR_SIZE = 3 NOISE_CUTOFF = 12 # Ah, but the third main parameter that affects movement detection # sensitivity is the time between frames. I like about 10 frames per # second. Even 4 FPS is fine. #FRAMES_PER_SECOND = 10 cam = cv2.VideoCapture(0) # 320*240 = 76800 pixels cam.set(3, 320) cam.set(4, 240) # 640*480 = 307200 pixels #cam.set(3,640) #cam.set(4,480) window_name = "delta view" cv2.namedWindow(window_name, cv2.CV_WINDOW_AUTOSIZE) # Stabilize the detector by letting the camera warm up and # seeding the first frames. frame_now = cam.read()[1] frame_now = cam.read()[1] frame_now = cv2.cvtColor(frame_now, cv2.COLOR_RGB2GRAY) frame_now = cv2.blur(frame_now, (BLUR_SIZE, BLUR_SIZE)) frame_prior = frame_now delta_count_last = 1 while True: frame_delta = cv2.absdiff(frame_prior, frame_now) frame_delta = cv2.threshold(frame_delta, NOISE_CUTOFF, 255, 3)[1] delta_count = cv2.countNonZero(frame_delta) # Visual detection statistics output. # Normalize improves brightness and contrast. # Mirror view makes self display more intuitive. cv2.normalize(frame_delta, frame_delta, 0, 255, cv2.NORM_MINMAX) frame_delta = cv2.flip(frame_delta, 1) cv2.putText(frame_delta, "DELTA: %d" % (delta_count), (5, 15), cv2.FONT_HERSHEY_PLAIN, 0.8, (255, 255, 255)) cv2.imshow(window_name, frame_delta) # Stdout output. # Only output when there is new movement or when movement stops. # Time codes are in epoch time format. if (delta_count_last == 0 and delta_count != 0): sys.stdout.write("MOVEMENT %f\n" % time.time()) sys.stdout.flush() elif delta_count_last != 0 and delta_count == 0: sys.stdout.write("STILL %f\n" % time.time()) sys.stdout.flush() delta_count_last = delta_count # Advance the frames. frame_prior = frame_now frame_now = cam.read()[1] frame_now = cv2.cvtColor(frame_now, cv2.COLOR_RGB2GRAY) frame_now = cv2.blur(frame_now, (BLUR_SIZE, BLUR_SIZE)) # Wait up to 10ms for a key press. Quit if the key is either ESC or 'q'. key = cv2.waitKey(10) if key == 0x1b or key == ord('q'): cv2.destroyWindow(window_name) break # vim: set ft=python fileencoding=utf-8 sr et ts=4 sw=4 : See help 'modeline'