2009-12-18 64 views
35

Cố gắng giải quyết vấn đề ngăn hình ảnh trùng lặp được tải lên.So sánh hai hình ảnh cách python/linux

Tôi có hai JPG. Nhìn vào họ tôi có thể thấy rằng họ đang ở trong thực tế giống hệt nhau. Nhưng đối với một số lý do họ có kích thước tập tin khác nhau (một là kéo từ một bản sao lưu, khác là một tải lên) và vì vậy họ có một checksum md5 khác nhau.

Làm thế nào tôi có thể so sánh hai hình ảnh một cách hiệu quả và tự tin giống như một con người có thể thấy rằng chúng giống hệt nhau?

Ví dụ: http://static.peterbe.com/a.jpghttp://static.peterbe.com/b.jpg

Cập nhật

tôi đã viết kịch bản này:

import math, operator 
from PIL import Image 
def compare(file1, file2): 
    image1 = Image.open(file1) 
    image2 = Image.open(file2) 
    h1 = image1.histogram() 
    h2 = image2.histogram() 
    rms = math.sqrt(reduce(operator.add, 
          map(lambda a,b: (a-b)**2, h1, h2))/len(h1)) 
    return rms 

if __name__=='__main__': 
    import sys 
    file1, file2 = sys.argv[1:] 
    print compare(file1, file2) 

Sau đó, tôi đã tải về hai hình ảnh giống hệt trực quan và chạy kịch bản. Đầu ra:

58.9830484122 

Ai có thể cho tôi biết điểm ngắt phù hợp là gì?

Cập nhật II

Sự khác biệt giữa a.jpg và b.jpg là điều thứ hai đã được lưu với PIL:

b=Image.open('a.jpg') 
b.save(open('b.jpg','wb')) 

này dường như áp dụng một số thay đổi chất lượng rất rất nhẹ. Bây giờ tôi đã giải quyết vấn đề của tôi bằng cách áp dụng cùng một PIL lưu vào tập tin được tải lên mà không làm bất cứ điều gì với nó và nó bây giờ hoạt động!

+0

Theo như tôi có thể cả hai đều không có dữ liệu Exif. –

+2

'reduce (operator.add (...))' -> 'sum (...)'. – jfs

+0

Đối với những gì nó có giá trị (chủ yếu là trong tài liệu tham khảo để thông tin nền) này giống như một phiên bản đơn giản của câu hỏi này: http://stackoverflow.com/questions/1819124/image-comparison-algorithm –

Trả lời

25

