5.30. Correspondência de modelo (template matching)

Os detectores abordados até agora respondem a perguntas sobre o conteúdo de um único quadro: onde estão os blobs, para onde vão as linhas, o que diz um código impresso. Uma classe diferente de pergunta compara uma imagem com outra. Esta região do quadro capturado se parece com o patch de referência que armazenei no momento da calibração? Os métodos de correspondência respondem a essa pergunta.

A análise tonal e estatística apresentou get_similarity() para a pergunta relacionada – quão parecidas são essas duas imagens de mesmo tamanho no geral? – com o SSIM como métrica subjacente. A pergunta de correspondência restante é a de localização: não “quão parecidas são essas duas imagens”, mas “onde, dentro desta imagem maior, aparece aquele patch menor?” A ferramenta certa para a pergunta de localização é a correspondência de modelo.

5.30.1. A chamada básica

find_template() procura o primeiro lugar onde uma pequena imagem de modelo aparece dentro do quadro capturado. A implementação usa correlação cruzada normalizada (NCC): o modelo desliza pelo quadro, a pontuação de correspondência por posição é calculada a partir da correlação entre os pixels do modelo e os pixels subjacentes do quadro (normalizada em relação às médias e variâncias locais, de modo que mudanças de ganho não enganem a correspondência), e a primeira posição cuja pontuação ultrapassa o threshold é retornada como uma caixa delimitadora:

template = image.Image("/sdcard/template.bmp", copy_to_fb=False)
template.to_grayscale()

match = img.find_template(template, threshold=0.7,
                           search=image.SEARCH_DS)

if match is not None:
    img.draw_rectangle(match, color=(255, 0, 0))

O método só funciona em imagens em escala de cinza. Capture em escala de cinza (a escolha natural para qualquer câmera sem um sensor de cor), ou converta no local via to_grayscale() antes da chamada. O mesmo se aplica ao modelo carregado do disco: um modelo colorido é convertido com o mesmo método, e o resultado é o que o comparador espera.

threshold é um float de 0.0 a 1.0. Um valor de 1.0 exige uma correspondência perfeita pixel a pixel (o que nunca acontece com imagens capturadas reais), 0.0 aceita qualquer coisa, e valores entre 0.6 e 0.8 cobrem o caso comum em que o modelo foi capturado sob iluminação semelhante e a cena não mudou drasticamente. Aumente o limiar para suprimir falsos positivos; reduza-o para aceitar correspondências mais ruidosas, ao custo de mais acertos espúrios.

5.30.2. Estratégia de busca

search escolhe entre duas estratégias. image.SEARCH_EX é a busca exaustiva: o modelo desliza por cada posição a cada step pixels no quadro e retorna o primeiro acerto acima do limiar. image.SEARCH_DS é a busca em diamante: o comparador faz uma amostragem grosseira primeiro e depois refina em torno da melhor pontuação, o que é dramaticamente mais rápido, mas pode perder uma correspondência verdadeira se a passagem grosseira acabar caindo perto de um máximo local que supera o global. Para um pipeline em tempo real onde o modelo é bem definido e improvável de ser confundido, SEARCH_DS é o padrão certo; para uma calibração única em que o custo de uma falha é maior que o custo de uma varredura mais lenta, SEARCH_EX é mais seguro.

step controla o salto de pixels durante a passagem exaustiva (a busca em diamante gerencia o próprio passo). Valores maiores de step aceleram a varredura ao custo da precisão de sub-pixel. roi restringe a busca a uma região do quadro, tanto estreitando o que o comparador considera quanto reduzindo o trabalho.

O valor retornado é uma tupla de caixa delimitadora (x, y, w, h) que identifica a melhor correspondência, ou None se nenhuma posição ultrapassar o limiar. A caixa delimitadora se encaixa diretamente em draw_rectangle() ou crop() para a próxima etapa do processamento.

5.30.3. A armadilha da escala e da rotação

A armadilha clássica da correspondência de modelo é a sensibilidade à escala e à rotação. O comparador compara o modelo com o quadro pixel a pixel; um modelo capturado a uma distância não corresponde ao mesmo objeto capturado a uma distância diferente, e um modelo capturado de frente não corresponde ao mesmo objeto visto fora do eixo. O limiar silenciosamente cai abaixo do nível de correspondência mesmo quando o objeto está claramente visível ao olho humano, e o método retorna None.

Existem algumas soluções alternativas para os casos simples. A aplicação pode capturar vários modelos em diferentes escalas e executar find_template() para cada um em sequência, aceitando o primeiro que ultrapassar o limiar; o custo cresce com o número de modelos. A aplicação pode pré-processar o quadro com rotation_corr() ou a transformada polar (Transformações geométricas) para remover a rotação problemática antes que a correspondência seja executada; o modelo correspondido ainda precisa corresponder à geometria corrigida.

Um idioma útil para pipelines de inspeção de QA combina o comparador de modelo com o pontuador de similaridade que a análise tonal e estatística apresentou: find_template() localiza a peça no quadro capturado e a caixa delimitadora retornada é recortada e passada para get_similarity() em comparação com o patch de referência. A etapa de correspondência de modelo decide onde a peça está; a etapa de pontuação de similaridade decide se a peça é aceitável. As duas etapas são executadas a cada quadro, o limiar sobre mean é o portão de aprovação/reprovação, e a caixa delimitadora correspondida desenhada de volta no quadro é a prévia na IDE que o operador observa.