[python] 두 이미지의 차이를 어떻게 정량화 할 수 있습니까?

내가하고 싶은 일은 다음과 같습니다.

웹캠으로 정기적으로 사진을 찍고 있습니다. 시간 경과와 같은 것입니다. 그러나 실제로 아무것도 변경되지 않은 경우, 즉 사진이 거의 동일 하게 보이기 때문에 최신 스냅 샷을 저장하고 싶지 않습니다.

차이를 정량화하는 방법이 있다고 생각하며 임계 값을 경험적으로 결정해야합니다.

나는 완벽 함보다는 단순함을 찾고 있습니다. 나는 파이썬을 사용하고 있습니다.



답변

일반적인 아이디어

옵션 1 : 두 이미지를 모두 배열 ( scipy.misc.imread) 로로드 하고 요소 별 (픽셀 별) 차이를 계산합니다. 차이의 규범을 계산하십시오.

옵션 2 : 두 이미지를 모두로드하십시오. 히스토그램과 같은 각각의 특징 벡터를 계산하십시오. 이미지가 아닌 특징 벡터 사이의 거리를 계산하십시오.

그러나 먼저 결정해야 할 사항이 있습니다.

질문

먼저 다음 질문에 대답해야합니다.

  • 모양과 치수가 같은 이미지입니까?

    그렇지 않은 경우 크기를 조정하거나 잘라 내야 할 수도 있습니다. PIL 라이브러리는 파이썬에서 도움이 될 것입니다.

    동일한 설정과 동일한 장치로 찍은 경우 아마도 동일 할 것입니다.

  • 이미지가 잘 정렬되어 있습니까?

    그렇지 않은 경우 먼저 가장 좋은 정렬을 찾기 위해 상호 상관을 실행하는 것이 좋습니다. SciPy는이를 수행하는 기능을 가지고 있습니다.

    카메라와 장면이 여전히 맞으면 이미지가 잘 정렬 된 것입니다.

  • 이미지 노출이 항상 동일합니까? (가벼움 / 대비가 동일합니까?)

    그렇지 않은 경우 이미지 를 정규화 할 수 있습니다.

    그러나 어떤 상황에서는 이것이 좋은 것보다 더 잘못 될 수 있으므로주의하십시오. 예를 들어, 어두운 배경에서 밝은 단일 픽셀은 정규화 된 이미지를 매우 다르게 만듭니다.

  • 색상 정보가 중요합니까?

    색상 변경을 확인하려면 회색조 이미지에서와 같이 스칼라 값이 아닌 점당 색상 값으로 구성된 벡터가 있습니다. 그러한 코드를 작성할 때 더주의를 기울여야합니다.

  • 이미지에 뚜렷한 가장자리가 있습니까? 그들은 움직일 가능성이 있습니까?

    그렇다면 가장자리 감지 알고리즘을 먼저 적용하고 (예 : Sobel 또는 Prewitt 변환으로 그라디언트 계산, 일부 임계 값 적용) 첫 번째 이미지의 가장자리와 두 번째 이미지의 가장자리를 비교할 수 있습니다.

  • 이미지에 노이즈가 있습니까?

    모든 센서는 약간의 노이즈로 이미지를 오염시킵니다. 저비용 센서는 더 많은 노이즈를 가지고 있습니다. 이미지를 비교하기 전에 노이즈 감소를 적용 할 수 있습니다. 흐림은 여기서 가장 단순하지만 최선의 방법은 아닙니다.

  • 어떤 종류의 변화를 알고 싶습니까?

    이는 이미지 간 차이에 사용할 표준 선택에 영향을 줄 수 있습니다.

    이미지가 얼마나 많이 변경되었는지 측정하려면 Manhattan norm (절대 값의 합) 또는 zero norm (0이 아닌 요소 수)을 사용하십시오. 전자는 이미지가 얼마나 꺼져 있는지를 알려주고 후자는 얼마나 많은 픽셀이 다른지를 알려줍니다.

귀하의 이미지는 크기와 모양이 동일하고 노출이 다르면 잘 정렬되어 있다고 가정합니다. 간단하게하기 위해 컬러 (RGB) 이미지 인 경우에도 그레이 스케일로 변환합니다.

다음과 같은 수입품이 필요합니다.

import sys

from scipy.misc import imread
from scipy.linalg import norm
from scipy import sum, average

주요 기능, 두 개의 이미지 읽기, 그레이 스케일로 변환, 결과 비교 및 ​​인쇄 :

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

비교하는 법. img1img22D SciPy 배열은 여기에 있습니다 :

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)

파일이 컬러 이미지 인 경우 imread강도를 얻기 위해 평균 RGB 채널 (마지막 배열 축) 인 3D 배열을 반환합니다. 회색조 이미지 (예 :)에는 필요하지 않습니다 .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

