halcon两张图片拼在一起(Halcon图像拼接)
图像拼接在实际的应用场景很广 ,比如无人机航拍 ,遥感图像等等 ,图像拼接是进一步做图像理解基础步骤 ,拼接效果的好坏直接影响接下来的工作 ,所以一个好的图像拼接算法非常重要 。
如按下图是将两张楼房图片拼接成一个图像 。
1 拼接步骤
要实现图像拼接 ,简单来说要实现以下步骤:
输入图像
图像几何校正
图像预处理
对每幅图进行特征点提取
对特征点进行匹配
进行图像配准
图像融合
对重叠边界进行特殊处理
2 拼接条件
图像的拼接要具备以下几个条件:
图像应具有一定的特征 。
图像要有重叠部分 ,一般重叠部分占总图像的1/4以上较合适 。
图像的背景亮度差异不能太大 ,应该低于10个灰度值 ,否则难以拼接成功 。
图像的方位差异不能太大 ,图像应该来源同一方位 。
拼合边界过渡应平滑 ,以消除接拼痕迹 。
3 特征点提取
基于SRUF 的特征点的提取与匹配
为了使拼接具有良好的精度和鲁棒性 ,同时又使其具有较好的实时性,本实验采用SURF 算法完成图像序列特征点的提取 。
SURF 算法又称快速鲁棒特征 ,借鉴了SIFT 中简化近似的思想 ,将DoH 中的高斯二阶微分模板进行了近似简化,使得模板对图像的滤波只需要进行几个简单的加减法运算 ,并且这种运算与滤波模板的尺寸无关 。实验证明 ,SURF 算法较SIFT 在运算速度上要快3 倍左右 ,综合性能要优于SIFT 算法 。
SURF 特征点提取与描述主要包含4 个步骤:
检测尺度空间极值 。
精炼特征点位置 。
计算特征点的描述信息。
生成描述特征点的特征向量 。
halcon特征点特取的算子如下:
select_obj (Images, ImageF, F) select_obj (Images, ImageT, T) * 提取两幅图像中的点 。 points_foerstner (ImageF, 1, 2, 3, 200, 0.3, gauss, false, RowJunctionsF, ColJunctionsF, CoRRJunctionsF, CoRCJunctionsF, CoCCJunctionsF, RowAreaF, ColAreaF, CoRRAreaF, CoRCAreaF, CoCCAreaF) points_foerstner (ImageT, 1, 2, 3, 200, 0.3, gauss, false, RowJunctionsT, ColJunctionsT, CoRRJunctionsT, CoRCJunctionsT, CoCCJunctionsT, RowAreaT, ColAreaT, CoRRAreaT, CoRCAreaT, CoCCAreaT)4 图像配准
图像配准是一种确定待拼接图像间的重叠区域以及重叠位置的技术 ,它是整个图像拼接的核心。本节采用的是基于特征点的图像配准方法 ,即通过匹配点对构建图像序列之间的变换矩阵 ,从而完成全景图像的拼接 。
变换矩阵H求解是图像配准的核心 ,其求解的算法流程如下 。
检测每幅图像中特征点 。
计算特征点之间的匹配 。
计算图像间变换矩阵的初始值 。
迭代精炼H变换矩阵 。
引导匹配 。用估计的H去定义对极线附近的搜索区域 ,进一步确定特征点的对应 。
重复迭代4)和5)直到对应点的数目稳定为止 。
图像配准算子如下:
* 确定当前图像对的点匹配和变换 。 proj_match_points_ransac (ImageF, ImageT, RowJunctionsF, ColJunctionsF, RowJunctionsT, ColJunctionsT, ncc, 21, 0, 0, 480, 640, 0, 0.5, gold_standard, 1, 4364537, ProjMatrix, Points1, Points2) * 累加变换矩阵 。 ProjMatrices := [ProjMatrices,ProjMatrix] * 累积点数匹配和点数匹配 。 Rows1 := [Rows1,subset(RowJunctionsF,Points1)] Cols1 := [Cols1,subset(ColJunctionsF,Points1)] Rows2 := [Rows2,subset(RowJunctionsT,Points2)] Cols2 := [Cols2,subset(ColJunctionsT,Points2)] NumMatches := [NumMatches,|Points1|] * 生成表示平铺图像中提取点的十字。 * 请注意 ,我们必须考虑平铺图像中图像的行偏移 。 gen_cross_contour_xld (PointsF, RowJunctionsF + (F - 1) * 500, ColJunctionsF, 6, rad(45)) gen_cross_contour_xld (PointsT, RowJunctionsT + (T - 1) * 500, ColJunctionsT, 6, rad(45)) * 生成匹配点对的线表示 。我们从线条中创建XLD轮廓 ,以便放大图形窗口 ,更仔细地查看匹配。 RowF := subset(RowJunctionsF,Points1) + (F - 1) * 500 ColF := subset(ColJunctionsF,Points1) RowT := subset(RowJunctionsT,Points2) + (T - 1) * 500 ColT := subset(ColJunctionsT,Points2) gen_empty_obj (Matches) for K := 0 to |RowF| - 1 by 1 gen_contour_polygon_xld (Match, [RowF[K],RowT[K]], [ColF[K],ColT[K]]) concat_obj (Matches, Match, Matches) endfor5 图像融合
根据仿射变换矩阵进行图像融合 。
关闭窗口后重新打开
gen_projective_mosaic (Images, MosaicImage, 2, From, To, ProjMatrices, default, false, MosaicMatrices2D) get_image_size (MosaicImage, Width, Height)6 图像合并
单纯将多张图片按几行几列合并成一张大图并显示 。
* 关闭窗口后重新打开 dev_close_window() dev_open_window (0, 0, 600, 400, black, WindowHandle) * 创建一张空白图片 gen_empty_obj (Images) * 遍历文件夹 list_files (C:/Users/Administrator/Desktop/test, [files,follow_links], ImageFiles) tuple_regexp_select (ImageFiles, [\\.(tif|tiff|gif|bmp|jpg|jpeg|jp2|png|pcx|pgm|ppm|pbm|xwd|ima|hobj)$,ignore_case], ImageFiles) for Index := 0 to |ImageFiles| - 1 by 1 read_image (Image, ImageFiles[Index]) * 缩放图片到图片统一大小 zoom_image_size (Image, ImageZoom, 200, 200, constant) concat_obj (Images, ImageZoom, Images) endfor * 合并图片(按行填充 ,一行填满4张图片后再填充下一行) tile_images (Images, TiledImage, 4, horizontal) dev_display (TiledImage)图像效果如下:
7 拼接示例
示例如图:
代码如下:
dev_update_off () dev_close_window () dev_open_window (0, 0, 640, 480, white, WindowHandle) dev_set_color (green) set_display_font (WindowHandle, 14, mono, true, false) * Read in the images and show them one-by-one. Please not the fold-like * degradations running across the PCB. gen_empty_obj (Images) for J := 1 to 6 by 1 read_image (Image, mosaic/pcb_ + J$02) concat_obj (Images, Image, Images) dev_display (Image) disp_message (WindowHandle, Image + J$d, window, 12, 12, black, true) *wait_seconds (1) endfor disp_continue_message (WindowHandle, black, true) stop () *显示用于计算投影的点匹配 *在图像之间进行转换,我们将以大屏幕显示所有图像 *平铺图像在图像之间留有一定的空间 ,以便 *这些图像中的大部分很容易看到 。 dev_set_window_extents (-1, -1, 640 / 4, 2980 / 4) tile_images_offset (Images, TiledImage, [0, 500, 1000, 1500, 2000, 2500], [0, 0, 0, 0, 0, 0], [-1, -1, -1, -1, -1, -1], [-1, -1, -1, -1, -1, -1], [-1, -1, -1, -1, -1, -1], [-1, -1, -1, -1, -1, -1], 640, 2980) dev_clear_window () dev_display (TiledImage) disp_message (WindowHandle, All 6 images, window, 12, 12, black, true) disp_message (WindowHandle, Click \Run\\nto continue, window, 2980 / 4 - 50, 12, black, true) stop () *现在我们计算五对图像之间的点匹配 ,并以此计算图像对之间的投影变换 。 *请注意,下面的代码为每个图像对调用点运算符 。由于图像形成一条带 , *只需稍加簿记 ,我们就可以通过保存上一次迭代的点来提高过程的效率 *(J对中的ImageT将与J+1对中的ImageF相同) 。这里没有这样做 , *因为在一般情况下 ,这样的优化会非常麻烦 ,因为图像可能位于无法用条带表示的一般配置中 。 dev_clear_window () dev_display (TiledImage) disp_message (WindowHandle, Point matches, window, 12, 3, black, true) *我们定义了图像对 ,即哪个图像应该映射到哪个图像 。 From := [1, 2, 3, 4, 5] To := [2, 3, 4, 5, 6] Num := |From| * 我们需要一个变量来累加投影变换矩阵 。 ProjMatrices := [] *此外 ,因为我们想要在下面创建一个刚性马赛克 ,所以我们需要累积所有点对应和匹配图像对的数量 。 Rows1 := [] Cols1 := [] Rows2 := [] Cols2 := [] NumMatches := [] * 现在我们可以确定五个图像对之间的转换 。 for J := 0 to Num - 1 by 1 F := From[J] T := To[J] select_obj (Images, ImageF, F) select_obj (Images, ImageT, T) * 提取两幅图像中的点 。 points_foerstner (ImageF, 1, 2, 3, 200, 0.3, gauss, false, RowJunctionsF, ColJunctionsF, CoRRJunctionsF, CoRCJunctionsF, CoCCJunctionsF, RowAreaF, ColAreaF, CoRRAreaF, CoRCAreaF, CoCCAreaF) points_foerstner (ImageT, 1, 2, 3, 200, 0.3, gauss, false, RowJunctionsT, ColJunctionsT, CoRRJunctionsT, CoRCJunctionsT, CoCCJunctionsT, RowAreaT, ColAreaT, CoRRAreaT, CoRCAreaT, CoCCAreaT) * 确定当前图像对的点匹配和变换。 proj_match_points_ransac (ImageF, ImageT, RowJunctionsF, ColJunctionsF, RowJunctionsT, ColJunctionsT, ncc, 21, 0, 0, 480, 640, 0, 0.5, gold_standard, 1, 4364537, ProjMatrix, Points1, Points2) * 累加变换矩阵 。 ProjMatrices := [ProjMatrices,ProjMatrix] * 累积点数匹配和点数匹配 。 Rows1 := [Rows1,subset(RowJunctionsF,Points1)] Cols1 := [Cols1,subset(ColJunctionsF,Points1)] Rows2 := [Rows2,subset(RowJunctionsT,Points2)] Cols2 := [Cols2,subset(ColJunctionsT,Points2)] NumMatches := [NumMatches,|Points1|] * 生成表示平铺图像中提取点的十字。 * 请注意 ,我们必须考虑平铺图像中图像的行偏移 。 gen_cross_contour_xld (PointsF, RowJunctionsF + (F - 1) * 500, ColJunctionsF, 6, rad(45)) gen_cross_contour_xld (PointsT, RowJunctionsT + (T - 1) * 500, ColJunctionsT, 6, rad(45)) * 生成匹配点对的线表示 。我们从线条中创建XLD轮廓 ,以便放大图形窗口 ,更仔细地查看匹配 。 RowF := subset(RowJunctionsF,Points1) + (F - 1) * 500 ColF := subset(ColJunctionsF,Points1) RowT := subset(RowJunctionsT,Points2) + (T - 1) * 500 ColT := subset(ColJunctionsT,Points2) gen_empty_obj (Matches) for K := 0 to |RowF| - 1 by 1 gen_contour_polygon_xld (Match, [RowF[K],RowT[K]], [ColF[K],ColT[K]]) concat_obj (Matches, Match, Matches) endfor * 现在显示提取的数据 。 dev_set_color (blue) dev_display (Matches) dev_set_color (green) dev_display (PointsF) dev_display (PointsT) endfor disp_message (WindowHandle, Click \Run\\nto continue, window, 2980 / 4 - 50, 12, black, true) stop () * 最后 ,我们可以从投影变换生成马赛克图像 。 gen_projective_mosaic (Images, MosaicImage, 2, From, To, ProjMatrices, default, false, MosaicMatrices2D) get_image_size (MosaicImage, Width, Height) dev_set_window_extents (-1, -1, Width / 3, Height / 3) dev_clear_window () dev_display (MosaicImage) disp_message (WindowHandle, Projective mosaic, window, 12, 12, black, true) disp_message (WindowHandle, Click \Run\\nto continue, window, Height / 3 - 50, 12, black, true) stop () * 为了更清楚地显示图像中可见的褶皱不是马赛克造成的,我们在马赛克图像中显示图像之间的接缝 。 *这可以通过创建包含图像边界的图像 、从中生成马赛克并分割生成的马赛克图像来实现 。 get_image_size (Image, Width, Height) gen_image_const (ImageBlank, byte, Width, Height) gen_rectangle1 (Rectangle, 0, 0, Height - 1, Width - 1) paint_region (Rectangle, ImageBlank, ImageBorder, 255, margin) gen_empty_obj (ImagesBorder) for J := 1 to 6 by 1 concat_obj (ImagesBorder, ImageBorder, ImagesBorder) endfor gen_projective_mosaic (ImagesBorder, MosaicImageBorder, 2, From, To, ProjMatrices, default, false, MosaicMatrices2D) threshold (MosaicImageBorder, Seams, 128, 255) dev_clear_window () dev_display (MosaicImage) disp_message (WindowHandle, Seams between the\nimages, window, 12, 12, black, true) dev_set_color (yellow) dev_display (Seams) disp_message (WindowHandle, Click \Run\\nto continue, window, 550, 12, black, true) stop () *如果你仔细观察上面的投影马赛克 ,你可能会注意到 *马赛克中有一个非常轻微的投影失真 。这种情况会发生 *因为变换不能精确地确定 *因为噪声导致的点坐标误差非常小 。因为 *在条形结构中 ,基本上是图像之间的重叠区域 *成对的图像可以像一个铰链一样绕着它旋转,离开图像平面 。 *在这个例子中 ,我们知道图像之间的映射必须是刚性变换 。如果我们想强制转换为刚性 , *我们可以简单地使用bundle_adjust_mosaic 。 bundle_adjust_mosaic (6, 1, From, To, ProjMatrices, Rows1, Cols1, Rows2, Cols2, NumMatches, rigid, MosaicMatrices2D, Rows, Cols, Error) * 现在 ,我们可以从刚性变换生成马赛克图像。 gen_bundle_adjusted_mosaic (Images, MosaicImageRigid, MosaicMatrices2D, default, false, TransMatrix2D) get_image_size (MosaicImageRigid, Width, Height) dev_set_window_extents (-1, -1, Width / 3, Height / 3) dev_clear_window () dev_display (MosaicImageRigid) disp_message (WindowHandle, Rigid mosaic, window, 12, 12, black, true)创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!