Halcon平铺tile三算子浅析
1. 平铺三算子
tile词典释意:
- 地砖;瓦片;(棋盘游戏的)棋子
- 铺瓦;铺地砖;贴瓷砖;平铺显示
Halcon中以tile开头的算子有三个,分别是tile_channels、tile_images、tile_images_offset, 它们的作用都是进行图像的平铺。通过平铺操作,可以实现图像的简单拼接(非融合拼接)。 下面我们来分别看一下这三个算子,并通过tile_images_offset来实现一个小小的拼图实例。
2. tile_channels
看看Halcon的文档里是怎么描述这个算子的: tile_channels - 将多张图像平铺成一张大图。 tile_channels(Image : TiledImage : NumColumns, TileOrder : ) 这个算子共有四个参数,分别是
- 输入参数1 Image 即输入的图像,是一个多通道的图像,但每一个通道大小需要一致;
- 输出参数1 TiledImage 即平铺后的结果图像,是一幅单通道灰度图;
- 输入参数2 NumColumns 即列数目;
- 输入参数3 TileOrder 即平铺的顺序。
将Halcon文档对此算子的说明简略翻译如下:
tile_channels将由多个通道组成的图像平铺为一个大的单通道图像。输入图像包含相同大小的Num个图像,这些图像分别存储在各个通道中。输出图像平铺图像包含一个单通道图像,其中Num输入通道已平铺到NumColumns列中。特别地,这意味着tile_channels无法平铺彩色图像。如果需要平铺彩色图像,可以使用tile_images。参数TileOrder确定在NumColumns不能确定图像复制到输出中的顺序(即,如果NumColumns!=1且NumColumns!=Num,分别对应竖排和横排)。如果TileOrder=‘horizontal’,则图像将在水平方向复制,即图像的第二个通道将位于第一个通道的右侧。如果TileOrder=‘vertical’,则在垂直方向复制图像,即图像的第二个通道将位于第一个通道的下方。通过将图像分别复制到输出图像中的相应位置来获得TileImage的图像。如果Num不是NumColumns的倍数,则输出图像的右下角将有未定义的灰度值。输出图像将反映这一点。
从这段描述里我们总结一下:
- tile_channels只能处理灰度图像,因为它的输出是一个单通道的图,且因为多个通道只是存在同一幅图像中,所以每个通道的大小必须完全一致;
- 可以通过NumColumns指定图像排列的列数;
- 如果NumColumns为1或Num(Num为图像的总数),则TileOrder就不会起作用,这两种情况以外,TileOrder若指定为’horizontal’则表明图像按通道顺序水平排列,排满NumColumns列后转到下一行,TileOrder若指定为’vertical’则表明图像按通道顺序垂直排列,排满NumColumns行后转到下一列;
- 如果图像的总数在排列时不是行(水平排)或列(竖直排)整倍数,则拼接后图像的右下角会出现空白区域。
好,让我们来看看这个算子的例程tile_channels.hdev:
read_image (Xing1, 'xing/xing000')
read_image (Xing2, 'xing/xing050')
read_image (Xing3, 'xing/xing100')
read_image (Xing4, 'xing/xing150')
read_image (Xing5, 'xing/xing200')
read_image (Xing6, 'xing/xing250')
read_image (Xing7, 'xing/xing300')
get_image_size (Xing1, Width, Height)
gen_rectangle1 (Image, 0, 0, Height - 1, Width - 1)
append_channel (Image, Xing1, Image)
append_channel (Image, Xing2, Image)
append_channel (Image, Xing3, Image)
append_channel (Image, Xing4, Image)
tile_channels (Image, TiledImage1, 1, 'vertical')
tile_channels (Image, TiledImage2, 2, 'vertical')
tile_channels (Image, TiledImage3, 2, 'horizontal')
tile_channels (Image, TiledImage4, 4, 'horizontal')
append_channel (Image, Xing5, Image)
append_channel (Image, Xing6, Image)
append_channel (Image, Xing7, Image)
tile_channels (Image, TiledImage5, 5, 'horizontal')
tile_channels (Image, TiledImage6, 3, 'vertical')
tile_channels (Image, TiledImage7, 5, 'vertical')
这个例程应用在监控图像的平铺上,这里面就应用到了按行、按列顺序进行排列的不同情况。 这里一共有七张结果图,我们就不一一分析了,单独把第七张拎出来看一下: Image里共有7个通道,TiledImages7是按照竖直方向排列,要排列5列,那么可以参考下图的排列方法 按这个排列方式我们就能看到右下角为什么会空出这样的三个位置。
3. tile_images
看看Halcon的文档里是怎么描述这个算子的: tile_images - 将多张图像平铺成一张大图。 tile_images(Images : TiledImage : NumColumns, TileOrder : ) 这个算子共有四个参数,分别是
- 输入参数1 Images 即输入的图像,是一个Hobject, 是由多个相同通道数的图像组合而成,图像的大小不要求一致;
- 输出参数1 TiledImage 即平铺后的结果图像,是一幅和Images通道数一致的图像;
- 输入参数2 NumColumns 即列数目;
- 输入参数3 TileOrder 即平铺的顺序。
将Halcon文档对该算子的描述简略翻译如下:
tile_images将多个输入图像对象平铺到一个大图像中,这些对象必须包含相同数量的通道。输入图像对象图像包含Num图像,其大小可能不同。输出图像平铺图像包含与输入图像一样多的通道。在输出图像中,Num个输入图像已平铺到NumColumns列中。每个“瓷砖” 都有相同的大小,这取决于所有输入图像的最大宽度和高度。如果输入图像小于分幅大小,则会将其复制到相应分幅的中心。参数TileOrder确定在NumColumns尚未确定图像复制到输出中的顺序(即,如果NumColumns!=1和NumColumns!=Num)。如果TileOrder=‘horizontal’,则在水平方向复制图像,即图像的第二个图像将位于第一个图像的右侧。如果TileOrder=‘vertical’,则沿垂直方向复制图像,即图像的第二个图像将位于第一个图像的下方。通过将图像域复制到输出图像中的相应位置,可以获得TileImage的图像。如果Num不是NumColumns的倍数,则输出图像的右下角将有未定义的灰度值。输出图像将反映这一点。
总结一下我们发现其实这个算子和上面tile_channels非常接近,所不同的是
- 可以处理彩色图像,每一幅用来平铺的图像要求通道数一致,但大小可以不一致;
- 每个格位大小是一样的,是Images中最大尺寸的那幅图像的大小;
- 如果当前格位中的图像小于标准尺寸,则会被放置在正中心。
我们还是来看一下例子tile_images_size.hdev
read_image (Image, 'claudia')
concat_obj (Image, Image, Images)
tile_images (Images, TiledImage1, 1, 'vertical')
tile_images (Images, TiledImage2, 2, 'horizontal')
crop_rectangle1 (Image, ImagePart1, 20, 20, 459, 349)
concat_obj (ImagePart1, ImagePart1, ImagesPart1)
concat_obj (Images, ImagesPart1, Images4)
tile_images (Images4, TiledImage3, 2, 'horizontal')
tile_images (Images4, TiledImage4, 2, 'vertical')
crop_rectangle1 (Image, ImagePart2, 30, 100, 229, 299)
concat_obj (Images4, ImagePart2, Images5)
tile_images (Images5, TiledImage5, 3, 'horizontal')
tile_images (Images5, TiledImage6, 3, 'vertical')
crop_rectangle1 (Image, ImagePart3, 250, 80, 399, 199)
concat_obj (ImagePart3, ImagePart3, ImagesPart3)
concat_obj (Images5, ImagesPart3, Images7)
tile_images (Images7, TiledImage7, 5, 'horizontal')
tile_images (Images7, TiledImage8, 5, 'vertical')
之所以选择这个例子,是因为它可以让我们看到这个算子的一些特性,我们选择TiledImage8来进行观察:
可以看到,按格位进行排列的方式和tile_channels是一致的,且小的图像被放在了格位的正中位置。
4. tile_images_offset
看看Halcon的文档里是怎么描述这个算子的: tile_image_offset - 根据显式地指定的位置信息将多张图像平铺成一张。
tile_images_offset(Images : TiledImage : OffsetRow, OffsetCol, Row1, Col1, Row2, Col2, Width, Height : )
参数比较多:
- 输入参数1: Images 即输入的图像,要求和tile_images中一样;
- 输出参数1: TiledImage 即输出的图像,和Images通道数一致;
- 输入参数2: OffsetRow 指定待平铺的每幅子图的左上角的Y坐标,注意每幅子图均需要分别指定;
- 输入参数3: OffsetCol 指定待平铺的每幅子图的左上角的X坐标,注意每幅子图均需要分别指定;
- 输入参数4: Row1 指定每幅图像裁剪的左上角Y坐标,如果默认大小则为-1,注意每幅子图均需要分别指定,如果都是默认大小,也要分别指定;
- 输入参数5: Col1 指定每幅图像裁剪的左上角X坐标,其他同输入参数4;
- 输入参数6: Row2 指定每幅图像裁剪的右下角Y坐标,其他同输入参数4;
- 输入参数7: Col2 指定每幅图像裁剪的右下角X坐标,其他同输入参数4;
- 输入参数8: Width 输出的图像的宽度;
- 输入参数9: Height 输出的图像的高度。
下面简略翻译一下Halcon文档中对该算子的描述:
tile_images_offset将多个输入图像对象平铺到一个大图像中,这些对象必须包含相同数量的通道。输入图像对象图像包含Num图像,其大小可能不同。输出图像平铺图像包含与输入图像一样多的通道。输出图像的大小由参数宽度和高度决定。输入图像左上角在输出图像中的位置由参数OffsetRow和OffsetCol确定。这两个参数必须正好包含Num值。或者,可以将每个输入图像裁剪为比输入图像小的任意矩形。为此,必须相应地设置参数Row1、Col1、Row2和Col2。如果这四个参数中的任何一个设置为-1,则不会裁剪相应的输入图像。在任何情况下,所有四个参数都必须包含Num值。如果剪切输入图像,位置参数OffsetRow和OffsetCol指的是剪切图像的左上角。如果输入图像在输出图像中彼此重叠(同时考虑其各自的域),则图像中具有较高索引的图像覆盖具有较低索引的图像的图像数据。通过将图像域复制到输出图像中的相应位置,可以获得TileImage的图像。
这里面我们能得到以下要点,总结一下:
- 指定裁剪参数的四个值里面如果有一个为-1,则不会进行裁剪;
- 如果图像在输出图像中相互重叠,则图像中具有较大索引的图像覆盖具有较小索引的图像;
特别注意的是,Halcon提醒:如果拼接时子图是大小一致,且对齐排列,那么用tile_images会比tile_images_offset要略快一些。
让我们来看一下例程tile_images_offset.hdev
read_image (Image, 'claudia')
crop_rectangle1 (Image, ImageParts, [10,230], [60,120], [219,339], [299,219])
concat_obj (Image, ImageParts, Images)
tile_images_offset (Images, TiledImage, [0,290,390], [0,340,210], [-1,-1,-1], [-1,-1,-1], [-1,-1,-1], [-1,-1,-1], 500, 500)
concat_obj (Image, Image, Image2)
concat_obj (Image2, Image, Image3)
tile_images_offset (Image3, TiledImage2, [0,290,390], [0,340,210], [-1,10,230], [-1,60,120], [-1,219,339], [-1,299,219], 500, 500)
sub_image (TiledImage, TiledImage2, ImageSub, 10, 128)
我们只来看一下TiledImage的结果就好:
注意重叠区,右下角的人物脸部图像的索引是2,胸口项链图像的索引是3,它们与半身人物图有重叠,但因为它们的索引值较大,所以覆盖在半身人物图之上。另外,超出最终输出图像大小的部分被裁剪掉了。如下图所示:是人物脸部图的完整图。
5. 利用tile_images_offset拼接一幅自己的图像
在了解了tile_images_offset 的使用方法后,我们就可以自己来写一个生成拼接图的小实例了。
- 下面这个实例首先生成了四幅单通道的黑白图像,尺寸分别是
200
×
200
200\times200
200×200,
300
×
200
300\times200
300×200,
100
×
300
100\times300
100×300,
400
×
300
400\times300
400×300,为了能够看清楚四幅图像的边界,我们给这四幅图像赋了不同的灰度值;
- 接下来将这四幅图像用
concat_obj 合并成同一个图像对象; - 最后用tile_images_offset算子来平铺,看到第三个参数是四个子图的左上点的Y坐标,第四个参数是四个子图的左上点的X坐标,剩下的裁剪参数均要赋成默认,即-1,注意此处参数的维数要和子图的数目一致;
- 最后两个参数为平铺后图像的总尺寸,在此例中是凑好的数字,长宽都是500。
gen_image_const (Image, 'byte', 200, 200)
paint_region (Image, Image, Image, 255, 'fill')
gen_image_const(Image1,'byte', 300, 200)
paint_region(Image1, Image1, Image1, 128, 'fill')
gen_image_const (Image2, 'byte', 100, 300)
paint_region(Image2, Image2, Image2, 100, 'fill')
gen_image_const (Image3, 'byte', 400, 300)
paint_region (Image3, Image3, Image3, 200, 'fill')
concat_obj (Image, Image1, ObjectsConcat)
concat_obj (ObjectsConcat, Image2, ObjectsConcat)
concat_obj (ObjectsConcat, Image3, ObjectsConcat)
tuple_gen_const (4, -1, defaultTuple)
* defaultTuple := [-1, -1, -1, -1]
tile_images_offset (ObjectsConcat, TiledImage, [0, 0, 200, 200], [0, 200, 0, 100], defaultTuple, defaultTuple, defaultTuple, defaultTuple, 500, 500)
上述程序的结果如下所示:
6.总结
- 平铺三算子里,tile_channels只能处理同等大小的灰度图像平铺;
- tile_images可以处理大小不一致,但通道数一致的彩色图像平铺, 如果图像的大小小于最大尺寸的图像,则该图像会被平铺在当前格位的正中;
- tile_images_offset功能最为强大,可以任意指定平铺图像的位置,并能对被平铺图像进行截取,但它的参数配置最为复杂,且它也要求待平铺的图像通道数必须保持一致;
- 在使用这三个算子时应该根据自己的需要进行选择,不一定非要使用最为灵活的tile_images_offset。
【创作不易,水平有限,若有错漏,轻喷勿骂】
|