调用关键函数findConcentricRingGrid进行标定时无法找到特征点
博主,您好!首先非常感谢您的分享,让我受益良多。有个疑问想请教,在调用关键函数findConcentricRingGrid进行标定时无法找到"角点",相机所拍摄的图像如下:
图像大小为:1920*1080;双圆环的直径依次为10、15、20、25,特征点的距离为40,行特征点个数为6,列特征点个数为4。我的目的是想要得到特征点的坐标!
调用关键函数findConcentricRingGrid的部分代码如下:
运行后的结果如下:
请问是拍摄图像角度的问题吗?如果是,那正确角度应该是怎么样的?如果不是,那是哪出问题了?
请使用markdown格式贴出代码,仅通过图片不好引用你的代码。
如果需要,可以上传一张图片,我尝试一下。
这是调用关键函数findConcentricRingGrid 得到特征点的坐标的main函数,是在concentricRingCalibrator.cpp中写的main函数。
当使用您提供的双圆环图像,并修改相应参数,可以得到特征点的坐标。使用的环境是VS2017+opencv-4.3.0
int main()
{
// 创建一个 ConcentricRingCalibrator 对象
slmaster::calibration::ConcentricRingCalibrator calibrator;
// 定义模式大小和圆环半径向量
cv::Size imageshape(1920, 1080);
cv::Size patternSize(6, 4); //
std::vector<float> radiusVector = {5.0f, 7.5f, 10.0f, 12.5f}; // 圆环的半径
int squareSize = 40; // 两圆心(特征点)的距离
std::vector<cv::Point3f> worldPoints; //存放世界角点
//std::vector<std::vector<cv::Point2f>> imagePoints; //存放图像角点
std::vector<cv::Point2f> imagePoints;
cv::Mat cameraMatrix, distCoeffs, rvecs, tvecs;
std::vector<cv::Point3f> tempPointSet;
std::vector<cv::Point3f> realpoint;
// 创建保存中心点坐标的 CSV 文件
std::ofstream outputFile("center_points.csv");
if (!outputFile.is_open())
{
std::cerr << "无法创建 CSV 文件!" << std::endl;
return -1;
}
// 写入 CSV 文件的列标题
outputFile << "图像文件,中心点 X 坐标,中心点 Y 坐标" << std::endl;
std::ofstream outFile("world_points.csv");
if (!outFile.is_open())
{
std::cerr << "无法创建 CSV 文件!" << std::endl;
return -1;
}
// 写入 CSV 文件的列标题
outFile << "X坐标,Y坐标,Z坐标" << std::endl;
// 目录路径,包含多个图像文件
std::string directory = "C:/Desktop/3/";
int numImages = 5; // 文件中包含的图像个数
// 遍历目录中的每个图像
for (int i = 1; i <= numImages; ++i)
{
// 构造当前图像的文件路径
std::string imagePath = directory + std::to_string(i) + ".bmp";
// 用于将图像文件写入到csv文件
std::string imgPath = std::to_string(i) + ".bmp";
// 加载输入图像
cv::Mat inputImage = cv::imread(imagePath, cv::IMREAD_GRAYSCALE);
if (inputImage.empty())
{
std::cerr << "无法打开或找到图像:" << imagePath << std::endl;
continue; // 如果加载失败,则跳过处理下一个图像
}
//行数
for (int a = 0; a < patternSize.height; a++)
{
//列数
for (int j = 0; j < patternSize.width; j++)
{
worldPoints.emplace_back(a * squareSize, j * squareSize, 0);
outFile << a * squareSize << "," << j * squareSize << "," << 0 <<std::endl;
}
}
// 调用 findConcentricRingGrid() 函数来检测同心圆网格
std::vector<cv::Point2f> centerPoints;
bool success = calibrator.findConcentricRingGrid(inputImage, patternSize, radiusVector, centerPoints);
if (!success)
{
std::cerr << "无法在第" << i << "幅图像中找到同心圆网格" << std::endl << "\n";
continue; // 如果未能找到网格,则跳过处理下一个图像
}
// 输出找到的中心点
std::cout << "在第 " << i << " 幅图像中找到了 " << centerPoints.size() << " 个中心点" << std::endl;
for (const auto& point : centerPoints)
{
std::cout << "中心点坐标:(" << point.x << ", " << point.y << ")" << std::endl;
// 将中心点坐标写入 CSV 文件
outputFile << imgPath << "," << point.x << "," << point.y << std::endl;
}
// 在图像上绘制中心点
drawCenterPoints(inputImage, centerPoints);
// 显示图像并等待一段时间
cv::namedWindow("Image");
cv::imshow("Image", inputImage);
cv::waitKey(2000);
cv::destroyWindow("Image");
}
// 关闭 CSV 文件
outputFile.close();
std::cout << "所有中心点坐标已保存到 center_points.csv 文件中。" << std::endl;
return 0;
}
请给出原图片,您上传的这种截图图片无法通过调试定位问题。
原因在于圆所占面积过大,而自适应阈值的领域过小,导致二值化失败。
请调整自适应阈值的领域findConcentricRingGrid.cpp。
将41改为51后效果如下:
软件将考虑将该邻域作为可修改参数,供使用者在GUI进行调整,预计将在下一版本中加入该功能。
刚想问您是不是这个原因,我将41改为601时,第一、二幅图能达到我想要的目的,但哪怕我将41改为1001也对第三、四、五幅图没影响(达不到我的目的)。那是不是意味着我要将圆环的半径调小?
cv::adaptiveThreshold(threshodFindCircle, threshodFindCircle, 255,
cv::ADAPTIVE_THRESH_MEAN_C, cv::THRESH_BINARY, 41, 0);
改邻域大小到合适的值就行,如果图像噪声比较小,可以用注释掉的大津法进行二值化
好的,非常感谢!那请问concentricRingCalibrator.cpp中的关键函数findConcentricRingGrid是只能用于同心双圆环吗?如果我想用您提供的代码用于棋盘格与圆点,那我该找哪些函数呢?
可以查看GUI中关于标定子模块的使用示例:getCalibrator
Calibrator * CalibrateEngine::getCalibrator(const AppType::TargetType targetType) {
Calibrator *calibrator = nullptr;
if (targetType == AppType::TargetType::ChessBoard) {//棋盘格
calibrator = new ChessBoardCalibrator();
} else if (targetType == AppType::TargetType::Blob) {//圆点
calibrator = new CircleGridCalibrator();
} else if (targetType == AppType::TargetType::ConcentricCircle) {//双圆环
calibrator = new ConcentricRingCalibrator();
calibrator->setRadius(
std::vector<float>{concentricRingParams_.innerCircleInnerRadius_,
concentricRingParams_.innerCircleExterRadius_,
concentricRingParams_.exterCircleInnerRadius_,
concentricRingParams_.exterCircleExterRadius_});
}
return calibrator;
}
Calibrator为纯虚基类,其他标定类型皆继承该类。因此查看calibrator.h的所有接口并根据需要调用即可。
试了您之前提供的方法“改邻域大小的值,如果图像噪声比较小,可以用注释掉的大津法进行二值化”,但第三至第五幅图还是没法找到特征点。是还有别的原因吗?相机的光轴不用垂直与双圆环标定区域吧?
已修复该问题,现在双圆环标定算法会在失败情况下尝试使用CALIB_CB_CLUSTERING标志位以搜索大视野和畸变较高情况下的特征点。
见源文件:
isFind = cv::findCirclesGrid(
threshodFindCircle, cv::Size(patternSize.width, patternSize.height),
pointsOfCell, cv::CALIB_CB_SYMMETRIC_GRID | cv::CALIB_CB_CLUSTERING, detector);
根据您之前的修改,确实好多了,还有几点想向您请教:
1、您说的特征点距离是指同心圆圆心的距离吗?
2、关于特征点的行列个数的设置问题:
我设置的行:5,列:6
这这幅图像所绘制的角点是正确的吧?
但这幅图像不应该这么绘制吧?
如果我设置成行:6,列:5 则会出现以下情况
3、还是有些达不到目的,例如这两幅图像
1、是同心圆圆心的距离
2、GUI中包含特征点横轴/纵轴翻转功能,请自行查看
3、这两幅图像经测试是OpenCV方法cv::findCirclesGrid()搜索失败,图片无法使用,请尽量使用1/3幅面标定板图像
您说的“使用1/3幅面标定板图像”是指尽量使标定板图像占整个拍摄图像的1/3吗?
对的