Detecting difference between 2 images - c++

I am working on the following code
#include <iostream>
#include <opencv2/core/core.hpp>
#include <string>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/video/background_segm.hpp>
using namespace std;
using namespace cv;
int main()
{
Mat current,currentGrey,next,abs;
VideoCapture cam1,cam2;
std:: vector<vector<Point>>contours;
vector<vector<Point>>contoursPoly(contours.size());
cam1.open(0);
cam2.open(0);
namedWindow("Normal");
namedWindow("Difference");
if(!cam1.isOpened())
{
cout << "Cam not found" << endl;
return -1;
}
while(true)
{
//Take the input
cam1 >> current;
currentGrey = current;
cam2 >> next;
//Convert to grey
cvtColor(currentGrey,currentGrey,CV_RGB2GRAY);
cvtColor(next,next,CV_RGB2GRAY);
//Reduce Noise
cv::GaussianBlur(currentGrey,currentGrey,Size(0,0),4);
cv::GaussianBlur(next,next,Size(0,0),4);
imshow("Normal",currentGrey);
//Get the absolute difference
absdiff(currentGrey,next,abs);
imshow("Difference",abs);
for(int i=0;i<abs.rows;i++)
{
for(int j=0;j<abs.cols;j++)
{
if(abs.at<int>(j,i)>0)
{
cout << "Change Detected" << endl;
j = abs.cols+1;
i = abs.rows+1;
}
}
}
if(waitKey(30)>=0)
{
break;
}
}
}
In here, what I am trying to do is print a message whenever a difference between images are detected. Following part is the technique
for(int i=0;i<abs.rows;i++)
{
for(int j=0;j<abs.cols;j++)
{
if(abs.at<int>(j,i)>0)
{
cout << "Change Detected" << endl;
j = abs.cols+1;
i = abs.rows+1;
}
}
}
Unfortunately, instead of printing messages when a difference is detected, it prints the message always. Why is this?

You should calculate the mean square error between the two frames.
MSE = sum((frame1-frame2)^2 ) / no. of pixels
There is an example of calculating it in an OpenCV tutorial.
Based on that code you could have
double getMSE(const Mat& I1, const Mat& I2)
{
Mat s1;
absdiff(I1, I2, s1); // |I1 - I2|
s1.convertTo(s1, CV_32F); // cannot make a square on 8 bits
s1 = s1.mul(s1); // |I1 - I2|^2
Scalar s = sum(s1); // sum elements per channel
double sse = s.val[0] + s.val[1] + s.val[2]; // sum channels
if( sse <= 1e-10) // for small values return zero
return 0;
else
{
double mse =sse /(double)(I1.channels() * I1.total());
return mse;
// Instead of returning MSE, the tutorial code returned PSNR (below).
//double psnr = 10.0*log10((255*255)/mse);
//return psnr;
}
}
You can use it in your code like this:
if(getMSE(currentGrey,next) > some_threshold)
cout << "Change Detected" << endl;
It is up to you to decide the magnitude of MSE below which you consider the images to be the same.
Also you should prefilter with GaussianBlur() to reduce noise, like you already do. The blur method suggested by #fatih_k is not a Gaussian filter; it is a box filter and although faster may introduce artifacts.

Image differencing has some tricks. Due to noise any 2 frames may not be same.
In order to alleviate the effect of the noise you can use method blur() or GaussianBlur() for every frame so that minute details may be removed with simple box or Gaussian filter.
Then, as a similarity criterion, you can take the difference of two frames and after taking the absolute value of the resulting difference matrix with abs, you can sum all the elements and calculate the ratio of this sum to the total pixel sum of the first frame. If this ratio is more than some threshold, lets say 0.05, then you can infer that image frames are sufficiently different.

Let's take a look what OpenCV documentation says about cv::waitKey returned value:
Returns the code of the pressed key or -1 if no key was pressed before the specified time had elapsed.
So... the loop is infinite and "Change Detected" is printed once for every two images compared until the program is terminated.

