5.20. Regressão e similaridade#

Duas medições adicionais na classe Image resumem a imagem como algo diferente de uma distribuição de valores de pixel. A regressão linear de pixels com limiar fornece a uma aplicação uma linha sobre a qual pode agir – a entrada clássica para um robô seguidor de linha. A medição de similaridade fornece a uma aplicação um único número que descreve o grau de semelhança entre duas imagens – a entrada natural para um teste de regressão de imagem de referência ou para um detetor de alterações grosseiras.

5.20.1. Regressão linear#

Quando os pixels de primeiro plano de uma imagem formam uma linha através do fotograma – a fita numa pista que um robô está a seguir, a linha de um horizonte, a orla de uma estrada ou de um corredor – a aplicação normalmente não quer cada pixel de primeiro plano individualmente. Quer a linha de melhor ajuste através de todos eles, parametrizada de modo a poder decidir como a linha está orientada e onde cruza o fotograma.

get_regression() realiza esse ajuste. Recebe o mesmo formato de tuplo de limiar que binary() e find_blobs() usam, identifica cada pixel que corresponde ao limiar e devolve um único resultado line que descreve a linha de melhor ajuste através desses pixels:

line = img.get_regression([(0, 60)])
if line:
    img.draw_line((line.x1(), line.y1(),
                   line.x2(), line.y2()),
                  color=(255, 0, 0))

O ajuste usa a regressão linear Theil-Sen – um método robusto que tolera valores atípicos melhor do que o ajuste por mínimos quadrados mais familiar. Um pequeno conjunto de pixels afastados da linha verdadeira não distorce o resultado da forma como o fariam com os mínimos quadrados, o que corresponde à realidade ruidosa de um primeiro plano com limiar real.

O resultado line contém os pontos extremos recortados ao retângulo da imagem (x1, y1, x2, y2), o comprimento e a magnitude da linha (length, magnitude), e a descrição geométrica da linha em forma polar (theta, rho) – o ângulo da linha relativamente à horizontal e a sua distância perpendicular à origem. A forma polar é o que um ciclo de controlo normalmente precisa: theta diz ao robô para que lado a linha está inclinada, rho diz-lhe onde a linha cruza a imagem, e um ciclo de retroação sobre os dois mantém o robô centrado na linha.

Alguns argumentos de palavra-chave ajustam a robustez e o custo. x_stride e y_stride saltam pixels durante o ajuste – strides maiores tornam a regressão mais económica ao custo de ajustar menos pixels. area_threshold e pixels_threshold rejeitam linhas que não têm pixels correspondentes suficientes. target_size redimensiona a entrada para um tamanho menor antes do ajuste – a regressão corre mais rapidamente num substituto de 80 por 60 da imagem sem grande perda de precisão na direção da linha.

Se não for possível ajustar nenhuma linha aceitável – se o limiar não correspondeu a nenhum pixel, ou correspondeu a um padrão que não se parece com uma linha – o método devolve None. O código real de seguimento de linha protege cada chamada a get_regression() com uma verificação de None antes de aceder aos atributos da linha.

5.20.2. Similaridade de imagens#

Um tipo diferente de medição: em vez de perguntar «o que contém a imagem?», pergunta-se «qual é o grau de semelhança entre estas duas imagens?». A operação a utilizar é get_similarity(), que calcula o Índice de Similaridade Estrutural (SSIM) entre a imagem de origem e uma imagem de referência.

s = img.get_similarity(reference)
print(s.mean, s.stdev)

O SSIM é a métrica padrão de similaridade de imagens utilizada em todo o processamento de imagem porque se comporta da forma como a intuição humana sobre similaridade funciona – um pequeno deslocamento ou uma pequena variação de brilho reduz ligeiramente a pontuação, enquanto uma grande alteração estrutural (objeto em falta, cena diferente) a reduz drasticamente. A pontuação varia entre -1 e +1: +1 significa que as duas imagens são idênticas, 0 significa que não têm relação, e -1 significa que são estruturalmente opostas. O objeto similarity devolvido expõe o SSIM médio ao longo da imagem, mais o desvio padrão, o mínimo e o máximo das pontuações por tile.

Para o tipo de comparação em que um número pequeno é melhor do que um grande – um teste de regressão que deve reportar zero para «nada mudou» e aumentar à medida que as alterações se acumulam – a opção dssim=True devolve a dissimilaridade estrutural: o SSIM médio subtraído de 1, pelo que o valor de retorno é 0.0 para imagens idênticas e aumenta à medida que diferem.

5.20.3. Casos de uso para SSIM#

As duas aplicações mais comuns:

Testes de regressão com imagem de referência. Uma estrutura de testes captura um fotograma de referência em condições conhecidamente boas e guarda-o como imagem de referência. As execuções de teste subsequentes capturam nas mesmas condições e comparam com a imagem de referência usando SSIM. Uma pontuação acima de um determinado limiar (0.95 ou 0.98 consoante a tolerância) é aprovação; abaixo é reprovação. A estrutura de testes não precisa de saber o que mudou – a pontuação SSIM é o sinal.

Deteção de alterações grosseiras. Uma aplicação que pretende uma versão mais grosseira da diferença entre fotogramas – uma que ignora pequenas variações de brilho mas reage a grandes alterações estruturais – pode usar o SSIM contra um fotograma de referência em vez do difference() por pixel seguido de um limiar. O SSIM é menos sensível à deriva de iluminação do que a diferença por pixel, o que o torna a melhor escolha quando o objetivo é detetar «a cena parece materialmente diferente» em vez de «algum pixel individual mudou.»

Ambas as aplicações usam a mesma chamada – img.get_similarity(reference) – e disparam sobre um limiar da pontuação devolvida. A diferença é apenas se o limiar é alto (teste de regressão, à procura de uma correspondência quase idêntica) ou baixo (deteção de alterações, à procura de qualquer grande alteração estrutural).

5.20.4. A forma de transformar e comparar#

Uma subtileza útil: get_similarity() aceita os mesmos parâmetros x, y, x_scale, y_scale, roi, rgb_channel, alpha, color_palette, alpha_palette, hint e transform que draw_image(). A imagem de referência é posicionada, dimensionada e transformada por esses parâmetros antes de a comparação SSIM ser executada.

Isto significa que uma aplicação pode perguntar «quão semelhante é esta cena a um fotograma de referência após um deslocamento / rotação / escala conhecidos» sem preparar uma imagem de referência pré-transformada. É a forma económica de construir um rastreador que pesquisa um espaço de parâmetros e reporta qual transformação da referência melhor corresponde ao fotograma atual.