Método simples e rápido para comparar imagens por similaridade

192

Preciso de uma maneira simples e rápida de comparar duas imagens por similaridade. Ou seja, eu quero obter um valor alto se eles contêm exatamente a mesma coisa, mas podem ter um fundo ligeiramente diferente e podem ser movidos / redimensionados por alguns pixels.

(Mais concreto, se isso importa: a imagem é um ícone e a outra é uma subárea de uma captura de tela e eu quero saber se essa subárea é exatamente o ícone ou não.)

Eu tenho o OpenCV em mãos, mas ainda não estou acostumado.

Uma possibilidade que pensei até agora: divida as duas imagens em células 10x10 e, para cada uma dessas 100 células, compare o histograma de cores. Então eu posso definir algum valor de limite inventado e, se o valor que obtiver estiver acima desse limite, presumo que eles sejam semelhantes.

Ainda não tentei o quão bem isso funciona, mas acho que seria bom o suficiente. As imagens já são bastante semelhantes (no meu caso de uso), para que eu possa usar um valor limite bastante alto.

Eu acho que existem dezenas de outras soluções possíveis para isso, que funcionariam mais ou menos (como a tarefa em si é bastante simples, pois eu só quero detectar semelhanças se elas forem realmente muito parecidas). O que você sugeriria?


Existem algumas perguntas muito relacionadas / similares sobre como obter uma assinatura / impressão digital / hash de uma imagem:

Além disso, deparei-me com essas implementações que têm essas funções para obter uma impressão digital:

Algumas discussões sobre hashes de imagem perceptivos: aqui


Um pouco offtopic: existem muitos métodos para criar impressões digitais de áudio. O MusicBrainz , um serviço da web que fornece pesquisa de músicas com base em impressões digitais, tem uma boa visão geral em seu wiki . Eles estão usando o AcoustID agora. Isso é para encontrar correspondências exatas (ou na maioria exatas). Para encontrar correspondências semelhantes (ou se você tiver apenas alguns trechos ou ruído alto), dê uma olhada no Echoprint . Uma questão SO relacionada está aqui . Parece que isso foi resolvido para o áudio. Todas essas soluções funcionam muito bem.

Uma pergunta um pouco mais genérica sobre a busca difusa em geral está aqui . Por exemplo, existe um hash sensível à localidade e a pesquisa de vizinhos mais próxima .

Albert
fonte
1
Talvez a impressão digital da imagem possa ajudar? stackoverflow.com/questions/596262/…
GWW 16/11
A métrica de Wasserstein, também conhecida como Distância do Movimentador da Terra (EMD), é algo que as pessoas parecem não conhecer, mas daria praticamente o que você deseja aqui.
mmgp 14/01
3
possível duplicata de comparação de imagem - algoritmo rápido
sashoalm
Oi, eu vim com o dHash aprimorado - chamei-o de IDHash: github.com/Nakilon/dhash-vips
Nakilon

Respostas:

107

A captura de tela ou o ícone pode ser transformada (dimensionada, girada, inclinada ...)? Existem alguns métodos em cima da minha cabeça que podem ajudá-lo:

  • Distância euclidiana simples, como mencionado por @carlosdc (não funciona com imagens transformadas e você precisa de um limite).
  • Correlação cruzada (normalizada) - uma métrica simples que você pode usar para comparar áreas da imagem. É mais robusto que a distância euclidiana simples, mas não funciona em imagens transformadas e você precisará novamente de um limite.
  • Comparação de histograma - se você usar histogramas normalizados, esse método funcionará bem e não será afetado por transformações afins. O problema está em determinar o limite correto. Também é muito sensível a mudanças de cor (brilho, contraste etc.). Você pode combiná-lo com os dois anteriores.
  • Detectores de pontos / áreas salientes - como MSER (regiões extremas maximamente estáveis) , SURF ou SIFT . Esses algoritmos são muito robustos e podem ser muito complicados para sua tarefa simples. O bom é que você não precisa ter uma área exata com apenas um ícone; esses detectores são poderosos o suficiente para encontrar a combinação certa. Uma boa avaliação desses métodos está neste artigo: Detectores locais de características invariantes: uma pesquisa .

A maioria deles já está implementada no OpenCV - veja, por exemplo, o método cvMatchTemplate (usa correspondência de histograma): http://dasl.mem.drexel.edu/~noahKuntz/openCVTut6.html . Os detectores de ponto / área salientes também estão disponíveis - consulte OpenCV Feature Detection .

