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

32 comments:

Unknown said...

Ah, so your domestic worker (pictured in the photos obviously), snatched your blue china wall-piece. It's so hard to find good help these days.

Anonymous said...

Hi, I'm work around real time motion detection solution. If you are interested we work together lorenzo83@email.it. Good work!!

Unknown said...

Here's a follow up post with some updates regarding the content of this article.

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

Unknown said...

Hi Patrick

I was surfing in the net searching searching information about image processing and staff. While I was surfing I find your web http://mindmeat.blogspot.com/2008/07/java-image-comparison.html. I am working real time motion detection and I need some help with my project. Would you help me or give me some advices? Maybe I can also help you with some of your staff.

Thanks

Charles

Anonymous said...

Hi, i found your method useful for motion detect on mobile devices. I made just one modification:

Some times you do not need to save shots where difference is result of candlelight changes, just because - imho there is no motion!
To solve this problem add next rows to public void compare():
//Calculating average brightness for source images
int sDiff = getAverageBrightness(img2) - getAverageBrightness(img1))/(comparex * comparey);

// Calcilatting verage brightness for blocks (in cicle)
int diff = Math.abs(b1 - b2 + sDiff);

Enjoy!

Unknown said...

Hi,i am an student from chennai.i am currently doing a project on finerprint recognition.i found your code really useful.thank you.but i would ike to know on what algorithm the code is based on.thank you

Unknown said...

@niranjini: i love the question. but i don't know the answer. i really just sat down one day, downloaded jmf, and started dismantling pictures from my webcam.

i did refer to the site http://homepages.inf.ed.ac.uk/rbf/HIPR2/linedet.htm for my line detection. otherwise, and there might be names for the techniques, i just trialed and erred my way through until i had some reasonable data to work with.

btw: my follow up post at: http://mindmeat.blogspot.com/2008/11/java-image-comparison.html offers some improvements.

Unknown said...

HI,
i am a M.Tech student frm Blore.currently im doing a project on motion detection.felt very happy to see your code for it cleared many of my doubts.thanks.....

Unknown said...

Very good work indeed.
That save a lot of my time.
Basically, I need to feature to detect the differences between two pictures so I have to make some change on your code since It is not detecting the color change by modifying these lines to accept the color input image

// convert to gray images. good for detecting the appearance of the object
//img1 = imageToBufferedImage(GrayFilter.createDisabledImage(img1));
//img2 = imageToBufferedImage(GrayFilter.createDisabledImage(img2));

// Color
img1 = imageToBufferedImage(img1);
img2 = imageToBufferedImage(img2);

iheb said...

your program is awesome

Chinmay Deodhar (hypo) said...

hey..

great program!!

thanks very much for providing such a cool demo...

-Chinmay, India

abcdef said...

sir,
i am a student
As a part of my curriculum i am doing my project on iris recognition
i want java code for comparison of more than two images
thank u

Dave Bates said...

Great Idea! Can't wait to try it out.

Unknown said...

I saw you code. You have done a great job. Lot can be added to these simple features. Let us know if you have done any more upgrades to this simple wonderful code.

SKR

Anonymous said...

hi, Very good work.
Can you explain getAverageBrightness method.
You can send to ardak_kh@mail.ru
Any help is valuable..
thanx!!

charmi said...

hiiiii this code is very helpful do you tell me is there any way to compare images and get output as percentage in java.

Unknown said...

i would calculate the percentage as:
(number of different blocks) / (total blocks) * 100. this will only give you an indication of the amount of surface area that is different, not necessarily the extremity of the differences in those areas.

JR said...

Hi, I'm using your tool to compare 2 images altogether. Basically there's one image that is compared against a set of images.

I keep getting a "java.awt.image.RasterFormatException: (y + height) is outside raster" exception and just wondering if you encountered it as well as what steps did you do to address to?

Kindly advice... and yup the image dimensions changes as the comparison goes along.

JR said...

Nevermind, simple solution:

if ((img1.getWidth() == img2.getWidth()) && (img1.getHeight() == img2.getHeight()))

Prevents all the trouble comparing. Currently don't require cropped versions of the image with another one so same dimension comparison will suffice.

Thanks. :)

Anonymous said...

Cool tool.

I use it in my unit test for comparing the output of a webpage renderer.

ShyLock said...

Love you man, your code helped me. I used it in combination with VLCj to stop recording from webcam when there is no motion in video stream. Thanks a billion :)

Paolo said...

Hi Pat, nice job with this code :-)
I'd like to compare small 71x14 jpeg images containing numbers. Which parameters would you suggest in the setParameters() method? Also any hint on how to speed up the process? Thanks, Paolo

Unknown said...

The parameters in this version were difficult to work with, I settled on mine by trial-and-error. If I remember right, they were affected by things like the contrast and brightness of the images. Maybe have a look at my next version of this code: http://mindmeat.blogspot.com/2008/11/java-image-comparison.html

Speeding it up?.. any ideas i did have i programmed into the next version i just mentioned. I haven't seen this code for a long time, so i can't comment further.

good luck ;)

Krishna Kumar Chourasiya said...

Hi All,
This Krishna Kumar Chourasiya, I want to make application which can compare two images one of them taken just now and other one is of before 10 years of the same person/things....something like that.
It would be great if any one can suggest me the way to execute my thoughts in a better way.

Thanks in advance

Krishna

Unknown said...

i'm a computer science student and i've been searching on how to compare images and luckily i found this.. :)
but i have some questions hope you'll answer.
you have mention about the use of the jmf.. can you please explain to me how the framing of the is done??
is it done solely by the jmf or there is coding needed?? what is a jmf actually?. i'm so sorry about my questions, the topic is quite new to..

Unknown said...

The JMF is only required to connect the webcam. I guess you'd also want to use the JMF if you wanted to step through video sequences as well. The JMF is the Java Media Framework - You can download it for free from Sun/Oracle's website.

The rest of the analysis is done by my code. I started i this with no prior knowledge of image comparison, so I just coded one step at a time.. It was quite a rudimentary process.
(ps: I haven't coded with java for nearly two years now, so I'm not up-to-date with current advancements in the field.)

Gyorskori said...

Professional!
  Thank you!

Nordin Developer said...

Hello,
Thanks for the algorithm my friend ;-) I ported it to Android and now it works.

The big changes I made were replacing BufferedImage with Bitmap, using Canvas instead of Graphic2D and Raster. Furthermore I had to create a function that converts a Bitmap object into a grayed scale version of it.

Igor said...

Hi Patrick,

Do you have a better solution for the parameters?

Thanks
Igor

Unknown said...

Hi Igor, I posted a follow up on this idea in this article. It's somewhat transformed from the original, but it allows for much better customization / parameterization of the code.

Ways & Means Technology Pvt Ltd said...

This will return false When i resize second image but images are same .

alan said...

Thanks a lot for your help
You have really simplified my work.
Thanks again.