Showing posts with label motion. Show all posts
Showing posts with label motion. Show all posts

Wednesday, March 27, 2013

PHP Image Comparison: Motion Detection

So a few years ago I spent quite a while using Java to dismantle images from a video feed to detect motion. Using some of the tricks I learned there, I started porting the concept to PHP. Some of my current goals here include but are not limited to the following:
  • Merging/blending images together.
  • Finding motion/changes between two or more images.
  • Reporting said motion/changes as a center coordinate, bounding rectangle, or best yet a vector.

Download

If you're anything like me and don't want to read all my mumbo-jumbo, then you can just go ahead and checkout and download the code on Github here.

Breakdown

First a break down of the main classes... What do they do:
SimpleImage
A class that represents an image. Has functions that help load images from files, stream images to a client, crop, resize, merge/overlay another image, etc.
State
Basically a wrapper for a 2D array of numbers. Provides functions for determining the average, standard-deviation and some other interesting things about said array of numbers. Also some manipulators to filter or change the numbers into other meaningful data. States can be derived from images, or functions of other states.

Code Examples

Blending images together.
What for?
  • Artificial super long exposure photography.
  • Part of my process to display motion detection in a feed of images from a webcam.
include "SimpleImage.php";
include "Util.php";

// get a list of images from a subdirectory
$imagePath = "./img/3/";
$files = listFilesInDirectory($imagePath);

// setup an image to work with
$baseImage = new SimpleImage($imagePath.$files[0]);

// setup an array of all other images
$images = array();
for ($i = 1; $i < count($files); $i++)
 $images[] = new SimpleImage($imagePath.$files[$i]);

// merge the latter images into the first image.
$baseImage->merge($images);

// stream image to client
$baseImage->output();
The above process was run on some images like this:
The result looking like this:
Motion/Change detection
In the following example, the actual change is determined within the first few lines. The second half of the code is just there to display results of said detection.
include "SimpleImage.php";
include "State.php";

// setup two images to work with
$i1 = new SimpleImage("./img/4/IMG_0392.JPG");
$i2 = new SimpleImage("./img/4/IMG_0393.JPG");

// setup the states that will work with and interpret the numbers
$state = new State(15, 8, $i1);
$state = $state->difference(new State(15, 8, $i2), rgbColorDistance);
$state->abs()->denoiseStdDev()->scale(10)->round(0);

// for purposes of visual debugging, merge the two images together
$i1->merge($i2);

// using the merged image, layer on a visual of state differences
$result = $state->drawImageIndicator($i1);

// $box will hold an array (x,y,w,h) that indicates location of change
$box = $state->getBoundingBox($i1->getWidth(), $i1->getHeight());
$color = imagecolorallocate($result->getImage(), 10, 255, 10);
imagerectangle($result->getImage(), $box["x"]-1, $box["y"]-1, 
 $box["x"]+$box["w"]+1, $box["y"]+$box["h"], 
 $color);

// $cog will hold an array (x,y) indicating center of change
$cog = $state->getCenterOfGravity($i1->getWidth(), $i1->getHeight());
imagearc($result->getImage(), 
 $cog["x"], $cog["y"], 7, 7,  0, 360, 
 imagecolorallocate($result->getImage(), 255, 255, 0));
imagearc($result->getImage(), 
 $cog["x"], $cog["y"], 9, 9,  0, 360, 
 imagecolorallocate($result->getImage(), 255, 0, 0));

// stream image to client
$result->output();
The above process was run on these two images:
The result looking like this:

Goals for my next version

  • Add edge detection and island detection (multiple hotspots of change) in one image.
  • Removing non-still objects from a set of images to produce one still image. (ie: Remove tourists from my picture of some famous monument)

Thursday, July 17, 2008

Java Image Comparison / Motion Detection

I've recently purchased a wireless security camera which is very conveniently smaller than a cubic inch. Well the imagination runs wild once you've got this device,... Who can I spy on? What can I monitor? What does the domestic worker actually do when I'm at work?.. and so the list continues.

I played around in java's JMF for a day and soon had a small app that recorded one still frame per second. Now I could review the recorded images when getting home at the end of the day. Hovever, 1 frame per second equates to 36000 images over the 10 hour work day that I'm not at home! I don't really want to record motion video - yet. I'm still happy with working with images,.. albeit a lot of them. So now I've got a haystack of images, and presumably somewhere inside of them is the needle of interesting footage.

I searched pretty hard for a Java image comparison class or library and could not find anything. Just a whole bunch of forum posts declaring how advanced the topic can get. So sleeves up, I created my own rudimentary ImageCompare class. (Download Link). It's not bulletproof, and its not lightspeed, but for my purposes it's done the job perfectly.

The class breaks up the images into smaller regions and compares the brightness of each corresponding region. If any particular pair of regions are vastly different then something must have changed in that part of the image.

How to use:

// Create a compare object specifying the 2 images for comparison.
ImageCompare ic = new ImageCompare("c:\\test1.jpg", "c:\\test2.jpg");
// Set the comparison parameters.
// (num vertical regions, num horizontal regions, sensitivity, stabilizer)
ic.setParameters(8, 6, 5, 10);
// Display some indication of the differences
in the image.
ic.setDebugMode(2);
// Compare.
ic.compare();
// Display if these images are considered a match according to our parameters.
System.out.println("Match: " + ic.match());
// If its not a match then write a file to show changed regions.
if (!ic.match()) saveJPG(ic.getChangeIndicator(), "c:\\changes.jpg");


The setParameters() accepts four parameters.
  1. The number of vertical columns in the comparison grid.
  2. The number of horizontal rows in the comparison grid.
  3. A threshold value. If the difference in brightness exceeds this then the region is considered different.
  4. A stabilization factor. In future I will calculate this automatically since it is proportional to parameters 1 and 2.
setDebugMode() is completely optional and only really usefull while designing your parameters.

After running compare(), match() will hold the result.

Example:
The above code was run on the following two images:




The console output looked like this:
|0,0,0,0,0,0,1,0|
|0,0,0,0,0,0,0,1|
|0,0,0,0,0,0,0,3|
|0,0,0,0,0,1,1,8|
|0,0,0,0,0,0,0,8|
|0,0,0,0,0,1,0,0|
Match: false
... and the image output (changes.jpg) looked like this:



On my todo list:
  • Provide an array/vector of changed regions, or number of changed regions.
  • Heuristically determine a stabilizing factor, thus not having to specifying one.
  • Allow for the input of area vectors before comparison for the purpose of excluding or only checking these areas.
  • I'd also like to integrate this into a more real time solution that then exposes events and the like.

What you see here is a very alpha stage development. Take it and use it if you like, but do so at your own risk,.. or frustration. I'll try post some updates to this code as and when significant changes have been made.

Follow-up post: http://mindmeat.blogspot.com/2008/11/java-image-comparison.html