[ruby] 자르기 / 비율이 약간 다른 경우에도 두 이미지가 “동일”하다는 것을 어떻게 감지합니까?

두 가지 다른 이미지가 있습니다.

100px 여기에 이미지 설명을 입력하십시오또는 400px여기에 이미지 설명을 입력하십시오

100px 여기에 이미지 설명을 입력하십시오또는 400px여기에 이미지 설명을 입력하십시오

보시다시피 두 사람은 분명히 인간의 관점에서 “동일”합니다. 이제 나는 그것들이 동일하다는 것을 프로그램 적으로 감지하고 싶습니다. 나는 rmagick다음과 같은 루비 보석을 통해 이미지 마술을 사용 해왔다 .

img1 = Magick::Image.from_blob(File.read("image_1.jpeg")).first
img2 = Magick::Image.from_blob(File.read("image_2.jpeg")).first

if img1.difference(img2).first < 4000.0 # I have found this to be a good threshold, but does not work for cropped images
  puts "they are the same!!!"
end

이것은 비율 / 자르기가 동일한 이미지에 적합하지만 잘림이 약간 다르고 동일한 너비로 크기가 조정 된 경우에는 적합하지 않습니다.

자르기가 다른 이미지에 적용 할 수있는 방법이 있습니까? 나는 다음과 같이 말할 수있는 솔루션에 관심이 있습니다 : 하나의 이미지가 다른 이미지 안에 포함되어 있고 이미지의 90 % 정도를 커버합니다.

추신. 도움이된다면 이미지를 더 높은 해상도로 얻을 수 있습니다 (예 : 이중)



답변

기능 일치를 살펴볼 수 있습니다. 아이디어는 두 이미지에서 기능을 찾아서 일치시키는 것입니다. 이 방법은 일반적으로 다른 이미지에서 템플릿 (예 : 로고)을 찾는 데 사용됩니다. 본질적으로 기능은 모서리 나 열린 공간과 같이 이미지에서 사람이 흥미롭게 찾을 수있는 것으로 설명 할 수 있습니다. 특징 검출 기술에는 여러 유형이 있지만, SIFT (scale-invariant feature transform)를 특징 검출 알고리즘으로 사용하는 것이 좋습니다. SIFT는 이미지 변환, 스케일링, 회전, 조명 변화에 부분적으로 불변, 로컬 기하학적 왜곡에 강합니다. 이미지의 비율이 약간 다를 수있는 사양과 일치하는 것 같습니다.

제공된 두 개의 이미지가 제공되면 FLANN 기능 매처를 사용하여 기능을 일치 시키려고합니다 . 두 이미지가 동일한 지 확인하기 위해 David G. Lowe의 Scale-Invariant Keypoints의 독특한 이미지 기능에 설명 된 비율 테스트를 통과하는 일치 수를 추적하는 미리 결정된 임계 값을 기준으로 할 수 있습니다. 테스트에 대한 간단한 설명은 비율 테스트에서 일치 항목이 모호하고 제거되어야하는지 검사하여 이상치 제거 기술로 취급 할 수 있다는 것입니다. 이 테스트를 통과 한 일치 횟수를 계산하여 두 이미지가 동일한 지 확인할 수 있습니다. 기능 일치 결과는 다음과 같습니다.


Matches: 42

점은 감지 된 모든 일치를 나타내고 녹색 선은 비율 테스트를 통과 한 “양호한 일치”를 나타냅니다. 비율 테스트를 사용하지 않으면 모든 점이 그려집니다. 이러한 방식으로이 필터를 임계 값으로 사용하여 가장 일치하는 기능 만 유지할 수 있습니다.


파이썬으로 구현했는데 Rails에 익숙하지 않습니다. 이것이 도움이되기를 바랍니다. 행운을 빕니다!

암호

import numpy as np
import cv2

# Load images
image1 = cv2.imread('1.jpg', 0)
image2 = cv2.imread('2.jpg', 0)

# Create the sift object
sift = cv2.xfeatures2d.SIFT_create(700)