The function getMSE() described above can be tweaked a little to better cover unsigned integer 8 data type. The difference on unsigned integer 8 datatype will produce 0 every time the result is negative. By converting matrices to double datatype at first and then computing the mean squared error, this problem would be avoided.
double getMSE(Mat& I1, Mat& I2)
{
Mat s1;
// save the I! and I2 type before converting to float
int im1type = I1.type();
int im2type = I2.type();
// convert to float to avoid producing zero for negative numbers
I1.convertTo(I1, CV_32F);
I2.convertTo(I2, CV_32F);
absdiff(I1, I2, s1); // |I1 - I2|
s1.convertTo(s1, CV_32F); // cannot make a square on 8 bits
s1 = s1.mul(s1); // |I1 - I2|^2
Scalar s = sum(s1); // sum elements per channel
double sse = s.val[0] + s.val[1] + s.val[2]; // sum channels
if( sse <= 1e-10) // for small values return zero
return 0;
else
{
double mse =sse /(double)(I1.channels() * I1.total());
return mse;
// Instead of returning MSE, the tutorial code returned PSNR (below).
//double psnr = 10.0*log10((255*255)/mse);
//return psnr;
}
// return I1 and I2 to their initial types
I1.convertTo(I1, im1type);
I2.convertTo(I2, im2type);
}
The above code returns zero for small mse values (under 1e-10). Terms s.val1 and s.val[2] are zero for 1D images.
If you want to check for 1D image input as well (it is basically supporting 3 channel image), use the following code to test (with random unsigned numbers):
Mat I1(12, 12, CV_8UC1), I2(12, 12, CV_8UC1);
double low = 0;
double high = 255;
cv::randu(I1, Scalar(low), Scalar(high));
cv::randu(I2, Scalar(low), Scalar(high));
double mse = getMSE(I1, I2);
cout << mse << endl;
If you want to check for 3D image input, use the following code to test (with random unsigned numbers):
Mat I1(12, 12, CV_8UC3), I2(12, 12, CV_8UC3);
double low = 0;
double high = 255;
cv::randu(I1, Scalar(low), Scalar(high));
cv::randu(I2, Scalar(low), Scalar(high));
double mse = getMSE(I1, I2);
cout << mse << endl;

Related

problem with sending a float number in a stream in vivado_hls