정규화는 쉽지 않으므로 [0,255] 대신 [0,1]로 정규화하도록 선택할 수 있습니다. arr여기 SciPy 배열이므로 모든 연산은 요소별로 수행됩니다.

def normalize(arr):
    rng = arr.max()-arr.min()
    amin = arr.min()
    return (arr-amin)*255/rng

main기능을 실행하십시오 .

if __name__ == "__main__":
    main()

이제이 모든 것을 스크립트에 넣고 두 이미지에 대해 실행할 수 있습니다. 이미지를 자체와 비교하면 차이가 없습니다.

$ python compare.py one.jpg one.jpg
Manhattan norm: 0.0 / per pixel: 0.0
Zero norm: 0 / per pixel: 0.0

이미지를 흐리게 처리하고 원본과 비교하면 약간의 차이가 있습니다.

$ python compare.py one.jpg one-blurred.jpg
Manhattan norm: 92605183.67 / per pixel: 13.4210411116
Zero norm: 6900000 / per pixel: 1.0

PS 전체 compare.py 스크립트.

업데이트 : 관련 기술

질문은 프레임이 거의 같은 비디오 시퀀스에 관한 것이며, 특이한 것을 찾고자 할 때, 관련이 될 수있는 몇 가지 대안을 언급하고자합니다.

  • 백그라운드 뺄셈 및 분할 (포 그라운드 개체 감지)
  • 스파 스 광학 흐름 (동작 감지)
  • 이미지 대신 히스토그램 또는 기타 통계 비교

“Learning OpenCV”책, 9 장 (이미지 부분 및 세그먼테이션) 및 10 (추적 및 모션)을 살펴 보는 것이 좋습니다. 전자는 백그라운드 빼기 방법을 사용하도록 가르치고, 후자는 광학 흐름 방법에 대한 정보를 제공합니다. 모든 메소드는 OpenCV 라이브러리에서 구현됩니다. 파이썬을 사용한다면 OpenCV ≥ 2.3을 사용하는 것이 좋습니다.cv2 파이썬 모듈 .

백그라운드 빼기의 가장 간단한 버전 :

  • 배경의 모든 픽셀에 대한 평균값 μ 및 표준 편차 σ를 학습
  • 현재 픽셀 값을 (μ-2σ, μ + 2σ) 또는 (μ-σ, μ + σ) 범위와 비교

보다 고급 버전은 모든 픽셀의 시계열을 고려하여 움직이지 않는 장면 (예 : 움직이는 나무 또는 잔디)을 처리합니다.

광학 흐름의 개념은 두 개 이상의 프레임을 가져 와서 모든 픽셀 (고밀도 광학 흐름) 또는 그 중 일부 (스파 스 광학 흐름)에 속도 벡터를 할당하는 것입니다. 희소 광학 흐름을 추정하기 위해 Lucas-Kanade 방법을 사용할 수 있습니다 (OpenCV에서도 구현 됨). 분명히 많은 흐름 (속도 필드의 최대 값에 대한 높은 평균)이 있으면 프레임에서 무언가가 움직이고 후속 이미지가 더 달라집니다.

히스토그램을 비교하면 연속 프레임 간의 갑작스러운 변화를 감지하는 데 도움이 될 수 있습니다. 이 접근법은 Courbon et al, 2010 에서 사용되었습니다 .

연속 프레임의 유사성. 두 개의 연속 프레임 사이의 거리가 측정됩니다. 너무 높으면 두 번째 프레임이 손상되어 이미지가 제거 된 것입니다. 쿨백 – 라이 블러 거리를 두 프레임에 대한 히스토그램, 또는 상호 엔트로피 :

$$ d (p, q) = \ sum_i p (i) \ log (p (i) / q (i)) $$

여기서 pq 는 프레임의 히스토그램입니다. 임계 값은 0.2로 고정됩니다.


답변

간단한 해결책 :

이미지를 jpeg 로 인코딩하고 filesize 의 실질적인 변화를 찾으십시오 .

비디오 섬네일과 비슷한 것을 구현했으며 많은 성공과 확장 성을 가졌습니다.


답변

PIL의 함수를 사용하여 두 이미지를 비교할 수 있습니다 .

import Image
import ImageChops

im1 = Image.open("splash.png")
im2 = Image.open("splash2.png")

diff = ImageChops.difference(im2, im1)

diff 객체는 모든 픽셀이 첫 번째 이미지에서 두 번째 이미지에서 해당 픽셀의 색상 값을 뺀 결과 인 이미지입니다. diff 이미지를 사용하면 몇 가지 작업을 수행 할 수 있습니다. 가장 간단한 것은 diff.getbbox()함수입니다. 두 이미지 사이의 모든 변경 사항이 포함 된 최소 사각형을 알려줍니다.

PIL의 함수를 사용하여 여기에 언급 된 다른 것들의 근사치를 구현할 수 있습니다.


답변

