Explorando o OpenCV no StereoPi: Mapa de Profundidade do Vídeo



Hoje, queremos compartilhar uma série de exemplos sobre os alunos do Python para OpenCV no Raspberry Pi, a saber, a placa StereoPi de duas câmaras. O código finalizado (mais a imagem Raspbian) ajudará você a executar todas as etapas, começando com a captura de uma imagem e terminando com a obtenção de um mapa de profundidade do vídeo capturado.

Introdutório


Devo enfatizar imediatamente que esses exemplos são para uma imersão confortável no tópico, e não para uma solução de produção. Se você é um usuário avançado do OpenCV e está lidando com framboesas, sabe que, para um trabalho completo, é aconselhável codificar em uma mordida e até usar uma GPU de framboesa. No final do artigo, abordarei os “gargalos” da solução python e o desempenho geral com mais detalhes.

Com o que estamos trabalhando


Temos uma configuração como o ferro:



Placa StereoPi a bordo do Raspberry Pi Compute Module 3+. As duas câmeras mais simples estão conectadas à versão V1 do Raspberry Pi (no sensor ov5647).

O que está instalado:

  • Raspbian Stretch (kernel 4.14.98-v7 +)
  • Python 3.5.3
  • OpenCV 3.4.4 (pré-compilado, 'pip' do Python Wheels)
  • Picamera 1.13
  • StereoVision lib 1.0.3 (https://github.com/erget/StereoVision)

O processo de instalação de todo o software está além do escopo deste artigo, e apenas sugerimos o download da imagem final do Raspbian (links para o github no final do artigo).

Etapa 1: tirar uma foto


Para fazer isso, use o script 1_test.py

Abra o console, vá da pasta inicial para a pasta com exemplos:

cd stereopi-tutorial 

Execute o script:

 python 1_test.py 

Após o início, uma visualização da nossa imagem estéreo é exibida na tela. O processo pode ser interrompido pressionando o botão Q. Isso salvará a última imagem capturada, que será usada em um dos seguintes scripts para configurar o mapa de profundidade.

Esse script permite garantir que todo o hardware esteja funcionando corretamente, além de obter a primeira imagem para uso futuro.

Aqui está a aparência do primeiro script:


Etapa 2: coletar fotos para calibração


Se falamos de um cavalo esférico no vácuo, para obter um mapa de profundidade de boa qualidade, precisamos ter duas câmeras absolutamente idênticas, cujos eixos verticais e ópticos são perfeitamente paralelos e os eixos horizontais coincidem. Mas, no mundo real, todas as câmeras são um pouco diferentes e não é possível organizá-las perfeitamente. Portanto, um truque de calibração de software foi inventado. Usando duas câmeras do mundo real, um grande número de fotos de um objeto conhecido anteriormente é tirado (temos uma foto com um tabuleiro de xadrez) e, em seguida, um algoritmo especial calcula todas as “imperfeições” e tenta corrigi-las para que fiquem próximas do ideal.

Este script faz a primeira etapa do trabalho, ou seja, ajuda a tirar uma série de fotos para calibração.

Antes de cada foto, o script inicia uma contagem regressiva de 5 segundos. Desta vez, como regra, é suficiente para mover a placa para uma nova posição, para garantir que em ambas as câmeras não rasteje pelas bordas e fixe sua posição (para que não haja desfoque na foto). Por padrão, o tamanho da série é definido para 30 fotos.

Lançamento:

 python 2_chess_cycle.py 

Processo:


Como resultado, temos uma série de fotos na pasta / scenes.

Cortamos fotos em pares


O terceiro script 3_pairs_cut.py corta as fotos tiradas nas fotos "esquerda" e "direita" e as salva na pasta / pairs. De fato, poderíamos excluir esse script e fazer o corte em tempo real, mas é muito útil em outras experiências. Por exemplo, você pode salvar fatias de séries diferentes, usar seus scripts para trabalhar com esses pares ou até mesmo tirar fotos tiradas em outras câmeras estéreo como pares.

Além disso, antes de cortar cada imagem, o script exibe sua imagem, o que geralmente permite ver fotos com falha antes da próxima etapa de calibração e simplesmente excluí-las.

Execute o script:

 python 3_pairs_cut.py 

Vídeo curto:


Na imagem final, há um conjunto de fotografias e pares de cortes que usamos em nossos experimentos.

Calibração


O script 4_calibration.py desenha todos os pares com os tabuleiros de xadrez e calcula as correções necessárias para corrigir as figuras. No roteiro, foi feita a rejeição automática de fotografias nas quais um tabuleiro de xadrez não foi encontrado, para que, no caso de fotografias malsucedidas, o trabalho não pare. Após o upload de todos os 30 pares de fotos, o cálculo é iniciado. Demora cerca de um minuto e meio conosco. Após a conclusão, o script pega um dos pares estéreo e, com base nos parâmetros de calibração calculados, “os corrige”, exibindo uma imagem retificada na tela. Neste ponto, você pode avaliar a qualidade da calibração.

Executar por comando:

 python 4_calibration.py 

Script de calibração em funcionamento:


Configuração do mapa de profundidade


O script 5_dm_tune.py carrega a foto tirada pelo primeiro script e os resultados da calibração. Em seguida, é exibida uma interface que permite alterar as configurações do mapa de profundidade e ver o que muda. Dica: antes de definir os parâmetros, pegue uma moldura na qual você terá objetos simultaneamente a diferentes distâncias: perto (30-40 centímetros), a uma distância média (metros ou dois) e à distância. Isso permitirá que você escolha os parâmetros nos quais objetos próximos serão vermelhos e objetos distantes serão azul escuro.

A imagem contém um arquivo com nossas configurações de mapa de profundidade. Você pode carregar nossas configurações em um script simplesmente clicando no botão "Carregar configurações"

Lançamos:

 python 5_dm_tune.py 

Veja como é o processo de instalação:


Mapa de profundidade em tempo real


O último script 6_dm_video.py cria um mapa de profundidade a partir do vídeo usando os resultados de scripts anteriores (calibração e configuração do mapa de profundidade).

Lançamento:

 python 6_dm_video.py 

Na verdade o resultado:


Esperamos que nossos scripts sejam úteis em suas experiências!

Por precaução, acrescentarei que todos os scripts têm processamento de pressionamento de tecla e você pode interromper o trabalho pressionando o botão Q. Se você parar "aproximadamente", por exemplo, Ctrl + C, o processo de interação do Python com a câmera poderá ser interrompido e será necessária uma reinicialização de framboesa.

Para avançados


  • O primeiro script do processo exibe o tempo médio entre as capturas de quadros e, após a conclusão, o FPS médio. Esta é uma ferramenta simples e conveniente para selecionar parâmetros de imagem nos quais o python ainda não está "sufocando". Com isso, captamos 1280x480 a 20 FPS, nos quais o vídeo é renderizado sem demora.
  • Você pode perceber que capturamos um par estéreo com resolução de 1280x480 e o escalamos para 640x240.

    Uma pergunta razoável: por que tudo isso e por que não pegar imediatamente a miniatura e não carregar nosso python reduzindo-o?

    Resposta: com captura direta em resoluções muito baixas, ainda existem problemas no núcleo da framboesa (a imagem é quebrada). Portanto, tomamos uma resolução maior e reduzimos a imagem. Aqui, usamos um pequeno truque: a imagem não é dimensionada com python, mas com a ajuda da GPU, para que não haja carga no núcleo do braço.
  • Por que capturar vídeo no formato BGRA, não BGR?
    Usamos os recursos da GPU para reduzir o tamanho da imagem e o nativo do módulo de redimensionamento é o formato BGRA. Se usarmos o BGR em vez do BGRA, teremos duas desvantagens. O primeiro é um pouco menor que o FPS final (em nossos testes - 20%). O segundo é o desgaste constante no console "PiCameraAlfaStripping: usando stripping alfa para converter para o formato não alfa; você pode encontrar o formato alfa equivalente mais rápido ”. A pesquisa no Google levou à seção de documentação do Picamera, que descreve esse truque.
  • Onde está o PiRGBArray?

    É como a classe Picamera nativa para trabalhar com a câmera, mas aqui ela não é usada. Já se descobriu que, em nossos testes, trabalhar com uma matriz numpy "feita à mão" é muito mais rápido (cerca de uma vez e meia) do que usar o PiRGBArray. Isso não significa que o PiRGBArray seja ruim, provavelmente essas são nossas mãos tortas.
  • Qual a carga da porcentagem no cálculo do mapa de profundidade?
    Vamos responder com uma figura:



    Vemos que dos 4 núcleos do processador, de fato, apenas um é carregado, ou seja, 70%. E isso apesar do fato de trabalharmos com uma GUI e estarmos produzindo imagens e mapas de profundidade para o usuário. Isso significa que há uma boa margem de desempenho, e o ajuste fino do OpenCV com o OpenMP e outros itens em C, bem como um modo de "combate" sem uma GUI, podem dar resultados muito interessantes.
  • Qual é o mapa de profundidade máxima do FPS obtido com essas configurações?

    O máximo atingido por nós foi de 17 FPS, ao capturar 20 quadros por segundo da câmera. Os mais "responsivos" em termos de parâmetros de velocidade nas configurações do mapa de profundidade são MinDisparity e NumOfDisparities. Isso é lógico, pois eles determinam o número de "etapas" executadas no algoritmo pela janela de pesquisa para comparar quadros. O segundo mais responsivo é o preFilterCap, que afeta, em particular, a "suavidade" do mapa de profundidade.
  • E a temperatura do processador?

    No Módulo de computação 3+ Lite (uma nova série, com uma “tampa” de ferro no processo), mostra aproximadamente os seguintes resultados:

  • Como usar GPU?

    No mínimo, pode ser usado para andistorização e retificação de imagens em tempo real, porque há exemplos ( aqui no WebGL ), Python Pi3d , bem como o projeto Processing ( exemplos para framboesas ).

    Há outro desenvolvimento interessante de Koichi Nakamura, chamado py-videocore . Em nossa correspondência com ele, ele expressou a ideia de que, para acelerar o StereoBM, você pode usar seu tipo de núcleo e OpenCV com o suporte da Cuda . Em geral, para otimização - uma vantagem intocada, como se costuma dizer.

Obrigado por sua atenção, e aqui está o link prometido para a fonte .

Source: https://habr.com/ru/post/pt446872/


All Articles