Inverse perspective mapping em python usando warpPerspective

Este artigo descreve as duas experiências no âmbito da visão artificial efectuadas numa simulação no gazebo com recurso ao método warpPerspective em python

Em python o método warpPerspective permite calcular como uma imagem seria vista de outra perspectiva.

dst = cv2.warpPerspective(src, transform_mat, dsize[, dst[, flags[, borderMode[, borderValue]]]] )

src: imagem original
transform_mat: matriz da transformação
dsize: tamanho da imagem gerada (w,h)
flags: (opcional) métodos de interpolação

A matriz da transformação é o ponto essencial sendo composta pelos elementos descritos na seguinte imagem.

O principal problema na aplicação deste método é a obtenção da matriz da transformação.

Para isso existem dois métodos no python, em ambos os casos são necessárias as coordenadas de 4 pontos, na imagem de origem e na imagem de destino.

  • transform_mat , status = cv2.findHomography(pts_src, pts_dst)
  • transform_mat = cv2.getPerspectiveTransform(pts_src, pts_dst)

# pts_src: coordenadas de 4 pontos na imagem original
# pts_dst: coordenadas desses 4 pontos na imagem transformada

No caso desta abordagem  para obter a vista superior (bird eye view) pelo método IPM, usaremos apenas o método getPerspectiveTransform.

Para descobrir as novas coordenadas dos quatro pontos foram colocadas em prática duas experiências no Gazebo.

Nestas experiências foi usado um programa de manipulação de perspectiva de modo a facilitar o calculo dos pontos. No entanto, o processo pode ser efectuado sem a ele se recorrer.

Experiência sem manter proporções

Foi montando o seguinte ambiente no gazebo. Um objecto rectangular e plano, de espessura desprezível, foi colocado à frente de uma viatura equipada com uma camera, de modo a ocupar todo o campo de imagem longitudinal.


imagem 1

Note-se que a quadricula tem 25 cm de lado, e que o centro da viatura e o centro da zona cinzenta estão sobre o mesmo eixo. A zona cinzenta tem 50 cm de largura. A imagem da camera existente da viatura, visualizada com o image view, é a seguinte.


imagem 2

Visualização com o programa de transformação, onde se constata que para os parâmetros exibidos a imagem é igual.


imagem 3

Visualização com o mesmo programa alterando apenas o parâmetro correspondente ao pitch


imagem 4

Com base na imagem 2 foram seleccionados os pontos dos 4 vertices do trapézio cinzento. Que têm as seguintes coordenadas, no sentido dos ponteiros do relógio, iniciando no topo superior esquerdo:

[200,0], [478,0], [200,479], [478,479]

Com base na imagem 4 foram seleccionados, os 4 pontos de correspondentes na imagem transformada.

[288, 0], [391, 0], [113, 479], [563, 479]

Estes pontos foram inseridos no seguinte programa (programa1), que quando executado, e conforme esperado, produziu uma imagem semelhante à da imagem 4.

import cv2
import numpy as np

img = cv2.imread('image2.jpg')


# define points coordinates
dst_pts = np.array([[200,0], [478,0], [200,479], [478,479]], dtype=np.float32)
src_pts = np.array([[288, 0], [391, 0], [113, 479], [563, 479]], dtype=np.float32)

# compute IPM matrix and apply it
transform_matrix = cv2.getPerspectiveTransform(src_pts, dst_pts)
ipm = cv2.warpPerspective(img, transform_matrix, img.shape[:2][::-1], flags=cv2.INTER_CUBIC)

# display image
cv2.imshow('ipm', ipm)
cv2.waitKey()

De seguida, aplicamos exactamente a mesma transformação à seguinte imagem.


image 5

O resultado da transformação obtida é o seguinte:


imagem 6

Nesta  imagem (6) verifica-se a compressão da distância que acontece na altura da imagem. Isto acontece porque, para o mesmo campo angular de visão, o deslocamento ao longo dos 45º de arco de circunferência, de A1 para A2, provoca a aproximação do ponto a a b, até serem coincidentes.

Esta situação está ilustrada na imagem seguinte.


imagem 7

Experiência mantendo as proporções

