Este artigo usa Python e OpenCV para uma transformação de perspetiva: passar as quatro esquinas de uma imagem para outras quatro. Em síntese, getPerspectiveTransform calcula a matriz e warpPerspective deforma os píxeis. O exemplo é uma grelha 600×600 com uma silhueta. (Os rótulos das figuras estão em inglês.)

Pontos-chave
| Olhe isto | Ideia |
|---|---|
| O que estamos a fazer | Fixe para onde vão as quatro esquinas e o plano estica-se em trapézio |
| Código e resultado | Execute o script abaixo com a mesma pasta da figura |
| Pastas | Coloque source_pics e save_pics ao lado do script |
| Coordenadas | Canto superior esquerdo é [0, 0]. Ordem: sup.-esq. → inf.-esq. → inf.-dir. → sup.-dir. |
| As restantes linhas | Leitura, tamanho, matriz, deformação, gravação |
| Tamanho de saída | Os píxeis fora do terceiro argumento são recortados |
| A sua imagem | Altere só a ordem dos pontos e o tamanho de saída para o retângulo que quer |
| Câmara limitada | Mesmo de lado, emparelhar as quatro esquinas do plano real continua a dar um retângulo de dados |
Uma transformação de perspetiva fixa para onde vão as quatro esquinas
Uma transformada afim (warpAffine) mantém as paralelas paralelas. Uma de perspetiva (getPerspectiveTransform e warpPerspective) move as quatro esquinas em separado, por isso consegue um trapézio largo à frente e estreito atrás. Endireitar um cartão de visita para um retângulo visto de cima, ou olhar um chão em planta, pertencem a estas funções.
Passe as esquinas de entrada e as de saída na mesma ordem. Este artigo não segue a álgebra da matriz. Se souber para onde vão as quatro esquinas, pode usar as funções.
O script e o antes/depois
Primeiro um script que funciona. As secções seguintes seguem as coordenadas e as restantes linhas.
import numpy as np
import cv2
image = cv2.imread("source_pics/test_1.png", cv2.IMREAD_COLOR)
height, width, channels = image.shape[:3]
source_points = np.array([[0, 0], [0, height], [width, height], [width, 0]], dtype=np.float32)
target_points = np.array([[200, 0], [0, 600], [600, 600], [400, 0]], dtype=np.float32)
mat = cv2.getPerspectiveTransform(source_points, target_points)
perspective_image = cv2.warpPerspective(image, mat, (width, height))
cv2.imwrite("save_pics/perspective_image.png", perspective_image)

Este exemplo aproxima o bordo superior, por isso o resultado é um trapézio estreito em cima e largo em baixo. A margem preta é a parte da tela de saída que fica fora do quadrilátero deformado.
Coloque as pastas ao lado do script

