Ý tưởng chung
Tùy chọn 1: Tải cả hai hình ảnh dưới dạng mảng ( scipy.misc.imread
) và tính toán chênh lệch phần tử (pixel-by-pixel). Tính định mức chênh lệch.
Tùy chọn 2: Tải cả hai hình ảnh. Tính toán một số vectơ đặc trưng cho mỗi trong số chúng (như biểu đồ). Tính khoảng cách giữa các vectơ đặc trưng hơn là hình ảnh.
Tuy nhiên, có một số quyết định để thực hiện đầu tiên.
Câu hỏi
Bạn nên trả lời những câu hỏi này trước:
Là hình ảnh của cùng một hình dạng và kích thước?
Nếu không, bạn có thể cần thay đổi kích thước hoặc cắt chúng. Thư viện PIL sẽ giúp làm điều đó trong Python.
Nếu chúng được chụp với cùng các cài đặt và cùng một thiết bị, chúng có thể giống nhau.
Là hình ảnh phù hợp?
Nếu không, bạn có thể muốn chạy tương quan chéo trước, để tìm sự liên kết tốt nhất trước. SciPy có chức năng để làm điều đó.
Nếu máy ảnh và cảnh vẫn còn, hình ảnh có thể được căn chỉnh tốt.
Là tiếp xúc của hình ảnh luôn luôn giống nhau? (Độ sáng / độ tương phản có giống nhau không?)
Nếu không, bạn có thể muốn bình thường hóa hình ảnh.
Nhưng hãy cẩn thận, trong một số tình huống điều này có thể làm sai nhiều hơn là tốt. Ví dụ, một pixel sáng đơn trên nền tối sẽ làm cho hình ảnh được chuẩn hóa trở nên rất khác biệt.
Là thông tin màu sắc quan trọng?
Nếu bạn muốn nhận thấy sự thay đổi màu sắc, bạn sẽ có một vectơ giá trị màu cho mỗi điểm, thay vì giá trị vô hướng như trong hình ảnh tỷ lệ xám. Bạn cần chú ý hơn khi viết mã như vậy.
Có các cạnh khác biệt trong hình ảnh? Họ có khả năng di chuyển?
Nếu có, trước tiên bạn có thể áp dụng thuật toán phát hiện cạnh (ví dụ: tính toán độ dốc với biến đổi Sobel hoặc Prewitt, áp dụng một số ngưỡng), sau đó so sánh các cạnh trên hình ảnh đầu tiên với các cạnh trên thứ hai.
Có nhiễu trong ảnh không?
Tất cả các cảm biến gây ô nhiễm hình ảnh với một số lượng nhiễu. Cảm biến giá rẻ có nhiều tiếng ồn hơn. Bạn có thể muốn áp dụng một số giảm nhiễu trước khi so sánh hình ảnh. Blur là cách tiếp cận đơn giản nhất (nhưng không phải là tốt nhất) ở đây.
Những loại thay đổi nào bạn muốn thông báo?
Điều này có thể ảnh hưởng đến việc lựa chọn định mức để sử dụng cho sự khác biệt giữa các hình ảnh.
Xem xét sử dụng định mức Manhattan (tổng của các giá trị tuyệt đối) hoặc định mức bằng không (số phần tử không bằng 0) để đo mức độ hình ảnh đã thay đổi. Cái trước sẽ cho bạn biết hình ảnh bị tắt bao nhiêu, cái sau sẽ chỉ cho biết có bao nhiêu pixel khác nhau.
Thí dụ
Tôi cho rằng hình ảnh của bạn được căn chỉnh tốt, cùng kích thước và hình dạng, có thể có độ phơi sáng khác nhau. Để đơn giản, tôi chuyển đổi chúng thành thang độ xám ngay cả khi chúng là hình ảnh màu (RGB).
Bạn sẽ cần những hàng nhập khẩu này:
import sys
from scipy.misc import imread
from scipy.linalg import norm
from scipy import sum, average
Chức năng chính, đọc hai hình ảnh, chuyển đổi sang thang độ xám, so sánh và in kết quả:
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
Cách so sánh. img1
và img2
là mảng 2D SciPy ở đây:
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)
Nếu tệp là hình ảnh màu, imread
trả về mảng 3D, các kênh RGB trung bình (trục mảng cuối cùng) để thu được cường độ. Không cần phải làm điều đó cho hình ảnh thang độ xám (ví dụ .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
Bình thường hóa là chuyện nhỏ, bạn có thể chọn bình thường hóa thành [0,1] thay vì [0,255]. arr
là một mảng SciPy ở đây, vì vậy tất cả các hoạt động đều là phần tử khôn ngoan:
def normalize(arr):
rng = arr.max()-arr.min()
amin = arr.min()
return (arr-amin)*255/rng
Chạy main
chức năng:
if __name__ == "__main__":
main()
Bây giờ bạn có thể đặt tất cả điều này trong một tập lệnh và chạy với hai hình ảnh. Nếu chúng ta so sánh hình ảnh với chính nó, không có sự khác biệt:
$ python compare.py one.jpg one.jpg
Manhattan norm: 0.0 / per pixel: 0.0
Zero norm: 0 / per pixel: 0.0
Nếu chúng ta làm mờ hình ảnh và so sánh với bản gốc, có một số khác biệt:
$ python compare.py one.jpg one-blurred.jpg
Manhattan norm: 92605183.67 / per pixel: 13.4210411116
Zero norm: 6900000 / per pixel: 1.0
Toàn bộ tập lệnh so sánh PS .
Cập nhật: kỹ thuật liên quan
Vì câu hỏi là về một chuỗi video, trong đó các khung hình có thể gần giống nhau và bạn tìm kiếm điều gì đó bất thường, tôi muốn đề cập đến một số phương pháp thay thế có thể có liên quan:
- trừ nền và phân đoạn (để phát hiện các đối tượng nền trước)
- dòng quang thưa (để phát hiện chuyển động)
- so sánh biểu đồ hoặc một số thống kê khác thay vì hình ảnh
Tôi thực sự khuyên bạn nên xem cuốn sách Học tập OpenCV của Chương, Chương 9 (Phần hình ảnh và phân đoạn) và 10 (Theo dõi và chuyển động). Cái trước dạy sử dụng phương pháp trừ nền, cái sau cung cấp một số thông tin về phương pháp dòng quang. Tất cả các phương thức được thực hiện trong thư viện OpenCV. Nếu bạn sử dụng Python, tôi khuyên bạn nên sử dụng OpenCV ≥ 2.3 và cv2
mô-đun Python của nó .
Phiên bản đơn giản nhất của phép trừ nền:
- tìm hiểu giá trị trung bình và độ lệch chuẩn cho mỗi pixel của nền
- so sánh các giá trị pixel hiện tại với phạm vi (- 2σ, μ + 2σ) hoặc (- σ, μ +)
Các phiên bản nâng cao hơn thực hiện tính đến chuỗi thời gian cho mỗi pixel và xử lý các cảnh không tĩnh (như di chuyển cây hoặc cỏ).
Ý tưởng của dòng quang là lấy hai hoặc nhiều khung và gán vectơ vận tốc cho mọi pixel (luồng quang dày đặc) hoặc cho một số trong số chúng (luồng quang thưa). Để ước tính lưu lượng quang thưa thớt, bạn có thể sử dụng phương pháp Lucas-Kanade (nó cũng được triển khai trong OpenCV). Rõ ràng, nếu có nhiều luồng (trung bình cao trên các giá trị tối đa của trường vận tốc), thì một cái gì đó đang di chuyển trong khung và các hình ảnh tiếp theo sẽ khác hơn.
So sánh biểu đồ có thể giúp phát hiện những thay đổi đột ngột giữa các khung liên tiếp. Cách tiếp cận này đã được sử dụng trong Courbon et al, 2010 :
Độ tương tự của các khung liên tiếp. Khoảng cách giữa hai khung liên tiếp được đo. Nếu nó quá cao, có nghĩa là khung hình thứ hai bị hỏng và do đó hình ảnh bị loại bỏ. Các Kullback-Leibler khoảng cách , hoặc entropy lẫn nhau, trên biểu đồ của hai khung:
Trong đó p và q là biểu đồ của các khung được sử dụng. Ngưỡng được cố định trên 0,2.