(a) 이미 제안 된 유클리드 거리 또는 (b) 표준화 된 상호 상관 법. 정규화 된 상호 상관은 단순한 상호 상관보다 조명 변화에 현저하게 더 강한 경향이 있습니다. Wikipedia는 정규화 된 상호 상관을 위한 공식을 제공합니다 . 더 정교한 방법도 있지만 훨씬 더 많은 작업이 필요합니다.

numpy와 같은 구문을 사용하여

dist_euclidean = sqrt (sum ((i1-i2) ^ 2)) / i1.size

dist_manhattan = 합 (abs (i1-i2)) / i1.size

dist_ncc = sum ((i1-평균 (i1)) * (i2-평균 (i2))) / (
  (i1.size-1) * stdev (i1) * stdev (i2))

가정 i1i22D 그레이 스케일 이미지의 배열입니다.


답변

시도해 볼만한 사소한 일 :

두 이미지를 작은 썸네일 (예 : 64 x 64)로 리샘플링하고 썸네일을 픽셀 단위로 특정 임계 값과 비교합니다. 원본 이미지가 거의 같은 경우, 리샘플링 된 썸네일은 매우 유사하거나 정확히 동일합니다. 이 방법은 특히 저 조명 장면에서 발생할 수있는 노이즈를 처리합니다. 그레이 스케일을 사용하는 것이 더 나을 수도 있습니다.


답변

나는 그들이 “충분히 다르다”면 어떻게 계산할 것인가에 대한 문제를 구체적으로 다루고 있습니다. 픽셀을 하나씩 빼는 방법을 알 수 있다고 가정합니다.

먼저, 아무 것도 변경 하지 않고 많은 이미지를 찍고 캡처, 이미지 시스템의 노이즈, JPEG 압축 아티팩트 및 조명의 순간 변화로 인해 픽셀이 변경되는 최대량을 찾습니다. . 아무것도 움직이지 않아도 1 또는 2 비트의 차이가 발생할 수 있습니다.

그런 다음 “실제”테스트의 경우 다음과 같은 기준을 원합니다.

  • 최대 P 픽셀이 E 이하만큼 다르면 동일합니다.

따라서 E = 0.02, P = 1000 인 경우 단일 픽셀이 ~ 5 단위 이상 (8 비트 이미지로 가정)으로 변경되거나 1000보다 큰 경우 “차이”임을 의미합니다. 픽셀에 전혀 오류가 없었습니다.

이는 주로 추가 검사가 필요하지 않은 이미지를 신속하게 식별하기위한 우수한 “심사”기법으로 사용됩니다. “실패”한 이미지는 예를 들어 카메라가 비트를 흔들거나 조명 변경에 대해 더 강력한 경우 잘못된 긍정을 갖지 않는보다 정교하고 비싼 기술에 더 가깝습니다.

오픈 소스 프로젝트 인 OpenImageIO를 실행 하는데 , 여기에는 이와 같은 임계 값과 차이를 비교하는 “idiff”유틸리티가 포함되어 있습니다 (실제로는 더 정교합니다). 이 소프트웨어를 사용하지 않으려는 경우에도 소스를보고 어떻게 수행했는지 확인할 수 있습니다. 상업적으로 많이 사용되며이 임계 값 기술은 플랫폼과 플랫폼간에 약간의 차이가있을 수있는 “참조 이미지”와 함께 렌더링 및 이미지 처리 소프트웨어를위한 테스트 스위트를 가질 수 있도록 개발되었습니다. 그 결과 알고리즘이 “허용 오차 범위 내”작업을 원했습니다.


답변

나는 직장에서 비슷한 문제가 있었고 이미지 변환 끝점을 다시 작성하고 있었고 새 버전이 이전 버전과 동일하거나 거의 동일한 출력을 생성하고 있는지 확인하고 싶었습니다. 그래서 나는 이것을 썼다 :

https://github.com/nicolashahn/diffimg

동일한 크기의 이미지와 픽셀 단위로 작동하는 각 채널의 값 차이를 측정합니다. R, G, B (, A), 해당 채널의 평균 차이를 취한 다음 그 차이를 평균화합니다. 모든 픽셀을 반환하고 비율을 반환합니다.

예를 들어, 10×10의 흰색 픽셀 이미지와 동일한 이미지이지만 하나의 픽셀이 빨간색으로 변경된 경우 해당 픽셀의 차이는 1/3 또는 0.33 … (RGB 0,0,0 대 255,0,0) ) 및 다른 모든 픽셀은 0입니다. 총 100 픽셀 인 경우 0.33 … / 100 = ~ 0.33 %의 이미지 차이입니다.

나는 이것이 OP 프로젝트에 완벽하게 작동 할 것이라고 생각합니다 (이것은 지금 매우 오래된 게시물이지만 파이썬에서 이미지를 비교하려는 미래의 StackOverflowers에 게시한다는 것을 알고 있습니다).