This task definition will definitely include some kind of heuristic.
The same image can be represented using a myriad of different ways in memory, so you're basically asking if the images are similar from a human's perspective.
Step 1:
Looking at the size of the image could be a very good first step. It's easy, cheap and actually serves as a good way baseline before trying to compare the actual contents of the pictures.
Step 2:
Now that we know the images are of the same size, we get to the hard part: how do we compare the contents.
There are a ton of different approaches here. The simplest ones are probably calculating the l-2 Norm or Mean Squared Error (MSE) or using Structural Similarity Index (SSIM).
Furthermore, you can try to account for small color deviations by converting the image to grayscale first.
Here is a python script that compares sizes, converts to grayscale and uses SSIM to compare them with a controllable threshold:
#!/usr/bin/env python
from cv2 import imread, cvtColor, COLOR_BGR2GRAY
from skimage.metrics import structural_similarity
def get_size(im):
w, h, d = im.shape
return (w, h)
def main(first, second, ssim_threshold):
first_im = imread(first)
first_sz = get_size(first_im)
second_im = imread(second)
second_sz = get_size(second_im)
if first_sz != second_sz:
print(f'Image sizes differ {first_sz} != {second_sz}')
return False
first_gs = cvtColor(first_im, COLOR_BGR2GRAY)
second_gs = cvtColor(second_im, COLOR_BGR2GRAY)
ssim = structural_similarity(first_gs, second_gs)
if ssim < ssim_threshold:
print(f'Image SSIM lower than allowed threshold [{ssim:.5f} < {ssim_threshold}]')
return False
print('Images are the same')
return True
if __name__ == '__main__':
import sys
import argparse
parser = argparse.ArgumentParser(description='Compare two images')
parser.add_argument('first', type=str,
help='First image for comparison')
parser.add_argument('second', type=str,
help='Second image for comparison')
parser.add_argument('ssim', type=float, default=0.95,
help='Structural similarity minimum (Range: (0, 1], 1 means identical)')
args = parser.parse_args()
same = main(args.first, args.second, args.ssim)
sys.exit(0 if same else 1)
I used it on the following photos:

Like this
>>> python compare.py 1.jpeg 2.jpeg 0.90
Image SSIM lower than allowed threshold [0.88254 < 0.95]
>>> python compare.py 1.jpeg 3.jpeg 0.90
Images are the same
Notes:
- Notice that with a 0.9 threshold, the same image with a filter was identifies as the same, but the grayscale one wasn't, you should probably find the right threshold for your usecase
- The script exits with
0 = equal
and 1 = not-equal
, so that you can automate it
- It uses Python3 and the following packages:
opencv-python
scikit-image