Halcon一日一练:图像拼接技术2:步骤与例程

时间:2023-03-09 09:53:21
Halcon一日一练:图像拼接技术2:步骤与例程

上一篇主要介绍了图像拼接的一些原理和方法,这一篇将主要介绍步骤和例程:

接上一篇:

基于特征的接拼方法,分为四个步骤

1、特征检测:从图像中检测出显著且独特的图像特征,诸如:闭合区域,直线段,边缘,轮廓,点等。

2、特征匹配:从相似度确定图像之间特征的对应关系,又分为如下几类:

2.1:使用空域关系的方法

2.2:使用不变描述符的方法

2.3:松弛方法

2.4:金字塔和小波方法

3、变换模型的估计:变换函数选择和函数参数估计

4、图像变换和重采样:可以通过前向或后向的方式来实现,插值的方法有最近邻插值、双线性插值、双三次函数插值、二次样条插值、三次B样条插值、高阶B样条插值。

基于特征的方法普遍适用于局部结构信息更显著的情况,能够处理图像之间复杂变形的情况,不足之处是特征检测困难且不稳定,最关键的一点是需要有一种判断力很强的、鲁棒性能好的且对图像之间变化保持不变的特征匹配算法。

下面是Halcon自带例程,如何拼接图像

 **此例程讲解了如何将几张局部的PCB图像拼接居一张大的马赛克PCB图像。