# Find keypoints and descriptors directly
kp1, des1 = sift.detectAndCompute(image2, None)
kp2, des2 = sift.detectAndCompute(image1, None)

# FLANN parameters
FLANN_INDEX_KDTREE = 1
index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
search_params = dict(checks=50)   # or pass empty dictionary
flann = cv2.FlannBasedMatcher(index_params,search_params)
matches = flann.knnMatch(des1,des2,k=2)

# Need to draw only good matches, so create a mask
matchesMask = [[0,0] for i in range(len(matches))]

count = 0
# Ratio test as per Lowe's paper (0.7)
# Modify to change threshold 
for i,(m,n) in enumerate(matches):
    if m.distance < 0.15*n.distance:
        count += 1
        matchesMask[i]=[1,0]

# Draw lines
draw_params = dict(matchColor = (0,255,0),
                   # singlePointColor = (255,0,0),
                   matchesMask = matchesMask,
                   flags = 0)

# Display the matches
result = cv2.drawMatchesKnn(image2,kp1,image1,kp2,matches,None,**draw_params)
print('Matches:', count)
cv2.imshow('result', result)
cv2.waitKey()


답변

ImageMagick은 매우 오래되고 고급이며 다양한 기능을 갖춘 도구이므로 대부분의 기능을 다루는 인터페이스를 구축하기가 어렵습니다. rmagick은 모든 기능을 다룰 수는 없습니다 (파이썬이 시도한 많은 시도도 마찬가지입니다).

많은 유스 케이스에서 명령 줄 메소드를 실행하고 읽는 것이 안전하고 훨씬 쉽다는 것을 상상합니다. 루비에서는 다음과 같이 보일 것입니다.

require 'open3'

def check_subimage(large, small)
    stdin, stdout, stderr, wait_thr = Open3.popen3("magick compare -subimage-search -metric RMSE #{large} #{small} temp.jpg")
    result = stderr.gets
    stderr.close
    stdout.close
    return result.split[1][1..-2].to_f < 0.2
end

if check_subimage('a.jpg', 'b.jpg')
    puts "b is a crop of a"
else
    puts "b is not a crop of a"
end

중요한 내용을 다루고 추가 메모에 대해 이야기하겠습니다.

이 명령은 magick compare를 사용하여 두 번째 이미지 ( small)가 첫 번째 이미지 ( )의 하위 이미지인지 확인합니다 large. 이 기능은 small이 큰 것 (높이 및 너비)보다 엄격하게 작은 지 확인하지 않습니다. 유사점으로 입력 한 숫자는 0.2 (오류 20 %)이며 제공 한 이미지의 값은 약 0.15입니다. 이것을 미세 조정하고 싶을 수도 있습니다! 엄격한 서브 세트 인 이미지는 0.01보다 작습니다.

  • 90 %가 겹치지 만 두 번째 이미지에 첫 번째 이미지에없는 추가 항목이있는 경우 오류 (작은 숫자)를 줄이고 싶다면 한 번 실행 한 다음 첫 번째 큰 이미지를 하위 이미지가 포함 된 위치로 자릅니다. 잘라낸 이미지를 “작은”이미지로, 원래 “작은”이미지를 큰 이미지로 다시 실행하십시오.
  • 루비에서 멋진 객체 지향 인터페이스를 원한다면 rmagick은 MagicCore API를 사용합니다. 이 (docs에 링크) 명령은 아마도 그것을 구현하기 위해 사용하려는 것일 수도 있으며, pr을 열어서 cext를 rmagick하거나 패키징 할 수 있습니다.
  • open3을 사용하면 스레드가 시작됩니다 ( docs 참조 ). 닫기 stderr과은 stdout“필요”가 아니라 당신이하는 거 야.
  • 세 번째 arg 인 “temp”이미지는 분석을 출력 할 파일을 지정합니다. 간단히 살펴보면 필요하지 않은 방법을 찾을 수 없었지만 자동으로 덮어 쓰므로 디버깅을 위해 저장하는 것이 좋습니다. 예를 들면 다음과 같습니다.