I am trying to do a simple image processing filter where the pixel values will be divided by half to reduce the intensity and I am trying to develop the hardware for the same. hence I am using vivado hls to generate the IP. As explained here https://forums.xilinx.com/t5/High-Level-Synthesis-HLS/Float-numbers-with-hls-stream/m-p/942747 to send floating numbers in a hls stream , an union needs to be used and I did the same. However, the results don't seem to be matching for the red and green components of the image whereas it is matching for the blue component of the image. It is a very simple algorithm where a pixel value will be divided by half.
I have been trying to resolve it but I am not able to see where the problem is. I have attached all the files below, can someone can help me resolve it??
////header file
#include "ap_fixed.h"
#include "hls_stream.h"
typedef union {
unsigned int i;
float r;
float g;
float b;
} conv;
typedef hls::stream <unsigned int> Stream_t;
void ftest(Stream_t& Sin,Stream_t& Sout);
////testbench
#include "stream_check_h.hpp"
int main()
{
Mat img_rev = imread("C:/Users/20181217/Desktop/images/imgs/output_fwd_v3.png");//(256x512)
Mat final_img(img_rev.rows,img_rev.cols,CV_8UC3);
Mat ref_img(img_rev.rows,img_rev.cols,CV_8UC3);
Stream_t S1,S2;
int err_r = 0;
int err_g = 0;
int err_b = 0;
for(int i=0;i<256;i++)
{
for(int j=0;j<512;j++)
{
conv c;
c.r = (float)img_rev.at<Vec3b>(i,j)[0];
c.g = (float)img_rev.at<Vec3b>(i,j)[1];
c.b = (float)img_rev.at<Vec3b>(i,j)[2];
S1 << c.i;
}
}
ftest(S1,S2);
conv c;
for(int i=0;i<256;i++)
{
for(int j=0;j<512;j++)
{
S2 >> c.i;
final_img.at<Vec3b>(i,j)[0]=(unsigned char)c.r;
final_img.at<Vec3b>(i,j)[1]=(unsigned char)c.g;
final_img.at<Vec3b>(i,j)[2]=(unsigned char)c.b;
ref_img.at<Vec3b>(i,j)[0] = (unsigned char)(((float)img_rev.at<Vec3b>(i,j)[0])/2.0);
ref_img.at<Vec3b>(i,j)[1] = (unsigned char)(((float)img_rev.at<Vec3b>(i,j)[1])/2.0);
ref_img.at<Vec3b>(i,j)[2] = (unsigned char)(((float)img_rev.at<Vec3b>(i,j)[2])/2.0);
}
}
Mat diff;
cout<<diff;
diff= abs(final_img-ref_img);
for(int i=0;i<256;i++)
{
for(int j=0;j<512;j++)
{
if((int)diff.at<Vec3b>(i,j)[0] > 0)
{
err_r++;
cout<<"expected value: "<<(int)ref_img.at<Vec3b>(i,j)[0]<<", final_value: "<<(int)final_img.at<Vec3b>(i,j)[0]<<", actual value:"<<(int)img_rev.at<Vec3b>(i,j)[0]<<endl;
}
if((int)diff.at<Vec3b>(i,j)[1] > 0)
err_g++;
if((int)diff.at<Vec3b>(i,j)[2] > 0)
err_b++;
}
}
cout<<"number of errors: "<<err_r<<", "<<err_g<<", "<<err_b;
return 0;
}
////core
#include "stream_check_h.hpp"
void ftest(Stream_t& Sin,Stream_t& Sout)
{
conv cin,cout;
for(int i=0;i<256;i++)
{
for(int j=0;j<512;j++)
{
Sin >> cin.i;
cout.r = cin.r/2.0 ;
cout.g = cin.g/2.0 ;
cout.b = cin.b/2.0 ;
Sout << cout.i;
}
}
}
when I debugged, it showed that the blue components of the pixels are matching. for one red pixel it showed me the following:
expected value: 22, final_value: 14, actual value:45
and the total errors for red, green, and blue are:
number of errors: 126773, 131072, 0
I am not able to see why it is going wrong for red and green. I posted here hoping a fresh set of eyes would help my problem.
Thanks in advance
I'm assuming you're using a 32bit-wide stream with 3 RGB pixels 8bit unsigned (CV_8U3). I believe the problem with the union type in your case is the overlapping of its three members (not just like the one float value in the example you cite). This means that by doing the division, you're actually doing it over the whole 32bit data you're receiving.
I possible workaround I quickly cam up with would be to cast the unsigned int you're getting from the stream into an ap_uint<32> type, then chop it in the R, G, B chunks (with the range() method) and divide. Finally, assemble back the result and stream it back.
unsigned int packet;
Sin >> packet;
ap_uint<32> packet_uint32 = *((ap_uint<32>*)&packet); // casting (not elegant, but works)
ap_int<8> b = packet_uint32.range(7, 0);
ap_int<8> g = packet_uint32.range(15, 8);
ap_int<8> r = packet_uint32.range(23, 16); // In case they are in the wrong bit range/order, just flip the r, g, b assignements
b /= 2;
g /= 2;
r /= 2;
packet_uint32.range(7, 0) = b;
packet_uint32.range(15, 8) = g;
packet_uint32.range(23, 16) = r;
packet = packet_uint32.to_int();
Sout << packet;
NOTE: I've reused the same variables in the code above: HLS shouldn't complain about it and come out with a good RTL anyway. In case it shouldn't, just create new ones.

Using saturate_cast or not

