Como posso avaliar a semelhança de duas imagens com o OpenCV?

141

O OpenCV suporta a comparação de duas imagens, retornando algum valor (talvez uma porcentagem) que indica o quão semelhantes essas imagens são? Por exemplo, 100% seria retornado se a mesma imagem fosse passada duas vezes, 0% seria retornado se as imagens fossem totalmente diferentes.

Eu já li muitos tópicos semelhantes aqui no StackOverflow. Eu também fiz bastante pesquisa no Google. Infelizmente, não consegui encontrar uma resposta satisfatória.

Boris
fonte
Veja também as respostas em stackoverflow.com/questions/4196453/…
B. Vá em

Respostas:

208

Este é um tópico enorme, com respostas de três linhas de código a revistas de pesquisa inteiras.

Vou descrever as técnicas mais comuns e seus resultados.

Comparando histogramas

Um dos métodos mais simples e rápidos. Há décadas, proposto como um meio de encontrar semelhanças de imagens. A ideia é que uma floresta tenha muito verde e um rosto humano muito rosa, ou o que seja. Portanto, se você comparar duas fotos com florestas, obterá uma semelhança entre os histogramas, porque possui muito verde nas duas.

Desvantagem: é muito simplista. Uma banana e uma praia terão a mesma aparência, pois ambas são amarelas.

Método OpenCV: compareHist ()

Correspondência de modelos

Um bom exemplo aqui é matchTemplate, encontrando uma boa correspondência . Convolve a imagem de pesquisa com a que está sendo pesquisada. Geralmente é usado para encontrar partes menores da imagem em uma maior.

Desvantagens: só retorna bons resultados com imagens idênticas, mesmo tamanho e orientação.

Método OpenCV: matchTemplate ()

Correspondência de recursos

Considerada uma das maneiras mais eficientes de fazer a pesquisa de imagens. Vários recursos são extraídos de uma imagem, de forma a garantir que os mesmos recursos sejam reconhecidos novamente, mesmo quando girados, redimensionados ou inclinados. Os recursos extraídos dessa maneira podem ser comparados com outros conjuntos de recursos de imagem. Outra imagem que possui uma alta proporção dos recursos correspondentes à primeira é considerada representando a mesma cena.

Encontrar a homografia entre os dois conjuntos de pontos também permitirá que você encontre a diferença relativa no ângulo de disparo entre as imagens originais ou a quantidade de sobreposição.

Há uma série de tutoriais / exemplos de OpenCV sobre isso e um bom vídeo aqui . Um módulo OpenCV inteiro (features2d) é dedicado a ele.

Desvantagens: Pode ser lento. Não é perfeito.


No site de perguntas e respostas do OpenCV, estou falando sobre a diferença entre descritores de recursos, que são ótimos ao comparar imagens inteiras e descritores de textura, que são usados ​​para identificar objetos como rostos humanos ou carros em uma imagem.

Sam
fonte
para comparar imagens semelhantes que possuem apenas algumas imagens distintas (por exemplo, um novo objeto movido para a mesma visualização), você também pode trabalhar com absdiff codota.com/code/java/methods/org.opencv.core.Core/absdiff Limiar o resultado produz uma máscara que permite destacar as regiões que mudaram de cena para cena.
Max F.
34

Se for para combinar imagens idênticas (mesmo tamanho / orientação)

// 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
}

Fonte

Kiran
fonte
12

A solução de Sam deve ser suficiente. Eu usei a combinação da diferença do histograma e da correspondência de modelos, porque nenhum método estava funcionando para mim 100% das vezes. Dei menos importância ao método histograma. Aqui está como eu implementei no script python simples.

import cv2

class CompareImage(object):

    def __init__(self, image_1_path, image_2_path):
        self.minimum_commutative_image_diff = 1
        self.image_1_path = image_1_path
        self.image_2_path = image_2_path

    def compare_image(self):
        image_1 = cv2.imread(self.image_1_path, 0)
        image_2 = cv2.imread(self.image_2_path, 0)
        commutative_image_diff = self.get_image_difference(image_1, image_2)

        if commutative_image_diff < self.minimum_commutative_image_diff:
            print "Matched"
            return commutative_image_diff
        return 10000 //random failure value

    @staticmethod
    def get_image_difference(image_1, image_2):
        first_image_hist = cv2.calcHist([image_1], [0], None, [256], [0, 256])
        second_image_hist = cv2.calcHist([image_2], [0], None, [256], [0, 256])

        img_hist_diff = cv2.compareHist(first_image_hist, second_image_hist, cv2.HISTCMP_BHATTACHARYYA)
        img_template_probability_match = cv2.matchTemplate(first_image_hist, second_image_hist, cv2.TM_CCOEFF_NORMED)[0][0]
        img_template_diff = 1 - img_template_probability_match

        # taking only 10% of histogram diff, since it's less accurate than template method
        commutative_image_diff = (img_hist_diff / 10) + img_template_diff
        return commutative_image_diff


    if __name__ == '__main__':
        compare_image = CompareImage('image1/path', 'image2/path')
        image_difference = compare_image.compare_image()
        print image_difference
Priyanshu Chauhan
fonte
Eu não entendo bem python. Mas qual é o tipo 'commutative_image_diff'? cv.Mat ou duplo. Se for cv.Mat, compare 'commutative_image_diff <self.minimum_commutative_image_diff' como ele funciona ou qual o objetivo dessa comparação. Você pode me explicar?
BulletRain
1

Um pouco fora de tópico, mas útil é a numpyabordagem pitônica . É robusto e rápido, mas apenas compara pixels e não os objetos ou dados que a imagem contém (e requer imagens do mesmo tamanho e forma):

Uma abordagem muito simples e rápida para fazer isso sem o openCV e qualquer biblioteca de visão computacional é padronizar as matrizes de imagem

import numpy as np
picture1 = np.random.rand(100,100)
picture2 = np.random.rand(100,100)
picture1_norm = picture1/np.sqrt(np.sum(picture1**2))
picture2_norm = picture2/np.sqrt(np.sum(picture2**2))

Após definir as duas imagens (ou matrizes), você pode somar a multiplicação das imagens que deseja comparar:

1) Se você comparar imagens semelhantes, a soma retornará 1:

In[1]: np.sum(picture1_norm**2)
Out[1]: 1.0

2) Se não forem semelhantes, você obterá um valor entre 0 e 1 (uma porcentagem se multiplicar por 100):

In[2]: np.sum(picture2_norm*picture1_norm)
Out[2]: 0.75389941124629822

Observe que, se você tiver imagens coloridas, precisará fazer isso nas três dimensões ou apenas comparar uma versão em escala de cinza. Costumo comparar grandes quantidades de fotos com conteúdo arbitrário e essa é uma maneira muito rápida de fazer isso.

Franz
fonte
2
oi, eu apenas sigo sua etapa, mas descobri que a parte normalizada não conseguiu o resultado adequado. O resultado final é muito maior que 1,0. Qualquer ideia?
G_cy