A detecção de cantos em imagens frequentemente fornece coordenadas inteiras, o que é insuficiente para aplicações que exigem alta precisão, como calibração de câmeras, reconstrução 3D e rastreamento visual. O OpenCV oferece o método cornerSubPix, que refina coordenadas inteiras de cantos para valores reais com precisão subpixel por meio de iteração de mínimos quadrados.
Princípio do Refinamento Subpixel
O algoritmo parte de uma coordenada inicial inteira próxima ao canto real. Considera-se uma janela ao redor do ponto e analisam-se os gradientes locais. Em regiões homogêneas, o gradiente é próximo de zero. Próximo a uma aresta, o gradiente é perpendicular à direção da aresta. A posição refinada é encontrada resolvendo um sistema de equações que minimiza o produto escalar entre os gradientes e os vetores que apontam do centro para os pixels vizinhos. A solução deste sistema fornece a posição do canto em coordenadas reais.
Definindo Critérios de Convergência
A iteração é controlada por um objeto cv::TermCriteria:
cv::TermCriteria criterio(
cv::TermCriteria::EPS + cv::TermCriteria::MAX_ITER,
maxIteracoes,
precisao
);
Os parâmetros são: tipo de critério (contagem máxima de iterações, precisão ou ambos), número máximo de iterações e limiar de precisão desejado.
Função cornerSubPix
cv::cornerSubPix(
imagemCinza,
cantos,
cv::Size(janelaX, janelaY),
cv::Size(zonaMortaX, zonaMortaY),
criterio
);
Os parâmetros são:
- imagemCinza: imagem de entrada em escala de cinza.
- cantos: vetor de
cv::Point2fcom as coordendaas iniciais e refinadas. - winSize: metade do tamanho da janela de busca ao redor de cada canto.
- zeroZone: metade da zona morta central, usada para evitar singularidades na matriz de autocorrelação.
- criteria: critérios de parada do processo ietrativo.
Exemplo Completo
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>
int main()
{
cv::Mat original = cv::imread("imagem.jpg");
if (original.empty())
{
std::cerr << "Erro ao carregar a imagem." << std::endl;
return -1;
}
cv::resize(original, original, cv::Size(640, 480));
cv::Mat cinza;
cv::cvtColor(original, cinza, cv::COLOR_BGR2GRAY);
std::vector<cv::Point2f> candidatos;
cv::goodFeaturesToTrack(
cinza,
candidatos,
80,
0.01,
10.0,
cv::noArray(),
3,
false,
0.04
);
cv::TermCriteria criterio(
cv::TermCriteria::EPS + cv::TermCriteria::MAX_ITER,
30,
0.001
);
cv::cornerSubPix(
cinza,
candidatos,
cv::Size(5, 5),
cv::Size(-1, -1),
criterio
);
cv::Mat saida = original.clone();
for (size_t i = 0; i < candidatos.size(); ++i)
{
cv::circle(saida, candidatos[i], 4, cv::Scalar(0, 0, 255), -1);
std::cout << "Canto " << i
<< ": (" << candidatos[i].x
<< ", " << candidatos[i].y << ")" << std::endl;
}
cv::imshow("Cantos Subpixel", saida);
cv::waitKey(0);
return 0;
}