Prosta i szybka metoda porównywania obrazów dla podobieństwa

192

Potrzebuję prostego i szybkiego sposobu na porównanie dwóch obrazów w celu uzyskania podobieństwa. Tzn. Chcę uzyskać wysoką wartość, jeśli zawierają dokładnie to samo, ale mogą mieć nieco inne tło i mogą zostać przesunięte / zmienione o kilka pikseli.

(Bardziej konkretnie, jeśli to ma znaczenie: jedno zdjęcie jest ikoną, a drugie zdjęcie jest podobszarem zrzutu ekranu i chcę wiedzieć, czy ten podobszar jest dokładnie ikoną, czy nie.)

Mam pod ręką OpenCV, ale wciąż nie jestem do tego przyzwyczajony.

Jedna możliwość, o której do tej pory myślałem: Podziel oba zdjęcia na 10 x 10 komórek i dla każdej z tych 100 komórek porównaj histogram kolorów. Następnie mogę ustawić pewną skompilowaną wartość progową i jeśli wartość, którą otrzymam, jest powyżej tego progu, zakładam, że są one podobne.

Nie próbowałem jeszcze tego, jak dobrze to działa, ale myślę, że byłoby wystarczająco dobrze. Obrazy są już bardzo podobne (w moim przypadku użycia), więc mogę użyć dość wysokiej wartości progowej.

Sądzę, że istnieją dziesiątki innych możliwych rozwiązań, które działałyby mniej więcej (ponieważ samo zadanie jest dość proste, ponieważ chcę wykryć podobieństwo tylko wtedy, gdy są naprawdę bardzo podobne). Co byś zasugerował?


Istnieje kilka bardzo powiązanych / podobnych pytań dotyczących uzyskiwania podpisu / odcisku palca / skrótu z obrazu:

Natknąłem się również na te implementacje, które mają takie funkcje, aby uzyskać odcisk palca:

Kilka dyskusji na temat skrótów obrazu percepcyjnego: tutaj


Trochę poza tematem: Istnieje wiele metod tworzenia odcisków palców audio. MusicBrainz , usługa internetowa, która umożliwia wyszukiwanie utworów na podstawie linii papilarnych, ma dobry przegląd na swojej wiki . Używają teraz AcoustID . Służy do znajdowania dokładnych (lub głównie dokładnych) dopasowań. Aby znaleźć podobne dopasowania (lub jeśli masz tylko fragmenty lub wysoki poziom hałasu), spójrz na Echoprint . Powiązane pytanie SO znajduje się tutaj . Wygląda na to, że rozwiązano problem audio. Wszystkie te rozwiązania działają całkiem dobrze.

Nieco bardziej ogólne pytanie o rozmytych wyszukiwania w ogóle jest tutaj . Np. Występuje hash wrażliwy na lokalizację i wyszukiwanie najbliższego sąsiada .

Albert
źródło
1
Może odcisk palca obrazu może pomóc? stackoverflow.com/questions/596262/…
GWW
Metryka Wassersteina, znana również jako odległość od Ziemi (MD), jest czymś, o czym ludzie wydają się nie wiedzieć, ale dałaby tutaj prawie wszystko, czego chcesz.
mmgp
3
możliwy duplikat porównania obrazu - szybki algorytm
sashoalm
Cześć, wymyśliłem ulepszony dHash - nazwałem go IDHash: github.com/Nakilon/dhash-vips
Nakilon

Odpowiedzi:

107

Czy zrzut ekranu lub ikona mogą zostać przekształcone (skalowane, obracane, pochylone ...)? Na mojej głowie jest kilka metod, które mogą ci pomóc:

  • Prosta odległość euklidesowa wspomniana przez @carlosdc (nie działa z przekształconymi obrazami i potrzebujesz progu).
  • (Znormalizowana) korelacja krzyżowa - prosta metryka, której można użyć do porównania obszarów obrazu. Jest bardziej wytrzymały niż zwykła odległość euklidesowa, ale nie działa na przekształconych obrazach i ponownie będziesz potrzebować progu.
  • Porównanie histogramów - jeśli używasz znormalizowanych histogramów, ta metoda działa dobrze i nie ma na nią wpływu transformacja afiniczna. Problemem jest określenie prawidłowego progu. Jest również bardzo wrażliwy na zmiany kolorów (jasność, kontrast itp.). Możesz połączyć to z poprzednimi dwoma.
  • Detektory istotnych punktów / obszarów - takich jak MSER (Maksymalnie stabilne regiony ekstremalne) , SURF lub SIFT . Są to bardzo solidne algorytmy i mogą być zbyt skomplikowane do wykonania prostego zadania. Dobrą rzeczą jest to, że nie musisz mieć dokładnego obszaru z tylko jedną ikoną, te detektory są wystarczająco mocne, aby znaleźć odpowiednie dopasowanie. Dobra ocena tych metod znajduje się w tym artykule: Lokalne niezmiennicze detektory cech: ankieta .

