I tried to combine your two examples, so that a circular buffer is used for a high definition stream and a lower resolution stream is used for motion detection. It doesn't quite seem to work, it seems to detect motion, but doesn't write out the files. Would appreciate if anyone could provide assistance.waveform80 wrote: Have you had a look at the stuff for querying motion vectors from the H.264 encoder in 1.5? Although it's not exactly the same as background averaging and subtraction it seems another reasonable basis for coding motion detection, and given that most of the processing is already done for you in the GPU it's *much* more efficient. There's an example hidden away in the custom outputs section of the docs, and another one buried in the array extensions section. I should probably have put those in a clearly labelled "motion detection" section in the advanced recipes section now I come to think of it!
Code: Select all
from __future__ import division import io import picamera import numpy as np import time import traceback, sys # customisable variables # for some reason the following seems to stall #record_width=1920 #record_height=1080 #analysis_width=480 #analysis_height=270 record_width=640 record_height=480 analysis_width=640 analysis_height=480 framerate=30 motion_detected = False motion_dtype = np.dtype([ ('x', 'i1'), ('y', 'i1'), ('sad', 'u2'), ]) class MyMotionDetector(object): def __init__(self, camera): self.cols = (analysis_width + 15) // 16 self.cols += 1 # there's always an extra column self.rows = (analysis_height + 15) // 16 def write(self, s): global motion_detected try: # Load the motion data from the string to the written data = np.fromstring(s, dtype=motion_dtype) # Re-shape it and calculate the magnitude of each vector data = data.reshape((self.rows, self.cols)) data = np.sqrt( np.square(data['x'].astype(np.float)) + np.square(data['y'].astype(np.float)) ).clip(0, 255).astype(np.uint8) # If there're more than 10 vectors with a magnitude greater # than 60, then say we've detected motion if (data > 60).sum() > 10: print('Motion detected!') motion_detected = True else: print('Motion stopped') motion_detected = False # Pretend we wrote all the bytes of s return len(s) except: traceback.print_exc(file=sys.stdout) raise def write_video(stream): # Write the entire content of the circular buffer to disk. No need to # lock the stream here as we're definitely not writing to it # simultaneously with io.open('before.h264', 'wb') as output: for frame in stream.frames: if frame.header: stream.seek(frame.position) break while True: buf = stream.read1() if not buf: break output.write(buf) # Wipe the circular stream once we're done stream.seek(0) stream.truncate() with picamera.PiCamera() as camera: try: camera.resolution = (record_width, record_height) camera.framerate = framerate camera.start_recording('/dev/null', format='h264', motion_output=MyMotionDetector(camera), resize=(analysis_width, analysis_height)) stream = picamera.PiCameraCircularIO(camera, seconds=2) camera.start_recording(stream, format='h264', splitter_port=2) while True: # wait recording doesn't seem to work, maybe because two recordings are being done # camera.wait_recording(1, splitter_port=2) time.sleep(1) if (motion_detected): print('Splitting recording') # As soon as we detect motion, split the recording to # record the frames "after" motion camera.split_recording('after.h264', splitter_port=2) # Write the seconds "before" motion to disk as well print('Writing \'before\' stream') write_video(stream) # Wait until motion is no longer detected, then split # recording back to the in-memory circular buffer while (motion_detected): time.sleep(1) # wait recording doesn't seem to work # camera.wait_recording(1, splitter_port=2) print('Motion stopped, splitting to stream') camera.split_recording(stream, splitter_port=2) except: traceback.print_exc(file=sys.stdout) raise finally: camera.stop_recording()