项目实战四 图像拼接

2025-08-22 23:48:05

一、简介

图像拼接是我们常用到的一种技术,我个人曾经天真的以为项目中最长用的是用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 pointsLift;

vector pointsMiddle;

vector 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 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> 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个像素左右,还可以。。。