Większość z nich jest już zaimplementowana w OpenCV - patrz na przykład metoda cvMatchTemplate (wykorzystuje dopasowanie histogramu): http://dasl.mem.drexel.edu/~noahKuntz/openCVTut6.html . Dostępne są również detektory punktu zerowego / obszaru - patrz Wykrywanie funkcji OpenCV .

Karel Petranek
źródło
1
Można go przeskalować lub nieznacznie przesunąć. Również tło ikony będzie inne. Próbowałem porównania histogramu, ale uzyskałem wiele wyników fałszywie dodatnich. Próbowałem także odległości euklidesowej, ale daje to również zbyt wiele fałszywych trafień (ale może mogę to trochę poprawić w zakresie obsługi wartości alfa w ikonie). Spróbuję tego trochę dalej, w przeciwnym razie sprawdzę MSER, SURF lub SIFT.
Albert,
1
Kolejny pomysł - czy nie zadziałałoby, gdybyś użył porównania histogramów obrazów po zastosowaniu operatora sobel? To porównałoby tylko podobieństwo krawędzi. Może lub nie może działać, w zależności od tego, jak „ostre” jest tło.
Karel Petranek
44

Ostatnio spotykam się z tymi samymi problemami, aby rozwiązać ten problem (prosty i szybki algorytm do porównania dwóch obrazów) raz na zawsze, wnoszę moduł img_hash do opencv_contrib, szczegóły można znaleźć w tym linku .

Moduł img_hash zapewnia sześć algorytmów mieszania obrazu, dość łatwych w użyciu.

Przykład kodów

pochodzenie Lenapochodzenie Lena

rozmycie Lenarozmycie Lena

zmień rozmiar lenazmień rozmiar lena

shift lenashift lena

#include <opencv2/core.hpp>
#include <opencv2/core/ocl.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/img_hash.hpp>
#include <opencv2/imgproc.hpp>

#include <iostream>

void compute(cv::Ptr<cv::img_hash::ImgHashBase> algo)
{
    auto input = cv::imread("lena.png");
    cv::Mat similar_img;

    //detect similiar image after blur attack
    cv::GaussianBlur(input, similar_img, {7,7}, 2, 2);
    cv::imwrite("lena_blur.png", similar_img);
    cv::Mat hash_input, hash_similar;
    algo->compute(input, hash_input);
    algo->compute(similar_img, hash_similar);
    std::cout<<"gaussian blur attack : "<<
               algo->compare(hash_input, hash_similar)<<std::endl;

    //detect similar image after shift attack
    similar_img.setTo(0);
    input(cv::Rect(0,10, input.cols,input.rows-10)).
            copyTo(similar_img(cv::Rect(0,0,input.cols,input.rows-10)));
    cv::imwrite("lena_shift.png", similar_img);
    algo->compute(similar_img, hash_similar);
    std::cout<<"shift attack : "<<
               algo->compare(hash_input, hash_similar)<<std::endl;

    //detect similar image after resize
    cv::resize(input, similar_img, {120, 40});
    cv::imwrite("lena_resize.png", similar_img);
    algo->compute(similar_img, hash_similar);
    std::cout<<"resize attack : "<<
               algo->compare(hash_input, hash_similar)<<std::endl;
}

int main()
{
    using namespace cv::img_hash;

    //disable opencl acceleration may(or may not) boost up speed of img_hash
    cv::ocl::setUseOpenCL(false);

    //if the value after compare <= 8, that means the images
    //very similar to each other
    compute(ColorMomentHash::create());

    //there are other algorithms you can try out
    //every algorithms have their pros and cons
    compute(AverageHash::create());
    compute(PHash::create());
    compute(MarrHildrethHash::create());
    compute(RadialVarianceHash::create());
    //BlockMeanHash support mode 0 and mode 1, they associate to
    //mode 1 and mode 2 of PHash library
    compute(BlockMeanHash::create(0));
    compute(BlockMeanHash::create(1));
}

