OpenCV compare two images and get different pixels
For some reason the code bellow is not working. I have two 640*480 images which are very similar but not the same (at least few hundred/thousand pixels should be different).
This is how I am comparing them and counting different pixels:
unsigned char* row;
unsigned char* row2;
int count = 0;
// this happens in a loop
// fIplImageHeader is current image
// lastFIplImageHeader is image from previous iteration
if ( NULL != lastFIplImageHeader->imageData ) {
for( int y = 0; y < fIplImageHeader->height; y++ )
{
row = &CV_IMAGE_ELEM( fIplImageHeader, unsigned char, y, 0 );
row2 = &CV_IMAGE_ELEM( lastFIplImageHeader, unsigned char, y, 0 );
for( int x = 0; x < fIplImageHeader->width*fIplImageHeader->nChannels; x += fIplImageHeader->nChannels )
{
if(row[x] != row2[x] || row[x+1] != row2[x+1] || row[x+2] != row2[x+2])
count++;
}
}
}
}
Now at the end I get number 3626 which would seem alright.
But, I tried opening one of the images in MS Paint and drawing thick red lines all over it which should increase the number of different pixels substantially. I got the same number again: 3626.
Obviously I am doing something wrong here.
I am comparing these images in a loop.
This line is before the loop:
IplImage* lastFIplImageHeader = cvCreateImageHeader(cvSize(640, 480), 8, 3);
Then insid开发者_如何学JAVAe the loop I load images like this:
IplImage* fIplImageHeader = cvLoadImage( filePath.c_str() );
// here I compare the pixels (the first code snippet)
lastFIplImageHeader->imageData = fIplImageHeader->imageData;
So lastFIplImageHeader is storing the image from the previous iteration and fIplImageHeader is storing the current image.
int count_diff_pixels(cv::Mat in1, cv::Mat in2) {
cv::Mat diff;
cv::compare(in1, in2, diff, cv::CMP_NE);
return cv::countNonZero(diff);
}
Might need some tweaking, but that's about how you do it. Also, you shouldn't be messing with cv* if you really use c++. Use the new c++ interface, and don't worry about freeing images. reading images, remembering the previous one becomes as simple as
cv::Mat prev;
while (...) {
cv::Mat current = cv::imread(fn); // or whereever your image comes from
// ... do something ...
prev = current;
} // automatic memory management!
It seems to me you aren't counting different pixels, not even pixels at all.
You are counting how often a color channel of one pixel in the first image matches the channel of the corresponding pixel in the other image.
You probably meant to do something along the lines of:
...
//in the inner for loop
if(row[x] != row2[x] || row[x+1] != row2[x+1] || row[x+2] != row2[x+2])
count++;
...
This will however not account for the alpha channel (if present) and will fail on grayscale images as you might read out of bounds of the data array.
EDIT: As long as you don't release the old image it should be fine. However, it might be better to do something like:
//make sure size and channels are correct.
//If unsure, load the image first and then create with the parameters taken from the loaded image.
cvCreateImage(cvSize(640, 480), 8, 3);
//use cvCopy to copy the contents and proceed as normal
cvCopy(fIplImageHeader , lastFIplImageHeader);
Alternatively, instead of copying, you could just hold a pointer to the old image and use that, as long as you don't release it.
lastFIplImageHeader = fIplImageHeader;
fIplImageHeader = cvLoadImage( filePath.c_str() );
EDIT2: If the difference is all you want, you could have a look at cvSub (with both images loaded as grayscale), followed by cvCountNonZero
精彩评论