version 1.0.0 : base version released
This commit is contained in:
jerryzeng 2026-08-03 08:42:53 +08:00
parent 6244873e6d
commit 1127ec7b25
7 changed files with 4307 additions and 4139 deletions

View File

@ -1,7 +1,7 @@
 
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16 # Visual Studio Version 17
VisualStudioVersion = 16.0.33027.164 VisualStudioVersion = 17.14.37411.7 d17.14
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PeakSearch", "CamAlgo\CamAlgo.vcxproj", "{2B6D42C0-7146-47D2-B6AA-9BC125C4CF93}" Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PeakSearch", "CamAlgo\CamAlgo.vcxproj", "{2B6D42C0-7146-47D2-B6AA-9BC125C4CF93}"
EndProject EndProject
@ -18,6 +18,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "camAlgoSW_test", "camAlgoSW
EndProject EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "camCalib", "camCalib\camCalib.vcxproj", "{3C3A4670-25E6-4E17-9723-2897316D2437}" Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "camCalib", "camCalib\camCalib.vcxproj", "{3C3A4670-25E6-4E17-9723-2897316D2437}"
EndProject EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "simDataGenerator", "simDataGenerator\simDataGenerator.vcxproj", "{D6675A67-4BD9-43DF-90E9-89EE7784C2A8}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64 Debug|x64 = Debug|x64
@ -74,6 +76,14 @@ Global
{3C3A4670-25E6-4E17-9723-2897316D2437}.Release|x64.Build.0 = Release|x64 {3C3A4670-25E6-4E17-9723-2897316D2437}.Release|x64.Build.0 = Release|x64
{3C3A4670-25E6-4E17-9723-2897316D2437}.Release|x86.ActiveCfg = Release|Win32 {3C3A4670-25E6-4E17-9723-2897316D2437}.Release|x86.ActiveCfg = Release|Win32
{3C3A4670-25E6-4E17-9723-2897316D2437}.Release|x86.Build.0 = Release|Win32 {3C3A4670-25E6-4E17-9723-2897316D2437}.Release|x86.Build.0 = Release|Win32
{D6675A67-4BD9-43DF-90E9-89EE7784C2A8}.Debug|x64.ActiveCfg = Debug|x64
{D6675A67-4BD9-43DF-90E9-89EE7784C2A8}.Debug|x64.Build.0 = Debug|x64
{D6675A67-4BD9-43DF-90E9-89EE7784C2A8}.Debug|x86.ActiveCfg = Debug|Win32
{D6675A67-4BD9-43DF-90E9-89EE7784C2A8}.Debug|x86.Build.0 = Debug|Win32
{D6675A67-4BD9-43DF-90E9-89EE7784C2A8}.Release|x64.ActiveCfg = Release|x64
{D6675A67-4BD9-43DF-90E9-89EE7784C2A8}.Release|x64.Build.0 = Release|x64
{D6675A67-4BD9-43DF-90E9-89EE7784C2A8}.Release|x86.ActiveCfg = Release|Win32
{D6675A67-4BD9-43DF-90E9-89EE7784C2A8}.Release|x86.Build.0 = Release|Win32
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@ -10,6 +10,13 @@
#define _DO_CAMERA_CALIB 1 #define _DO_CAMERA_CALIB 1
//version 1.0.0 : base version released
std::string m_strVersion = "camCalib 1.0.0";
const char* wd_camCalibVersion(void)
{
return m_strVersion.c_str();
}
void sg_outputCalibK(const char* fileName, cv::Mat& fitMap) void sg_outputCalibK(const char* fileName, cv::Mat& fitMap)
{ {
std::ofstream sw(fileName); std::ofstream sw(fileName);
@ -157,6 +164,67 @@ void initForwardRectMap(const cv::Mat& K, const cv::Mat& D, const cv::Mat& R,
return; return;
} }
void lineFitting(std::vector< cv::Point2f>& inliers, double* _k, double* _b)
{
//最小二乘拟合直线参数
double xx_sum = 0;
double x_sum = 0;
double y_sum = 0;
double xy_sum = 0;
int num = 0;
for (int i = 0; i < inliers.size(); i++)
{
x_sum += inliers[i].x; //x的累加和
y_sum += inliers[i].y; //y的累加和
xx_sum += inliers[i].x * inliers[i].x; //x的平方累加和
xy_sum += inliers[i].x * inliers[i].y; //xy的累加和
num++;
}
*_k = (num * xy_sum - x_sum * y_sum) / (num * xx_sum - x_sum * x_sum); //根据公式求解k
*_b = (-x_sum * xy_sum + xx_sum * y_sum) / (num * xx_sum - x_sum * x_sum);//根据公式求解b
}
//拟合成通用直线方程ax+by+c=0包括垂直
void lineFitting_abc(std::vector< cv::Point2f>& inliers, double* _a, double* _b, double* _c)
{
//判断是否为垂直
int dataSize = (int)inliers.size();
if (dataSize < 2)
return;
double deltaX = abs(inliers[0].x - inliers[dataSize - 1].x);
double deltaY = abs(inliers[0].y - inliers[dataSize - 1].y);
std::vector< cv::Point2f> fittingData;
if (deltaX < deltaY)
{
//x=ky+b 拟合
for (int i = 0; i < dataSize; i++)
{
cv::Point2f a_fitPt;
a_fitPt.x = inliers[i].y;
a_fitPt.y = inliers[i].x;
fittingData.push_back(a_fitPt);
}
double k = 0, b = 0;
lineFitting(fittingData, &k, &b);
//ax+by+c
*_a = 1.0;
*_b = -k;
*_c = -b;
}
else
{
//y = kx+b拟合
double k = 0, b = 0;
lineFitting(inliers, &k, &b);
//ax+by+c
*_a = k;
*_b = -1;
*_c = b;
}
return;
}
#define MONO_CALIB_TEST_GROUP 5 #define MONO_CALIB_TEST_GROUP 5
int MonoCamLaserCalib() int MonoCamLaserCalib()
{ {
@ -493,7 +561,40 @@ int MonoCamLaserCalib()
// 创建棋盘格区域的掩码 // 创建棋盘格区域的掩码
cv::Mat chessMask; cv::Mat chessMask;
if (CALIB_CHARUCO == calibType) if (CALIB_CHARUCO == calibType)
chessMask = cv::Mat::ones(img.size(), CV_8UC1); {
chessMask = cv::Mat::zeros(img.size(), CV_8UC1);
float roi_L = corners[0].x;
float roi_R = roi_L;
float roi_T = corners[0].y;
float roi_B = roi_T;
for (int i = 1; i < (int)corners.size(); i++)
{
roi_L = roi_L > corners[i].x ? corners[i].x : roi_L;
roi_R = roi_R < corners[i].x ? corners[i].x : roi_R;
roi_T = roi_T > corners[i].y ? corners[i].y : roi_T;
roi_B = roi_B < corners[i].y ? corners[i].y : roi_B;
}
int left = (int)roi_L;
if (left < 0)
left = 0;
int right = (int)roi_R;
if (right >= chessMask.cols)
right = chessMask.cols - 1;
int top = (int)roi_T - 100;
if (top < 0)
top = 0;
int btm = (int)roi_B + 100;
if (btm >= chessMask.rows)
btm = chessMask.rows - 1;
for (int y = top; y <= btm; y++)
{
for (int x = left; x <= right; x++)
{
chessMask.at<uchar>(y, x) = 0xff;
}
}
}
else else
{ {
chessMask = cv::Mat::zeros(img.size(), CV_8UC1); chessMask = cv::Mat::zeros(img.size(), CV_8UC1);
@ -576,7 +677,7 @@ int MonoCamLaserCalib()
cv::Mat laserImg; cv::Mat laserImg;
cv::bitwise_and(laserImg_unMask, laserImg_unMask, laserImg, chessMask); cv::bitwise_and(laserImg_unMask, laserImg_unMask, laserImg, chessMask);
#if 1 #if 1
sprintf_s(filename, "%slaser_mask_%d.png", calibDataPath[grp], index); sprintf_s(filename, "%sresult/laser_mask_%d.png", calibDataPath[grp], index);
cv::imwrite(filename, laserImg); cv::imwrite(filename, laserImg);
cv::Mat laserCalibImg; cv::Mat laserCalibImg;
@ -587,13 +688,19 @@ int MonoCamLaserCalib()
cv::INTER_LINEAR, cv::INTER_LINEAR,
cv::BORDER_CONSTANT, cv::BORDER_CONSTANT,
cv::Scalar(0, 0, 0)); cv::Scalar(0, 0, 0));
cv::Size laserImgSize = laserCalibImg.size(); sprintf_s(filename, "%sresult/laser_%d_calib.bmp", calibDataPath[grp], index);
laserImgSize.width = laserImgSize.width * 5;
cv::resize(laserCalibImg, laserCalibImg, laserImgSize, 0, 0, cv::INTER_NEAREST);
sprintf_s(filename, "%slaser_%03d_calib.bmp", calibDataPath[grp], index);
cv::imwrite(filename, laserCalibImg); cv::imwrite(filename, laserCalibImg);
cv::Mat enlargeLaserImg = laserCalibImg.clone();
cv::Size laserImgSize = enlargeLaserImg.size();
laserImgSize.width = laserImgSize.width * 5;
cv::resize(enlargeLaserImg, enlargeLaserImg, laserImgSize, 0, 0, cv::INTER_NEAREST);
sprintf_s(filename, "%sresult/laser_%d_enlarge_calib.bmp", calibDataPath[grp], index);
cv::imwrite(filename, enlargeLaserImg);
#endif #endif
std::vector<cv::Point2f> pts2d = detectLaserLine(laserImg); bool multiPeakFlag = false; //每行一个Peak
std::vector<cv::Point2f> pts2d = detectLaserLine(laserImg, multiPeakFlag);
#if 0
//显示亚像素点 //显示亚像素点
cv::Mat enlargeImg; cv::Mat enlargeImg;
if (laserImg.channels() == 1) if (laserImg.channels() == 1)
@ -603,38 +710,76 @@ int MonoCamLaserCalib()
cv::Size objSize = laserImg.size(); cv::Size objSize = laserImg.size();
objSize.width = objSize.width * 5; objSize.width = objSize.width * 5;
cv::resize(enlargeImg, enlargeImg, objSize, 0, 0, cv::INTER_NEAREST); cv::resize(enlargeImg, enlargeImg, objSize, 0, 0, cv::INTER_NEAREST);
#endif
if (pts2d.size() > 0) // 去畸变
if (pts2d.size() == 0)
continue;
std::vector<cv::Point2f> undistortedPoints;
cv::undistortPoints(pts2d, undistortedPoints,
K, D, cv::noArray(), newCamMatrix);
#if 1
//直线拟合
double _a, _b, _c;
lineFitting_abc(undistortedPoints, &_a, &_b, &_c);
std::vector<cv::Point2f> fittingPts2d;
for (int i = 0; i < (int)undistortedPoints.size(); i++)
{ {
sprintf_s(filename, "%slaser_rotate_enlarge_%03d_subpixData.txt", calibDataPath[grp], index); cv::Point2f a_pt;
saveSubpixData(filename, pts2d); a_pt.y = undistortedPoints[i].y;
a_pt.x = -(a_pt.y * _b + _c) / _a;
fittingPts2d.push_back(a_pt);
} }
for (int i = 0, i_max = (int)pts2d.size(); i < i_max; i++) #else
std::vector<cv::Point2f> fittingPts2d;
fittingPts2d.insert(fittingPts2d.end(), undistortedPoints.begin(), undistortedPoints.end());
#endif
if (fittingPts2d.size() > 0)
{ {
cv::Point2f a_subPix = pts2d[i]; sprintf_s(filename, "%sresult/laser_enlarge_%d_subpixData.txt", calibDataPath[grp], index);
saveSubpixData(filename, fittingPts2d);
}
cv::Mat cloneEnlageImg = enlargeLaserImg.clone();
for (int i = 0, i_max = (int)undistortedPoints.size(); i < i_max; i++)
{
cv::Point2f a_subPix = undistortedPoints[i];
int row = (int)(a_subPix.y + 0.5); int row = (int)(a_subPix.y + 0.5);
int col = (int)(a_subPix.x * 5 + 0.5); int col = (int)(a_subPix.x * 5 + 0.5);
enlargeImg.at<cv::Vec3b>(row, col)[0] = 0; cloneEnlageImg.at<cv::Vec3b>(row, col)[0] = 0;
enlargeImg.at<cv::Vec3b>(row, col)[1] = 0; cloneEnlageImg.at<cv::Vec3b>(row, col)[1] = 0;
enlargeImg.at<cv::Vec3b>(row, col)[2] = 255; cloneEnlageImg.at<cv::Vec3b>(row, col)[2] = 255;
} }
sprintf_s(filename, "%slaser_rotate_enlarge_%03d_subpix.png", calibDataPath[grp], index); sprintf_s(filename, "%sresult/laser_enlarge_%d_raw_subpix.png", calibDataPath[grp], index);
cv::imwrite(filename, enlargeImg); cv::imwrite(filename, cloneEnlageImg);
std::vector<cv::Point3f> pts3d = project2DTo3D(pts2d, pe, K, D);
#if 1
//保存3D点
#endif for (int i = 0, i_max = (int)fittingPts2d.size(); i < i_max; i++)
{
cv::Point2f a_subPix = fittingPts2d[i];
int row = (int)(a_subPix.y + 0.5);
int col = (int)(a_subPix.x * 5 + 0.5);
enlargeLaserImg.at<cv::Vec3b>(row, col)[0] = 0;
enlargeLaserImg.at<cv::Vec3b>(row, col)[1] = 0;
enlargeLaserImg.at<cv::Vec3b>(row, col)[2] = 255;
}
sprintf_s(filename, "%sresult/laser_enlarge_%d_subpix.png", calibDataPath[grp], index);
cv::imwrite(filename, enlargeLaserImg);
std::vector<cv::Point3f> pts3d = project2DTo3D(fittingPts2d, pe, K, D);
all_pts3d.insert(all_pts3d.end(), pts3d.begin(), pts3d.end()); all_pts3d.insert(all_pts3d.end(), pts3d.begin(), pts3d.end());
printf(" %sImage_%d_Laser.png ... done\n", calibDataPath[grp], index);
} }
cv::Vec4f pe = fitPlaneToPoints(all_pts3d); cv::Vec4f laser_pe = fitPlaneToPoints(all_pts3d);
std::cout << "pe: " << pe << std::endl; std::cout << "pe: " << laser_pe << std::endl;
//output K and D //output K and D
char calibKDPName[256]; char calibKDPName[256];
sprintf_s(calibKDPName, "%scalib_param_K_D.txt", calibDataPath[grp]); sprintf_s(calibKDPName, "%sresult/calib_param_K_D.txt", calibDataPath[grp]);
sg_outputCalibKD(calibKDPName, K, D, pe); sg_outputCalibKD(calibKDPName, K, D, laser_pe);
} }
return 0; return 0;
} }

