lewisdale.dev/src/blog/posts/2014/7/motion-sensitive-image-capturing-in-python.md
2023-12-26 14:35:09 +00:00

81 lines
3.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: "Motion Sensitive Image Capturing In Python"
date: 2014-07-28T00:00:00
slug: motion-sensitive-image-capturing-in-python
---
This is quite a nice little script that uses the PyGame library to access the computers webcam, and then takes a succession of photos, comparing percentage differences between the photos taken to determine whether or not any motion has occurred. If it does detect motion, then it captures 2 photos per second for 30 seconds and saves them to the file system.
While this script uses the PyGame library for image capture, it could easily be modified to use other libraries or image capture methods - such as using the camera module on a Raspberry Pi (see [this](http://www.raspberrypi.org/documentation/usage/camera/python/README.md) guide for instructions)
## Prerequisites
* [PyGame Library](http://www.pygame.org/news.html)
* Python Imaging Library (PIL) - [Pillow](http://pillow.readthedocs.org/en/latest/index.html)
* A webcam with up-to-date drivers installed
## The clever bit
The clever bit wasnt actually my idea - for a while I was struggling for ways to compare still images, until I found [this gem](http://rosettacode.org/wiki/Percentage_difference_between_images#Python) on Rosetta Code that returns a percentage difference between the two images.
So what this little bit of code actually does is:
* Turn the two arrays of image data into one array of tuples, with each tuple representing equivalent pixels in each image
* If the image is grayscale, just goes through each pixel and adds the difference in the numerical values of those pixels together
* If the image is in RGB mode, it does exactly the same but with the values of the different colour bands, instead of just the pixel values
* _ncomponents_ is the number of the components in the image (i.e. width _height_ number of bands)
* *hen get the percentage of “dif” relative to the number of possible colours in the image (255), and divide it by the number of components
Its a little convoluted, but it gives quite a good difference in the image.
One thing Im not 100% sure on is the boundary for motion capture - Ive set it to a 2.5% difference which worked well for me, but if youre outdoors its best to make that higher, to account for all of the extra movement (e.g. wind)
## The script
Download the script [here](/files/motion_33.py)
```python
import pygame
import pygame.camera as camera
import time
import pygame.image as im
from PIL import Image
from itertools import izip
import os
camera.init()
cam = camera.Camera(camera.list_cameras()[0],(640,480))
cam.start()
size = cam.get_size()
#This code is from Rosetta Code http://rosettacode.org/wiki/Percentage_difference_between_images#Python
def check_images(i1,i2):
i1 = im.tostring(i1,"RGB")
i1 = Image.frombytes("RGB",size,i1)
i2 = im.tostring(i2,"RGB")
i2 = Image.frombytes("RGB",size,i2)
pairs = izip(i1.getdata(), i2.getdata())
if len(i1.getbands()) == 1:
dif = sum(abs(p1 - p2) for p1,p2 in pairs)
else:
dif = sum(abs(c1 - c2) for p1,p2 in pairs for c1,c2 in zip(p1,p2))
ncomponents = size[0] * size[1] * 3
return (dif / 255.0 * 100) / ncomponents
while 1:
i1 = cam.get_image()
time.sleep(1)
i2 = cam.get_image()
dif = check_images(i1,i2)
if dif > 2.5:
for x in range(0,30):
timestamp = time.strftime("%Y-%m-%d--%H:%M:%S")
image.save(cam.get_image(), timestamp + ".jpg")
time.sleep(0.5)
time.sleep(1)
```