Как я могу определить, что два изображения "одинаковы", даже если одно имеет немного другое соотношение обрезки / соотношения? - PullRequest
11 голосов
/ 31 января 2020

У меня есть два разных изображения:

в 100px с enter image description here или 400px enter image description here

и

шириной 100 пикселей enter image description here или 400 пикселей enter image description here

Как вы можете видеть, эти два явно "одинаковы" с человеческой точки зрения. Теперь я хочу программно обнаружить, что они одинаковы. Я использовал image magi c через ruby gem под названием 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%.

PS. Я могу получить изображения в более высоком разрешении, если это помогает (например, двойное)

Ответы [ 5 ]

6 голосов
/ 04 февраля 2020

Возможно, вы захотите взглянуть на соответствие функции. Идея состоит в том, чтобы найти функции на двух изображениях и сопоставить их. Этот метод обычно используется для поиска шаблона (скажем, lo go) в другом изображении. По сути, особенность может быть описана как вещи, которые люди находят интересными на изображении, такие как углы или открытые пространства. Существует много типов методов обнаружения признаков, однако я рекомендую использовать масштабно-инвариантное преобразование признаков (SIFT) в качестве алгоритма обнаружения признаков. SIFT инвариантен к трансляции изображения, масштабированию, повороту, частично инвариантен к изменениям освещения и устойчив к локальному искажению геометрии c. Похоже, это соответствует вашей спецификации, где изображения могут иметь немного отличающиеся rat ios.

Учитывая ваши два предоставленных изображения, вот попытка сопоставить функции, используя Соответствие функций FLANN . Чтобы определить, совпадают ли эти два изображения, мы можем основать его на некотором заранее определенном пороговом значении, которое отслеживает число совпадений, которые проходят тест на соотношение, описанный в Отличительные особенности изображения из масштабируемых инвариантных ключевых точек на Дэвид Дж. Лоу . Простое объяснение теста состоит в том, что тест отношения проверяет, являются ли совпадения неоднозначными и должны быть удалены, вы можете рассматривать это как метод удаления выбросов. Мы можем подсчитать количество совпадений, прошедших этот тест, чтобы определить, совпадают ли два изображения. Вот результаты соответствия функции:

image image

image

Matches: 42

Точки представляют все обнаруженные совпадения, в то время как зеленые линии представляют "хорошо совпадения », которые проходят тест на соотношение. Если вы не используете тест отношения, то все точки будут нарисованы. Таким образом, вы можете использовать этот фильтр в качестве порога, чтобы сохранить только наиболее подходящие функции.


Я реализовал это в Python, я не очень знаком с 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()
4 голосов
/ 06 февраля 2020

Поскольку ImageMagick - это очень старый, продвинутый и многофункциональный инструмент, было бы сложно создать интерфейс, который охватывал бы большинство функций. Как ни круто, rmagick не делает (и не делает много попыток, предпринятых python) близко к тому, чтобы охватить все функции.

Я думаю, что для многих случаев использования будет достаточно безопасно и намного проще просто выполнить метод командной строки и прочитать его. В ruby это будет выглядеть так;

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

Я расскажу о важных вещах, а затем расскажу о дополнительных заметках.

Команда использует магическое сравнение, чтобы проверить, является ли второе изображение (small) подизображением первого (large). Эта функция не проверяет, что small строго меньше, чем large (как по высоте, так и по ширине). Число, которое я указал для сходства, составляет 0,2 (ошибка 20%), а значение для предоставленных вами изображений составляет около 0,15. Вы можете настроить это! Я считаю, что изображения со строгим подмножеством получают менее 0,01.

  • Если вы хотите меньше ошибок (меньших чисел) в случаях, когда у вас есть 90% -ное перекрытие, но у второго изображения есть некоторые дополнительные вещи, которых нет у первого, вы можете запустить его один раз, а затем обрезать сначала большое изображение в том месте, где содержится подизображение, затем снова запустите его с обрезанным изображением в качестве «маленького» и исходным «маленьким» изображением в качестве большого.
  • Если вам действительно нужен хороший объектно-ориентированный интерфейс в Ruby, rmagick использует MagicCore API. Эта (ссылка на документы) команда, вероятно, является тем, что вы хотите использовать для ее реализации, и вы можете открыть pr для rmagick или упаковать cext самостоятельно.
  • Использование open3 запустит поток ( см. Документы ). Закрытие stderr и stdout не является "необходимым", но вы должны это сделать.
  • "временное" изображение, являющееся третьим аргументом, указывает файл для вывода анализа. Быстро посмотрев, я не смог найти способ не требовать этого, но он просто перезаписывал автоматически и его можно было бы сохранить для отладки. Для вашего примера это будет выглядеть так;

enter image description here

  • Полный вывод в формате 10092,6 (0,154003) @ 0,31. Первое число - это значение rmse из 655535, второе (которое я использую) - нормализованный процент. Последние два числа представляют местоположение исходного изображения, с которого начинается маленькое изображение.
  • Поскольку не существует объективного источника правды о том, насколько «похожи» изображения, я выбрал RMSE (см. Больше метри c options здесь ). Это довольно распространенная мера различий между ценностями. Абсолютный счетчик ошибок (AE) может показаться хорошей идеей, однако кажется, что некоторые программы кадрирования не совсем сохраняют пиксели, поэтому вам, возможно, придется настроить фазз, а это не нормализованное значение, поэтому вам придется сравнивать счетчик ошибок с размером изображения и еще много чего.
3 голосов
/ 07 февраля 2020

Обычно сопоставление с шаблоном дает хороший результат в таких ситуациях. Сопоставление с шаблоном - это метод поиска областей изображения, которые соответствуют (подобны) шаблону изображения (второе изображение). Этот алгоритм дает оценку для наилучшего положения в исходном изображении (второе).

В opencv с использованием TM_CCOEFF_NORMED метод дает оценку от 0 до 1. Если оценка равна 1, это означает, что изображение шаблона является в точности частью (Rect) исходного изображения, но если у вас есть небольшое изменение в освещении или перспективе между двумя изображениями, оценка будет ниже, чем 1.

Теперь Рассматривая порог для оценки сходства, вы можете выяснить, если они одинаковы или нет. Этот порог может быть получен путем проб и ошибок на нескольких образцах изображений. Я попробовал ваши изображения и получил оценку 0,823863 . Вот код (opencv C ++) и общая область между двумя изображениями, полученные путем сопоставления:

enter image description here

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);
3 голосов
/ 03 февраля 2020

Получите гистограмму обоих изображений и сравните их. Это будет очень хорошо работать для кадрирования и масштабирования, если из-за них не будет слишком драматического c изменения.

Это лучше, чем текущий подход, при котором вы непосредственно вычитаете изображения. Но такого подхода пока мало.

2 голосов
/ 02 февраля 2020

Рассмотрим метод find_simil_region . Используйте меньшее из двух изображений в качестве целевого изображения. Попробуйте различные значения атрибутов fuzz на изображении и на целевом изображении.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...