This is a simple program to change contrast and brightness of an image. I have noticed that there is a an another program with one simple difference:saturate_cast is added to code.
And I don't realize what is the reason of doing this and there is no need to converting to unsigned char or uchar both code (with saturate_cast<uchar> and to not use this) are outputting the same result. I appreciate if anyone help.
Here it is code :
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
#include "Source.h"
using namespace cv;
double alpha;
int beta;
int main(int, char** argv)
{
/// Read image given by user
Mat image = imread(argv[1]);
Mat image2 = Mat::zeros(image.size(), image.type());
/// Initialize values
std::cout << " Basic Linear Transforms " << std::endl;
std::cout << "-------------------------" << std::endl;
std::cout << "* Enter the alpha value [1.0-3.0]: ";std::cin >> alpha;
std::cout << "* Enter the beta value [0-100]: "; std::cin >> beta;
for (int x = 0; x < image.rows; x++)
{
for (int y = 0; y < image.cols; y++)
{
for (int c = 0; c < 3; c++)
{
image2.at<Vec3b>(x, y)[c] =
saturate_cast<uchar>(alpha*(image.at<Vec3b>(x, y)[c]) + beta);
}
}
/// Create Windows
namedWindow("Original Image", 1);
namedWindow("New Image", 1);
/// Show stuff
imshow("Original Image", image);
imshow("New Image", image2);
/// Wait until user press some key
waitKey();
return 0;
}
Since the result of your expression may go outside the valid range for uchar, i.e. [0,255], you'd better always use saturate_cast.
In your case, the result of the expression: alpha*(image.at<Vec3b>(x, y)[c]) + beta is a double, so it's safer to use saturate_cast<uchar> to clamp values correctly.
Also, this improves readability, since it's easy to see that you want a uchar out of an expression.
Without using saturate_cast you may have unexpected values:
uchar u1 = 257; // u1 = 1, why a very bright value is set to almost black?
uchar u2 = saturate_cast<uchar>(257); // u2 = 255, a very bright value is set to white
inline unsigned char saturate_cast_uchar(double val) {
val += 0.5; // to round the value
return unsigned char(val < 0 ? 0 : (val > 0xff ? 0xff : val));
}
if val lies between 0 to 255 than this function will return rounded value,
if val lies outside the range [0, 255] than it will return lower or upper boundary value.

opencv brg color histogram not work

I am reading OpenCV 2 Computer Vision Application Programming Cookbook and implementing examples in it.
In chapter 4, color histogram example doesn't work unfortunately.
The code is below. But this code does not give me histogram or any error. Also, it says that color images histogram is three dimensional. I don't understand why it is thee and it is not two.
#include <opencv2\imgproc\imgproc.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\core\core.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main(){
Mat image = imread("waves.jpg");
int histSize[3];
float hranges[2];
const float* ranges[3];
int channels[3];
// Prepare arguments for a color histogram
histSize[0] = histSize[1] = histSize[2] = 256;
hranges[0] = 0.0; // BRG range
hranges[1] = 255.0;
ranges[0] = hranges; // all channels have the same range
ranges[1] = hranges;
ranges[2] = hranges;
channels[0] = 0; // the three channels
channels[1] = 1;
channels[2] = 2;
Mat hist;
// Compute histogram
calcHist(&image,
1, // histogram of 1 image only
channels, // the channel used
cv::Mat(), // no mask is used
hist, // the resulting histogram
3, // it is a 3D histogram
histSize, // number of bins
ranges // pixel value range
);
cout << hist.at<int>(100, 100, 0) << endl;
cout << hist.at<int>(100, 100, 1) << endl;
cout << hist.at<int>(100, 100, 2) << endl;
return 0;
}
This code above DOES give you a 3D histogram. I don't quite understand why you think it does not.
Why is it three-dimensional? Because argument int dim in method calcHist() has value 3. If you want 2D histogram then it would be 2.
You want to print values of 3D histogram with cout << hist.at<int>(x, y, z) << endl; where x, y and z are coordinates for 3D histogram.

How to do per-element comparison and do different operation according to result

