Este post foi o ponto de partida da minha solução, com muitas boas idéias aqui, então eu gostaria de compartilhar meus resultados. O principal insight é que eu encontrei uma maneira de contornar a lentidão da correspondência de imagens com base em pontos-chave, explorando a velocidade do phash.
Para a solução geral, é melhor empregar várias estratégias. Cada algoritmo é mais adequado para certos tipos de transformação de imagem e você pode tirar vantagem disso.
No topo, os algoritmos mais rápidos; na parte inferior, o mais lento (embora mais preciso). Você pode pular os mais lentos se uma boa correspondência for encontrada no nível mais rápido.
- baseado em hash de arquivo (md5, sha1, etc) para duplicatas exatas
- hash perceptivo (phash) para imagens redimensionadas
- baseado em recursos (SIFT) para imagens modificadas
Estou tendo resultados muito bons com o phash. A precisão é boa para imagens redimensionadas. Não é bom para imagens (perceptivamente) modificadas (cortadas, giradas, espelhadas, etc). Para lidar com a velocidade do hash, devemos empregar um cache / banco de dados em disco para manter os hashes do palheiro.
O mais interessante sobre o phash é que, depois de criar seu banco de dados de hash (que para mim é de cerca de 1000 imagens / s), as pesquisas podem ser muito, muito rápidas, principalmente quando você pode armazenar todo o banco de dados de hash na memória. Isso é bastante prático, pois um hash é de apenas 8 bytes.
Por exemplo, se você tiver 1 milhão de imagens, será necessário um array de 1 milhão de valores de hash de 64 bits (8 MB). Em algumas CPUs, isso se encaixa no cache L2 / L3! Em uso prático, vi um Corei7 comparar a mais de 1 Giga-hamm / s; é apenas uma questão de largura de banda de memória para a CPU. Um banco de dados de 1 bilhão de imagens é prático em uma CPU de 64 bits (é necessário 8 GB de RAM) e as pesquisas não excederão 1 segundo!
Para imagens modificadas / cortadas, parece que um recurso / detector de ponto-chave invariável à transformação, como o SIFT, é o caminho a percorrer. O SIFT produzirá bons pontos-chave que detectarão cortar / girar / espelhar etc. No entanto, a comparação do descritor é muito lenta em comparação com a distância de hamming usada pelo phash. Essa é uma grande limitação. Há muitas comparações a serem feitas, pois o descritor IxJxK máximo se compara à pesquisa de uma imagem (I = número de imagens do palheiro, J = pontos-chave de destino por imagem do palheiro, K = pontos-chave de destino por imagem da agulha).
Para contornar o problema de velocidade, tentei usar phash em torno de cada ponto-chave encontrado, usando o tamanho / raio do recurso para determinar o sub-retângulo. O truque para fazer isso funcionar bem é aumentar / diminuir o raio para gerar diferentes níveis sub-retos (na imagem da agulha). Normalmente, o primeiro nível (sem escala) corresponderá, no entanto, muitas vezes são necessários mais alguns. Não sei ao certo por que isso funciona, mas posso imaginar que ele permita recursos muito pequenos para o phash funcionar (o phash reduz as imagens para 32x32).
Outra questão é que o SIFT não distribuirá os pontos-chave de maneira ideal. Se houver uma seção da imagem com muitas arestas, os pontos-chave se agruparão ali e você não encontrará nenhum em outra área. Estou usando o GridAdaptedFeatureDetector no OpenCV para melhorar a distribuição. Não tenho certeza de qual tamanho de grade é melhor, estou usando uma grade pequena (1x3 ou 3x1, dependendo da orientação da imagem).
Você provavelmente deseja dimensionar todas as imagens do palheiro (e agulha) para um tamanho menor antes da detecção de recurso (eu uso 210px na dimensão máxima). Isso reduzirá o ruído na imagem (sempre um problema para os algoritmos de visão computacional), além de focar o detector em recursos mais importantes.
Para imagens de pessoas, você pode tentar a detecção de rosto e usá-lo para determinar o tamanho da imagem a ser dimensionado e o tamanho da grade (por exemplo, o maior rosto dimensionado para 100px). O detector de recursos é responsável por vários níveis de escala (usando pirâmides), mas há uma limitação de quantos níveis ele usará (é possível ajustar isso, é claro).
O detector de ponto-chave provavelmente está funcionando melhor quando retorna menos do que o número de recursos que você queria. Por exemplo, se você pedir 400 e receber 300 de volta, isso é bom. Se você receber 400 de volta todas as vezes, provavelmente alguns recursos bons precisariam ser deixados de fora.
A imagem da agulha pode ter menos pontos-chave que as imagens do palheiro e ainda assim obter bons resultados. Adicionar mais não significa necessariamente grandes ganhos, por exemplo, com J = 400 e K = 40, minha taxa de acerto é de cerca de 92%. Com J = 400 e K = 400, a taxa de acerto apenas sobe para 96%.
Podemos tirar proveito da velocidade extrema da função hamming para resolver escala, rotação, espelhamento, etc. Uma técnica de múltiplas passagens pode ser usada. Em cada iteração, transforme os sub-retângulos, re-hash e execute a função de pesquisa novamente.