一、简介
图像拼接是我们常用到的一种技术,我个人曾经天真的以为项目中最长用的是用halcon 案例中的那种来拼图,但是真正做项目的时候并不是这样的,这是项目中的一个一个真实案例。
二、项目背景
1、有 上、中、下三张图片,且每张图片的右边是一排标定圆
2、上面图的标定圆能拍到9个圆孔,中间的图片拍到上图的全部和下图的部分保证有重叠的,下图和中图肯定也要有重叠,如下图:
三、拼图原理
如前面两步所示:
第一步:我们需要找到图片右边的标定圆,找圆的时候,a、先通过blob 分析找出想要的圆孔的轮廓,然后拟合圆,还有一种方法就是计算出圆孔的area_center,然后用二维测量卡尺来拟合圆
* 通过面积来计算半径
Radius:=pow(mean(Area)/rad(180),0.5)
* 创建卡尺的
create_metrology_model (MetrologyHandle)
get_image_size (ImageReduced, Width, Height)
set_metrology_model_image_size (MetrologyHandle, Width, Height)
tuple_gen_const (|Row|, Radius, Radiuses)
add_metrology_object_circle_measure (MetrologyHandle, Row, Column, Radiuses, 5, 10, 1, 30, ['num_measures','measure_transition','measure_select','min_score'], [16,'positive','all',0.1], Index_circle)
apply_metrology_model (ImageReduced, MetrologyHandle)
get_metrology_object_result (MetrologyHandle, Index_circle, 'all', 'result_type', 'all_param', Parameter_circle)
第二步:第一步中我们得到的各个图片的圆孔行列坐标,这一步只要是计算重叠位置的差,分布是上图和中图 的offsetx1 offsety1,以及中图和下图的offsetx2,offsety2 。计算的方式有很多,这里就不说了。
第三步:通过tile_images_offset 算子来拼图
四、显示效果
核心代码:
找圆
* 得到9个圆的半径和 行列坐标
Albert_Carib_Circle (ImageResult, ContCircleT, 'TOP', tilParams[6], [tilParams[7],tilParams[9],tilParams[10]], [tilParams[8],1,tilParams[11]], [], ProcessObjOut, ProcessObjOut, RowT, ColumnT)
SetDictObject (ContCircleT, ProcessObjOut, '上图找圆结果')
* 计算相邻两个圆之间的距离
distance_pp (RowT[0:|RowT|-2], ColumnT[0:|RowT|-2], RowT[1:|RowT|-1], ColumnT[1:|RowT|-1], Distance)
a := max(Distance)-min(Distance)
拼图:
OffsetYX := [0,0,OffsetY11,OffsetX11,OffsetY11+OffsetY22,OffsetX11+OffsetX22]
* set_dict_tuple (DictHandle_tile, '标定圆提取', ProcessObjOut)
*拼图
gen_empty_obj (TiledImageLight)
gen_empty_obj (TiledImageDark)
get_image_size (ObjectSelected, Width, Height)
tile_images_offset (ImageLights, TiledImageLight, [OffsetYX[0],OffsetYX[2],OffsetYX[4]], [OffsetYX[1],OffsetYX[3],OffsetYX[5]], [0,0,0], [0,0,0], [Height,Height,Height], [Width,Width,Width], Width, Height*3-2000)
最终效果:
五、opencv
//
int MyTileImages(Mat& src1, Mat& src2, Mat& src3, Mat& dst)
{
vector<Point2f> pointsLift;
vector<Point2f> pointsMiddle;
vector<Point2f> pointsRight;
Mat LeftGray, MidGray, RightGray;
Mat LeftGrayScale, MidGrayScale, RightGrayScale;
// 图像放缩
Size size (src1.cols, src1.rows*6);
Tiff2Gray(src1, -10, LeftGray);
resize(LeftGray, LeftGrayScale, size,3);
Size size2(src2.cols, src2.rows * 6);
Tiff2Gray(src2, -10, MidGray);
resize(MidGray, MidGrayScale, size2, 3);
Size size3(src3.cols, src3.rows * 6);
Tiff2Gray(src3, -10, RightGray);
resize(RightGray, RightGrayScale, size3, 3);
vector<float> left, middle, right;
FileStorage fs2("./my.yaml", FileStorage::READ);
string leftstr, middlestr, rightstr;
fs2["Left"] >> leftstr;
fs2["Middle"] >> middlestr;
fs2["Right"] >> rightstr;
fs2.release();
left = Mysplit(leftstr, ",");
middle = Mysplit(middlestr, ",");
right = Mysplit(rightstr, ",");
//============左图===================
GetCalibrateCircles(LeftGrayScale, left, pointsLift);
vector<vector<Point2f>> sortCentLeft,ssss, sortCentMiddle, sortCentRight;
// 开始排序 计算出标定板的点
SortByRowOrCol(pointsLift, "row", 2, sortCentLeft);
//
Point2f left_right_up(sortCentLeft.at(0).at(1));
Point2f left_right_down(sortCentLeft.at(1).at(1));
Point2f pointleft((left_right_up.x+ left_right_down.x)/2, (left_right_up.y + left_right_down.y) / 2);
//cout << pointleft << endl;
circle(LeftGrayScale, left_right_up, 20, Scalar(255), 2, 8, 0);//在原图画出圆心
circle(LeftGrayScale, left_right_down, 32, Scalar(255), 2, 8, 0);//在原图画出圆心
circle(LeftGrayScale, pointleft, 20, Scalar(55), 2, 8, 0);//在原图画出圆心
// 中间的图像需要将其镜像一下=============================================
Mat Midgraymirror;
flip(MidGrayScale, Midgraymirror, 0);
//string flieNameR = "D:\\work\\pcl_workplaces\\Algorithm2D_opencv\\splice_images\\Midgraymirror.png";
//imwrite(flieNameR, Midgraymirror);
GetCalibrateCircles(Midgraymirror, middle,pointsMiddle);
//
SortByRowOrCol(pointsMiddle, "row", 2, sortCentMiddle);
Point2f left_middle_up(sortCentMiddle.at(0).at(0));
Point2f left_middle_down(sortCentMiddle.at(1).at(0));
Point2f pointmiddle_1((left_middle_up.x + left_middle_down.x) / 2, (left_middle_up.y + left_middle_down.y) / 2);
Point2f right_middle_up(sortCentMiddle.at(0).at(7));
Point2f right_middle_down(sortCentMiddle.at(1).at(7));
Point2f pointmiddle_2((right_middle_up.x + right_middle_down.x) / 2, (right_middle_up.y + right_middle_down.y) / 2);
circle(Midgraymirror, pointmiddle_1, 20, Scalar(25), 2, 8, 0);//在原图画出圆心
circle(Midgraymirror, pointmiddle_2, 12, Scalar(25), 2, 8, 0);//在原图画出圆心
//circle(Midgraymirror, right_middle_up, 20, Scalar(255), 2, 8, 0);//在原图画出圆心
//circle(Midgraymirror, right_middle_down, 12, Scalar(255), 2, 8, 0);//在原图画出圆心
//cout << pointmiddle_1 << endl;
//cout << pointmiddle_2 << endl;
GetCalibrateCircles(RightGrayScale,right, pointsRight);
SortByRowOrCol(pointsRight, "row", 2, sortCentRight);
Point2f right_left_up(sortCentRight.at(0).at(0));
Point2f right_left_down(sortCentRight.at(1).at(0));
Point2f pointright((right_left_up.x + right_left_down.x) / 2, (right_left_up.y + right_left_down.y) / 2);
circle(RightGrayScale, pointright, 20, Scalar(25), 2, 8, 0);//在原图画出圆心
//circle(RightGrayScale, left_right_down, 12, Scalar(255), 2, 8, 0);//在原图画出圆心
//cout << pointright << endl;
// 将上面的选出的圆的坐标排列出来
// 现将第一张图贴上去
Size size_t = src2.size();
dst = Mat::zeros(Size(size_t.width * 3, size_t.height * 6), CV_8UC1);
TileImageBy2Images(LeftGrayScale, pointleft, Midgraymirror,pointmiddle_1, 0.5,dst);
Point2f lesmd(pointleft.x+(pointmiddle_2.x- pointmiddle_1.x), pointmiddle_2.y);
TileImageBy2Images(dst, lesmd, RightGrayScale, pointright, 0.5, dst);
// 计算
cout << "拼接完成" << endl;
return 0;
}
误差是2个像素左右,还可以。。。