3

I'm trying to create a program that checks if 2 images are the same. I have the following code which gets executed with both the images:

img = cv2.imread('canvas.png')
mask = np.zeros(img.shape[:2],np.uint8)
bgdModel = np.zeros((1,65),np.float64)
fgdModel = np.zeros((1,65),np.float64)
rect = (70,70,300,250)
cv2.grabCut(img,mask,rect,bgdModel,fgdModel,5,cv2.GC_INIT_WITH_RECT)
mask2 = np.where((mask==2)|(mask==0),0,1).astype('uint8')
img = img*mask2[:,:,np.newaxis]
img = img[84:191, 84:203]
count = 1
cv2.imwrite("tmp/"+str(count)+".png", img)

First time I run this, I get the following output: First image. After some time ~7 seconds, I do it with exactly the same image, and I get the following output: Second image. I tried it again, and I again got a different output: Third try?

I'm trying to check if the image is the same (as in the contents), but I can't get this working. I'm using the following piece of code found on Stackoverflow to check similarities:

def is_similar(image1, image2):
    return image1.shape == image2.shape and not(np.bitwise_xor(image1,image2).any())

And that returns false when check the first with the second image. How can I make this work out?

Thanks for your time,

==== EDIT ====

Here is canvas.png

==== EDIT 2 ====

After looking at @Rotem their answer, I've tried it out, but it still shows a slight difference, which the function above return False on: Picture 1 and Picutre 2

1
  • 1
    Can you please post 'canvas.png', to make the problem reproducible? Commented Mar 31, 2020 at 18:07

2 Answers 2

3

cv2.grabCut doesn't give deterministic results because the GrabCut algorithm uses built in randomness.

According to Wikipedia:

This is used to construct a Markov random field over the pixel labels...

You may avoid randomness by resetting the seed of the random generator of OpenCV before executing cv2.grabCut:

cv2.setRNGSeed(0)

Here is a code sample:

for count in range(10):
    cv2.setRNGSeed(0)
    img = cv2.imread('canvas.png')
    mask = np.zeros(img.shape[:2],np.uint8)
    bgdModel = np.zeros((1,65),np.float64)
    fgdModel = np.zeros((1,65),np.float64)
    rect = (70,70,300,250)
    cv2.grabCut(img,mask,rect,bgdModel,fgdModel,5,cv2.GC_INIT_WITH_RECT)
    mask2 = np.where((mask==2)|(mask==0),0,1).astype('uint8')
    img = img*mask2[:,:,np.newaxis]
    img = img[84:191, 84:203]
    cv2.imwrite(str(count)+".png", img)

Update:

You may use the following loop to compare the images:

# Verify that all images are the same
for count in range(10):
    im = cv2.imread(str(count)+".png")
    is_same = is_similar(im, img)
    if not is_same:
        print('Images are not the same, and it is strange!')

In my machine they are all the same.


Complete code:

import cv2
import numpy as np

# Disable OpenCL and disable multi-threading.
cv2.ocl.setUseOpenCL(False)
cv2.setNumThreads(1)


def is_similar(image1, image2):
    return image1.shape == image2.shape and not(np.bitwise_xor(image1,image2).any())

for count in range(10):
    cv2.setRNGSeed(0)
    img = cv2.imread('canvas.png')
    mask = np.zeros(img.shape[:2],np.uint8)
    bgdModel = np.zeros((1,65),np.float64)
    fgdModel = np.zeros((1,65),np.float64)
    rect = (70,70,300,250)
    cv2.grabCut(img,mask,rect,bgdModel,fgdModel,5,cv2.GC_INIT_WITH_RECT)
    mask2 = np.where((mask==2)|(mask==0),0,1).astype('uint8')
    img = img*mask2[:,:,np.newaxis]
    img = img[84:191, 84:203]
    cv2.imwrite(str(count)+".png", img)


# Verify that all images are the same
for count in range(10):
    im = cv2.imread(str(count)+".png")
    is_same = is_similar(im, img)
    if not is_same:
        print('Images are not the same, and it is strange!')
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for the answer! I've tried out xv.setRNGSeed(0), but it somehow still returns 2 slightly different images, which my function to check if the images are the same, doesnt like. Check the question update, anything on this?
I added a loop for comparing the results (updated my post). In my machine all the images are the same. It could be that in your system resting the seed of the random generator is not enough. There are cases when floating point operations gives different results due to rounding errors (common thing in parallel computations like GPU execution, but also CPU multi-threading is not deterministic). I can't say if is the case here.
You may try disable OpenCL and disable multi-threading: cv2.ocl.setUseOpenCL(False) and cv2.setNumThreads(1).
1

There is an element of randomness in cv2.grabCut(). Gaussian Mixture Models are used to detect the foreground and background after initial labeling. More information here or in the cited paper for grabcut.

It's because of this randomness that you're seeing different results. You should consider using some noise reduction techniques like morphological operations (cv2.erode() and cv2.dilate()) to handle this.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.