需要指出的是是本项目共使用了四个相机,中心一个相机,边缘三个相机,旨在更好得提取螺纹特征并进行分类。因此该节中定位定位销并得到其他螺纹孔位置后,提取的图像是对应边缘相机投射到中心相机平面的图像。
需要注意此处标定板平面与法兰盘螺纹孔平面在同一平面上,方便后续拼接
//棋盘格坐标点
vector<Point2d> points0;
vector<Point2d> points1;
vector<Point2d> points2;
vector<Point2d> points3;
//读取棋盘格坐标点
ReadPoints("C:\\Users\\sunrise_oneday\\Desktop\\20221107\\Calib\\CalibrationResults\\0_res.txt", points0);
ReadPoints("C:\\Users\\sunrise_oneday\\Desktop\\20221107\\Calib\\CalibrationResults\\1_res.txt", points1);
ReadPoints("C:\\Users\\sunrise_oneday\\Desktop\\20221107\\Calib\\CalibrationResults\\2_res.txt", points2);
ReadPoints("C:\\Users\\sunrise_oneday\\Desktop\\20221107\\Calib\\CalibrationResults\\3_res.txt", points3);
//计算单应性矩阵(1与0,2与0,3与0)
cv::Mat Homography10 = cv::findHomography(
points1,
points0, // 对应的点
cv::RANSAC, // RANSAC 方法
1.); // 到重复投影点的最大距离
cv::Mat Homography20 = cv::findHomography(
points2,
points0, // 对应的点
cv::RANSAC, // RANSAC 方法
1.); // 到重复投影点的最大距离
cv::Mat Homography30 = cv::findHomography(
points3,
points0, // 对应的点
cv::RANSAC, // RANSAC 方法
1.); // 到重复投影点的最大距离
//读取法兰盘图像
Mat image0 = cv::imread("D:\\Graduate\\Data\\0.bmp", 0);
Mat image1 = cv::imread("D:\\Graduate\\Data\\1.bmp", 0);
Mat image2 = cv::imread("D:\\Graduate\\Data\\2.bmp", 0);
Mat image3 = cv::imread("D:\\Graduate\\Data\\3.bmp", 0);
Mat tempImg = imread("D:\\Graduate\\Data\\temp.bmp", 0);
//法兰盘投影到中心相机图像
Mat imageResult1, imageResult2, imageResult3;
cv::warpPerspective(image1, // 输入图像
imageResult1, // 输出图像
Homography10, // 单应矩阵
cv::Size(image1.cols, image1.rows));// 输出图像的尺寸
cv::warpPerspective(image2, // 输入图像
imageResult2, // 输出图像
Homography20, // 单应矩阵
cv::Size(image1.cols, image1.rows));// 输出图像的尺寸
cv::warpPerspective(image3, // 输入图像
imageResult3, // 输出图像
Homography30, // 单应矩阵
cv::Size(image1.cols, image1.rows));// 输出图像的尺寸
//5.模板匹配
Mat houghImg;
Mat dst;
maskImg.copyTo(dst);
GaussianBlur(tempImg, houghImg, cv::Size(5, 5), 1.5);
vector<cv::Vec3f> circles;
HoughCircles(tempImg, circles, cv::HOUGH_GRADIENT,
2, 50, 220, 50,
1, 80);//最小和最大半径
//霍夫变换检测模板图像的圆得到圆心坐标与半径(模板图像为原图裁剪得到的定位销图像)
circle(houghImg, Point(circles[0][0], circles[0][1]), circles[0][2], Scalar(255), 2);
int match_method = TM_SQDIFF;
//int match_method = TM_CCORR;
int width = maskImg.cols - tempImg.cols + 1;
int height = maskImg.rows - tempImg.rows + 1;
Mat result(width, height, CV_32FC1);
matchTemplate(maskImg, tempImg, result, match_method, Mat());//模板匹配
normalize(result, result, 0, 1, NORM_MINMAX, -1, Mat());//归一化处理
Point minLoc;
Point maxLoc;
double min, max;
Point2f temLoc;
minMaxLoc(result, &min, &max, &minLoc, &maxLoc, Mat()); //定位最匹配的位置
if (match_method == TM_SQDIFF || match_method == TM_SQDIFF_NORMED) {
temLoc = minLoc;
}
else {
temLoc = maxLoc;
}
// 绘制矩形
rectangle(dst, Rect(temLoc.x, temLoc.y, tempImg.cols, tempImg.rows), Scalar(0, 0, 255), 5, 8);
rectangle(result, Rect(temLoc.x, temLoc.y, tempImg.cols, tempImg.rows), Scalar(0, 0, 255), 5, 8);
imshow("result", result);
imshow("resultdst", dst);
Point2f basePoint = Point2f(temLoc.x + circles[0][0], temLoc.y + circles[0][1]);//定位销位置
float baseRadian = pointsToRadian(centerPos, basePoint);//定位销与x轴正方向夹角
//float dis = calDistance2D(centerPos.x, centerPos.y, basePoint.x, basePoint.y);//定位销与圆心之间的距离
//设置螺纹结构体,根据与x轴正半轴角度关系确定螺纹孔对应相机
struct Thread {
float radian;
Point2f point;
Rect rect;
int camera;
bool type;
};
Thread threads[12];
//threads[0].radian = baseRadian + PI / 12;
//threads[0].point = radianToPoints(centerPos, cirRadius, threads[0].radian);
for (int i = 0; i < 12; i++) {
threads[i].radian = radianRangePi(baseRadian + PI / 12 + i * PI / 6);//映射到-PI到PI之间
threads[i].point = radianToPoints(centerPos, cirRadius, threads[i].radian);//由弧度得到位置坐标
threads[i].rect = Rect(threads[i].point.x-56, threads[i].point.y - 56, 112,112);//图像大小为7*2的指数次方合适
if (threads[i].radian >= PI / 2 || threads[i].radian < -5 * PI / 6) {//三分之一区域
threads[i].camera = 1;
}
else if (threads[i].radian >= -5 * PI / 6 && threads[i].radian < -PI / 6)//三分之一区域
threads[i].camera = 3;
else
threads[i].camera = 2;
threads[i].type = false;
}
Mat groupImg = Mat::zeros(image0.size(), CV_8UC1);//将不同螺纹图像组合到同一张图中
//Mat groupImgRoi(groupImg, threads[2].rect);
//Mat imageResult1Roi(imageResult1, threads[2].rect);
//imageResult1Roi.copyTo(groupImgRoi);//copyTo操作到groupImgRoi,groupImg也将改变
int j = 0;
for (int i = 0; i < 12; i++) {
if (threads[i].camera == 1) {
Mat groupImgRoi(groupImg, threads[i].rect);
Mat imageResult1Roi(imageResult1, threads[i].rect);
j++;
string img_Name = "D:\\Graduate\\Data\\38\\" + to_string(j) + ".bmp";
imwrite(img_Name, imageResult1Roi);
imageResult1Roi.copyTo(groupImgRoi);
}
else if (threads[i].camera == 2) {
Mat groupImgRoi(groupImg, threads[i].rect);
Mat imageResult2Roi(imageResult2, threads[i].rect);
j++;
string img_Name = "D:\\Graduate\\Data\\38\\" + to_string(j) + ".bmp";
imwrite(img_Name, imageResult2Roi);
imageResult2Roi.copyTo(groupImgRoi);
}
else {
Mat groupImgRoi(groupImg, threads[i].rect);
Mat imageResult3Roi(imageResult3, threads[i].rect);
j++;
string img_Name = "D:\\Graduate\\Data\\38\\" + to_string(j) + ".bmp";
imwrite(img_Name, imageResult3Roi);
imageResult3Roi.copyTo(groupImgRoi);
}
}
imshow("s", groupImg);
imwrite("D:\\Graduate\\Data\\38\\0.bmp",groupImg);
#define PI acos(-1)
void ReadPoints(String txtpath, std::vector<cv::Point2d>& Pos)//读取坐标点
{
ifstream inFile(txtpath, ios::in);//ios::in 表示以只读的方式读取文件
string lineStr;
vector<double> numb;
while (getline(inFile, lineStr))//读取一行
{
stringstream ss(lineStr);
string str;
while (getline(ss, str, ','))//间隔逗号,逗号分离
{
double num = stod(str);//转为double
numb.push_back(num);//所有值保存到向量
}
}
for (int i = 0; i < 64; i++)
{
Point2d temppoint;
int tempx = 5 * i + 1;//x坐标
int tempy = 5 * i + 2;//y坐标
temppoint.x = numb[tempx];
temppoint.y = numb[tempy];
Pos.push_back(temppoint);
}
numb.clear();
inFile.clear();
lineStr.clear();
}
float pointsToRadian(Point2f p1, Point2f p2)//由坐标到弧度
{
float radian = atan2((p2.y - p1.y), (p2.x - p1.x));//弧度 该函数返回值范围是[-pi,pi]
//float angle = radian * 180 / 3.1415926;//角度
return radian;
}
Point2f radianToPoints(Point2f centerPos, float radius, float radian)//由弧度到坐标
{
float x = centerPos.x + radius * cos(radian);//cos要用弧度表示
float y = centerPos.y + radius * sin(radian);
return Point2f(x,y);
}
float radianRangePi(float radian)//将弧度映射到-PI到PI
{
if (radian > PI)
radian -= 2 * PI;
else if (radian < -PI)
radian += 2 * PI;
return radian;
}