Reputation: 431
I have two instances of cv::Mat : m1 and m2. They are of the same numeric type and sizes. Is there any function in OpenCV that returns whether the matrices are identical (have all the same values)?
Upvotes: 43
Views: 53223
Reputation: 4521
As mentioned by Acme, you can use cv::compare
although it is not as clean as you might hope.
In the following example, cv::compare
is called by using the !=
operator:
// Get a matrix with non-zero values at points where the
// two matrices have different values
cv::Mat diff = a != b;
// diff is boolean matrix so all elements are non-negative. Equal if all elements in diff are zero.
bool eq2 = cv::sum(diff) == cv::Scalar(0,0,0,0);
If you know the type you can use the STL equal function. The Mat iterators take care of cases where the data is non-contiguous, and you can use a vector type for multichannel matrices.
bool eq = std::equal(a.begin<uchar>(), a.end<uchar>(), b.begin<uchar>());
Edit: Improve my earlier update to handle multichannel case based on Miki's answer.
Upvotes: 66
Reputation: 1
bool matIsEqual(const cv::Mat &m1, const cv::Mat &m2)
{
if(m1.type() != m2.type())
return false;
if(m1.size() != m2.size())
return false;
return std::equal(m1.begin<uchar>(), m1.end<uchar>(), m2.begin<uchar>());
}
Upvotes: -1
Reputation: 101
For multichannel images, you could use cv::Mat::reshape to create a single channel image without any extra overhead.
An update of Antonio's answer would be
bool areEqual(const cv::Mat& a, const cv::Mat& b)
{
cv::Mat temp;
cv::bitwise_xor(a,b,temp);
return !(cv::countNonZero(temp.reshape(1)));
}
Upvotes: 3
Reputation: 857
The problem of using cv::countNonZero
is that this function only works for one-channel images. If you want to work with multichannel images, you have to take care of each channel individually. The first step is to split an image into channels. As Antonio explained, you can use cv::split
function for this purpose. After splitting, you can use cv::countNonZero
for each channel and sum the results along all channels by using iteration. cv::Mat::channels
gives you the number of channels.
This is the code that I use to check whether two matrices are identical.
bool isEqual(cv::Mat firstImage, cv::Mat secondImage){
cv::Mat dst;
std::vector<cv::Mat>channels;
int count = 0;
cv::bitwise_xor(firstImage, secondImage, dst);
cv::split(dst, channels);
for (int ch = 0; ch<dst.channels();ch++){
count += cv::countNonZero(channels[ch]);
}
return count == 0 ? true : false;
}
Upvotes: 0
Reputation: 351
Another way using a single function would be to use:
bool areIdentical = !cv::norm(img1,img2,NORM_L1);
Since L1 norm is calculated as ∑I|img1(I)−img2(I)|
reference: OpenCV norm
Upvotes: 16
Reputation: 141
This is the code I use to compare generic (not depending on dimensions or type of elements) cv::Mat
instances:
bool matIsEqual(const cv::Mat Mat1, const cv::Mat Mat2)
{
if( Mat1.dims == Mat2.dims &&
Mat1.size == Mat2.size &&
Mat1.elemSize() == Mat2.elemSize())
{
if( Mat1.isContinuous() && Mat2.isContinuous())
{
return 0==memcmp( Mat1.ptr(), Mat2.ptr(), Mat1.total()*Mat1.elemSize());
}
else
{
const cv::Mat* arrays[] = {&Mat1, &Mat2, 0};
uchar* ptrs[2];
cv::NAryMatIterator it( arrays, ptrs, 2);
for(unsigned int p = 0; p < it.nplanes; p++, ++it)
if( 0!=memcmp( it.ptrs[0], it.ptrs[1], it.size*Mat1.elemSize()) )
return false;
return true;
}
}
return false;
}
I don't understand, why cv::Mat
does not have an operator ==
according to this implementation.
Upvotes: 14
Reputation: 41765
The following will work also for multi-channel matrices:
bool isEqual = (sum(img1 != img2) == Scalar(0,0,0,0));
Since sum accepts matrices with 1 to 4 channels, and returns a Scalar
, where the element at [0] is the result of the sum for first channel, and so on.
Upvotes: 20
Reputation: 20266
I use this:
bool areEqual(const cv::Mat& a, const cv::Mat& b) {
cv::Mat temp;
cv::bitwise_xor(a,b,temp); //It vectorizes well with SSE/NEON
return !(cv::countNonZero(temp) );
}
If you have to do this operation many times, you can make this into a class, have temp
as member and prevent the image to be allocated every time. Detail: Make temp
mutable so that areEqual
can be a const
method.
Note though that cv::countNonZero
only works with cv::Mat
of one channel. It's overkill, but in that case one could use cv::split
to split each channel into separate images and do cv::countNonZero
on them.
Upvotes: 3
Reputation: 4453
As mentioned by Acme and Tim, you can use cv::compare
. This is the code I use to compare my cv::Mat
:
bool matIsEqual(const cv::Mat mat1, const cv::Mat mat2){
// treat two empty mat as identical as well
if (mat1.empty() && mat2.empty()) {
return true;
}
// if dimensionality of two mat is not identical, these two mat is not identical
if (mat1.cols != mat2.cols || mat1.rows != mat2.rows || mat1.dims != mat2.dims) {
return false;
}
cv::Mat diff;
cv::compare(mat1, mat2, diff, cv::CMP_NE);
int nz = cv::countNonZero(diff);
return nz==0;
}
It is important to stand out that the function cv::countNonZero
only works with cv::Mat
of one channel, so if you need to compare two cv::Mat
images, you need first to convert your cv::Mat
in this way:
Mat gray1, gray2;
cvtColor(InputMat1, gray1, CV_BGR2GRAY);
cvtColor(InputMat2, gray2, CV_BGR2GRAY);
where InputMat1
and InputMat2
are the cv::Mat
you want to compare.
After that you can call the function:
bool equal = matsEqual(gray1, gray2);
I took this code of this site: OpenCV: compare whether two Mat is identical
I hope this help you.
Upvotes: 5
Reputation: 22821
Use cv::compare combined with cv::countNonZero.
An SO question that might help you further OpenCV compare two images and get different pixels
Upvotes: 7