Objetivo:
O objetivo do trabalho é fazer um programa que recebe um vídeo (de um arquivo ou da webcam) e detecte os cantos usando a biblioteca OpenCV, calcule os parâmetros da câmera utilizando os métodos de Tsai e Zhang e desenhe num canvas OpenGL a imagem do tabuleiro e três linhas simbolizando os eixos x, y e z.
Implementação:
Tsai
O programa consiste das seguintes etapas:
1) Captura do vídeo em um canvas OpenGL.
Para fazer a captura do vídeo utilizamos a função cvCaptureFromAVI e cvQueryFrame da biblioteca OpenCV.
2) Localização dos cantos.
A localização dos corners é feita utilizando-se a função cvFindChessboardCorners da biblioteca OpenCV.
3) Calibração da câmera.
Para encontrar os parâmetros intrínsecos e extrínsecos utilizamos as funções tsaiBuildNewData, tsaiCalibration2D, tsaiCalibration2D_Tr, tsaiCameraPosition encontradas na implementação do tsai2D fornecida no site da matéria.
4) Integração com o OpenGL.
A integração com o OpenGL é feita utilizando-se as funções tsaiOGLModelViewMatrix
e tsaiOGLProjectionMatrix encontradas na implementação do tsai2D fornecida no
site da matéria. Essas funções preenchem, respectivamente, a matriz de modelView e a matriz de Projeção do OpenGL.
5) Correção da inversão.
A inversão do eixo é causada pela mudança na ordem dos cantos identificados pelo OpenCV. É possível identificar quando essa mudança de ordem ocorre através da inspeção do primeiro elemento identificado em dois frames consecutivos. Se a distância é próxima da diagonal do tabuleiro, então é necessário inverter a ordem dos cantos encontrados pelo OpenCV.
O programa consiste das seguintes etapas:
1) Captura do vídeo em um canvas OpenGL e localização dos cantos.
Essas etapas são feitas da mesma maneira que na implementação do Tsai.
2) Cálculo dos parâmetros intrínsecos da câmera.
Para calcular os parâmetros intrínsecos da câmera é utilizada a função cvCalibrateCamera2 do OpenCV como mostrada abaixo:
onde,
object_points = matriz Nx3 contendo os pontos, de todas as vistas consideradas, no espaço do mundo.
image_points = matriz Nx2 contendo os pontos, de todas as vistas consideradas, no espaço da imagem.
point_counts = matriz Nx1 contendo o número de pontos em cada uma das vistas consideradas.
Para que os parâmetros intrínsecos sejam calculados corretamente é necessário utilizar vistas diferentes do tabuleiro, para garantir que a calibração seja feita corretamente. Para isso utilizamos as primeiras 50 imagens que detectamos todos os corners necessários. O valor de 50 frames foi obtido experimentalmente. Foram utilizados vários frames por que sabemos que no início do vídeo os frames são praticamente iguais, no entanto esse número poderia ser bem menor caso garantíssemos que os frames são diferentes. Isso é necessário pois devemos ter uma boa caracterização do espaço, o que não acontece quando usamos vistas muito parecidas ou iguais.
3) Cálculo dos parâmetros extrínsecos da câmera.
Após achar os parâmetros intrínsecos da câmera usa-se a função cvFindExtrinsicCameraParams2 para calcular os parâmetros extrínsecos da câmera (matriz de rotação e translação). Os parâmetros extrínsecos são diferentes para cada um dos frames . Desta forma, a função cvFindExtrinsicCameraParams2 recebe como parâmetros os pontos no espaço do mundo, os pontos no espaço da imagem e o número de corners do frame. Além disso recebe a matriz de intrínsecos e os coeficientes de distorção da câmera obtidos com a função cvCalibrateCamera2. Essa função retorna então, o vetor de rotação e o vetor de translação. O vetor de rotação deve ser transformado em uma matriz utilizando-se a função cvRodrigues2.
cvFindExtrinsicCameraParams2( curr_object_pts, curr_image_pts, intrinsic, distortion, rot, trans );
4) Integração com o OpenGL.
A integração com o OpenGL tem 3 passos importantes:
- Preenchimento da Projection
- Preenchimento da ModelView
- Desenho dos eixos e do teapot
Para que objetos virtuais possam ser desenhados utilizando o OpenGL é necessário que a matriz de Projeção e a matriz de ModelView sejam preenchidas corretamente.

=======================================
Matriz de ModelView
=======================================
void setModelView(CvMat *rot2)
{
if(rot2)
{
mv[0] = cvmGet(rot2, 0, 0);
mv[1] = cvmGet(rot2, 1, 0);
mv[2] = cvmGet(rot2, 2, 0);
mv[3] = 0;
mv[4] = cvmGet(rot2, 0, 1);
mv[5] = cvmGet(rot2, 1, 1);
mv[6] = cvmGet(rot2, 2, 1);
mv[7] = 0;
mv[8] = -cvmGet(rot2, 0, 2);
mv[9] = -cvmGet(rot2, 1, 2);
mv[10] = -cvmGet(rot2, 2, 2);
mv[11] = 0;
mv[12] = 0;
mv[13] = 0;
mv[14] = 0;
mv[15] = 1;
}
}
=======================================
Matriz de Projeção
void setProjection(CvMat *intrinsic)
{
double fx,fy,cx,cy;
if(intrinsic)
{
fx = intrinsic->data.fl[0];
fy = intrinsic->data.fl[4];
cx = intrinsic->data.fl[2];
cy = intrinsic->data.fl[5];
np = 0.1;
fp = 100.0;
pr[0] = 2 * fx / w;
pr[1] = 0;
pr[2] = 0;
pr[3] = 0;
pr[4] = 0;
pr[5] = 2 * fy / h;
pr[6] = 0;
pr[7] = 0;
pr[8] = (2 * cx / w) - 1;
pr[9] = (2 * cy / h) - 1;
pr[10] = (fp+np)/(fp-np);
pr[11] = 1;
pr[12] = 0;
pr[13] = 0;
pr[14] = -2*fp*np/(fp-np);
pr[15] = 0;
}
}
Para desenhar objetos virtuais como o bule usado no trabalho, basta carregar a modelview e a projection obtidas acima.
Resultados:

Figura1: Calibração de câmera usando Zhang

Figura 2: Calibração de câmera usando Tsai
============================================================================
============================================================================