5.23. Correção de perspetiva¶
Aviso
A matriz transform arbitrária de 3 por 3 é suportada apenas na OpenMV Cam N6 – a palavra-chave é ignorada silenciosamente em qualquer outra placa. As aplicações que precisem de funcionar noutras placas devem usar o método rotation_corr() incorporado (com a sua forma corners=) ou pré-calcular a imagem corrigida fora da placa.
O método rotation_corr() incorporado agrupa uma família específica de distorções de perspetiva atrás de um pequeno conjunto de parâmetros, e corre em todas as placas suportadas. Algumas aplicações necessitam de uma distorção que não se enquadra nessa forma: um remapeamento projetivo arbitrário de um quadrilátero para outro, uma correção calibrada para uma montagem conhecida que já foi determinada fora de linha, uma matriz de distorção fornecida pronta por algum algoritmo a montante. Para esses casos, draw_image() – juntamente com copy(), crop(), e scale() – aceita uma palavra-chave transform que recebe uma matriz 3 por 3 construída manualmente descrevendo a distorção diretamente.
5.23.1. Transformações afins e projetivas¶
As distorções geométricas são expressas em coordenadas homogéneas: a posição do pixel (x, y) com um 1 acrescentado, multiplicada por uma matriz 3 por 3.
A forma afim é o ponto de partida. A sua linha inferior é fixada em \((0, 0, 1)\):
Desenvolvendo, cada coordenada de saída é uma combinação linear das coordenadas de entrada mais uma constante:
o que abrange escalonamento, rotação, cisalhamento e translação em qualquer combinação – e em todas elas, as linhas paralelas permanecem paralelas.
A forma projetiva (perspetiva) liberta a linha inferior:
Desenvolvendo:
A divisão por \(w' = g x + h y + 1\) é o que torna a transformação projetiva em vez de meramente afim. Quando \(g\) e \(h\) são ambos zero, \(w'\) permanece em um e a divisão não faz nada – a forma afim novamente. Quando qualquer um é diferente de zero, \(w'\) varia com a posição de entrada e os pixels em posições diferentes ficam com diferentes graus de encurtamento em perspetiva, o que já não mantém as linhas paralelas paralelas – é exatamente o efeito de trapézio de observar um plano plano de um ângulo oblíquo. Uma transformação projetiva é a distorção geométrica mais geral que transforma linhas retas em linhas retas; escalonamento, inversão, transposição, rotação e a correção de rotação de quatro cantos são todos casos especiais de uma.
As transformações nomeadas derivam diretamente da forma afim. A transformação identidade é a matriz identidade, e:
Para a maioria das transformações construídas manualmente, uma aplicação começa com uma delas como base e multiplica matrizes adicionais para cada operação adicional, terminando com uma única matriz 3 por 3 que descreve a distorção composta. As matrizes aplicam-se da direita para a esquerda: \(M = T R S\) executa primeiro o escalonamento, depois a rotação, depois a translação. A composição de que todos acabam por necessitar é a rotação em torno do centro da imagem – uma matriz de rotação simples faz girar a imagem em torno da origem do pixel no canto superior esquerdo, pelo que a versão centrada move o centro \((c_x, c_y)\) para a origem, roda, e move-o de volta:
5.23.2. A palavra-chave transform¶
A matriz é passada através de uma palavra-chave transform, fornecida como um ulab.numpy.ndarray de 3 por 3. O método a utilizar é draw_image(), que distorce a fonte através da matriz ao desenhá-la sobre um destino – o resultado fica num buffer que a aplicação controla, e a distorção compõe-se com tudo o mais na chamada: o escalonamento, a mistura alfa, a máscara.
import ulab.numpy as np
M = np.array([[1.2, 0.0, -20.0],
[0.0, 1.2, -15.0],
[0.0, 0.0, 1.0]])
canvas.draw_image(img, transform=M)
O exemplo distorce img sobre canvas com escalonamento de 1.2 em cada direção e deslocado para a esquerda e para cima em 20 e 15 pixels respetivamente – uma distorção afim construída diretamente a partir das entradas da matriz descritas acima. A mesma palavra-chave em copy(), crop(), e scale() aplica a distorção à própria imagem.