Una vez localizadas las esquinas interiores (organizadas por filas y columnas, pero sin saber el orden), lo primero que haremos es ordenarlas para que siempre sigan un orden determinado: ordenadas horizontalmente de izquierda a derecha, de arriba a abajo.
Para poder ordenarlas, necesitamos saber antes que orden están siguiendo. El método utilizado para encontrar el orden sique el siguiente esquema:
La numeración corresponde a cada caso particular:
0 –> Ordenado verticalmente, de derecha a izquierda, de abajo a arriba
1 –> Ordenado verticalmente, de derecha a izquierda, de arriba a abajo
2 –> Ordenado verticalmente, de izquierda a derecha, de abajo a arriba 3 –> Ordenado verticalmente, de izquierda a derecha, de arriba a abajo
4 –> Ordenado horizontalmente, de derecha a izquierda, de abajo a arriba
5 –> Ordenado horizontalmente, de derecha a izquierda, de arriba a abajo
6 –> Ordenado horizontalmente, de izquierda a derecha, de abajo a arriba
7 –> Ordenado horizontalmente, de izquierda a derecha, de arriba a abajo
Para que se cumplan los ordenados verticalmente, comprobamos a que punto es igual la esquina nº [alto – 1], y para los ordenados horizontalmente, la esquina nº [ancho –1].
0 => esquina[alto – 1] = Esquina Superior Derecha
1 => esquina[alto – 1] = Esquina Inferior Derecha
2 => esquina[alto – 1] = Esquina Superior Izquierda
3 => esquina[alto – 1] = Esquina Inferior Izquierda
4 => esquina[ancho – 1] = Esquina Inferior Izquierda
5 => esquina[ancho – 1] = Esquina Superior Izquierda
6 => esquina[ancho – 1] = Esquina Inferior Derecha
7 => esquina[ancho – 1] = Esquina Superior Derecha
///////////////////////////////////////////////////////////////////////////////// check_order//// Check the order (horizontal/vertical, left/right, top/bottom) of the points//// Parameters:// CvPoint2D32f* points -> Pointer to the corners// int pointsCount-> Number of corners// CvSize cornerSize -> size of the corners [i.e. CvSize(7,7)]// CvSize size -> size of the image in px [i.e. CvSize(480,480)]//// Return:// 0) Ordered vertical, from rigth to left, from bottom to up// 1) Ordered vertical, from rigth to left, from up to bottom// 2) Ordered vertical, from left to rigth, from bottom to up// 3) Ordered vertical, from left to rigth, from up to bottom// 4) Ordered horizontal, from rigth to left, from bottom to up// 5) Ordered horizontal, from rigth to left, from up to bottom// 6) Ordered horizontal, from left to rigth, from bottom to up// 7) Ordered horizontal, from left to rigth, from up to bottom/////////////////////////////////////////////////////////////////////////////////int check_order(CvPoint2D32f *points, int pointsCount, CvSize cornerSize, CvSize size)
{CvPoint2D32f top_left = closest_to(points, pointsCount, cvPoint2D32f(0,0));
CvPoint2D32f top_right = closest_to(points, pointsCount, cvPoint2D32f(size.width, 0));
CvPoint2D32f bottom_right = closest_to(points, pointsCount, cvPoint2D32f(size.width, size.height));
CvPoint2D32f bottom_left = closest_to(points, pointsCount, cvPoint2D32f(0, size.height));
int width = cornerSize.width; int height = cornerSize.height; if(top_left.x == points[0].x && top_left.y == points[0].y) if(top_right.x == points[width - 1].x && top_right.y == points[width - 1].y) return 7;else if(bottom_left.x == points[height - 1].x && bottom_left.y == points[height - 1].y)
return 3; elsethrow "Error checking order: points start at top_left";
if(top_right.x == points[0].x && top_right.y == points[0].y) if(top_left.x == points[width - 1].x && top_left.y == points[width - 1].y) return 5;else if(bottom_right.x == points[height - 1].x && bottom_right.y == points[height - 1].y)
return 1; elsethrow "Error checking order: points start at top_right";
if(bottom_left.x == points[0].x && bottom_left.y == points[0].y) if(bottom_right.x == points[width - 1].x && bottom_right.y == points[width - 1].y) return 6;else if(top_left.x == points[height - 1].x && top_left.y == points[height - 1].y)
return 2; elsethrow "Error checking order: points start at bottom_left";
if(bottom_right.x == points[0].x && bottom_right.y == points[0].y) if(bottom_left.x == points[width - 1].x && bottom_left.y == points[width - 1].y) return 4;else if(top_right.x == points[height - 1].x && top_right.y == points[height - 1].y)
return 0; elsethrow "Error checking order: points start at bottom_right";
throw "Error checking order: no point start";
}
ORDENACIÓN DE LAS ESQUINAS
Una vez obtenido el orden, rellenamos una matriz de 2 dimensiones (filas x columnas) que va a representar las esquinas del tablero, ordenado horizontalmente, de izquierda a derecha, de arriba a abajo.
[0][0] –> Esquina Superior Izquierda
[0][ancho - 1] –> Esquina Superior Derecha
[alto][0] –> Esquina Inferior Izquierda
[alto][ancho - 1] –> Esquina Inferior Derecha
///////////////////////////////////////////////////////////////////////////////// ordenate//// Finds all the corners of the checkerboard with the internal corners//// Parameters:// CvPoint2D32f *points -> Pointer to the corners// int cornerCount -> Number of corners// CvSize cornerSize -> Size of the corners [i.e. CvSize(7,7)]// CvSize size -> Size of the image [i.e. CvSize(480,480)]/////////////////////////////////////////////////////////////////////////////////void ordenate(CvPoint2D32f *points, int cornerCount, CvSize cornerSize, CvSize size)
{ int state = check_order(points, cornerCount, cornerSize, size); int NUM_V_CORNERS = cornerSize.width; int NUM_H_CORNERS = cornerSize.height; CvPoint2D32f** temp = (CvPoint2D32f**)malloc(NUM_H_CORNERS * sizeof(CvPoint2D32f*));for(int i = 0;i < NUM_H_CORNERS; i++)
temp[i] = (CvPoint2D32f*) malloc(NUM_V_CORNERS * sizeof(CvPoint2D32f)); //CvPoint2D32f** temp = new CvPoint2D32f* [NUM_H_CORNERS]; //for(int i = 0;i < NUM_H_CORNERS; i++) // *(temp+i)=new CvPoint2D32f[NUM_V_CORNERS]; switch(state) {case 7: // Ordered horizontal, from left to rigth, from up to bottom
for(int i = 0; i < cornerCount; i++)
temp[i / NUM_H_CORNERS][i % NUM_H_CORNERS] = points[i];
break;case 6: // Ordered horizontal, from left to rigth, from bottom to up
break;case 5: // Ordered horizontal, from rigth to left, from up to bottom
break;case 4: // Ordered horizontal, from rigth to left, from bottom to up
break;case 3: // Ordered vertical, from left to rigth, from up to bottom
break;case 2: // Ordered vertical, from left to rigth, from bottom to up
for(int i = 0; i < cornerCount; i++)
temp[(NUM_V_CORNERS - 1) - (i % NUM_V_CORNERS)][i / NUM_V_CORNERS] = points[i];
break;case 1: // Ordered vertical, from rigth to left, from up to bottom
for(int i = 0; i < cornerCount; i++)
temp[i % NUM_V_CORNERS][(NUM_H_CORNERS - 1) - i / NUM_V_CORNERS] = points[i];
break;case 0: // Ordered vertical, from rigth to left, from bottom to up
break;}
for(int i = 0; i < NUM_V_CORNERS; i++)
for(int j = 0; j < NUM_H_CORNERS; j++)
{points[i * NUM_H_CORNERS + j] = temp[i][j];
//printf("Corner %d en (%d, %d)\n", i * NUM_H_CORNERS + j, cvRound(points[i * NUM_H_CORNERS + j].x), cvRound(points[i * NUM_H_CORNERS + j].y));}
}
ALGORITMO DE RECONSTRUCCIÓN
Ahora que disponemos de las casillas ordenadas siempre igual (horizontalmente, de izquierda a derecha, de arriba a abajo), ya simplemente nos queda aplicar el algoritmo de reconstrucción: estimar la posición de las esquinas “no interiores”. Es importantes resaltar que ahora estamos trabajando con la proyección sobre el PH, por lo que tenemos la imagen sin distorsión. Esto nos simplifica muchísimo la complejidad del algoritmo de reconstrucción:
Para la estimación de puntos en una imagen real (la de la izquierda), vemos que para estimar habría que calcular el punto de fuga, y hacer una relación de proporcionalidad para estimar los puntos.
Para la estimación de puntos en la imagen proyectada sobre el PH, simplemente tenemos que sumarle una distancia constante.
Esta vez, en principio, el código no lo vamos a adjuntar. Este algoritmo es un poco confuso a la hora de tomar los índices de las matrices (imaginarios como sería para una imagen real o para una sobre el PH con los puntos desordenados… y ahora para una real con los puntos desordenados!!!)
FORMANDO LAS CASILLAS
Ahora que tenemos todas las esquinas (tanto interiores como exteriores) y ordenadas, ya simplemente tenemos que formar rectángulos con ellas.
El proceso seguido es bastante intuitivo: creamos un array con todas las casillas, y por cada una escogemos las 4 esquinas correspondientes.
El tema de los índices no es tan confuso como en el algoritmo de reconstrucción, pero aún así hay que tener cuidado.
/////////////////////////////////////////////////////////////////////////////////// Finds all the corners of the checkerboard with the internal corners//// Parameters:// CvPoint2D32f* corners -> Pointer to the corners// CvSize patternSize -> Board Size in squares (width, height)//// Return:// CvSquare* -> Pointer to the squares of the board/////////////////////////////////////////////////////////////////////////////////CvSquare* findSquaresFromCorners(CvPoint2D32f* corners, CvSize patternSize)
{ int NUM_H_SQUARES = patternSize.width; int NUM_V_SQUARE = patternSize.height; CvSquare* squares = new CvSquare[NUM_H_SQUARES * NUM_V_SQUARE];for(int i = 0; i < NUM_V_SQUARE; i++)
{for(int j = 0; j < NUM_H_SQUARES; j++)
{ double bottom_left_x = corners[(i+1) * (NUM_H_SQUARES + 1) + j].x; double bottom_left_y = corners[(i+1) * (NUM_H_SQUARES + 1) + j].y;CvPoint2D32f bottom_left = cvPoint2D32f(bottom_left_x, bottom_left_y);
double bottom_right_x = corners[(i+1) * (NUM_H_SQUARES + 1) + j + 1].x; double bottom_right_y = corners[(i+1) * (NUM_H_SQUARES + 1) + j + 1].y;CvPoint2D32f bottom_right = cvPoint2D32f(bottom_right_x, bottom_right_y);
double top_left_x = corners[ i * (NUM_H_SQUARES + 1) + j].x; double top_left_y = corners[ i * (NUM_H_SQUARES + 1) + j].y;CvPoint2D32f top_left = cvPoint2D32f(top_left_x, top_left_y);
double top_right_x = corners[ i * (NUM_H_SQUARES + 1) + j + 1].x; double top_right_y = corners[ i * (NUM_H_SQUARES + 1) + j + 1].y;CvPoint2D32f top_right = cvPoint2D32f(top_right_x, top_right_y);
CvPoint2D32f* points = new CvPoint2D32f[4];points[0] = bottom_left;
points[1] = bottom_right;
points[2] = top_right;
points[3] = top_left;
squares[i * NUM_H_SQUARES + j].points = points;
printf("Square %d (%d,%d) - (%d,%d) - (%d,%d) - (%d,%d)\n", i * NUM_H_SQUARES + j, cvRound(bottom_left.x), cvRound(bottom_left.y), cvRound(bottom_right.x), cvRound(bottom_right.y), cvRound(top_right.x), cvRound(top_right.y), cvRound(top_left.x), cvRound(top_left.y));}
}
return squares;}
Y el resultado lo podemos comprobar en la siguiente imagen
No hay comentarios:
Publicar un comentario