Foi montando o seguinte ambiente no gazebo. Um objecto quadrado e plano, com 1 m de aresta e de espessura desprezível, foi colocado à frente de uma viatura equipada com uma camera.


imagem 8

A aresta mais próxima do quadrado, dista 1 metro da projecção vertical da camera sobre a estrada.

A quadricula tem 25 cm de lado, e que o centro da viatura e o centro da zona cinzenta estão sobre o mesmo eixo.

Neste caso a imagem da camera existente da viatura, visualizada com o image view, é a seguinte.


imagem 9

Desta vez omitimos a exibição da imagem no programa de transformação, já que se sabe ser igual a imagem 9.

Quando se aplica um pitch de 37º (à pouco foi usado 36) o aspecto da imagem transformada fica o seguinte.


imagem 10

Neste programa de transformação não acontece o efeito de compressão, o efeito da compressão resultou da escolha, na experiência anterior de transformar os pontos apenas quanto à componente transversal da coordenadas, mantendo as coordenadas longitudinais iguais.

No entanto, o quadrado não aparece todo visível, pelo que se torna necessário ajustar a distancia do quadrado ao carro de modo a que se consiga ver todo.


image 11


image 12


image 13

Após procurar ajustar a distância do quadrado à viatura, ficando conforme visível nas imagens 11, 12 e 13, conclui que sem mudar outros parâmetros era impossível visualizar o quadrado de 1 metro na sua totalidade, pelo que reduzi o tamanho para 50 cm.


image 14


image 15

image 16

As imagens 14, 15 e 16, exibem, respectivamente, o ambiente no gazebo, a perspectiva da camera, e a perspectiva transformada.

Neste caso o quadrado é integralmente visível, no entanto, quando medido ao pormenor ele continua a estar comprimido na altura, já que o seu tamanho em pixels é de 279 x 259.

Aplicando ao programa1 os seguintes pontos:

source: [218, 191], [462, 191], [530, 374], [151, 374]
destination: [202, 145], [480, 145], [480, 404], [202, 404]

verificou-se que para a mesma imagem (imagem 15) correspondia uma imagem semelhante à da imagem 16.

Como a discrepância na dimensão dos pixels do quadrado é de 20 pixels, foi efectuada uma alteração na coordenada y dos dois primeiros pontos de destino. Globalmente ficou assim.

destination: [202, 125], [480, 125], [480, 404], [202, 404]

O resultado melhorou bastante, e o quadrado ficou com 279 x 279 px, pelo que também se pode  concluir que nesta vista existem 558 pixels por metro.

Aplicando esta nova transformação a imagem 5, obteve-se então a seguinte imagem que está menos comprimida.

Conclusão, para obter empiricamente as coordenadas dos pontos dos quatro pontos na imagem de origem e respectivas as coordenadas dos pontos na imagem de destino, deve-se usar um quadrado de dimensões conhecidas que esteja inteiramente visível.

Referencias

https://theailearner.com/tag/cv2-warpperspective/

Geometry of Image Formation

https://www.edmundoptics.com/knowledge-center/application-notes/imaging/understanding-focal-length-and-field-of-view/

 

Tentativa de estimar distancias e pixels por metro no gazebo

De modo a tentar estimar o numero de pixels por metro e a distancia de objectos no gazebo, efectuei a experiência que descrevo neste artigo

Foi colocado no pavimento um quadrado com um metro de largo, que está visível à frente do veiculo.

O veiculo tem uma camera instalada a cerca de 547 milímetros do solo com cerca de 55,5 graus de inclinação face ao pavimento, com uma distancia focal de 563 (px).

A imagem que a camera publica, visível abaixo, permite ver toda a sua largura, que é 1 metro. Esta imagem está em perspectiva, o que não impede que se aplique a equação acima. Para a aplicar só falta determinar a largura, daquela face do quadrado em pixels.

As coordenadas dos pixels nos vertices dessa face são: 148, 246 e 618, 246, pelo que, a distancia que está aquela face, a 1 metro correspondem 417 pixels.

Sendo assim aplicando a equação para calcular a distância aproximada com base numa imagem: Distancia = (distancia_focal x largura ) / pixels

Distancia = (563 x 1) / 417 = 1.35 metros