View File

@ -1,4 +1,5 @@
1004.94, 0, 619.327 1006.74, 0, 620.098
0, 1004.94, 1023.07 0, 1006.74, 1023.51
0, 0, 1 0, 0, 1
-0.0869453, 0.0919085, -1.61443e-05, -2.84163e-05, -0.0224749 -0.0817425, 0.0844376, -2.2634e-05, 0.000762232, -0.0165931
-2.67733, 0.0501805, 631.944

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -655,7 +655,7 @@ void getLinePeaks(double* lineData, int dataSize, int row, int scaleWin, double
} }
std::vector<cv::Point2f> detectLaserLine( std::vector<cv::Point2f> detectLaserLine(
const cv::Mat& inputImage) const cv::Mat& inputImage, bool multiPeaksPerLine)
{ {
std::vector<cv::Point2f> laserPoints; std::vector<cv::Point2f> laserPoints;
if (inputImage.empty()) if (inputImage.empty())
@ -673,8 +673,8 @@ std::vector<cv::Point2f> detectLaserLine(
//高斯滤波 //高斯滤波
cv::Mat img; cv::Mat img;
gray.convertTo(img, CV_64FC1); gray.convertTo(img, CV_64FC1);
cv::GaussianBlur(img, img, cv::Size(3, 3), 0.9, 0.9); cv::GaussianBlur(img, img, cv::Size(0, 0), 2.0, 2.0);
//cv::imwrite("gauss_blur_src.bmp", img); cv::imwrite("gauss_blur_src.bmp", img);
// 提取最大值点 // 提取最大值点
int scaleWin = 5; int scaleWin = 5;
double minPkValue = 20; double minPkValue = 20;
@ -684,6 +684,23 @@ std::vector<cv::Point2f> detectLaserLine(
std::vector< cv::Point> linePeaks; std::vector< cv::Point> linePeaks;
double* lineData = img.ptr<double>(i); double* lineData = img.ptr<double>(i);
getLinePeaks(lineData, img.cols, i, scaleWin, minPkValue, linePeaks); getLinePeaks(lineData, img.cols, i, scaleWin, minPkValue, linePeaks);
if ((false == multiPeaksPerLine) && (linePeaks.size() > 1))
{
//每行保留一个Peak,
cv::Point bestOne = linePeaks[0];
double bestValue = lineData[bestOne.x];
for (int i = 1; i < (int)linePeaks.size(); i++)
{
double value = lineData[linePeaks[i].x];
if (bestValue < value)
{
bestValue = value;
bestOne = linePeaks[i];
}
}
linePeaks.clear();
linePeaks.push_back(bestOne);
}
pkPoints.insert(pkPoints.end(), linePeaks.begin(), linePeaks.end()); pkPoints.insert(pkPoints.end(), linePeaks.begin(), linePeaks.end());
} }
//取亚像素值 //取亚像素值
@ -701,27 +718,22 @@ std::vector<cv::Point2f> detectLaserLine(
#endif #endif
/*Breif: 根据棋盘格平面和2d坐标计算3d值*/ /*Breif: 根据棋盘格平面和2d坐标计算3d值*/
std::vector<cv::Point3f> project2DTo3D( std::vector<cv::Point3f> project2DTo3D(
const std::vector<cv::Point2f>& imagePoints, const std::vector<cv::Point2f>& undistortedPoints,
const cv::Vec4f& planeEquation, // [a,b,c,d] for ax+by+cz+d=0 const cv::Vec4f& planeEquation, // [a,b,c,d] for ax+by+cz+d=0
const cv::Mat& cameraMatrix, // 相机内参K const cv::Mat& cameraMatrix, // 相机内参K
const cv::Mat& distCoeffs) // 畸变系数D const cv::Mat& distCoeffs) // 畸变系数D
{ {
std::vector<cv::Point3f> objectPoints; std::vector<cv::Point3f> objectPoints;
if (imagePoints.empty()) if (undistortedPoints.empty())
return objectPoints; return objectPoints;
// 1. 去畸变 // 1. 提取平面参数
std::vector<cv::Point2f> undistortedPoints;
cv::undistortPoints(imagePoints, undistortedPoints,
cameraMatrix, distCoeffs, cv::noArray(), cameraMatrix);
// 2. 提取平面参数
const float a = planeEquation[0], b = planeEquation[1]; const float a = planeEquation[0], b = planeEquation[1];
const float c = planeEquation[2], d = planeEquation[3]; const float c = planeEquation[2], d = planeEquation[3];
const float denom = a * a + b * b + c * c; const float denom = a * a + b * b + c * c;
if (fabs(denom) < 1e-6f) return objectPoints; if (fabs(denom) < 1e-6f) return objectPoints;
// 3. 构造射线并求交平面 // 2. 构造射线并求交平面
const cv::Mat invK = cameraMatrix.inv(); const cv::Mat invK = cameraMatrix.inv();
for (const auto& pt : undistortedPoints) { for (const auto& pt : undistortedPoints) {
// 生成归一化射线方向 // 生成归一化射线方向

View File

@ -69,7 +69,7 @@ void fitChessboardPlane_charuco(
/*Brief: 激光线检测函数*/ /*Brief: 激光线检测函数*/
std::vector<cv::Point2f> detectLaserLine( std::vector<cv::Point2f> detectLaserLine(
const cv::Mat& inputImage); const cv::Mat& inputImage, bool multiPeaksPerLine);
/*Breif: 根据棋盘格平面和2d坐标计算3d值*/ /*Breif: 根据棋盘格平面和2d坐标计算3d值*/
std::vector<cv::Point3f> project2DTo3D( std::vector<cv::Point3f> project2DTo3D(