**此例程使用算子proj_match_points_ransac和算子 gen_projective_masaic完成上述工作。
**请注意:这个PCB图像有一几处看起来像拼接逢合线的破损点,为了更好的区分真正的缝合线,例程呈现逢合线。
dev_update_off ()
dev_close_window ()
dev_open_window (, , , , 'white', WindowHandle)
dev_set_color ('green')
set_display_font (WindowHandle, , 'mono', 'true', 'false')
**一张一张的读取图像。
gen_empty_obj (Images)
for J := to by
read_image (Image, 'mosaic/pcb_' + J$'')
concat_obj (Images, Image, Images)
dev_display (Image)
disp_message (WindowHandle, 'Image ' + J$'d', 'image', -, -, 'black', 'true')
wait_seconds ()
endfor
disp_continue_message (WindowHandle, 'black', 'true')
stop ()
* To show the point matches that are used to compute the projective
* transformation between the images, we will show all images in a large
* tiled image with some space between the images so that the extents
* of the images are easily visible.
dev_set_window_extents (-, -, / , / )
tile_images_offset (Images, TiledImage, [,,,,,], [,,,,,], [-,-,-,-,-,-], [-,-,-,-,-,-], [-,-,-,-,-,-], [-,-,-,-,-,-], , )
dev_clear_window ()
dev_display (TiledImage)
disp_message (WindowHandle, 'All 6 images', 'window', , , 'black', 'true')
disp_message (WindowHandle, 'Click \'Run\'\nto continue', 'window', / - , , 'black', 'true')
stop ()
* Now we compute point matches between the five pairs of images and with this
* the projective transformation between the image pairs. Note that the code
* below calls the point operator for each image pair. Since the images form
* a strip, with a little book keeping we could make the process a little more
* efficient by saving the points from the last iteration (ImageT in pair J will
* be identical to ImageF in pair J+). This is not done here because such an
* optimization would be quite cumbersome in the general case where the images
* can lie in a general configuration that cannot be represented by a strip.
dev_clear_window ()
dev_display (TiledImage)
disp_message (WindowHandle, 'Point matches', 'window', , , 'black', 'true')
* We define the image pairs, i.e., which image should be mapped to which image.
From := [,,,,]
To := [,,,,]
Num := |From|
* We need a variable to accumulate the projective transformation matrices.
ProjMatrices := []
* Furthermore, since we want to create a rigid mosaic below we need to
* accumulate all the point correspondences and the number of matches per
* image pair.
Rows1 := []
Cols1 := []
Rows2 := []
Cols2 := []
NumMatches := []
* Now we can determine the transformations between the five image pairs.
for J := to Num - by
F := From[J]
T := To[J]
select_obj (Images, ImageF, F)
select_obj (Images, ImageT, T)
* Extract the points in both images.
points_foerstner (ImageF, , , , , 0.3, 'gauss', 'false', RowJunctionsF, ColJunctionsF, CoRRJunctionsF, CoRCJunctionsF, CoCCJunctionsF, RowAreaF, ColAreaF, CoRRAreaF, CoRCAreaF, CoCCAreaF)
points_foerstner (ImageT, , , , , 0.3, 'gauss', 'false', RowJunctionsT, ColJunctionsT, CoRRJunctionsT, CoRCJunctionsT, CoCCJunctionsT, RowAreaT, ColAreaT, CoRRAreaT, CoRCAreaT, CoCCAreaT)
* Determine the point matches and the transformation for the current
* image pair.
proj_match_points_ransac (ImageF, ImageT, RowJunctionsF, ColJunctionsF, RowJunctionsT, ColJunctionsT, 'ncc', , , , , , , 0.5, 'gold_standard', , , ProjMatrix, Points1, Points2)
* Accumulate the transformation matrix.
ProjMatrices := [ProjMatrices,ProjMatrix]
* Accumulate the point matches and number of point matches.
Rows1 := [Rows1,subset(RowJunctionsF,Points1)]
Cols1 := [Cols1,subset(ColJunctionsF,Points1)]
Rows2 := [Rows2,subset(RowJunctionsT,Points2)]
Cols2 := [Cols2,subset(ColJunctionsT,Points2)]
NumMatches := [NumMatches,|Points1|]
* Generate crosses that represent the extracted points in the tiled image.
* Note that we have to take the row offsets of the images in the tiled image
* into account.
gen_cross_contour_xld (PointsF, RowJunctionsF + (F - ) * , ColJunctionsF, , rad())
gen_cross_contour_xld (PointsT, RowJunctionsT + (T - ) * , ColJunctionsT, , rad())
* Generate a representation of the matched point pairs as lines. We create
* XLD contours from the lines so that we can zoom into the graphics window
* to take a closer look at the matches.
RowF := subset(RowJunctionsF,Points1) + (F - ) *
ColF := subset(ColJunctionsF,Points1)
RowT := subset(RowJunctionsT,Points2) + (T - ) *
ColT := subset(ColJunctionsT,Points2)
gen_empty_obj (Matches)
for K := to |RowF| - by
gen_contour_polygon_xld (Match, [RowF[K],RowT[K]], [ColF[K],ColT[K]])
concat_obj (Matches, Match, Matches)
endfor
* Now display the extracted data.
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', / - , , 'black', 'true')
stop ()
* Finally, we can generate the mosaic image from the projective transformations.
gen_projective_mosaic (Images, MosaicImage, , From, To, ProjMatrices, 'default', 'false', MosaicMatrices2D)
get_image_size (MosaicImage, Width, Height)
dev_set_window_extents (-, -, Width / , Height / )
dev_clear_window ()
dev_display (MosaicImage)
disp_message (WindowHandle, 'Projective mosaic', 'window', , , 'black', 'true')
disp_message (WindowHandle, 'Click \'Run\'\nto continue', 'window', Height / - , , 'black', 'true')
stop ()
* To show more clearly that the folds visible in the image do not result from the
* mosaicking, we display the seams between the images in the mosaic image.
* This can be done most easily by creating an image that contains the border
* of the images, generating a mosaic from it, and segmenting the resulting
* mosaic image.
get_image_size (Image, Width, Height)
gen_image_const (ImageBlank, 'byte', Width, Height)
gen_rectangle1 (Rectangle, , , Height - , Width - )
paint_region (Rectangle, ImageBlank, ImageBorder, , 'margin')
gen_empty_obj (ImagesBorder)
for J := to by
concat_obj (ImagesBorder, ImageBorder, ImagesBorder)
endfor
gen_projective_mosaic (ImagesBorder, MosaicImageBorder, , From, To, ProjMatrices, 'default', 'false', MosaicMatrices2D)
threshold (MosaicImageBorder, Seams, , )
dev_clear_window ()
dev_display (MosaicImage)
disp_message (WindowHandle, 'Seams between the\nimages', 'window', , , 'black', 'true')
dev_set_color ('yellow')
dev_display (Seams)
disp_message (WindowHandle, 'Click \'Run\'\nto continue', 'window', , , 'black', 'true')
stop ()
* If you look very closely at the projective mosaic above, you may note that
* there is a very slight projective distortion in the mosaic. This happens
* because the transformations cannot be determined with perfect accuracy
* because of very small errors in the point coordinates due to noise. Because
* of the strip configuration, essentially the overlapping area between the image
* pairs can act like a hinge around which the images may rotate out of the image
* plane. In this example, we know that the mapping between the images must
* be a rigid transformation. If we want to force the transformation to be rigid
* we can simply use bundle_adjust_mosaic.
bundle_adjust_mosaic (, , From, To, ProjMatrices, Rows1, Cols1, Rows2, Cols2, NumMatches, 'rigid', MosaicMatrices2D, Rows, Cols, Error)
* Now, we can generate the mosaic image from the rigid transformations.
gen_bundle_adjusted_mosaic (Images, MosaicImageRigid, MosaicMatrices2D, 'default', 'false', TransMatrix2D)
get_image_size (MosaicImageRigid, Width, Height)
dev_set_window_extents (-, -, Width / , Height / )
dev_clear_window ()
dev_display (MosaicImageRigid)
disp_message (WindowHandle, 'Rigid mosaic', 'window', , , 'black', 'true')

Halcon一日一练:图像拼接技术2:步骤与例程Halcon一日一练:图像拼接技术2:步骤与例程

Halcon一日一练:图像拼接技术2:步骤与例程

Halcon一日一练:图像拼接技术2:步骤与例程

Halcon一日一练:图像拼接技术2:步骤与例程

Halcon一日一练:图像拼接技术2:步骤与例程

带逢合线的图像                    找定位点

