*如何获取程序运行时间
*测量时间,每次调用都会返回一个时间值,两次连续调用的值的差异提供以秒为单位的时间间隔
*返回当前时间戳
count_seconds (T1)
*读取图像变量
read_image(Image, 'fabrik')
*获取图像大小
get_image_size(Image, Width, Height)
*创建一个新的活动图形窗口
dev_open_window(0, 0, Width, Height, 'black', WindowHandle)
*返回当前时间戳
count_seconds (T2)
*返回程序运行时间(秒 - > 毫秒) 1秒等于1000毫秒
RumTime := (T2 - T1) * 1000
*展示图像变量
dev_display(Image)
*输出文本信息
*参数1 活动窗口ID
*参数2 包含要显示的文本消息的字符串元组。元组的每个值将显示在一行中
*参数3 如果设置为'window',则文本位置是相对于窗口坐标系统给出的。如果设置为'image',则使用图像坐标(这在缩放图像时可能很有用)
*参数4 文本位置的行坐标 默认值:12
*参数5 文本位置的列坐标 默认值:12
*参数6 颜色(也参与)→(字符串) 将文本的颜色定义为字符串。如果设置为[],或“使用当前设置的颜色。”如果传入一个字符串元组,则对每个新位置或文本行循环使用颜色
*参数7 如果设置为“true”,文本将写入一个橙色框中。如果设置为false,则不显示框。如果设置为一个颜色字符串(例如:'white', '#FF00CC'等),文本被写在该颜色的框中。另一个值可控制框阴影的外观。
disp_message (WindowHandle, '程序的运行时间:' + RumTime + 'ms', 'window', 10, 10, 'black', ['white', '#FF00CC'])
拜耳阵列简介,可参考博客:http://bbs.fengniao.com/forum/3055346.html(着重建议,品读一下)
Bayer格式介绍:
bayer格式图片是伊士曼·柯达公司科学家Bryce Bayer发明的,Bryce Bayer所发明的拜耳阵列被广泛运用数字图像。
bayer格式图片在一块滤镜上设置的不同的颜色。通过分析人眼对颜色的感知发现,人眼对绿色比较敏感,所以一般bayer格式的图片绿色格式的像素是是r和g像素的和。
bayer格式图片是相机内部的原始图片, 一般后缀名为.raw。很多软件都可以查看, 比如PS。我们相机拍照下来存储在存储卡上的.jpeg或其它格式的图片, 都是从.raw格式转化过来的。如下图,为bayer色彩滤波阵列,由一半的G,1/4的R,1/4的B组成。
Bayer格式插值算法实现
bayer每一个像素仅仅包括了光谱的一部分,必须通过插值来实现每个像素的RGB值。为了从Bayer格式得到每个像素的RGB格式,我们需要通过插值填补缺失的2个色彩。
四种滤色器阵列类型,如图所示:
在(a)与(b)中,R和B分别取邻域的平均值,G等于其本身。
在(c)中,G和B分别取邻域的4个G和B的均值,R等于其本身。
在(d)中,G和R分别取邻域的4个G和R的均值,B等于其本身。
举例(d),Bayer图像转换为RGB:
以B22为例计算:
R通道:
R 22 = ( R 11 + R 13 + R 31 + R 33 ) / 4 R22 = (R11 + R13 + R31 + R33) / 4 R22=(R11+R13+R31+R33)/4
B通道:
B 22 = B 22 B22 = B22 B22=B22
G通道:
G 22 = ( G 12 + G 21 + G 32 + G 23 ) / 4 G22 = (G12 + G21 + G32 + G23) / 4 G22=(G12+G21+G32+G23)/4
其他内容详情可参考博客:https://blog.csdn.net/bingqingsuimeng/article/details/61917513
*如何将bayer图像转换为彩色图像
*读取图像变量
read_image(Image, 'patras')
*获取图像大小
get_image_size(Image, Width, Height)
*打开图像窗口
dev_open_window(0, 0, Width, Height, 'black', WindowHandle)
*显示图像变量
dev_display(Image)
*模拟Bayer图像(该接口是一个自定义函数,奈何没有源码,跪求)
*simulate_bayer_image (Image, ImageCFA)
*创建一个空对象元组
gen_empty_obj(RGBImage)
*Bayer图像转换为RGB图像
*参数1 输入图像
*参数2 输出图像
*参数3 滤色器阵列类型 'bayer_bg'、'bayer_gb'、'bayer_gr'、'bayer_rg' 默认:'bayer_bg'
*参数4 差值类型 'bilinear'、'bilinear_dir'、'bilinear_enhanced' 默认:'bilinear'
*cfa_to_rgb (ImageCFA, RGBImage1, 'bayer_gb', 'bilinear')
上述代码有个很大的遗憾就是没有
模拟bayer图像接口源码,所以没有办法展示效果了,若谁有的可以私信发我哦。感谢
*如何将图像转换为矩阵形式
*读取图像变量
read_image(Image, 'fabrik')
*获取图像大小
get_image_size(Image, Width, Height)
*获取区域里各点坐标
*参数1 输入图像区域
*参数2 区域中的像素行
*参数3 区域里的像素列
get_region_points(Image, Rows, Columns)
*获取图像中每个点的像素值
*参数1 输入图像
*参数2 输入像素行坐标
*参数3 输入像素列坐标
*参数4 像素灰度值
get_grayval(Image, Rows, Columns, Grayval)
*创建一个空的矩阵
*参数1 矩阵的行数
*参数2 矩阵的列数
*参数3 矩阵元素初始值
*参数4 矩阵变量
create_matrix (Height, Width, 0, GrayMatrix)
*给矩阵填充图像数据
*参数1 矩阵变量
*参数2 输入矩阵行坐标
*参数3 输入矩阵列坐标
*参数4 输入像素灰度值
set_value_matrix (GrayMatrix, Rows, Columns, Grayval)
*如何让图像自适应窗口(补充)
*读取图像变量
read_image(Image, 'fabrik')
*获取图像大小
get_image_size (Image, Width, Height)
*关闭图像窗口
dev_close_window()
*创建一个新的图像窗口
dev_open_window(0, 0, Width, Height, 'black', WindowHandle)
*修改显示图像部分
*参数1 修改显示部分左上角的行
*参数2 修改显示部分左上角的列
*参数3 修改显示部分右下角的行
*单数4 修改显示部分右下角的列
dev_set_part(0, 0, Width / 2, Height / 2)
*展示图像变量
dev_display(Image)
补充HACLON学习之旅(三)中的创建自适应图形窗口,因为这段代码里面有一个新出现的算子dev_set_part()。算子注释已放在代码上了。
*如何实现彩色图像转化为灰色图像
***********方法一***********
*读取图像变量
read_image(Image, 'patras')
*展示图像变量
dev_display(Image)
*rgb图像转化为灰度图
*参数1 输入三通道图像
*参数2 灰度图像
*转换公式 灰色 = 0.299 * 红色 + 0.587 * 绿色 + 0.114 * 蓝色。
rgb1_to_gray(Image, GrayImage)
*展示图像变量
dev_display(GrayImage)
*清空图形窗口
dev_clear_window()
***********方法二***********
*三通道图像转换为三个具有相同定义域的单通道图像
*参数1 输入三通道图像
*参数2 红色通道灰度图
*参数3 绿色通道灰度图
*参数4 蓝色通道灰度图
decompose3(Image, ImageR, ImageG, ImageB)
*将RGB转换为成灰度图像
*参数1 输入红色通道
*参数2 输入绿色通道
*参数3 输入蓝色通道
*参数4 灰色图像
*转换公式:灰色 = 0.299 * 红色 + 0.587 * 绿色 + 0.114 * 蓝色
rgb3_to_gray (ImageR, ImageG, ImageB, ImageGray)
*如何实现图像数据类型的互相转换
*读取图像变量
read_image (Image, 'meningg5')
*图像和高斯的导数卷积,进行滤波平滑
*参数1 输入图像
*参数2 平滑后的结果图像
*参数3 高斯的Sigma(σ) 默认值为:1
*参数4 要计算的导数或特征 'none', 'x', 'y', 'gradient', 'gradient_dir', 'xx', 'yy', 'xy', 'xxx', 'yyy', 'xxy', 'xyy', 'laplace' 默认值:'x'
derivate_gauss (Image, Smoothed, 2, 'none')
*图像实数(real)类型转换为byte(8位)类型
*参数1 要更改的图像类型
*参数2 转换后的图像
*参数3 所需的图像类型 'int1', 'int2', 'uint2', 'int4', 'byte', 'real', 'direction', 'cyclic', 'complex' 默认值'byte'
convert_image_type (Smoothed, ImageConverted, 'byte')
*分水岭算法(基于灰度值的拓扑分割图像。该图像被解释为“山脉”。较高的灰度值对应“山”,而较低的灰度值对应“山谷”。在由此产生的山脉分水岭中提取。)
*参数1 平滑输入图像
*参数2 输出分段盆地
*参数3 盆地之间的分水岭
watersheds(ImageConverted, Basins, Watersheds)
*显示图像变量
dev_display(Image)
*显示区域的填充方式
dev_set_draw('margin')
*输出对象的显示颜色数目
dev_set_colored(6)
*显示分割后的盆地
dev_display(Basins)
上述算子derivate_gauss()涉及到了高斯平滑,在此解说一下高斯平滑计算流程。
高斯平滑(高斯模糊)
1、高斯模糊之所以叫高斯模糊,是因为它运用了高斯的正态分布的密度函数
其中,μ是x的均值,σ是x的标准差。由于每次计算都以当前计算点为原点,所以μ等于0。于是公式进一步简化为:
在图形上,正态分布是一种钟形曲线,越接近中心,取值越大,越远离中心,取值越小。
计算平均值的时候,我们只需要将"中心点"(也就是上图中的0点)作为原点,其他点按照其在正态曲线上的位置,分配权重,就可以得到一个加权平均值。
举例:μ = 0, σ = 1.5,假设x轴上有三个像素点,中间点为原点,如图
带入公式后,计算三个点的值为:
这3个像素的权重总和等于0.80858986,如果只计算这3个点的加权平均,还必须让它们的权重之和等于1,因此上面3个值还要分别除以0.80858986。结果如下:
上述三个值就是计算出来的一维高斯核。
2、假设有三个相邻的像素点,像素值如下图(单通道):
现将该三个数与高斯核进行卷积,计算流程: 27 * 0.2533 + 125 * 0.4933 + 22 * 0.2533 = 75.51,记为75,则这三个像素点变为:
绘制拟合灰度值曲线:如下图:
滤波后的波峰曲线要较之滤波前相对平滑一些。
但上面给出的高斯分布公式是一维的,若掩膜是二维的怎么办?
3、很简单,只需要把数值 x 变成向量 x ⃗ \vec{x} x 坐标即可
x ⃗ = ( x , y ) \vec{x} = (x,y) x=(x,y)
即高斯二维方程:
(其中不必纠结于系数 1 ( 2 π σ ) 2 \frac{1}{(\sqrt{2\pi}\sigma)^2} (2πσ)21 ,因为它只是一个常数,并不会影响互相之间的比例关系。并且最终都要进行归一化,所以在实际计算时我们是忽略它而只计算后半部分的)
特别注意
x ⃗ \vec{x} x 本质上都是二维空间中的坐标: x ⃗ \vec{x} x 是掩膜内任一点的坐标, μ ⃗ \vec{\mu} μ 是掩膜中心的坐标
从二维空间看,图像中的任意一点 (x,y),它周围的坐标都有:
假设中间的 (x,y) 是我们公式中的 μ ⃗ \vec{\mu} μ 。 即 μ ⃗ \vec{\mu} μ = (0,0), 将其带入 ( x ⃗ − μ ⃗ ) 2 (\vec{x} - \vec{\mu})^2 (x−μ)2 或 ( x 2 + y 2 ) (x^2 + y^2) (x2+y2) 可得:
取 σ \sigma σ = 1.0, 计算 e − ( x ⃗ − μ ⃗ ) 2 2 σ 2 e^\frac{-(\vec{x} - \vec{\mu})^2}{2\sigma^2} e2σ2−(x−μ)2可得:
归一化(如1中所述方式)求得(高斯掩膜):
4、最后,利用高斯掩膜对图像进行卷积求解高斯模糊,如下:
计算流程:
117 =
102 * 0.075 + 108 * 0.124 + 110 * 0.075 +
119 * 0.124 + 120 * 0.204 + 110 * 0.124 +
129 * 0.075 + 130 * 0.124 + 132 * 0.075
以上就是高斯模糊计算的全部流程,上述结论来源于知乎:https://www.zhihu.com/question/54918332,想要了解多的可以进入该博客进行学习哦。
这边再提供一个具体代码实现的博客,帮助你更好地理解和掌握高斯滤波,链接如下:
https://www.cnblogs.com/wangguchangqing/p/6407717.html 干货多多
上述代码还有一个watersheds()算子,该算子归于图像分割之地形学操作,在此只做演示作用。
*图像分割之地形学操作
*关闭图形窗口
dev_close_window()
*读取图像变量
read_image(Image, 'particle')
*获取图片大小
get_image_size(Image, Width, Height)
*创建一个新的图像窗口
dev_open_window(0, 0, Width, Height, 'black', WindowHandle)
*设置区域填充方式
dev_set_draw('margin')
*设置输出变量显示颜色个数
dev_set_colored(12)
*清空图形窗口
dev_clear_window()
*高斯滤波
*参数1 要平滑的图像
*参数2 过滤后的图像
*参数3 所需要的的过滤器尺寸
gauss_filter(Image, ImageGauss, 9)
*图像反选
*参数1 输入图片
*参数2 具有反转灰度值的图像
*计算流程
*灰度图 g' = 255 - g
*“方向”类型 g’ = (g + 90) % 180
*在有符号类型的情况下,值被否定。生成的图像与输入图像具有相同的像素类型。
invert_image(ImageGauss, ImageInvert)
*从图像中提取分水岭和盆地
watersheds(ImageInvert, Basins, Watersheds)
*展示图像变量
dev_display(Image)
*展示图像变量
*参数1 输入图像
*参数2 分割后的盆地区域
*参数3 分割后的分水岭区域
dev_display(Basins)
分水岭介绍
分水岭算子属于图像分割接口,它是一种基于拓扑理论的数学形态学的分割算法,其基本思想是把图像看作是地质学上的拓扑地貌,图像中每一像素的灰度值表示该点的海拔高度,每一个局部极小值及其周边区域称为集水盆地,而集水盆地的边界则形成分水岭。如下图所示:
分水岭算法整个流程
分水岭算法中会用到一个重要的概念——测地线距离。
测地线距离(Geodesic Distance)
测地线距离就是地球表面两点之间的最短路径(可执行路径)的距离。在此注意,Geodesic Distance(测地线距离) 和 Euclidean Distance(欧氏距离)是有所区别的。
如下图:两个黑点的 Euclidean Distance 是用虚线所表示的线段的长度 d 15 d_{15} d15,而 Geodesic Distance 作为实际路径的最短距离,其距离应为沿途实线段距离之和的最小值,即 d 12 + d 23 + d 34 + d 45 d_{12} + d_{23} + d_{34} + d_{45} d12+d23+d34+d45
图像的灰度空间很像地球表面的整个地理结构,每个像素的灰度值代表高度。其中的灰度值较大的像素连成的线可以看做山脊,也就是分水岭。
其中的水就是用于二值化的灰度阈值,二值化阈值可以理解为水平面,比水平面低的区域会被淹没,刚开始用水填充每个孤立的山谷(局部最小值)。当水平面上升到一定高度时,水就会溢出当前山谷,可以通过在分水岭上修大坝,从而避免两个山谷的水汇集,这样图像就被分成2个像素集,一个是被水淹没的山谷像素集,一个是分水岭线像素集。最终这些大坝形成的线就对整个图像进行了分区,实现对图像的分割。
算法流程:
1、把梯度图像中的所有像素按照灰度值进行分类,并设定一个测地距离阈值
2、找到灰度值最小的像素点(默认标记为灰度值最低点),让threshold从最小值开始增长,这些点为起始点
3、水平面在增长的过程中,会碰到周围的邻域像素,测量这些像素到起始点(灰度值最低点)的测地距离,如果小于设定阈值,则将这些像素淹没,否则在这些像素上设置大坝,这样就对这些邻域像素进行了分类。
4、随着水平面越来越高,会设置更多更高的大坝,直到灰度值的最大值,所有区域都在分水岭线上相遇,这些大坝就对整个图像像素的进行了分区。
5、整体过程可查看下面这个动图:
6、用上面的算法对图像进行分水岭运算,由于噪声点或其它因素的干扰,可能会得到密密麻麻的小区域,即图像被分得太细(over-segmented,过度分割),这因为图像中有非常多的局部极小值点,每个点都会自成一个小区域。解决方法:
①、对图像进行高斯平滑操作,抹除很多小的最小值,这些小分区就会合并。
②、不从最小值开始增长,可以将相对较高的灰度值像素作为起始点(需要用户手动标记),从标记处开始进行淹没,则很多小区域都会被合并为一个区域,这被称为基于图像标记(mark)的分水岭算法。
下面三个图分别是原图,分水岭过分割的图以及基于标记的分水岭算法得到的图:
但其中标记的每个点就相当于分水岭中的注水点,从这些点开始注水使得水平面上升,但是如上图所示,图像中需要分割的区域太多了,手动标记太麻烦,我们可是使用距离转换的方法进行标记,OpenCV中就是使用的这种方法。
上述内容来均参考知乎网,想要了解跟多的,可以进入该网址接进行学习哦。链接如下:
https://zhuanlan.zhihu.com/p/67741538
*如何填充区域之间的空隙或分割重叠区域
*读取图像
read_image(Image, 'fabrik')
*使用区域生长分割图像(将图像分割成相同强度的区域 - 光栅化成大小的矩形。为了判断相邻的两个矩形是否属于同一区域,只使用它们中心点的灰度值。如果灰度值差小于等于公差,则矩形合并为一个区域。)
*参数1 输入图像
*参数2 分割图像区域
*参数3 测试像素之间的垂直距离(光缆高度)
*参数4 测试像素之间的水平距离(光缆宽度)
*参数5 输入公差
*参数6 输出矩形的最小尺寸
regiongrowing(Image, Regions, 1, 1, 3, 1000)
*创建一个空区域
gen_empty_region(NoPixel)
*填充区域之间的空隙或分割重叠区域
*'image'
* 输入区域被迭代扩展,直到它们接触到另一个区域或图像边界。由于同时处理所有区域,因此区域之间的间隙均匀分布到所有区域。通过将重叠区域均匀分布到两个区域来分割重叠区域
*'region'
* 不执行输入区域的扩展。相反,通过将重叠区域均匀分布到各个区域,仅分割重叠区域。因为与原始区域的交集是在输出区域的缩小操作间隙之后计算的,即分割不完整。这可以通过使用原始区域的补充作为“禁区”进行第二次调用来防止。
*参数1 间距要闭合或要分离的区域
*参数2 不发生扩张的区域
*参数3 扩展或分离的区域
*参数4 迭代次数 默认值:'maximal' 建议值:'maximal', 0, 1, 2, 3, 5, 7, 10, 15, 20, 30, 50, 70, 100, 200
*参数5 扩展模式 'image'填充区域间隙 , 'region'分割重叠区域 默认值:'image'
expand_region(Regions, NoPixel, RegionExpanded, 'maximal', 'image')
*展示图像变量
dev_display (Image)
*设置区域的填充方式
dev_set_draw('margin')
*设置输出对象的显示颜色数目
dev_set_colored(6)
*显示图像变量
dev_display (RegionExpanded)
*如何在窗口上显示中文汉字
*关闭图形窗口
dev_close_window()
*读取图像变量
read_image(Image, 'particle')
*获取图像大小
get_image_size(Image, Width, Height)
*创建一个新的图形窗口
dev_open_window(0, 0, Width, Height, 'black', WindowHandle)
*设置区域填充方式
dev_set_draw('margin')
*设置输出变量显示颜色
dev_set_color('green')
*二值化
threshold(Image, Region, 128, 255)
*获取连通区域
connection(Region, ConnectedRegions)
*特征筛选
select_shape_std(ConnectedRegions, SelectedRegions, 'max_area', 70)
*区域和区域中心
*参数1 要检查的区域
*参数2 该区域的面积
*参数3 该区域中心行索引
*参数4 该区域中心列所以
area_center(SelectedRegions, Area, Row, Column)
*展示图像变量
dev_display(Image)
*展示图像变量
dev_display (SelectedRegions)
*设置文本光标的位置
*参数1 活动图形窗口ID
*参数2 文本光标位置的行索引
*参数3 文本光标位置的列索引
set_tposition (WindowHandle, Height / 2, 0)
*在窗口中打印文本
*参数1 活动窗口Id
*参数2 输出的元组
write_string(WindowHandle,'面积最大区域的行坐标:' + Row + '面积最大区域的列坐标' + Column)
自动阈值
*图像分割之阈值操作
*************自动阈值*************
*关闭图形窗口
dev_close_window()
*读取图像变量
read_image(Image, 'egypt1')
*获取图像大小
get_image_size(Image, Width, Height)
*创建一个新的图像窗口
dev_open_window(0, 0, Width, Height, 'black', WindowHandle)
*清空图形窗口
dev_clear_window ()
*使用多个阈值分割单通道图像。
*首先,确定灰度值的直方图。然后,从直方图中提取相关的最小值,这些最小值被连续用作阈值操作的参数(通过平滑直方图,您可以影响在输入图像中找到的类数)。
*参数1 输入图像
*参数2 分割后的区域
*参数3 Sigma用于直方图的高斯平滑
auto_threshold (Image, Regions, 4)
自动阈值计算流程:
1、获取灰度直方图后,对直方图进行高斯滤波平滑。
2、计算图像直方图中的局部最小值以确定阈值
3,上图可见出现多个局部最小值。再对直方图进行深层次平滑
4,计算直方图的局部最小值以确定阈值
5,由上可得:通过平滑直方图,您可以影响在输入图像中找到的类数。如下图:
输入图像(原图) 输入图像(原图) 输入图像(原图)
从稍微平滑的直方图中提取四个类别 从稍微平滑的直方图中提取四个类别 从稍微平滑的直方图中提取四个类别
进一步平滑的直方图产生两个区域 进一步平滑的直方图产生两个区域 进一步平滑的直方图产生两个区域
*************二值化阈值*************
*清空图形窗口
dev_clear_window ()
*使用二值阈值分割图像
*'max_separability'
*通过选择'max_separability',会调用基于 Otsu 的灰度直方图的自动阈值处理(大津法)。该算法首先计算图像的直方图,然后使用统计矩找到将像素划分为前景和背景并使这两类之间的可分离性最大化的最佳阈值。此方法仅适用于 byte 和 uint2 图像
*'smooth_histo'
*通过选择'smooth_histo'按以下方式确定阈值:首先,确定灰度值的直方图。然后,从直方图中提取相关的最小值,用作阈值操作的参数。为了减少最小值的数量,直方图用高斯平滑,如。掩模尺寸被放大,直到平滑的直方图中只有一个最小值。然后,将阈值设置为该最小值的位置
*'light' 所有灰度值大于或等于的像素都被选中
*'dark' 所有灰度值小于的像素都被选中
*参数1 输入图像
*参数2 分割后的区域
*参数3 分割方法
*参数4 提取前景还是背景
*参数5 返回使用的阈值
binary_threshold (Image, Region, 'max_separability', 'dark', UsedThreshold)
*************字符阈值*************
*关闭图形窗口
dev_close_window()
*读取图像变量
read_image(Image1, 'F:/halcon_Learning/字符分割.png')
*获取图像大小
get_image_size(Image1, Width1, Height1)
*创建一个新的图像窗口
dev_open_window(0, 0, Width1, Height1, 'black', WindowHandle)
*清空图形窗口
dev_clear_window ()
*字符分割(主要应用是在亮纸上分割暗字符的单通道图像。直方图中的最大峰值对应于浅色背景。假设文本比背景更暗,您检查最大值左侧的平滑直方图。该参数决定了设置的阈值离最大值有多远,同时考虑了灰度值的频率。)
*该算子的工作方式如下:
*首先,计算图像中灰度值的直方图,为了消除噪声,直方图使用给定的(Sigma)进行平滑
*在直方图中,背景(白纸)在高灰度值处对应一个大峰,而字符在低灰度值处形成一个小峰。
*与运算符binary_threshold(与'Method'='smooth_histo')相比,它位于两个峰值之间的最小值。这里分割的阈值是根据直方图的最大值来确定的,即背景,有以下条件
*直方图阈值 * 100 < 直方图最大值 * (100 - 百分比)
*如果选择Percent = 95,该操作符将定位频率最多为最大频率的5%的灰色值。因为char_threshold假设字符比背景颜色深,所以阈值是在最大值的“左边”搜索的。
*提示一下
*与binary_threshold相比,如果字符和背景对应的直方图峰值之间没有明确的最小值,或者字符根本没有对应的峰值,则应该使用该操作符。这可能发生,例如,如果图像只包含少数字符或在不均匀照明的情况下。
*参数1 输入图像
*参数2 计算直方图区域
*参数3 分割后的区域
*参数4 用与直方图的高斯平滑
*参数5 灰度值差异的百分比 默认值:95 即直方图阈值 < 直方图最大值 * (100 - 95)
*参数6 计算的阈值
char_threshold (Image1, Image1, Characters, 6, 95, Threshold)
对于字符分割算子,下面给一些图例做参考,有助于更好的理解算子实现的功能。
输入图像(原始图) 输入图像(原始图) 输入图像(原始图)
直方图被平滑并识别全局最大值 直方图被平滑并识别全局最大值 直方图被平滑并识别全局最大值
参数确定最大值左侧的阈值 参数确定最大值左侧的阈值 参数确定最大值左侧的阈值
结果图像 结果图像 结果图像
签名图像的阈值分割
*************签名图像的阈值分割*************
*关闭图形窗口
dev_close_window()
*读取图像变量
read_image(Traffic1, 'traffic1')
*读取图像变量
read_image(Traffic2, 'traffic2')
*将图像由byte类型转化为int2类型
convert_image_type (Traffic1, ImageConverted1, 'int2')
*将图像由byte类型转化为int2类型
convert_image_type(Traffic2, ImageConverted2, 'int2')
*将两个图像相减
*计算流程 g' = (g2 - g1) * 修正系数 + 修正值
*参数1 被减数图像变量
*参数2 减数图像变量
*参数3 减完之后的图像变量
*参数4 修正系数 默认值:1.0
*参数5 修正值 默认值:128
sub_image (ImageConverted1, ImageConverted2, ImageSub, 1, 0)
*将有符号图像分割为正负两个区域
*将输入图像分割为一个灰度值大于等于threshold(“正”区域)和一个灰度值小于或等于负Threshold的区域(“负”区域)
*只考虑大小大于MinSize的“正”或“负”区域。对最大灰度值(绝对值)小于MinGray的区域进行抑制
*进行的分割是不完整的,即“正”和“负”区域不一定同时覆盖整个图像
*没有考虑灰度值分别在Threshold和负Threshold之间、MinGray和负MinGray之间的区域
*参数1 输入图像
*参数2 分割后的区域
*参数3 小于 MinSize 的区域被抑制。
*参数4 对灰度值(绝对值)小于 MinGray 的区域被抑制。
*参数5 分割正区,负区threshold值
dual_threshold(ImageSub, RegionCrossings, 500, 20, 10)
*************局部阈值分割1*************
*关闭图形窗口
dev_close_window()
*读取图像变量
read_image(Image2, 'egypt1')
*获取图像大小
get_image_size(Image2, Width2, Height2)
*创建一个新的图像窗口
dev_open_window(0, 0, Width2, Height2, 'black', WindowHandle)
*清空图形窗口
dev_clear_window ()
*平均平滑滤波
*参数1 要平滑的图像
*参数2 平滑后的图像
*参数3 过滤器掩码宽度
*参数4 过滤器掩码高度
mean_image(Image2, ImageMean, 9, 9)
*使用局部阈值分割图像(一般用于提取对象的轮廓,其中对象的大小(直径)由低通滤波器的掩码大小和对象边缘的幅度决定)
*从输入图像中选择像素满足阈值条件的区域。
*令g_{o} = g_{原始图像}, g_{t} = g_{滤波后的图像}。
*那么LightDark = 'light'的条件是:g_{o} >= g_{t} + Offset
*对于LightDark = 'dark',条件是:g_{o} <= g_{t} - Offset
*对于LightDark = 'equal',它是:g_{t} - Offset <= g_{o} <= g_{t} + Offset
*最后,对于LightDark = 'not_equal',它是:g_{t} - Offset > g_{o} || g_{o} > g_{t} + Offset
*参数1 输入原始图像
*参数2 输入滤波后的图像
*参数3 分割后的区域
*参数4 用于g_{阈值图}的偏移量
*参数5 提取亮区,暗区或类似相似区域
dyn_threshold (Image2, ImageMean, RegionDynThresh, 15, 'not_equal')
*展示图像变量
dev_display(RegionDynThresh)
对于dyn_局部分割算子,下面给一些图例做参考,有助于更好的理解算子实现的功能。
输入图像(原始图) 输入图像(原始图) 输入图像(原始图)
由于光照条件不均匀,无法用全局阈值分割划痕 由于光照条件不均匀,无法用全局阈值分割划痕 由于光照条件不均匀,无法用全局阈值分割划痕
检测原始图像 ( 左 ) 与图像均值 ( 中 ) 逐像素的灰度值差到分割区域 ( 右 ) 超过 O f f s e t ( 图像细节 ) 检测原始图像(左)与图像均值(中)逐像素的灰度值差到分割区域(右)超过Offset(图像细节) 检测原始图像(左)与图像均值(中)逐像素的灰度值差到分割区域(右)超过Offset(图像细节)
选择所选区域后的结果 选择所选区域后的结果 选择所选区域后的结果
*************局部阈值分割2*************
*关闭图形窗口
dev_close_window()
*读取图像变量
read_image(Image3, 'F:/halcon_Learning/局部阈值分割2.bmp')
*获取图像大小
get_image_size(Image3, Width3, Height3)
*创建一个新的图像窗口
dev_open_window(0, 0, Width3, Height3, 'black', WindowHandle)
*清空图形窗口
dev_clear_window ()
*使用局部阈值分割图像。
*基于局部平均值和标准差的局部自适应阈值。使用场合不均匀的照明或噪声区域
*使用的方式为'adapted_std_deviation' 目前只有一种此一种
*参数1 输入图像
*参数2 分割后的区域
*参数3 分割方法
*参数4 提取前景(亮色区)还是背景(暗色区)默认值:'dark'
*参数5 设置特定于“adapted_std_deviation”方法的附加参数 'mask_size', 'range', 'scale' 默认值:mask_size
*参数6 参数值列表(对应参数5)
local_threshold (Image3, Region3, 'adapted_std_deviation', 'dark', [], [])
对于local_局部分割算子,考虑到局部均值和标准偏差,为每个像素计算单独的阈值。邻域的大小由用户设置。当光照条件或背景不均匀时,这个操作符特别适合文本分割。
在此,对局部均值和标准差说明一下:
局部:即图像在某个窗口内的值。例如求图像3×3窗口内的均值和标准差。
均值公式: 1 n ∗ ( x 1 + x 2 + x 3 + ⋯ + x n ) \frac{1}{n}*(x_1 +x_2+x_3+\cdots+x_n) n1∗(x1+x2+x3+⋯+xn)
标准差公式: 1 n ∑ i = 1 n ( x i − μ ) 2 \sqrt{\frac{1}{n}\sum_{i=1}^n (x_i - \mu)^2} n1∑i=1n(xi−μ)2
举例:黑暗前景的分割(LightDark = ‘dark’),即浅色背景上的深色结构将被分割。选取灰度值小于计算的局部阈值T(r,c)的每一个像素p(r,c)。一个局部阈值T(r,c)在(mask_size * mask_size)的窗口内计算,如下所示:
T ( r , c ) = μ ( r , c ) ( 1 + k ( σ ( r , c ) R − 1 ) ) T(r,c) = \mu(r,c) (1 + k(\frac{\sigma(r,c)}{R} - 1)) T(r,c)=μ(r,c)(1+k(Rσ(r,c)−1))
其中 μ ( r , c ) \mu(r,c) μ(r,c)为窗口内局部均值, σ ( r , c ) \sigma(r,c) σ(r,c)为对应的标准差,参数R是假定的标准偏差的最大值(对于字节图像R = 128),k是一个参数,控制阈值T(R,c)与平均值的差异程度。如果在一个点(r,c)的邻域有高对比度,则标准差有一个接近r的值,从而产生一个接近局部均值的阈值T(r,c)。如果对比度较低,则局部阈值低于局部均值。对于包含较暗区域的浅色背景上的深色文本,这个较低的阈值使得即使在较暗区域也可以分割文本。
‘mask_size’:指定掩码大小,即计算本地阈值所在的邻域的大小。窗口尺寸越小,分割的笔画越细。‘mask_size’必须设置为大于要分割的字符或结构的笔画宽度的值。如果’mask_size’是偶数,则使用下一个更大的奇数值。
‘scale’:设置参数k(),该参数控制阈值与本地平均值的差异程度。使用较小的“比例”值来分割结构,使其与背景的对比更低。使用较大的值来抑制杂乱。建议值:0.2(默认)、0.3、0.5
range’:设置标准差R的最大假设值。该参数应根据预期的灰度值范围进行调整。作为一个经验法则,“range”的值可以设置为range = 0.5 * (MaxGray - MinGray),其中MinGray和MaxGray是图像中的最小和最大灰度值,这可以通过min_max_gray确定。
建议值:字节图像128(默认),uint2图像32767.5(默认)
下面给一些图例做参考,有助于更好的理解算子实现的功能。
输入图像(原始图) 输入图像(原始图) 输入图像(原始图)
使用全局阈值进行分割并不仅仅只选择到想要的文本 使用全局阈值进行分割并不仅仅只选择到想要的文本 使用全局阈值进行分割并不仅仅只选择到想要的文本
将阈值分别适应于每个像素的邻域 将阈值分别适应于每个像素的邻域 将阈值分别适应于每个像素的邻域
文本分割 文本分割 文本分割
*************全阈值分割*************
*关闭图形窗口
dev_close_window()
*读取图像变量
read_image(Image4, 'egypt1')
*获取图像大小
get_image_size(Image4, Width4, Height4)
*创建一个新的图像窗口
dev_open_window(0, 0, Width4, Height4, 'black', WindowHandle)
*清空图形窗口
dev_clear_window ()
*使用全阈值分割图像
*从输入图像中选择灰度值 g 满足以下条件的像素:MinGray <= g <= MaxGray
*参数1 输入图像
*参数2 分割后的区域
*参数3 灰度值下限MinGray
*参数4 灰度值上限MaxGray
threshold(Image4, Region4, 128, 255)
*************局部均值和标准差分析阈值分割*************
*关闭图形窗口
dev_close_window()
*读取图像变量
read_image(Image5, 'F:/halcon_Learning/局部均值和标准差分割.png')
*获取图像大小
get_image_size(Image5, Width5, Height5)
*创建一个新的图像窗口
dev_open_window(0, 0, Width5, Height5, 'black', WindowHandle)
*清空图形窗口
dev_clear_window ()
*通过局部平均值和标准偏差进行阈值图像。试用场合不均匀的照明或噪声的图像
*参数1 输入图像
*参数2 分割后的图像
*参数3 均值和标准差掩码宽度 默认值:15
*参数4 均值和标准差掩码高度 默认值:15
*参数5 灰度值标准差的因子 默认值:0.2
*参数6 最小灰度值与平均值的差异 默认值:2
*参数7 阈值类型 'dark'、'equal'、'light'、'not_equal' 默认值:'dark'
var_threshold (Image5, Region5, 15, 15, 1.0, 40, 'dark')
运算符var_局部分割算子的工作方式与local_局部分割算子类似,只不过它选择的是符合特定条件的图像点,即它们的局部标准偏差和亮度。
*文本文件相关操作算子
**************向文本文件写入字符串内容**************
*新建一个图形窗口
dev_open_window(0, 0, 512, 512, 'black', WindowHandle)
*读取图像变量
read_image(Image, 'fabrik')
*全局阈值分割
threshold(Image, Region, 0, 128)
*展示图形变量
dev_display(Region)
*获取每个region的面积和中心坐标
area_center(Region, Area, Row, Column)
*打开文本或二进制格式的文件
*参数1 要打开的文件名(不存在会自动创建)
*参数2 文件访问方式和字符串编码 默认:'output'
*'input' 打开已存在的输入文件以文本格式读取
*'output' 打开一个新的输出文件以进行文本格式的写入
*'append' 打开一个已经存在的输出文件,以文本格式写入文件的末尾
*'input_binary' 打开已存在的输入文件以二进制格式读取
*'output_binary' 打开一个新的输出文件以二进制格式写入
*'append_binary' 打开一个已经存在的输出文件,以便在文件末尾以二进制格式写入
*对于文本文件,传递给FileType的元组可以通过以下编码设置之一扩展
*'utf8_encoding' 文件中的字符串使用UTF-8编码。这是默认值,所以对于UTF-8编码的文件和所有只使用纯7位US-ASCII字符的文件,这个值可以省略
*'locale_encoding' 文件中的字符串以local-8位编码,这取决于系统当前的语言环境设置。例如,在Windows下由代码页1252(微软拉丁语-1方言)或932 (Shift-JIS)定义有效编码,在Linux上由locale en_US定义有效编码。use utf8, de_DE, iso885915或ja_JP.sjis
*'ignore_encoding' 不处理从文件中读取或写入的字符串的编码。在这种模式下,多字节字符既不处理也不解释,操作符freread_char总是返回一个字节,而依赖于特定区域的分隔符字符不会在freread_line和freread_string中处理。此外,不会将字符串转换为或从HALCON库的当前编码
*参数3 文件句柄(ID)
open_file('area.txt', 'output', FileHandle)
*将字符串和数字写入文本文件
*运算符将一个或多个字符串或数字写入由句柄定义的输出文件。输出文件必须以文本格式打开
*要写入文件的字符串和数字通过输入参数传递给运算符。该参数还接受字符串和数字混合的元组。元组的所有元素被连续写入文件,中间没有空格或其他分隔符。数字在写入之前被转换为字符串。
*参数1 文件句柄
*参数2 要输入的文件值
fwrite_string (FileHandle, '面积是:' + Area + '像素\n')
*将字符串和数字写入文本文件
fwrite_string (FileHandle, 'Row:' + Row + ' Column:' + Column + '\n')
*关闭文本文件
close_file (FileHandle)
**************向文本文件写入字符串内容**************
*打开文本或二进制格式的文件
open_file('area.txt', 'input', FileHandle1)
*从文本文件中读取字符行
*运算符从句柄定义的输入文件中读取整行(包括换行符)。输入文件必须以文本格式打开
*如果在将任何字符写入输出字符串之前到达文件末尾,则参数IsEOF将返回值1,否则为0
*参数1 要打开的文件名
*参数2 读取行
*参数3 参数IsEOF
fread_line (FileHandle1, OutLine, IsEOF)
*将文本打印在图像窗口上
disp_message(WindowHandle, OutLine, 'window', 12, 12, 'black', 'true')
a := 30
while(IsEOF != 1)
fread_line (FileHandle1, OutLine, IsEOF)
*打印文本信息
disp_message(WindowHandle, OutLine, 'window', 12 + a, 12, 'black', 'true')
a := a + 30
endwhile
*关闭文本文件
close_file (FileHandle)
**************获取文件夹下面的所有文件目录**************
*获取 HALCON 系统参数的当前值
*参数1 所需的系统参数
*参数2 系统参数当前值
get_system('example_dir', HalconExamples)
ExamplesDir := HalconExamples + '\\hdevelop'
*列出目录里面的所有文件
*参数1 要列出的目录名称
*参数2 处理选项
*'files' 文件
*'directories' 目录
*'recursive' 可以指定应该通过检查所有子目录来递归搜索目录树
*'follow_links' 可用于指定应遵循文件或目录的符号链接。在默认设置中,符号链接不会被取消引用,因此如果它们指向目录,则不会搜索,如果它们指向文件,则不会返回
*'max_depth 5' 对于递归搜索,指定最大搜索深度
*'max_files 1000' 对于递归搜索,指定最大搜索文件数量
*参数3 找到的文件(和目录)
list_files (ExamplesDir, ['files', 'recursive'], ExampleFiles)
**************判断文件是否存在**************
*关闭图形窗口
dev_close_window()
*创建一个新的图形窗口
dev_open_window(Row, Column, 512, 512, 'black', WindowHandle1)
FileName := '1.jpg'
*判断文件是否存在
*参数1 要检测的文件名
*参数2 布尔值(若检测存在则返回true, 检测不存在返回false)
file_exists(FileName, FileExists)
if(FileExists)
*打印文本信息
disp_message (WindowHandle1, '文件存在', 'window', 12, 12, 'black', 'true')
else
disp_message (WindowHandle1, '文件不存在', 'window', 12, 12, 'black', 'true')
endif
效果展示:
文本文件读写
获取文件夹下面的所有文件和目录
判断文件是否存在
*形态学操作
********************膨胀********************
*读取图像变量
read_image(Image, 'fabrik')
*全局阈值分割
threshold(Image, Region, 128, 255)
*获取连通区域
connection(Region, ConnectedRegions)
*特征过滤筛选
select_shape(ConnectedRegions, SelectedRegions, 'area', 'and', 8000, 9000)
*用矩形结构元素膨胀
*参数1 要扩张的区域
*参数2 扩张区域
*参数3 结构矩形宽度
*参数4 结构矩形高度
dilation_rectangle1(SelectedRegions, RegionDilation1, 3, 3)
*用圆形结构元素膨胀
*参数1 要扩张的区域
*参数2 扩张区域
*参数3 圆形结构元素的半径
dilation_circle (SelectedRegions, RegionDilation2, 2.5)
*用矩形结构元素腐蚀
*参数1 被侵蚀的区域
*参数2 侵蚀区域
*参数3 结构矩形宽度
*参数4 结构矩形高度
erosion_rectangle1(SelectedRegions, RegionErosion1, 3, 3)
*用圆形结构元素腐蚀
*参数1 被侵蚀的区域
*参数2 侵蚀区域
*参数3 圆形结构元素的半径
erosion_circle(SelectedRegions, RegionErosion2, 2.5)
*用矩形结构元素开运算
*参数1 待开运算区域
*参数2 开运算区域
*参数3 结构矩形宽度
*参数4 结构矩形高度
opening_rectangle1(SelectedRegions, RegionOpening1, 10, 10)
*用圆形结构元素开运算
*参数1 待开运算区域
*参数2 开运算区域
*参数3 圆形结构元素的半径
opening_circle(SelectedRegions, RegionOpening2, 2.5)
*用矩形结构元素闭运算
*参数1 待闭运算区域
*参数2 闭运算区域
*参数3 结构矩形宽度
*参数4 结构矩形高度
closing_rectangle1(SelectedRegions, RegionClosing1, 10, 10)
*用圆形结构元素闭运算
*参数1 待闭运算区域
*参数2 闭运算区域
*参数3 圆形结构元素的半径
closing_circle(SelectedRegions, RegionClosing2, 1.5)
在此,详细讲解一下形态学操作(在图形处理过程中,该算法占据很重要
的地位哦)。
形态学概述
简单来讲,形态学操作就是基于形状的一系列图像处理操作。其基本的运算包括:腐蚀和膨胀、开闭运算、骨架抽取、形态学梯度等。其作用主要有:消除噪声、分割出独立的图像元素、在图像中连接相邻的元素、寻找图像中的明显的极大值区域或极小值区域、求出图像的梯度等。
进行腐蚀和膨胀的讲解之前,特别注意
:腐蚀和膨胀是对白色部分(高亮部分)而言的,不是黑色部分。膨胀就是图像中的高亮部分进行膨胀,“领域扩张”,效果图拥有比原图更大的高亮区域。腐蚀就是原图中的高亮部分被腐蚀,“领域被蚕食”,效果图拥有比原图更小的高亮区域。
膨胀
从数学方面来说,膨胀或者腐蚀操作就是将图像(或图像的一部分区域,我们称之为A)与核(我们称之为B)进行卷积。核可以是任何的形状和大小,它拥有一个单独定义出来的参考点,我们称其为锚点(anchorpoint)。多数情况下,核是一个小的中间带有参考点和实心正方形或者圆盘,其实,我们可以把核视为模板或者掩码。即计算核B覆盖的区域的像素点的最大值,并把这个最大值赋值给参考点指定的像素。这样就会使图像中的高亮区域逐渐增长。
而膨胀就是求局部最大值的操作,核B与图形卷积,即计算核B覆盖的区域的像素点的最大值,并把这个最大值赋值给参考点指定的像素。这样就会使图像中的高亮区域逐渐增长。如下图所示,这就是膨胀操作的初衷(这边注意一下,它并非真正膨胀之后的效果图
)。
实际的膨胀效果如下:
中间的⭐️号,必须附着在原始图像上。
腐蚀
腐蚀与膨胀是完全相反的操作,腐蚀是求局部最小值的操作。原理图如下:
实际的腐蚀效果如下:
开运算
先腐蚀后膨胀叫开运算。
其作用:开操作可以删除二值图像中小的干扰块,降低图像二值化之后噪点过多的问题,最重要的是可以保持物体原有大小不变。
实际的开运算效果如下:
闭运算
先膨胀后腐蚀。
其作用:闭操作可以排除小型黑洞,将两个临近的区域连接起来,形成连通域。最重要的是可以保持物体原有大小不变。
实际的闭运算效果如下:
上述结论参考博客:https://blog.csdn.net/poem_qianmo/article/details/23710721,其中还有很多代码讲解,感兴趣的可以一观。