Os caminhos no código são relativos ao test_1.py que executa. A imagem lida é source_pics/test_1.png; o ficheiro escrito é save_pics/perspective_image.png. Com os seus ficheiros, iguale esses nomes ou altere as cadeias de caminho.
Se o imread falhar, image é None e a linha seguinte, image.shape, pára. Antes de executar, confira a ortografia das pastas e o diretório de trabalho atual.
As coordenadas começam em [0, 0] no canto superior esquerdo
source_points = np.array([[0, 0], [0, height], [width, height], [width, 0]], dtype=np.float32)
target_points = np.array([[200, 0], [0, 600], [600, 600], [400, 0]], dtype=np.float32)
Os arrays seguem esta ordem. A origem não está em baixo, como no papel milimétrico.
| Ordem | source_points | target_points |
|---|---|---|
| Superior esquerdo | [0, 0] | [200, 0] |
| Inferior esquerdo | [0, height] | [0, 600] |
| Inferior direito | [width, height] | [600, 600] |
| Superior direito | [width, 0] | [400, 0] |
Este exemplo é 600×600, por isso height e width valem 600. Preenchidos, os arrays ficam assim.
source_points = np.array([[0, 0], [0, 600], [600, 600], [600, 0]], dtype=np.float32)
target_points = np.array([[200, 0], [0, 600], [600, 600], [400, 0]], dtype=np.float32)
![Mapa de quatro esquinas para uma transformação de perspetiva, segundo source_points e target_points. Com origem no canto superior esquerdo, aproximar o bordo superior encurta o topo. [0, 0] e [600, 0] passam a [200, 0] e [400, 0].](https://toolcluster.app/wp-content/uploads/2026/08/opencv-perspective-coords.png)
Os dois pontos de cima aproximam-se, por isso o bordo superior encurta. Os dois de baixo não se mexem. Altere os números e o mesmo emparelhamento das quatro esquinas faz outro trapézio.
Se a ordem de entrada e a de saída não coincidirem, o mapeamento torce. Escreva ambas como sup.-esq. → inf.-esq. → inf.-dir. → sup.-dir. Se tomar pontos num eixo y matemático (cima é positivo), a imagem parece invertida.
O que fazem as restantes linhas
Importações
import numpy as np
import cv2
O NumPy guarda os arrays de pontos. O cv2 do OpenCV lê, escreve e deforma imagens.
Leitura da imagem
image = cv2.imread("source_pics/test_1.png", cv2.IMREAD_COLOR)
cv2.imread carrega test_1.png de source_pics, ao lado do script, a cores, em image.
Tamanho da imagem
height, width, channels = image.shape[:3]
shape dá altura, largura e número de canais. Este exemplo não usa os canais, mas o número de valores que desempacota tem de coincidir, por isso tomam-se os três.
Iguale o número de variáveis ao número de valores que desempacota.
Pontos para a matriz
source_points = np.array([[0, 0], [0, height], [width, height], [width, 0]], dtype=np.float32)
target_points = np.array([[200, 0], [0, 600], [600, 600], [400, 0]], dtype=np.float32)
Quatro pontos XY em cada lado. Nesta imagem de origem [height, width] é [600, 600], por isso os arrays contêm:
source_points = [[ 0. 0.]
[ 0. 600.]
[600. 600.]
[600. 0.]]
target_points = [[200. 0.]
[ 0. 600.]
[600. 600.]
[400. 0.]]
São as coordenadas da figura. A deformação usa esse emparelhamento.
A matriz de transformação
mat = cv2.getPerspectiveTransform(source_points, target_points)
cv2.getPerspectiveTransform calcula a matriz a partir das quatro esquinas de entrada e das quatro de saída. Neste exemplo os valores são:
mat = [[ 3.33333333e-01 -3.33333333e-01 2.00000000e+02]
[ 0.00000000e+00 3.33333333e-01 0.00000000e+00]
[ 0.00000000e+00 -1.11111111e-03 1.00000000e+00]]
O significado de cada entrada não é preciso aqui. Se as esquinas forem para onde pretendia, passe o valor de retorno à linha seguinte.
A deformação
perspective_image = cv2.warpPerspective(image, mat, (width, height))
cv2.warpPerspective recebe a imagem de origem, a matriz e a largura e altura de saída. Este exemplo só encurta um bordo, por isso o tamanho de saída fica (width, height), igual à origem. Escolha o tamanho real a olhar para as coordenadas transformadas.
Gravação
cv2.imwrite("save_pics/perspective_image.png", perspective_image)
cv2.imwrite escreve perspective_image.png em save_pics, ao lado do script. Crie a pasta primeiro; se faltar, a escrita falha.
Se a saída for demasiado pequena, o excesso é recortado
Deixe a saída em 600×600 e tome pontos como estes, e o topo alargado sai da tela.
source_points = np.array([[200, 0], [0, 600], [600, 600], [400, 0]], dtype=np.float32)
target_points = np.array([[0, 0], [0, 600], [600, 600], [600, 0]], dtype=np.float32)
![Recorte quando a tela de saída é demasiado pequena, segundo pontos fora da moldura. Explica que o terceiro argumento de warpPerspective é o tamanho escrito. O [0, 0] e [600, 0] originais ficam fora de 600×600, por isso o topo é cortado.](https://toolcluster.app/wp-content/uploads/2026/08/opencv-perspective-output-crop-en.png)
O terceiro argumento é o tamanho da imagem que escreve, não o tamanho do mundo deformado. Os píxeis cujos pontos ficam fora desse retângulo desaparecem. Para os guardar, aumente primeiro a saída, ou escolha pontos target que caibam todos.
Se um x ou y de target passar a largura ou a altura de saída, e deixar o terceiro argumento no tamanho de origem, recorta. Quando parece cortado, compare primeiro os pontos com o tamanho de saída.
Os mesmos passos servem nas suas próprias fotos
Mantenha a ordem das quatro esquinas e as mesmas duas funções servem em fotos que não são uma grelha. Altere só os números de target e o tamanho de saída para o retângulo que quer.
Mesmo com a câmara de lado, emparelhar quatro esquinas continua a dar um retângulo
O que fez é uma cadeia curta: emparelhar as esquinas de entrada com as de saída, resolver a matriz e escrever na tela de saída. As funções são as mesmas na grelha e numa foto do chão. No local, só mudam a forma de tomar os pontos e o tamanho de saída.
Quando a posição da câmara é fixada pela máquina
Em equipamentos e bancadas de inspeção muitas vezes não consegue pôr a câmara mesmo por cima da peça. Coberturas, tubagens, proteções e o alcance do robot decidem-se primeiro; a lente entra depois. Medir ou ler essa imagem tal qual deixa um trapézio largo à frente e estreito atrás, por isso comprimentos e posições saem enviesados.
Emparelhe as quatro esquinas e recupera o retângulo do plano que quer, sem reconstruir o quadro. A recolha de dados continua possível quando a pose da câmara está limitada pela estrutura. O truque real é pôr os pontos nas quatro esquinas do plano físico, não nas quatro do ecrã. Um canto de peça, um furo de dispositivo, uma junta do chão — qualquer ponto que possa apontar outra vez mais tarde — e os passos deste artigo aplicam-se tal como estão.
Não precisa de escrever a matemática sozinho
O OpenCV é uma implementação para esse trabalho. Dispor os pontos com NumPy e passá-los a getPerspectiveTransform e warpPerspective do OpenCV é escolher uma implementação mantida em vez de escrever a álgebra sozinho. Essa escolha não se limita ao OpenCV. Em torno do processamento de imagem, do cálculo numérico, das comunicações e do controlo, o mesmo trabalho já tem implementações, mantidas e versionadas por outras pessoas. Ler e usar esse código devolve-o mais cedo às limitações da máquina do que derivar a matemática projetiva do zero.
Usar chega. Se encontrar um buraco ou algo pouco claro, pode reportar, corrigir a documentação ou enviar um patch. Quanto devolve varia. Mesmo zero ainda o coloca do lado que mantém esse trabalho, à espera da versão seguinte. Ajuste a ferramenta à sua máquina e, se precisar, devolva um pouco. Esse ciclo gasta melhor o tempo de engenharia do que carregar com uma reescrita interna completa.
Mesmo com uma câmara de lado, quatro esquinas continuam a fazer um retângulo de dados. Depois, o trabalho verdadeiro é medir, ler e guardar o que está nesse retângulo.

Leave a Reply