Halcon一日一练:图像拼接技术2:步骤与例程Halcon一日一练:图像拼接技术2:步骤与例程

最终图像:

Halcon一日一练:图像拼接技术2:步骤与例程

下面我们看一下另一个例程:

这个例程使用proj_match_points_ransac_guided 和 gen_projective_mosaic

主要介绍如何使用金字塔算法快速获取两个图像的特征点进行拼接。

 * This example program shows how images can be combined
* into a mosaic image using proj_match_points_ransac_guided
* and gen_projective_mosaic.
* It is shown how the calculation of the projection between two
* images can be accelerated using an image pyramid.
*
* Initializations
ImgPath := '3d_machine_vision/mosaic/'
ImgName := 'bga_r_'
Times := []
Colors := ['red','coral','yellow','lime green']
read_image (Images, ImgPath + ImgName + ['',''])
dev_update_off ()
dev_close_window ()
dev_open_window_fit_size (, , , , , , WindowHandle)
dev_open_window_fit_size (, , , , , , WindowHandle1)
set_display_font (WindowHandle, , 'mono', 'true', 'false')
set_display_font (WindowHandle1, , 'mono', 'true', 'false')
* The internal camera parameters of the used camera
* (necessary to eliminate radial distortions)
CamParam := [0.0121693,-2675.63,7.40046e-006,7.4e-006,290.491,258.887,,]
change_radial_distortion_cam_par ('adaptive', CamParam, , CamParOut)
change_radial_distortion_image (Images, Images, Images, CamParam, CamParOut)
* To show the point matches that are used to compute the
* transformation between the images, we will show both images in a
* tiled image with some space between the images so that the extents
* of the images are easily visible.
tile_images_offset (Images, TiledImage, [,], [,], [-,-], [-,-], [-,-], [-,-], , )
*
* Now we can determine the transformations between the image pairs.
From :=
To :=
select_obj (Images, ImageF, From)
select_obj (Images, ImageT, To)
*
* Repeat the calculation times with a different number of pyramid levels
for NumLevels := to by
*
dev_clear_window ()
dev_set_window (WindowHandle)
dev_clear_window ()
dev_display (TiledImage)
disp_message (WindowHandle, ['Calculate point matches','with ' + NumLevels + ' pyramid levels','Please wait ...'], 'window', , , 'black', 'true')
*
* Calculate the projection between the two images
* Check the procedure's comments for details
count_seconds (S1)
proj_match_points_ransac_pyramid (ImageF, ImageT, NumLevels, RowFAll, ColFAll, RowTAll, ColTAll, ProjMatrix, Points1, Points2)
count_seconds (S2)
Times := [Times,S2 - S1]
*
* Display point correspondences
gen_cross_contour_xld (PointsF, RowFAll, ColFAll, , rad())
gen_cross_contour_xld (PointsT, RowTAll + , ColTAll, , rad())
RowF := subset(RowFAll,Points1)
ColF := subset(ColFAll,Points1)
RowT := subset(RowTAll,Points2) +
ColT := subset(ColTAll,Points2)
gen_empty_obj (Matches)
for K := to |RowF| - by
gen_contour_polygon_xld (Match, [RowF[K],RowT[K]], [ColF[K],ColT[K]])
concat_obj (Matches, Match, Matches)
endfor
dev_display (TiledImage)
dev_set_color ('blue')
dev_display (Matches)
dev_set_color ('green')
dev_display (PointsF)
dev_display (PointsT)
disp_message (WindowHandle, [|RowF| + ' point matches','Time used: ' + (S2 - S1)$'.3' + ' s'], 'window', , , 'black', 'true')
*
* Generate the mosaic image
gen_projective_mosaic (Images, MosaicImage, , From, To, ProjMatrix, [,], 'false', MosaicMatrices2D)
*
* Display mosaic image
get_image_size (MosaicImage, Width, Height)
dev_set_window (WindowHandle1)
dev_resize_window_fit_image (MosaicImage, , , [,], )
dev_clear_window ()
dev_display (MosaicImage)
disp_message (WindowHandle1, 'Projective mosaic (used ' + NumLevels + ' pyramid levels)', 'window', , , 'black', 'true')
disp_continue_message (WindowHandle1, 'black', 'true')
stop ()
endfor
*
* Display execution times
dev_set_window (WindowHandle)
dev_close_window ()
MaxTime := max(Times)
BaseRow :=
RectHeight :=
disp_message (WindowHandle1, ['Time in s:','(#levels used)'], 'image', BaseRow + , , 'black', 'true')
for Index := to |Times| - by
gen_rectangle1 (Rectangle, BaseRow - RectHeight * Times[Index] / MaxTime, + Index * , BaseRow, + Index * )
disp_message (WindowHandle1, [Times[Index]$'.3','(' + (Index + ) + ')'], 'image', BaseRow + , + * Index, 'black', 'true')
dev_set_color (Colors[Index])
dev_set_draw ('fill')
dev_display (Rectangle)
endfor
disp_finished_message (WindowHandle1, 'black', 'true')