问题
Here\'s what I would like to do:
I\'m taking pictures with a webcam at regular intervals. Sort of like a time lapse thing. However, if nothing has really changed, that is, the picture pretty much looks the same, I don\'t want to store the latest snapshot.
I imagine there\'s some way of quantifying the difference, and I would have to empirically determine a threshold.
I\'m looking for simplicity rather than perfection. I\'m using python.
回答1:
General idea
Option 1: Load both images as arrays (scipy.misc.imread
) and calculate an element-wise (pixel-by-pixel) difference. Calculate the norm of the difference.
Option 2: Load both images. Calculate some feature vector for each of them (like a histogram). Calculate distance between feature vectors rather than images.
However, there are some decisions to make first.
Questions
You should answer these questions first:
Are images of the same shape and dimension?
If not, you may need to resize or crop them. PIL library will help to do it in Python.
If they are taken with the same settings and the same device, they are probably the same.
Are images well-aligned?
If not, you may want to run cross-correlation first, to find the best alignment first. SciPy has functions to do it.
If the camera and the scene are still, the images are likely to be well-aligned.
Is exposure of the images always the same? (Is lightness/contrast the same?)
If not, you may want to normalize images.
But be careful, in some situations this may do more wrong than good. For example, a single bright pixel on a dark background will make the normalized image very different.
Is color information important?
If you want to notice color changes, you will have a vector of color values per point, rather than a scalar value as in gray-scale image. You need more attention when writing such code.
Are there distinct edges in the image? Are they likely to move?
If yes, you can apply edge detection algorithm first (e.g. calculate gradient with Sobel or Prewitt transform, apply some threshold), then compare edges on the first image to edges on the second.
Is there noise in the image?
All sensors pollute the image with some amount of noise. Low-cost sensors have more noise. You may wish to apply some noise reduction before you compare images. Blur is the most simple (but not the best) approach here.
What kind of changes do you want to notice?
This may affect the choice of norm to use for the difference between images.
Consider using Manhattan norm (the sum of the absolute values) or zero norm (the number of elements not equal to zero) to measure how much the image has changed. The former will tell you how much the image is off, the latter will tell only how many pixels differ.
Example
I assume your images are well-aligned, the same size and shape, possibly with different exposure. For simplicity, I convert them to grayscale even if they are color (RGB) images.
You will need these imports:
import sys
from scipy.misc import imread
from scipy.linalg import norm
from scipy import sum, average
Main function, read two images, convert to grayscale, compare and print results:
def main():
file1, file2 = sys.argv[1:1+2]
# read images as 2D arrays (convert to grayscale for simplicity)
img1 = to_grayscale(imread(file1).astype(float))
img2 = to_grayscale(imread(file2).astype(float))
# compare
n_m, n_0 = compare_images(img1, img2)
print "Manhattan norm:", n_m, "/ per pixel:", n_m/img1.size
print "Zero norm:", n_0, "/ per pixel:", n_0*1.0/img1.size
How to compare. img1
and img2
are 2D SciPy arrays here:
def compare_images(img1, img2):
# normalize to compensate for exposure difference, this may be unnecessary
# consider disabling it
img1 = normalize(img1)
img2 = normalize(img2)
# calculate the difference and its norms
diff = img1 - img2 # elementwise for scipy arrays
m_norm = sum(abs(diff)) # Manhattan norm
z_norm = norm(diff.ravel(), 0) # Zero norm
return (m_norm, z_norm)
If the file is a color image, imread
returns a 3D array, average RGB channels (the last array axis) to obtain intensity. No need to do it for grayscale images (e.g. .pgm
):
def to_grayscale(arr):
"If arr is a color image (3D array), convert it to grayscale (2D array)."
if len(arr.shape) == 3:
return average(arr, -1) # average over the last axis (color channels)
else:
return arr
Normalization is trivial, you may choose to normalize to [0,1] instead of [0,255]. arr
is a SciPy array here, so all operations are element-wise:
def normalize(arr):
rng = arr.max()-arr.min()
amin = arr.min()
return (arr-amin)*255/rng
Run the main
function:
if __name__ == "__main__":
main()
Now you can put this all in a script and run against two images. If we compare image to itself, there is no difference:
$ python compare.py one.jpg one.jpg
Manhattan norm: 0.0 / per pixel: 0.0
Zero norm: 0 / per pixel: 0.0
If we blur the image and compare to the original, there is some difference:
$ python compare.py one.jpg one-blurred.jpg
Manhattan norm: 92605183.67 / per pixel: 13.4210411116
Zero norm: 6900000 / per pixel: 1.0
P.S. Entire compare.py script.
Update: relevant techniques
As the question is about a video sequence, where frames are likely to be almost the same, and you look for something unusual, I'd like to mention some alternative approaches which may be relevant:
- background subtraction and segmentation (to detect foreground objects)
- sparse optical flow (to detect motion)
- comparing histograms or some other statistics instead of images
I strongly recommend taking a look at “Learning OpenCV” book, Chapters 9 (Image parts and segmentation) and 10 (Tracking and motion). The former teaches to use Background subtraction method, the latter gives some info on optical flow methods. All methods are implemented in OpenCV library. If you use Python, I suggest to use OpenCV ≥ 2.3, and its cv2
Python module.
The most simple version of the background subtraction:
- learn the average value μ and standard deviation σ for every pixel of the background
- compare current pixel values to the range of (μ-2σ,μ+2σ) or (μ-σ,μ+σ)
More advanced versions make take into account time series for every pixel and handle non-static scenes (like moving trees or grass).
The idea of optical flow is to take two or more frames, and assign velocity vector to every pixel (dense optical flow) or to some of them (sparse optical flow). To estimate sparse optical flow, you may use Lucas-Kanade method (it is also implemented in OpenCV). Obviously, if there is a lot of flow (high average over max values of the velocity field), then something is moving in the frame, and subsequent images are more different.
Comparing histograms may help to detect sudden changes between consecutive frames. This approach was used in Courbon et al, 2010:
Similarity of consecutive frames. The distance between two consecutive frames is measured. If it is too high, it means that the second frame is corrupted and thus the image is eliminated. The Kullback–Leibler distance, or mutual entropy, on the histograms of the two frames:
where p and q are the histograms of the frames is used. The threshold is fixed on 0.2.
回答2:
A simple solution:
Encode the image as a jpeg and look for a substantial change in filesize.
I've implemented something similar with video thumbnails, and had a lot of success and scalability.
回答3:
You can compare two images using functions from PIL.
import Image
import ImageChops
im1 = Image.open("splash.png")
im2 = Image.open("splash2.png")
diff = ImageChops.difference(im2, im1)
The diff object is an image in which every pixel is the result of the subtraction of the color values of that pixel in the second image from the first image. Using the diff image you can do several things. The simplest one is the diff.getbbox()
function. It will tell you the minimal rectangle that contains all the changes between your two images.
You can probably implement approximations of the other stuff mentioned here using functions from PIL as well.
回答4:
Two popular and relatively simple methods are: (a) the Euclidean distance already suggested, or (b) normalized cross-correlation. Normalized cross-correlation tends to be noticeably more robust to lighting changes than simple cross-correlation. Wikipedia gives a formula for the normalized cross-correlation. More sophisticated methods exist too, but they require quite a bit more work.
Using numpy-like syntax,
dist_euclidean = sqrt(sum((i1 - i2)^2)) / i1.size dist_manhattan = sum(abs(i1 - i2)) / i1.size dist_ncc = sum( (i1 - mean(i1)) * (i2 - mean(i2)) ) / ( (i1.size - 1) * stdev(i1) * stdev(i2) )
assuming that i1
and i2
are 2D grayscale image arrays.
回答5:
A trivial thing to try:
Resample both images to small thumbnails (e.g. 64 x 64) and compare the thumbnails pixel-by-pixel with a certain threshold. If the original images are almost the same, the resampled thumbnails will be very similar or even exactly the same. This method takes care of noise that can occur especially in low-light scenes. It may even be better if you go grayscale.
回答6:
I am addressing specifically the question of how to compute if they are "different enough". I assume you can figure out how to subtract the pixels one by one.
First, I would take a bunch of images with nothing changing, and find out the maximum amount that any pixel changes just because of variations in the capture, noise in the imaging system, JPEG compression artifacts, and moment-to-moment changes in lighting. Perhaps you'll find that 1 or 2 bit differences are to be expected even when nothing moves.
Then for the "real" test, you want a criterion like this:
- same if up to P pixels differ by no more than E.
So, perhaps, if E = 0.02, P = 1000, that would mean (approximately) that it would be "different" if any single pixel changes by more than ~5 units (assuming 8-bit images), or if more than 1000 pixels had any errors at all.
This is intended mainly as a good "triage" technique to quickly identify images that are close enough to not need further examination. The images that "fail" may then more to a more elaborate/expensive technique that wouldn't have false positives if the camera shook bit, for example, or was more robust to lighting changes.
I run an open source project, OpenImageIO, that contains a utility called "idiff" that compares differences with thresholds like this (even more elaborate, actually). Even if you don't want to use this software, you may want to look at the source to see how we did it. It's used commercially quite a bit and this thresholding technique was developed so that we could have a test suite for rendering and image processing software, with "reference images" that might have small differences from platform-to-platform or as we made minor tweaks to tha algorithms, so we wanted a "match within tolerance" operation.
回答7:
Most of the answers given won't deal with lighting levels.
I would first normalize the image to a standard light level before doing the comparison.
回答8:
I had a similar problem at work, I was rewriting our image transform endpoint and I wanted to check that the new version was producing the same or nearly the same output as the old version. So I wrote this:
https://github.com/nicolashahn/diffimg
Which operates on images of the same size, and at a per-pixel level, measures the difference in values at each channel: R, G, B(, A), takes the average difference of those channels, and then averages the difference over all pixels, and returns a ratio.
For example, with a 10x10 image of white pixels, and the same image but one pixel has changed to red, the difference at that pixel is 1/3 or 0.33... (RGB 0,0,0 vs 255,0,0) and at all other pixels is 0. With 100 pixels total, 0.33.../100 = a ~0.33% difference in image.
I believe this would work perfectly for OP's project (I realize this is a very old post now, but posting for future StackOverflowers who also want to compare images in python).
回答9:
Another nice, simple way to measure the similarity between two images:
import sys
from skimage.measure import compare_ssim
from skimage.transform import resize
from scipy.ndimage import imread
# get two images - resize both to 1024 x 1024
img_a = resize(imread(sys.argv[1]), (2**10, 2**10))
img_b = resize(imread(sys.argv[2]), (2**10, 2**10))
# score: {-1:1} measure of the structural similarity between the images
score, diff = compare_ssim(img_a, img_b, full=True)
print(score)
If others are interested in a more powerful way to compare image similarity, I put together a tutorial and web app for measuring and visualizing similar images using Tensorflow.
回答10:
Have you seen the Algorithm for finding similar images question? Check it out to see suggestions.
I would suggest a wavelet transformation of your frames (I've written a C extension for that using Haar transformation); then, comparing the indexes of the largest (proportionally) wavelet factors between the two pictures, you should get a numerical similarity approximation.
回答11:
I apologize if this is too late to reply, but since I've been doing something similar I thought I could contribute somehow.
Maybe with OpenCV you could use template matching. Assuming you're using a webcam as you said:
- Simplify the images (thresholding maybe?)
- Apply template matching and check the max_val with minMaxLoc
Tip: max_val (or min_val depending on the method used) will give you numbers, large numbers. To get the difference in percentage, use template matching with the same image -- the result will be your 100%.
Pseudo code to exemplify:
previous_screenshot = ...
current_screenshot = ...
# simplify both images somehow
# get the 100% corresponding value
res = matchTemplate(previous_screenshot, previous_screenshot, TM_CCOEFF)
_, hundred_p_val, _, _ = minMaxLoc(res)
# hundred_p_val is now the 100%
res = matchTemplate(previous_screenshot, current_screenshot, TM_CCOEFF)
_, max_val, _, _ = minMaxLoc(res)
difference_percentage = max_val / hundred_p_val
# the tolerance is now up to you
Hope it helps.
回答12:
Earth movers distance might be exactly what you need. It might be abit heavy to implement in real time though.
回答13:
What about calculating the Manhattan Distance of the two images. That gives you n*n values. Then you could do something like an row average to reduce to n values and a function over that to get one single value.
回答14:
I have been having a lot of luck with jpg images taken with the same camera on a tripod by (1) simplifying greatly (like going from 3000 pixels wide to 100 pixels wide or even fewer) (2) flattening each jpg array into a single vector (3) pairwise correlating sequential images with a simple correlate algorithm to get correlation coefficient (4) squaring correlation coefficient to get r-square (i.e fraction of variability in one image explained by variation in the next) (5) generally in my application if r-square < 0.9, I say the two images are different and something happened in between.
This is robust and fast in my implementation (Mathematica 7)
It's worth playing around with the part of the image you are interested in and focussing on that by cropping all images to that little area, otherwise a distant-from-the-camera but important change will be missed.
I don't know how to use Python, but am sure it does correlations, too, no?
回答15:
you can compute the histogram of both the images and then calculate the Bhattacharyya Coefficient, this is a very fast algorithm and I have used it to detect shot changes in a cricket video (in C using openCV)
回答16:
Check out how Haar Wavelets are implemented by isk-daemon. You could use it's imgdb C++ code to calculate the difference between images on-the-fly:
isk-daemon is an open source database server capable of adding content-based (visual) image searching to any image related website or software.
This technology allows users of any image-related website or software to sketch on a widget which image they want to find and have the website reply to them the most similar images or simply request for more similar photos at each image detail page.
回答17:
I had the same problem and wrote a simple python module which compares two same-size images using pillow's ImageChops to create a black/white diff image and sums up the histogram values.
You can get either this score directly, or a percentage value compared to a full black vs. white diff.
It also contains a simple is_equal function, with the possibility to supply a fuzzy-threshold under (and including) the image passes as equal.
The approach is not very elaborate, but maybe is of use for other out there struggling with the same issue.
https://pypi.python.org/pypi/imgcompare/
回答18:
A somewhat more principled approach is to use a global descriptor to compare images, such as GIST or CENTRIST. A hash function, as described here, also provides a similar solution.
回答19:
import os
from PIL import Image
from PIL import ImageFile
import imagehash
#just use to the size diferent picture
def compare_image(img_file1, img_file2):
if img_file1 == img_file2:
return True
fp1 = open(img_file1, 'rb')
fp2 = open(img_file2, 'rb')
img1 = Image.open(fp1)
img2 = Image.open(fp2)
ImageFile.LOAD_TRUNCATED_IMAGES = True
b = img1 == img2
fp1.close()
fp2.close()
return b
#through picturu hash to compare
def get_hash_dict(dir):
hash_dict = {}
image_quantity = 0
for _, _, files in os.walk(dir):
for i, fileName in enumerate(files):
with open(dir + fileName, 'rb') as fp:
hash_dict[dir + fileName] = imagehash.average_hash(Image.open(fp))
image_quantity += 1
return hash_dict, image_quantity
def compare_image_with_hash(image_file_name_1, image_file_name_2, max_dif=0):
"""
max_dif: The maximum hash difference is allowed, the smaller and more accurate, the minimum is 0.
recommend to use
"""
ImageFile.LOAD_TRUNCATED_IMAGES = True
hash_1 = None
hash_2 = None
with open(image_file_name_1, 'rb') as fp:
hash_1 = imagehash.average_hash(Image.open(fp))
with open(image_file_name_2, 'rb') as fp:
hash_2 = imagehash.average_hash(Image.open(fp))
dif = hash_1 - hash_2
if dif < 0:
dif = -dif
if dif <= max_dif:
return True
else:
return False
def compare_image_dir_with_hash(dir_1, dir_2, max_dif=0):
"""
max_dif: The maximum hash difference is allowed, the smaller and more accurate, the minimum is 0.
"""
ImageFile.LOAD_TRUNCATED_IMAGES = True
hash_dict_1, image_quantity_1 = get_hash_dict(dir_1)
hash_dict_2, image_quantity_2 = get_hash_dict(dir_2)
if image_quantity_1 > image_quantity_2:
tmp = image_quantity_1
image_quantity_1 = image_quantity_2
image_quantity_2 = tmp
tmp = hash_dict_1
hash_dict_1 = hash_dict_2
hash_dict_2 = tmp
result_dict = {}
for k in hash_dict_1.keys():
result_dict[k] = None
for dif_i in range(0, max_dif + 1):
have_none = False
for k_1 in result_dict.keys():
if result_dict.get(k_1) is None:
have_none = True
if not have_none:
return result_dict
for k_1, v_1 in hash_dict_1.items():
for k_2, v_2 in hash_dict_2.items():
sub = (v_1 - v_2)
if sub < 0:
sub = -sub
if sub == dif_i and result_dict.get(k_1) is None:
result_dict[k_1] = k_2
break
return result_dict
def main():
print(compare_image('image1\\815.jpg', 'image2\\5.jpg'))
print(compare_image_with_hash('image1\\815.jpg', 'image2\\5.jpg', 6))
r = compare_image_dir_with_hash('image1\\', image2\\', 10)
for k in r.keys():
print(k, r.get(k))
if __name__ == '__main__':
main()
output:
False
True
image2\5.jpg image1\815.jpg
image2\6.jpg image1\819.jpg
image2\7.jpg image1\900.jpg
image2\8.jpg image1\998.jpg
image2\9.jpg image1\1012.jpgthe example pictures:
815.jpg
5.jpg
回答20:
I think you could simply compute the euclidean distance (i.e. sqrt(sum of squares of differences, pixel by pixel)) between the luminance of the two images, and consider them equal if this falls under some empirical threshold. And you would better do it wrapping a C function.
回答21:
There are many metrics out there for evaluating whether two images look like/how much they look like.
I will not go into any code here, because I think it should be a scientific problem, other than a technical problem.
Generally, the question is related to human's perception on images, so each algorithm has its support on human visual system traits.
Classic approaches are:
Visible differences predictor: an algorithm for the assessment of image fidelity (https://www.spiedigitallibrary.org/conference-proceedings-of-spie/1666/0000/Visible-differences-predictor--an-algorithm-for-the-assessment-of/10.1117/12.135952.short?SSO=1)
Image Quality Assessment: From Error Visibility to Structural Similarity (http://www.cns.nyu.edu/pub/lcv/wang03-reprint.pdf)
FSIM: A Feature Similarity Index for Image Quality Assessment (https://www4.comp.polyu.edu.hk/~cslzhang/IQA/TIP_IQA_FSIM.pdf)
Among them, SSIM (Image Quality Assessment: From Error Visibility to Structural Similarity ) is the easiest to calculate and its overhead is also small, as reported in another paper "Image Quality Assessment Based on Gradient Similarity" (https://www.semanticscholar.org/paper/Image-Quality-Assessment-Based-on-Gradient-Liu-Lin/2b819bef80c02d5d4cb56f27b202535e119df988).
There are many more other approaches. Take a look at Google Scholar and search for something like "visual difference", "image quality assessment", etc, if you are interested/really care about the art.
回答22:
There's a simple and fast solution using numpy by calculating mean squared error:
before = np.array(get_picture())
while True:
now = np.array(get_picture())
MSE = np.mean((now - before)**2)
if MSE > threshold:
break
before = now
来源:https://stackoverflow.com/questions/189943/how-can-i-quantify-difference-between-two-images