Karel Petranek
fonte
1
Pode ser dimensionado ou movido levemente. Além disso, o plano de fundo do ícone será diferente. Tentei comparar o histograma, mas obtive muitos falsos positivos. Eu também tentei a distância euclidiana, mas isso também fornece muitos falsos positivos (mas talvez eu possa melhorar um pouco a manipulação do valor alfa no ícone). Vou tentar um pouco mais, caso contrário, vou verificar MSER, SURF ou SIFT.
Albert
1
Outra idéia - não funcionaria se você usasse a comparação histograma das imagens depois de aplicar um operador sobel? Isso só compararia similaridade de arestas. Pode ou não funcionar, dependendo de quão "nervoso" é o plano de fundo.
Karel Petranek
44

Eu enfrento os mesmos problemas recentemente, para resolver esse problema (algoritmo simples e rápido para comparar duas imagens) de uma vez por todas, contribuo com um módulo img_hash para opencv_contrib, você pode encontrar os detalhes neste link .

O módulo img_hash fornece seis algoritmos de hash de imagem, bastante fáceis de usar.

Exemplo de códigos

origem lenaorigem lena

blur lenablur lena

redimensionar lenaredimensionar lena

turno lenaturno 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));
}

Nesse caso, o ColorMomentHash nos fornece o melhor resultado

  • ataque de desfoque gaussiano: 0.567521
  • ataque de turno: 0.229728
  • redimensionar ataque: 0,229358

Prós e contras de cada algoritmo

Desempenho sob diferentes ataques

O desempenho de img_hash também é bom

Comparação de velocidade com a biblioteca PHash (100 imagens de ukbench) calcular o desempenho desempenho de comparação

Se você deseja conhecer os limites de recomendação para esses algoritmos, verifique esta postagem ( http://qtandopencv.blogspot.my/2016/06/introduction-to-image-hash-module-of.html ). Se você tiver interesse em saber como faço para medir o desempenho dos módulos img_hash (incluindo velocidade e ataques diferentes), verifique este link ( http://qtandopencv.blogspot.my/2016/06/speed-up-image-hashing-of -opencvimghash.html ).

StereoMatching
fonte
11

A captura de tela contém apenas o ícone? Nesse caso, a distância L2 das duas imagens pode ser suficiente. Se a distância L2 não funcionar, o próximo passo é tentar algo simples e bem estabelecido, como: Lucas-Kanade . O que tenho certeza está disponível no OpenCV.

carlosdc
fonte
A subárea contém exatamente apenas o ícone (com algum fundo aleatório) ou algo diferente. Eu quero ver qual é o caso. No entanto, pode ser um pouco alterado ou redimensionado, é por isso que eu não tinha certeza se poderia apenas olhar para a distância (em qualquer norma). Mas vou tentar com uma versão reduzida.
Albert
6

Se você deseja obter um índice sobre a semelhança das duas imagens, sugiro a partir das métricas o índice SSIM. É mais consistente com o olho humano. Aqui está um artigo sobre isso: Índice de Similaridade Estrutural

Também é implementado no OpenCV e pode ser acelerado com a GPU: OpenCV SSIM with GPU

Milan Tenk
fonte
5

Se você tiver certeza de ter um alinhamento preciso do seu modelo (o ícone) com a região de teste, qualquer soma antiga de diferenças de pixel funcionará.

Se o alinhamento for apenas um pouquinho menor, você poderá passar ambas as imagens com cv :: GaussianBlur antes de encontrar a soma das diferenças de pixel.

Se a qualidade do alinhamento for potencialmente ruim, eu recomendaria um histograma de gradientes orientados ou um dos convenientes algoritmos de detecção / descritor de ponto-chave do OpenCV (como SIFT ou SURF ).

rcv
fonte
4

Se para combinar imagens idênticas - codifique a distância 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
}

Rápido. Mas não robusta para alterações na iluminação / ponto de vista etc. Fonte

Kiran
fonte
2

Se você deseja comparar imagens por similaridade, sugiro que você use o OpenCV. No OpenCV, existem poucas características correspondentes e modelos correspondentes. Para correspondência de recursos, existem SURF, SIFT, RÁPIDO e assim por diante. Você pode usar isso para detectar, descrever e combinar a imagem. Depois disso, você pode usar o índice específico para encontrar o número de correspondências entre as duas imagens.

Hua Er Lim
fonte
1
você disse "Depois disso, você pode usar o índice específico para encontrar o número de correspondências entre as duas imagens". qual pode ser o número mínimo de correspondências entre as duas imagens para dizer que elas "contêm" o mesmo objeto?
Inês Martins