kirkor
kirkor

Reputation: 431

how to check whether two matrices are identical in OpenCV

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

Answers (10)

Tim MB
Tim MB

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

user3753619
user3753619

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

solosuper
solosuper

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

black
black

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

fmigneault
fmigneault

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

user5309470
user5309470

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

Miki
Miki

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

Antonio
Antonio

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

Angie Quijano
Angie Quijano

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

Sadique
Sadique

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

Related Questions