여기에 이미지 설명을 입력하십시오

  • 전체 출력은 10092.6 (0.154003) @ 0,31 형식입니다. 첫 번째 숫자는 655535 중에서 rmse 값이며 두 번째 숫자는 정규화 된 백분율입니다. 마지막 두 숫자는 작은 이미지가 시작되는 원본 이미지의 위치를 ​​나타냅니다.
  • “유사한”이미지가 어떻게되는지에 대한 객관적인 진실의 원천이 없기 때문에 RMSE를 선택했습니다 ( 여기에서 더 많은 메트릭 옵션 참조 ). 값들 사이의 차이에 대한 상당히 일반적인 척도입니다. AE (Absolute Error Count)는 좋은 생각처럼 보이지만 일부 자르기 소프트웨어가 픽셀을 완벽하게 보존하지 못하는 것처럼 보이므로 퍼즈를 조정해야하고 정규화 된 값이 아니므로 오류 수를 비교해야합니다. 이미지의 크기와 그렇지 않은 것.

답변

두 이미지의 히스토그램을 가져 와서 비교하십시오. 이로 인해 너무 급격한 변화가 없다면 자르기와 줌에 매우 효과적입니다.

이것은 이미지를 직접 빼는 현재 방법보다 낫습니다. 그러나이 방법은 여전히 ​​거의 없습니다.


답변

이러한 상황에서 일반적으로 템플릿 일치 는 좋은 결과를 가져옵니다. 템플릿 일치는 템플릿 이미지 (두 번째 이미지)와 일치하는 이미지 영역을 찾는 기술입니다. 이 알고리즘은 소스 이미지 (두 번째 이미지)에서 가장 좋은 위치에 점수를 매 깁니다.

TM_CCOEFF_NORMED 방법을 사용하는 opencv 에서 점수는 0과 1 사이입니다. 점수가 1이면 템플릿 이미지가 소스 이미지의 일부 (Rect)임을 의미하지만 밝게 또는 원근감 사이에서 약간의 변화가있는 경우 두 이미지의 경우 점수가 1보다 낮습니다.

이제 유사성 점수의 임계 값을 고려하여 동일한 지 여부를 알 수 있습니다. 이 임계 값은 몇 개의 샘플 이미지에서 시행 착오를 통해 얻을 수 있습니다. 나는 당신의 이미지를 시도하고 0.823863 점수를 얻었습니다 . 다음은 코드 (opencv C ++)와 두 이미지 사이의 공통 영역입니다.

여기에 이미지 설명을 입력하십시오

Mat im2 = imread("E:/1/1.jpg", 1);
//Mat im2;// = imread("E:/1/1.jpg", 1);
Mat im1 = imread("E:/1/2.jpg", 1);

//im1(Rect(0, 0, im1.cols - 5, im1.rows - 5)).copyTo(im2);

int result_cols = im1.cols - im2.cols + 1;
int result_rows = im1.rows - im2.rows + 1;

Mat result = Mat::zeros(result_rows, result_cols, CV_32FC1);

matchTemplate(im1, im2, result, TM_CCOEFF_NORMED);

double minVal; double maxVal;
Point minLoc; Point maxLoc;
Point matchLoc;

minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc, Mat());

cout << minVal << " " << maxVal << " " << minLoc << " " << maxLoc << "\n";
matchLoc = maxLoc;

rectangle(im1, matchLoc, Point(matchLoc.x + im2.cols, matchLoc.y + im2.rows), Scalar::all(0), 2, 8, 0);
rectangle(result, matchLoc, Point(matchLoc.x + im2.cols, matchLoc.y + im2.rows), Scalar::all(0), 2, 8, 0);

imshow("1", im1);
imshow("2", result);
waitKey(0);


답변

find_similar_region 메소드를 고려하십시오 . 두 이미지 중 작은 이미지를 대상 이미지로 사용하십시오. 이미지 및 대상 이미지에서 퍼지 속성에 대한 다양한 값을 시도하십시오.


답변