SLMaster icon indicating copy to clipboard operation
SLMaster copied to clipboard

调用关键函数findConcentricRingGrid进行标定时无法找到特征点

Open MechineVID opened this issue 1 year ago • 15 comments

博主,您好!首先非常感谢您的分享,让我受益良多。有个疑问想请教,在调用关键函数findConcentricRingGrid进行标定时无法找到"角点",相机所拍摄的图像如下: 1 图像大小为:1920*1080;双圆环的直径依次为10、15、20、25,特征点的距离为40,行特征点个数为6,列特征点个数为4。我的目的是想要得到特征点的坐标! 调用关键函数findConcentricRingGrid的部分代码如下: 3 运行后的结果如下: 2 请问是拍摄图像角度的问题吗?如果是,那正确角度应该是怎么样的?如果不是,那是哪出问题了?

MechineVID avatar Apr 11 '24 13:04 MechineVID

请使用markdown格式贴出代码,仅通过图片不好引用你的代码。

如果需要,可以上传一张图片,我尝试一下。

Practice3DVision avatar Apr 11 '24 13:04 Practice3DVision

这是调用关键函数findConcentricRingGrid 得到特征点的坐标的main函数,是在concentricRingCalibrator.cpp中写的main函数。 当使用您提供的双圆环图像,并修改相应参数,可以得到特征点的坐标。使用的环境是VS2017+opencv-4.3.0 1

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;
}

MechineVID avatar Apr 12 '24 01:04 MechineVID

请给出原图片,您上传的这种截图图片无法通过调试定位问题。

image

Practice3DVision avatar Apr 12 '24 04:04 Practice3DVision

1 2 3 4 5

MechineVID avatar Apr 12 '24 04:04 MechineVID

原因在于圆所占面积过大,而自适应阈值的领域过小,导致二值化失败。

请调整自适应阈值的领域findConcentricRingGrid.cpp

41改为51后效果如下:

image

软件将考虑将该邻域作为可修改参数,供使用者在GUI进行调整,预计将在下一版本中加入该功能。

Practice3DVision avatar Apr 12 '24 05:04 Practice3DVision

刚想问您是不是这个原因,我将41改为601时,第一、二幅图能达到我想要的目的,但哪怕我将41改为1001也对第三、四、五幅图没影响(达不到我的目的)。那是不是意味着我要将圆环的半径调小?

cv::adaptiveThreshold(threshodFindCircle, threshodFindCircle, 255,
                      cv::ADAPTIVE_THRESH_MEAN_C, cv::THRESH_BINARY, 41, 0);

MechineVID avatar Apr 12 '24 05:04 MechineVID

改邻域大小到合适的值就行,如果图像噪声比较小,可以用注释掉的大津法进行二值化

Practice3DVision avatar Apr 12 '24 10:04 Practice3DVision

好的,非常感谢!那请问concentricRingCalibrator.cpp中的关键函数findConcentricRingGrid是只能用于同心双圆环吗?如果我想用您提供的代码用于棋盘格与圆点,那我该找哪些函数呢?

MechineVID avatar Apr 12 '24 11:04 MechineVID

可以查看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的所有接口并根据需要调用即可。

Practice3DVision avatar Apr 12 '24 11:04 Practice3DVision

试了您之前提供的方法“改邻域大小的值,如果图像噪声比较小,可以用注释掉的大津法进行二值化”,但第三至第五幅图还是没法找到特征点。是还有别的原因吗?相机的光轴不用垂直与双圆环标定区域吧?

MechineVID avatar Apr 15 '24 04:04 MechineVID

已修复该问题,现在双圆环标定算法会在失败情况下尝试使用CALIB_CB_CLUSTERING标志位以搜索大视野和畸变较高情况下的特征点。

源文件

isFind = cv::findCirclesGrid(
            threshodFindCircle, cv::Size(patternSize.width, patternSize.height),
            pointsOfCell, cv::CALIB_CB_SYMMETRIC_GRID | cv::CALIB_CB_CLUSTERING, detector);

image

Practice3DVision avatar Apr 15 '24 05:04 Practice3DVision

根据您之前的修改,确实好多了,还有几点想向您请教: 1、您说的特征点距离是指同心圆圆心的距离吗? 16 2、关于特征点的行列个数的设置问题: 我设置的行:5,列:6 这这幅图像所绘制的角点是正确的吧?

chessboard_2

但这幅图像不应该这么绘制吧?

chessboard_3

如果我设置成行:6,列:5 则会出现以下情况

2 3 3、还是有些达不到目的,例如这两幅图像

1 10

MechineVID avatar Apr 16 '24 02:04 MechineVID

1、是同心圆圆心的距离 2、GUI中包含特征点横轴/纵轴翻转功能,请自行查看 3、这两幅图像经测试是OpenCV方法cv::findCirclesGrid()搜索失败,图片无法使用,请尽量使用1/3幅面标定板图像

Practice3DVision avatar Apr 17 '24 06:04 Practice3DVision

您说的“使用1/3幅面标定板图像”是指尽量使标定板图像占整个拍摄图像的1/3吗?

MechineVID avatar Apr 17 '24 07:04 MechineVID

对的

Practice3DVision avatar Apr 17 '24 10:04 Practice3DVision