I'm using OpenCV in C++ and I'm stuck on a point. I need to do the following:
if(src(I) < aNumber)
do operation1
else
do operation2
For loop takes 100+ ms for 1000x750 image. I don't want to use a for loop because it takes a lot of time. I want to use an (some) OpenCV function(s) with that function I could be able to edit some of the values in the matrix. For example, my array is
[1 4 5;
4 6 2;
3 2 1]
I want:
if(an element of mat < 4)
pow(element,2)
else
element--;
According to this if-else
[1 3 4;
3 5 4
9 4 1]
is going to be my result matrix.
Does anybody know any functions to handle this except using two for loops?
You may want to check out compare. Example:
//Mat mask; compare(src, 10.0, mask, CMP_LT);
Mat mask = src < 10.0;
Depending on the actual operation you wish to preform you may be able to use the result from compare, otherwise you could take a look at the gpu module. In particular, the Per-element Operations.
Personally, I feel that OpenCV should be treated a bit like MATLAB, avoid loops, use matrices, and try to use the built-in functions whenever possible (even if they are just implemented as a loop it saves you typing out the same thing again and again).
EDIT: Following is an example piece of code to achieve the task in your updated question using loops and using the built-in matrix operators:
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
//#include <opencv2/gpu/gpu.hpp>
using namespace cv;
#include <iostream>
using namespace std;
int main(int argc, char** argv)
{
// Load Image
Mat matImage = imread("Test.png");
// Convert to Grayscale
Mat matGray(matImage.rows, matImage.cols, CV_8UC1);
cvtColor(matImage, matGray, CV_BGR2GRAY);
double time, dThreshold = 50.0;
//int TIMES = 1000;
//namedWindow("Display", WINDOW_NORMAL);
//char chKey;
//imshow("Display", matGray);
//chKey = '\0'; while (chKey != '') chKey = waitKey(0);
//----------------------------- Loop Method -------------------------------
time = (double) getTickCount();
//for (int k = 0; k < TIMES; k++)
//{
Mat matLoop = matGray.clone();
for (int i = 0; i < matLoop.rows; ++i)
{
for (int j = 0; j < matLoop.cols; ++j)
{
uchar& unValue = matLoop.at<uchar>(i, j);
if (unValue < dThreshold)
unValue = pow(unValue, 2);
else
unValue--;
}
}
//}
time = 1000*((double)getTickCount() - time)/getTickFrequency();
//time /= TIMES;
cout << "Loop Method Time: " << time << " milliseconds." << endl;
//imshow("Display", matLoop);
//chKey = '\0'; while (chKey != '') chKey = waitKey(0);
//---------------------------- Matrix Method ------------------------------
time = (double) getTickCount();
//for (int i = 0; i < TIMES; i++)
//{
Mat matMask, matMatrix;
matMask = matGray < dThreshold;
bitwise_and(matGray, matMask, matMatrix);
pow(matMatrix, 2.0, matMatrix);
subtract(matGray, 1.0, matMatrix, ~matMask);
//}
time = 1000*((double)getTickCount() - time)/getTickFrequency();
//time /= TIMES;
cout << "Matrix Method Time: " << time << " milliseconds." << endl;
//imshow("Display", matMatrix);
//chKey = '\0'; while (chKey != '') chKey = waitKey(0);
return 0;
}
As well as reducing the number of lines of code you need to type from 12 to 5, the matrix method is also faster. Enabling the timing loops (with TIMES = 1000;), I get the following times for a medium sized image:
Loop Method Time: 9.19669 milliseconds.
Matrix Method Time: 2.82657 milliseconds.
With the gpu module I am sure that you could reduce the second time further, but unfortunately I don't currently have a suitable graphics card attached to my current system.

Different Pixel Values in MATLAB and C++ with OpenCV