Có một dự án PMNM sử dụng WebDriver để chụp ảnh màn hình và sau đó so sánh hình ảnh để xem có bất kỳ sự cố nào không (http://code.google.com/p/fighting-layout-bugs/)). Nó làm điều đó bằng cách mở tập tin vào một dòng và sau đó so sánh từng bit.

Bạn có thể làm điều gì đó tương tự với PIL.

EDIT:

Sau khi nghiên cứu tôi càng thấy

h1 = Image.open("image1").histogram() 
h2 = Image.open("image2").histogram() 

rms = math.sqrt(reduce(operator.add, 
    map(lambda a,b: (a-b)**2, h1, h2))/len(h1)) 

trên http://snipplr.com/view/757/compare-two-pil-images-in-python/http://effbot.org/zone/pil-comparing-images.htm

+0

Tôi đã áp dụng kỹ thuật đó nhưng cho hai hình ảnh tôi liên kết đến ở trên, RMS là 58,9. Nếu tôi so sánh a.jpg với a.jpg tôi nhận được 0,0 được mong đợi. Tôi thay đổi một trong gimp bằng cách vẽ một số crap vào nó và RMS đến 675.6 –

+9

Mã này tìm thấy lỗi RMS giữa các biểu đồ của hình ảnh, ** KHÔNG ** giữa các hình ảnh. Hãy xem xét rằng hai hình ảnh có thể có biểu đồ giống hệt nhau nhưng hoàn toàn khác nhau, ví dụ: nếu bạn “tranh giành” các pixel. – musicinmybrain

+6

Cần lưu ý rằng nếu bạn thay thế cuộc gọi map + reduce với khả năng hiểu danh sách tương đương, bạn sẽ nhận được một tốc độ tăng rất tốt (nhanh hơn khoảng 80% trên máy Mac của tôi). Sử dụng đoạn mã sau để thay thế (xin lỗi cho định dạng nghèo): 'diff_squares = [(h1 [i] - h2 [i]) ** 2 cho i in xrange (len (h1))]; rms = math.sqrt (tổng (diff_squares)/len (h1)); '. Đây là trên Python 2.7.3 và tôi đã nhân rộng các kết quả trên OS X và Linux. YMMV, nhưng sự khác biệt là đủ lớn để đảm bảo điều tra. – CadentOrange

11

Tôi đoán bạn nên giải mã những hình ảnh và làm một điểm ảnh bằng cách so sánh điểm ảnh để xem nếu họ hợp lý tương tự.

Với PIL và NumPy bạn có thể làm điều đó khá dễ dàng:

import Image 
import numpy 
import sys 

def main(): 
    img1 = Image.open(sys.argv[1]) 
    img2 = Image.open(sys.argv[2]) 

    if img1.size != img2.size or img1.getbands() != img2.getbands(): 
     return -1 

    s = 0 
    for band_index, band in enumerate(img1.getbands()): 
     m1 = numpy.array([p[band_index] for p in img1.getdata()]).reshape(*img1.size) 
     m2 = numpy.array([p[band_index] for p in img2.getdata()]).reshape(*img2.size) 
     s += numpy.sum(numpy.abs(m1-m2)) 
    print s 

if __name__ == "__main__": 
    sys.exit(main()) 

này sẽ cung cấp cho bạn một giá trị số đó nên rất gần với 0 nếu những hình ảnh khá giống nhau.

Lưu ý rằng hình ảnh được dịch chuyển/xoay sẽ được báo cáo là rất khác, vì các pixel sẽ không khớp nhau.

+0

Biểu đồ màu chuẩn hóa này (trong câu hỏi của OP) không bị ảnh hưởng bởi sự thay đổi/xoay vòng. Bạn có thể giảm độ nhạy của thuật toán của mình đối với các phép quay/thay đổi nhỏ bằng cách lấy mẫu trước khi phân biệt, ví dụ: tổng hợp 10x10 hộp pixel và phân biệt ít hơn 100 pixel "pixel". – hobs

1

Bạn có thể so sánh nó bằng cách sử dụng PIL (lặp qua pixel/phân đoạn của hình ảnh và so sánh) hoặc nếu bạn đang tìm kiếm bản so sánh hoàn chỉnh giống hệt nhau, hãy thử so sánh MD5 băm của cả hai tệp.

2

Trước tiên, tôi nên lưu ý rằng chúng là không phải giống nhau; b đã được recompressed và bị mất chất lượng. Bạn có thể thấy điều này nếu bạn nhìn kỹ trên một màn hình tốt.

Để xác định rằng chúng chủ quan là "giống nhau", bạn sẽ phải làm điều gì đó giống như những gì mà fortran đề xuất, mặc dù bạn sẽ phải tự ý thiết lập ngưỡng cho "mẫu." Để tạo độc lập với kích thước hình ảnh và xử lý các kênh một cách hợp lý hơn một chút, tôi sẽ xem xét thực hiện RMS (bình phương trung bình gốc) Khoảng cách Euclide trong không gian màu giữa các điểm ảnh của hai hình ảnh. Tôi không có thời gian để viết ra mã ngay bây giờ, nhưng về cơ bản cho mỗi pixel, bạn tính toán

(R_2 - R_1) ** 2 + (G_2 - G_1) ** 2 + (B_2 - B_1) ** 2 

, thêm vào một (A_2 - A_1)

** 2

hạn nếu hình ảnh có kênh alpha, v.v. Kết quả là hình vuông của khoảng cách khoảng cách giữa hai hình ảnh. Tìm giá trị trung bình (trung bình) trên tất cả các pixel, sau đó lấy căn bậc hai của vô hướng kết quả. Sau đó, quyết định ngưỡng hợp lý cho giá trị này.

Hoặc, bạn có thể chỉ quyết định rằng các bản sao của cùng một hình ảnh gốc với nén mất dữ liệu khác nhau không thực sự là "giống nhau" và gắn bó với tệp băm.

4

sự cố khi biết điều gì làm cho một số tính năng của hình ảnh quan trọng hơn so với các tính năng khác là toàn bộ chương trình khoa học. Tôi xin đề xuất một số giải pháp thay thế tùy thuộc vào giải pháp mà bạn muốn:

  • nếu vấn đề của bạn là để xem nếu có một lật bit trong hình ảnh JPEG của bạn, sau đó cố gắng hình ảnh hình ảnh khác biệt (có lẽ là một sửa đổi nhỏ tại địa phương?),

  • để xem hình ảnh được trên toàn cầu như nhau, sử dụng khoảng cách Kullback Leibler để so sánh biểu đồ của bạn,

  • để xem nếu bạn có một số thay đổi qualittative, trước khi áp dụng câu trả lời khác, lọc hình ảnh của bạn sử dụng các chức năng dưới đây để nâng cao tầm quan trọng của tần số cao:

mã:

def FTfilter(image,FTfilter): 
    from scipy.fftpack import fft2, fftshift, ifft2, ifftshift 
    from scipy import real 
    FTimage = fftshift(fft2(image)) * FTfilter 
    return real(ifft2(ifftshift(FTimage))) 
    #return real(ifft2(fft2(image)* FTfilter)) 


#### whitening 
def olshausen_whitening_filt(size, f_0 = .78, alpha = 4., N = 0.01): 
    """ 
    Returns the whitening filter used by (Olshausen, 98) 

    f_0 = 200/512 

    /!\ you will have some problems at dewhitening without a low-pass 

    """ 
    from scipy import mgrid, absolute 
    fx, fy = mgrid[-1:1:1j*size[0],-1:1:1j*size[1]] 
    rho = numpy.sqrt(fx**2+fy**2) 
    K_ols = (N**2 + rho**2)**.5 * low_pass(size, f_0 = f_0, alpha = alpha) 
    K_ols /= numpy.max(K_ols) 

    return K_ols 

def low_pass(size, f_0, alpha): 
    """ 
    Returns the low_pass filter used by (Olshausen, 98) 

    parameters from Atick (p.240) 
    f_0 = 22 c/deg in primates: the full image is approx 45 deg 
    alpha makes the aspect change (1=diamond on the vert and hor, 2 = anisotropic) 

    """ 

    from scipy import mgrid, absolute 
    fx, fy = mgrid[-1:1:1j*size[0],-1:1:1j*size[1]] 
    rho = numpy.sqrt(fx**2+fy**2) 
    low_pass = numpy.exp(-(rho/f_0)**alpha) 

    return low_pass 

(bản sao không biết xấu hổ từ http://www.incm.cnrs-mrs.fr/LaurentPerrinet/Publications/Perrinet08spie)

16

Từ here

Cách nhanh nhất để xác định xem hai hình ảnh có chính xác cùng một nội dung là để có được sự khác biệt giữa hai hình ảnh, và sau đó tính toán hộp giới hạn của các vùng khác 0 trong hình ảnh này. Nếu các hình ảnh giống hệt nhau, tất cả các pixel trong hình ảnh khác nhau là 0 và hàm hộp giới hạn trả về Không.

import ImageChops 

def equal(im1, im2): 
    return ImageChops.difference(im1, im2).getbbox() is None 
10

Sử dụng ImageMagick, bạn chỉ có thể sử dụng trong vỏ của bạn [hoặc gọi qua thư viện OS từ bên trong một chương trình]

compare image1 image2 output 

này sẽ tạo ra một hình ảnh đầu ra với sự khác biệt đánh dấu

compare -metric AE -fuzz 5% image1 image2 output 

Sẽ cung cấp cho bạn hệ số mờ 5% để bỏ qua các khác biệt nhỏ về pixel. Bạn có thể mua thêm thông tin từ here

+3

Có cách nào bạn có thể lập trình xác định nếu kết quả 'đầu ra 'là trống? – Flimm

+0

Bạn có thể sử dụng 'NULL:' làm 'đầu ra'. Sau đó nó sẽ in giá trị để stdout. – aspyct

2

Tôi đã thử nghiệm tính năng này và hoạt động tốt nhất trong tất cả các phương pháp và cực nhanh!

def rmsdiff_1997(im1, im2): 
    "Calculate the root-mean-square difference between two images" 

    h = ImageChops.difference(im1, im2).histogram() 

    # calculate rms 
    return math.sqrt(reduce(operator.add, 
     map(lambda h, i: h*(i**2), h, range(256)) 
    )/(float(im1.size[0]) * im1.size[1])) 

here link để tham khảo