Como posso estimar a pose da câmera com correspondências 3d para 2d (usando opencv)

Olá, meu objetivo é desenvolverfuncionalidade de rastreamento de cabeça a ser usada no cockpit de uma aeronave (simulador), a fim de fornecer AR para apoiar pilotos civis para pousar e voar com más condições visuais.

Minha abordagem é detectar pontos característicos (nos LEDs escuros do simulador) dos quais conheço as coordenadas 3D e calcular a posição estimada (da câmera de cabeça) estimada [R | t] (rotação concatinada com a tradução).

O problema que tenho é que a pose estimada parece estar sempre errada e uma projeção dos meus pontos 3D (que eu também costumava estimar a pose)não se sobrepõe aos pontos de imagem 2D (ou não é visível).

Minhas perguntas são:

Como posso estimar a pose da câmera com um determinado conjunto de correspondências de pontos 2D para 3D.

Por que não funciona como eu o tento e onde podem estar as fontes de erro?

Qual a precisão das medições (dos pontos 3D e 2D e da matriz da câmera) para que a solução teórica funcione em um ambiente da vida real?

A abordagem funcionará para pontos coplanares (eixo x, y alterado) em teoria?

O hardware que eu uso é o Epson BT-200.

Na aeronave, defini uma ordenada fixa para a qual espero traduções e rotações relativas como resultado do meu programa. O programa detecta as coordenadas da imagem dos LEDs (únicos) e as combina com as coordenadas 3D correspondentes. Com uma matriz de câmera, obtive o código android de amostra open-cv (https://github.com/Itseez/opencv/tree/master/samples/android/camera-calibration) Eu tento estimar a pose usando resolvePnP.

A matriz e a distorção da minha câmera variam ligeiramente. Aqui estão alguns valores que recebi do procedimento. Assegurei-me de que a distância do círculo do meu padrão de círculo impresso fosse a mesma anotada no código-fonte (medido em metros).

Aqui estão alguns exemplos e como eu crio o OpenCV Mat dele.

//  protected final double[] DISTORTION_MATRIX_VALUES = new double[]{
//          /*This matrix should have 5 values*/
//          0.04569467373955304,
//          0.1402980385369059,
//          0,
//          0,
//          -0.2982135315849994
//  };

//  protected final double[] DISTORTION_MATRIX_VALUES = new double[]{
//          /*This matrix should have 5 values*/
//          0.08245931646421553,
//          -0.9893762277047577,
//          0,
//          0,
//          3.23553287438898
//  };

//  protected final double[] DISTORTION_MATRIX_VALUES = new double[]{
//          /*This matrix should have 5 values*/
//          0.07444480392067945,
//          -0.7817175834131075,
//          0,
//          0,
//          2.65433773093283
//  };
    protected final double[] DISTORTION_MATRIX_VALUES = new double[]{
            /*This matrix should have 5 values*/
            0.08909941096327206,
            -0.9537960457721699,
            0,
            0,
            3.449728790843752
    };

    protected final double[][] CAMERA_MATRIX_VALUES = new double[][]{
            /*This matrix should have 3x3 values*/
//          {748.6595405553738, 0, 319.5},
//          {0, 748.6595405553738, 239.5},
//          {0, 0, 1}
//          {698.1744297982436, 0, 320},
//          {0, 698.1744297982436, 240},
//          {0, 0, 1}
//          {707.1226937511951, 0, 319.5},
//          {0, 707.1226937511951, 239.5},
//          {0, 0, 1}
            {702.1458656346429, 0, 319.5},
            {0, 702.1458656346429, 239.5},
            {0, 0, 1}
    };

    private void initDestortionMatrix(){
        distortionMatrix = new MatOfDouble();
        distortionMatrix.fromArray(DISTORTION_MATRIX_VALUES);
    }

    private void initCameraMatrix(){
        cameraMatrix = new Mat(new Size(3,3), CvType.CV_64F);
        for(int i=0;i<CAMERA_MATRIX_VALUES.length,; i++){
            cameraMatrix.put(i, 0, CAMERA_MATRIX_VALUES[i]);
        }
    }

Para estimar a pose da câmera, eu uso o discoverPnP (eresolvePnPRansac), conforme descrito em vários locais (1,2,3,4) O resultado do resolvePnP eu uso como entrada para a projeção (Calib3d.projectPoints) O inverso do resultado concatinado [R | t] eu uso como pose estimada.

Como meus resultados no ambiente produtivo eram muito ruins, criei um ambiente de teste. Nesse ambiente, coloco a câmera (que é por causa da sua forma 3D (é um copo), levemente girada para baixo na borda de uma mesa.Esta borda que eu uso como ordenada do sistema de coordenadas do mundo.Pesquisei como o cv aberto sistema de coordenadas pode ser orientado e encontrado respostas diferentes (umastackoverflow e um em uma conversa oficial no youtube sobre opencv). De qualquer forma, testei se consegui acertar o sistema de coordenadas por pontos 3D de projeção (descritos nesse sistema de coordenadas) em uma imagem e verifiquei se a forma do mundo fornecida permanece constante.

Então eu vim com z apontando para a frente, y para baixo e x para a direita.

Para me aproximar da minha solução, estimei a pose no meu ambiente de teste. A saída vetor de conversão e a saída do anjo euler referem-se ao inverso de [R | t]. Os anjos de Euler podem não ser exibidos corretos (eles podem ser trocados ou errados, se levarmos em conta a ordem) porque eu o computo com as equações conveticionais (presumo que se refira ao sistema de coordenadas do avião), usando um sistema de coordenadas de cv aberto. (O cálculo acontece na classe Pose que irei anexar). De qualquer forma, mesmo o vetor de tradução (do inverso) parecia errado (no meu teste simples).

Em um teste com essa imagem, obtive um rolo (que pode ter inclinação nas coordenadas do avião) de 30 ° e uma translação acima de 50cm. Isso parecia ser mais razoável. Então, assumi que, como meus pontos são coplanares, posso obter resultados ambíguos. Então eu realizei outro teste com um ponto que mudou no eixo Z. Mas com este teste, mesmo a projeção falhou.

Para o resolvPnP, tentei todas as diferentes bandeiras de algoritmos de resolução e parâmetros diferentes para oalgoritmo ransac.

Talvez você possa, de alguma forma, me ajudar a encontrar meu erro ou me mostrando um bom caminho para resolver meu problema inicial. Vou anexar também meu código-fonte de depuração com muitas instruções println e as imagens de depuração.Este código contém minhas medições pontuais.

Agradecemos sua ajuda antecipadamente.

ClasseMain.java: ClassePose.java: 0.png

1.png

EDIT 22.03.2015: Finalmente, consegui encontrar os erros que cometi.

Modifiquei um objeto Mat em um loop for, porque o OpenCV funciona muito com chamadas por referência e não tomei cuidado o suficiente aqui. Portanto, o tvec e o rvec para a reprojeção não estavam certos.Um dos meus pontos no ambiente de teste (nas coordenadas da imagem) foi marcado incorretamente devido a uma confusão na direção do eixo.

Então, minha abordagem em geral estava certa. Não estou recebendo pelo menos (frequentemente) reprojeções válidas no meu conjunto de dados de teste.

Infelizmente, os algoritmos OpenCV PnP: "ITERATIVE, P3P, EPNP" retornam vários resultados e, mesmo usando um palpite intrínseco muito impreciso, mas próximo, os resultados às vezes são corretos. oAlgoritmo P3P deve fornecer 3 soluções, mas o OpenCV fornece apenas uma. O EPNP deve retornar bons resultados, mas comEPNP O OpenCV retorna os piores resultados, avaliados a partir da minha observação humana.

O problema agora é como filtrar os valores imprecisos ou garantir que a função OpenCV retorne valores válidos. (Talvez eu deva modificar o código nativo para receber 3 soluções para PnP).

oimagens compactadas aqui (37MB), mostre meus resultados atuais (com o ITERATIVE PnP-Solver), com um palpite intrínseco de rotação zero e 75 cm para cima. A impressão possui um eixo x para a frente, eixo y à esquerda e z-down e ângulos de rolagem, inclinação e guinada com resistência à corrosão.

questionAnswers(1)

yourAnswerToTheQuestion