Esta não é a melhor solução, mas é uma solução. Eu gostaria de aprender melhores técnicas:
Se eles não fossem rotacionados ou redimensionados, você poderia usar uma simples correlação cruzada das imagens. Haverá um pico brilhante onde quer que a imagem pequena ocorra na imagem grande.
Você pode acelerar a correlação cruzada usando um método FFT, mas se estiver apenas combinando uma imagem de origem pequena com uma imagem de destino grande, o método de multiplicação e adição de força bruta às vezes é (geralmente não) mais rápido.
Fonte:
Alvo:
Correlação cruzada:
Os dois pontos positivos são os locais correspondentes.
Mas você faz ter um parâmetro de rotação na sua imagem exemplo, de modo que não irá funcionar por si só. Se apenas a rotação é permitida, e não a escala, ainda é possível usar a correlação cruzada, mas você precisa correlacionar, girar a fonte, correlacioná-la com toda a imagem de destino, girá-la novamente, etc. para todas as rotações.
Observe que isso nem sempre encontrará a imagem. Se a imagem de origem for ruído aleatório e o alvo for ruído aleatório, você não a encontrará, a menos que pesquise exatamente no ângulo certo. Em situações normais, provavelmente o encontrará, mas depende das propriedades da imagem e dos ângulos nos quais você pesquisa.
Esta página mostra um exemplo de como isso seria feito, mas não fornece o algoritmo.
Qualquer deslocamento em que a soma esteja acima de algum limite é uma correspondência. Você pode calcular a qualidade da correspondência correlacionando a imagem de origem consigo mesma e dividindo todas as suas somas por esse número. Uma combinação perfeita será 1.0.
Isso será muito pesado computacionalmente, e provavelmente existem métodos melhores para combinar padrões de pontos (sobre os quais eu gostaria de saber).
Exemplo rápido de Python usando escala de cinza e método FFT:
from __future__ import division
from pylab import *
import Image
import ImageOps
source_file = 'dots source.png'
target_file = 'dots target.png'
# Load file as grayscale with white dots
target = asarray(ImageOps.invert(Image.open(target_file).convert('L')))
close('all')
figure()
imshow(target)
gray()
show()
source_Image = ImageOps.invert(Image.open(source_file).convert('L'))
for angle in (0, 180):
source = asarray(source_Image.rotate(angle, expand = True))
best_match = max(fftconvolve(source[::-1,::-1], source).flat)
# Cross-correlation using FFT
d = fftconvolve(source[::-1,::-1], target, mode='same')
figure()
imshow(source)
# This only finds a single peak. Use something that finds multiple peaks instead:
peak_x, peak_y = unravel_index(argmax(d),shape(d))
figure()
plot(peak_y, peak_x,'ro')
imshow(d)
# Keep track of all these matches:
print angle, peak_x, peak_y, d[peak_x,peak_y] / best_match
Bitmaps de uma cor
Para bitmaps de uma cor, isso seria muito mais rápido. A correlação cruzada se torna:
- Coloque a imagem de origem sobre a imagem de destino
- Mover a imagem de origem em 1 pixel
- E bit a bit todos os pixels sobrepostos
- somar todos os 1s
- ...
Limitar uma imagem em escala de cinza para binária e fazer isso pode ser bom o suficiente.
Nuvem
Se a origem e o destino forem ambos padrões de pontos, um método mais rápido seria encontrar os centros de cada ponto (correlacionar uma vez com um ponto conhecido e depois encontrar os picos) e armazená-los como um conjunto de pontos; para segmentar girando, traduzindo e encontrando o erro de mínimos quadrados entre os pontos mais próximos nos dois conjuntos.