5.20. Regressão e similaridade#
Mais duas medições na classe Image resumem a imagem como algo diferente de uma distribuição de valores de pixels. A regressão linear de pixels limiarizados dá a uma aplicação uma linha sobre a qual ela pode agir – a entrada clássica para um robô seguidor de linha. A medição de similaridade dá a uma aplicação um único número que descreve o quão parecidas duas imagens são – a entrada natural para um teste de regressão de imagem de referência (golden image) ou um detector de mudanças grosseiras.
5.20.1. Regressão linear#
Quando os pixels de primeiro plano de uma imagem por acaso formam uma linha através do quadro – a fita em uma pista que um robô está seguindo, a linha de um horizonte, a borda de uma estrada ou de um corredor – a aplicação geralmente não quer cada pixel individual de primeiro plano. Ela quer a linha de melhor ajuste através de todos eles, parametrizada para que possa decidir como a linha está orientada e onde ela cruza o quadro.
get_regression() faz esse ajuste. Ela recebe a mesma forma de tupla de limiar que binary() e find_blobs() usam, identifica cada pixel que corresponde ao limiar e retorna 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 é a regressão linear de Theil-Sen – um método robusto que tolera outliers melhor do que o ajuste de mínimos quadrados, mais familiar. Um pequeno punhado de pixels distantes da linha verdadeira não distorce o resultado como o faria com mínimos quadrados, o que combina com a realidade de primeiro plano ruidoso de uma saída de limiar real.
O resultado line carrega os pontos finais recortados para o 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 em relação à horizontal e sua distância perpendicular à origem. A forma polar é o que um laço de controle geralmente quer: theta diz ao robô para que lado a linha está inclinada, rho diz onde a linha cruza a imagem, e um laço de realimentação sobre os dois mantém o robô centrado na linha.
Um punhado de argumentos de palavra-chave ajusta a robustez e o custo. x_stride e y_stride pulam pixels durante o ajuste – passos maiores tornam a regressão mais barata ao custo de ajustar menos pixels. area_threshold e pixels_threshold rejeitam linhas que não têm pixels correspondentes suficientes por trás delas. target_size reescala a entrada para um tamanho menor antes do ajuste – a regressão roda mais rápido em um substituto de 80 por 60 da imagem sem muita perda na precisão da direção da linha.
Se nenhuma linha aceitável pôde ser ajustada – 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 retorna None. Um código real de seguimento de linha protege cada chamada a get_regression() com uma verificação de None antes de acessar os atributos da linha.
5.20.2. Similaridade de imagens#
Um tipo diferente de medição: em vez de perguntar “o que a imagem contém?”, pergunta “o quão parecidas são estas duas imagens?”. A operação a ser usada é get_similarity(), que computa o Índice de Similaridade Estrutural (SSIM) entre a imagem fonte 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 usada em todo o processamento de imagens porque se comporta da forma como a intuição humana sobre similaridade se comporta – um pequeno deslocamento ou uma pequena mudança de brilho reduz a pontuação ligeiramente, enquanto uma grande mudança estrutural (objeto ausente, cena diferente) a reduz drasticamente. A pontuação varia de -1 a +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. Um objeto similarity retornado expõe o SSIM médio em toda a imagem, além do desvio padrão, mínimo e 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 relatar zero em “nada mudou” e crescer à medida que as mudanças se acumulam – a flag dssim=True retorna a dissimilaridade estrutural: o SSIM médio subtraído de 1, de modo que o valor de retorno é 0.0 para imagens idênticas e cresce à medida que elas diferem.
5.20.3. Casos de uso para SSIM#
As duas aplicações comuns:
Teste de regressão com imagem de referência (golden image). Um framework de teste captura um quadro de referência sob condições reconhecidamente boas e o armazena como a imagem de referência. Execuções de teste subsequentes capturam sob as mesmas condições e comparam com a imagem de referência usando SSIM. Uma pontuação acima de algum limiar (0.95 ou 0.98 dependendo da tolerância) é uma aprovação; abaixo é uma falha. O framework de teste não precisa saber o que mudou – a pontuação SSIM é o sinal.
Detecção de mudanças grosseiras. Uma aplicação que quer uma versão mais grosseira da diferenciação de quadros – uma que ignore pequenas mudanças de brilho mas reaja a grandes mudanças estruturais – pode usar SSIM contra um quadro de referência em vez da difference() por pixel seguida de um limiar. O SSIM é menos sensível à deriva de iluminação do que a diferenciação por pixel, o que o torna a melhor escolha quando o objetivo é detectar que “a cena parece materialmente diferente” em vez de que “algum pixel individual mudou.”
Ambas as aplicações usam a mesma chamada – img.get_similarity(reference) – e disparam com base em um limiar da pontuação retornada. A diferença é apenas se o limiar é alto (teste de regressão, procurando uma correspondência quase idêntica) ou baixo (detecção de mudanças, procurando qualquer grande mudança estrutural).
5.20.4. A forma de transformar e comparar#
Uma sutileza ú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, escalada e transformada por esses parâmetros antes de a comparação SSIM rodar.
Isso significa que uma aplicação pode perguntar “o quão similar é esta cena a um quadro de referência após um deslocamento / rotação / escala conhecidos” sem preparar uma imagem de referência pré-transformada. É a maneira barata de construir um rastreador que pesquisa um espaço de parâmetros e relata qual transformação da referência melhor corresponde ao quadro atual.