I see there are similar questions to this but don't quiet answer what I am asking so here is my question.
In C++ with OpenCV I run the code I will provide below and it returns an average pixel value of 6.32. However, when I open the image and use the mean function in MATLAB it returns an average pixel intensity of approximately 6.92ish. As you can see I convert the OpenCV values to double to try to ease this issue and have found that openCV loads the image as a set of integers whereas MATLAB loads the image as decimal values that are approximately but not quite the same obviously as the integers. So my question is, being new to coding, which is correct? I'm assuming MATLAB is returning more accurate values and if that is the case I would like to know if there is a way to load the images in the same fashion to avoid the discrepancy.
Thank you, Code below
Mat img = imread("Cells2.tif");
cv::cvtColor(img, img, CV_BGR2GRAY);
cv::imshow("stuff",img);
Mat dst;
if(img.channels() == 3)
{
img.convertTo(dst, CV_64FC1);
}
else if (img.channels() == 1)
{
img.convertTo(dst, CV_64FC1);
}
cv::imshow("output",dst/255);
int NumPixels = img.total();
double avg;
double c = 0;
double std;
for(int y = 0; y < dst.cols; y++)
{
for(int x = 0; x < dst.rows; x++)
{
c+=dst.at<double>(x,y)*255;
}
}
avg = c/NumPixels;
cout << "asfa = " << c << endl;
double deviation;
double var;
double z = 0;
double q;
//for(int a = 0; a<= img.cols; a++)
for(int y = 0; y< dst.cols; y++)
{
//for(int b = 0; b<= dst.rows; b++)
for(int x = 0; x< dst.rows; x++)
{
q=dst.at<double>(x,y);
deviation = q - avg;
z = z + pow(deviation,2);
//cout << "q = " << q << endl;
}
}
var = z/(NumPixels);
std = sqrt(var);
cv::Scalar avgPixel = cv::mean(dst);
cout << "Avg Value = " << avg << endl;
cout << "StdDev = " << std << endl;
cout << "AvgPixel =" << avgPixel;
cvWaitKey(0);
return 0;
}
According to your comment, the image seems to be stored with a 16-bit depth. MATLAB loads the TIFF image as is, while by default OpenCV will load images as 8-bit. This might explain the difference in precision that you are seeing.
Use the following to open the image in OpenCV:
cv::Mat img = cv::imread("file.tif", cv::IMREAD_ANYDEPTH|cv::IMREAD_ANYCOLOR);
In MATLAB, it's simply:
img = imread('file.tif');
Next you need to be aware of the data type you are working with. In OpenCV its CV_16U, in MATLAB its uint16. Therefore you need to convert types accordingly.
For example, in MATLAB:
img2 = double(img) ./ double(intmax('uint16'));
would convert it to a double image with values in the range [0,1]
When you load the image, you must use similar methods in both environments (MATLAB and OpenCV) to avoid possible conversions which may be done by default in either environment.
You are converting the image if certain conditions are met, this can change some color values while MATLAB can choose to not convert the image but use the raw image
colors are mostly represented in hex format with popular implementations in the format of 0xAARRGGBB or 0xRRGGBBAA, so 32 bit integers will do (unsigned/signed doesn't matter, the hex value is still the same), create a 64 bit variable, add all the 32 bit variables together and then divide by the amount of pixels, this will get you a quite accurate result (for images up to 16384 by 16384 pixels (where a 32 bit value is representing the color of one pixel), if larger, then a 64 bit integer will not be enough).
long long total = 0;
long long divisor = image.width * image.height;
for(int x = 0; x < image.width; ++x)
{
for(int y = 0; x < image.height; ++x)
{
total += image.at(x,y).color;
}
}
double avg = total / divisor;
std::cout << "Average color value: " << avg << std::endl;
Not sure what difficulty you are having with mean value in Matlab versus OpenCV. If I understand your question correctly, your goal is to implement Matlab's mean(image(:)) in OpenCV. For example in Matlab you do the following:
>> image = imread('sheep.jpg')
>> avg = mean(image(:))
ans =
119.8210
Here's how you do the same in OpenCV:
Mat image = imread("sheep.jpg");
Scalar avg_pixel;
avg_pixel = mean(image);
float avg = 0;
cout << "mean pixel (RGB): " << avg_pixel << endl;
for(int i; i<image.channels(); ++i) {
avg = avg + avg_pixel[i];
}
avg = avg/image.channels();
cout << "mean, that's equivalent to mean(image(:)) in Matlab: " << avg << endl;
OpenCV console output:
mean pixel (RGB): [77.4377, 154.43, 127.596, 0]
mean, that's equivalent to mean(image(:)) in Matlab: 119.821
So the results are the same in Matlab and OpenCV.
Follow up
Found some problems in your code.
OpenCV stores data differently from Matlab. Look at this answer for a rough explanation on how to access a pixel in OpenCV. For example:
// NOT a correct way to access a pixel in CV_F32C3 type image
double pixel = image.at<double>(x,y);
//The correct way (where the pixel value is stored in a vector)
// Note that Vec3d is defined as: typedef Vec<double, 3> Vec3d;
Vec3d pixel = image.at<Vec3d>(x, y);
Another error I found
if(img.channels() == 3)
{
img.convertTo(dst, CV_64FC1); //should be CV_64FC3, instead of CV_64FC1
}
Accessing Mat elements may be confusing. I suggest getting a book on OpenCV to get started, for example this one, and read OpenCV tutorials and documentation. Hope this helps.