W takim przypadku ColorMomentHash daje nam najlepszy wynik

  • atak rozmycia gaussowskiego: 0,567521
  • Shift Attack: 0,229728
  • zmiana rozmiaru ataku: 0.229358

Plusy i minusy każdego algorytmu

Wydajność pod różnymi atakami

Wydajność img_hash też jest dobra

Porównanie prędkości z biblioteką PHash (100 zdjęć z ukbench) wydajność obliczeniowa porównanie wydajności

Jeśli chcesz poznać zalecane progi dla tych algorytmów, sprawdź ten post ( http://qtandopencv.blogspot.my/2016/06/introduction-to-image-hash-module-of.html ). Jeśli interesuje Cię sposób pomiaru wydajności modułów img_hash (obejmują szybkość i różne ataki), sprawdź ten link ( http://qtandopencv.blogspot.my/2016/06/speed-up-image-hashing-of -opencvimghash.html ).

StereoMatching
źródło
11

Czy zrzut ekranu zawiera tylko ikonę? Jeśli tak, odległość L2 dwóch obrazów może być wystarczająca. Jeśli odległość L2 nie działa, następnym krokiem jest wypróbowanie czegoś prostego i dobrze ustalonego, na przykład: Lucas-Kanade . Jestem pewien, że jest dostępny w OpenCV.

carlosdc
źródło
Podobszar zawiera dokładnie tylko ikonę (z losowym tłem) lub coś innego. Chcę zobaczyć, który to przypadek. Chociaż może być bardzo nieznacznie przesunięty lub zmieniony, dlatego nie byłem pewien, czy mógłbym po prostu spojrzeć na odległość (w jakiejkolwiek normie). Ale spróbuję ze zmniejszoną wersją.
Albert,
6

Jeśli chcesz uzyskać indeks podobieństwa dwóch zdjęć, sugeruję na podstawie wskaźników indeks SSIM. Jest bardziej zgodny z ludzkim okiem. Oto artykuł na ten temat: Indeks podobieństwa strukturalnego

Jest także zaimplementowany w OpenCV i można go przyspieszyć za pomocą GPU: OpenCV SSIM z GPU

Milan Tenk
źródło
5

Jeśli możesz mieć pewność dokładnego wyrównania szablonu (ikony) do regionu testowego, wówczas każda stara suma różnic pikseli będzie działać.

Jeśli wyrównanie będzie tylko odrobinę wyłączone, możesz dolnoprzepustowo oba obrazy za pomocą cv :: GaussianBlur przed znalezieniem sumy różnic pikseli.

Jeśli jakość wyrównania jest potencjalnie słaba, zaleciłbym albo Histogram Zorientowanych Gradientów, albo jeden z wygodnych algorytmów wykrywania / deskryptora punktów kluczowych OpenCV (takich jak SIFT lub SURF ).

rcv
źródło
4

Jeśli chcesz dopasować identyczne obrazy - kod odległości L2

// Compare two images by getting the L2 error (square-root of sum of squared error).
double getSimilarity( const Mat A, const Mat B ) {
if ( A.rows > 0 && A.rows == B.rows && A.cols > 0 && A.cols == B.cols ) {
    // Calculate the L2 relative error between images.
    double errorL2 = norm( A, B, CV_L2 );
    // Convert to a reasonable scale, since L2 error is summed across all pixels of the image.
    double similarity = errorL2 / (double)( A.rows * A.cols );
    return similarity;
}
else {
    //Images have a different size
    return 100000000.0;  // Return a bad value
}

Szybki. Ale nie odporny na zmiany oświetlenia / punktu widzenia itp. Źródło

Kiran
źródło
2

Jeśli chcesz porównać obraz pod względem podobieństwa, proponuję użyć OpenCV. W OpenCV istnieje kilka dopasowań funkcji i dopasowywania szablonów. Do dopasowania funkcji dostępne są detektory SURF, SIFT, FAST i tak dalej. Możesz użyć tego do wykrycia, opisania, a następnie dopasowania obrazu. Następnie możesz użyć określonego indeksu, aby znaleźć liczbę dopasowań między dwoma obrazami.

Hua Er Lim
źródło
1
powiedziałeś „Następnie możesz użyć określonego indeksu, aby znaleźć liczbę dopasowań między dwoma obrazami”. jaka może być minimalna liczba dopasowań między dwoma obrazkami, aby powiedzieć, że „zawierają” ten sam obiekt?
Inês Martins