Considerando a imagem do gazebo, é perceptível que o centro do carro dista 1.5 metros da face do quadrado que estamos a considerar. Tendo em conta que o carro tem cerca de 26 cm de comprimento, e que a câmara está recuada 5 cm da frente da viatura, podemos concluir que distancia real será de 1.42m, ou seja próxima do valor calculado.

No entanto é de salientar que

Consideramos agora a transformação para birds eye view exibida na imagem abaixo.

Neste caso a largura da face do rectângulo, que tem 1 metro, é de 517 pixels.

No entanto se aplicarmos a mesma equação temos: Distancia = 1.08 metros

Este valor tem uma grande discrepância face a distancia real, e não permitiu esclarecer as duvidas que tenho sobre o efeito da altura real da camera, e o facto do tamanho da imagem transformada pelo método ipm ser directamente proporcional a distancia, ou seja quando a distancia aumenta, o tamanho também aumenta, é como se a camera fica-se mais próxima.

 

 

 

gazebo calibration package

Este é o relato da experiência de utilização do pacote calibration_gazebo que está disponível no seguinte repositório:

https://github.com/oKermorgant/calibration_gazebo

Na primeira execução reparei que este pacote depende do pacote slide_publisher. Esta dependência pode ser satisfeita com o seguinte comando

sudo apt install ros-noetic-slider-publisher

Também está disponível no seguinte repositório

https://github.com/oKermorgant/slider_publisher

O erro que denuncia a dependência é o seguinte

Resource not found: slider_publisher

O comando que faz o spawn do quadro de xadrez para a calibração da camera é o seguinte:

roslaunch calibration_gazebo landmark.launch

Este comando pressupõe que o ambiente simulado já esteja em execução, pelo que no caso do projecto do automec será necessário executar por exemplo o seguinte comando:

roslaunch simulation_bringup traxxas_free_drive.launch

No entanto na experiência efectuada o quadro de xadrez não apareceu.

Segundo a descrição do pacote o programa tentará identificar o link da camera (o primeiro a ter a palavra camera) e a fazer o spawn do quadro de xadrez á sua frente, caso não encontre o quadro aparece numa posição fixa.

O pacote contem os launch files que permite lançar dois ambientes simulados para testar o seu funcionamento.

  • roslaunch calibration_gazebo perspective.launch
  • roslaunch calibration_gazebo fisheye.launch

A execução do primeiro, seguido do comando para lançar o quadro, permitiu verificar que o quadro aparece em frente da camera.

Numa tentativa de identificar o que estava a acontecer, foi efectuada uma correcção do launch file de modo a poder observar as mensagens geradas pelo node.

No caso do do ambiente exemplo pré configurado no pacote 0 resultado na consola foi  o seguinte:

process[calibration_gazebo/landmark_bridge-2]: started with pid [404599]
False
False
Found camera link at camera::camera_link
[INFO] [1632494079.717182, 0.000000]: Loading model XML from file /home/inaciose/catkin_ws/src/calibration_gazebo/sdf/landmark.sdf
[INFO] [1632494079.735229, 0.000000]: Waiting for service /gazebo/spawn_sdf_model
[INFO] [1632494079.743581, 16.158000]: Calling service /gazebo/spawn_sdf_model
[INFO] [1632494080.021423, 16.313000]: Spawn status: SpawnModel: Successfully spawned entity

Ficando o processo de calibração disponível conforme a imagem acima

No caso do ambiente simulado do projecto do automec o quadro de xadrez nunca aparece no ecrã e surgem as seguintes mensagens de erro.

process[calibration_gazebo/landmark_bridge-2]: started with pid [415518]
False
(...)
False
Could not find camera link, spawning landmark at (0,0,0.5)
[INFO] [1632494966.819862, 0.000000]: Loading model XML from file /home/inaciose/catkin_ws/src/calibration_gazebo/sdf/landmark.sdf
[INFO] [1632494966.824028, 24.350000]: Waiting for service /gazebo/spawn_sdf_model

Como após algumas pesquisas e verificações não consegui colocar o paccote a funcionar no ambiente de simulação usado no projecto do automec, passei para outra abordagem.

A nova abordagem foi usar apenas ficheiro sdf que define o quadro de xadrez, integrando-no pacote que define o ambiente de simulação.