引言
auto_threshold — 使用从直方图确定的阈值对图像进行分割。 auto_threshold 使用多个阈值分割单通道图像。
首先,确定灰度值的绝对直方图。
然后,从直方图中提取相关的最小值,这些最小值被连续用作阈值操作的参数。用于字节图像的阈值为 0、255,以及从直方图中提取的所有最小值(在直方图使用标准差为 Sigma 的高斯滤波器平滑后)。
对于每个灰度值区间,生成一个区域。因此,区域的数量是最小值的数量 + 1。对于 uint2 图像,类似地使用上述过程。然而,这里的最高阈值是 65535。
此外,对于 uint2 图像,Sigma 的值(实际上)指的是具有 256 个值的直方图,尽管内部使用了更高分辨率的直方图。这样做是为了方便在图像类型之间切换,而无需更改参数 Sigma。对于浮点图像,阈值是图像中的最小和最大灰度值以及从直方图中提取的所有最小值。这里,参数Sigma的缩放是指图像的原始灰度值。
Sigma 的值越大,提取的区域就越少。如果要提取的区域表现出相似的灰度值(同质区域),则此运算符很有用。
auto_threshold(Image : Regions : Sigma : )
/*
Image (输入input_object)
输入图像。
Regions (output_object)
在自动确定的间隔内具有灰度值的区域。
Sigma (input_control)
Sigma 用于直方图的高斯平滑。
默认值:2.0
建议值:0.0、0.5、1.0、2.0、3.0、4.0、5.0
典型值范围:0.0 ≤ Sigma ≤ 100.0 (lin)
最小增量:0.01
推荐增量:0.3
限制:Sigma >= 0.0
*/
一、结果
1.1 Halcon演示结果
1.2 MFC演示结果
二、Halcon代码
dev_close_window ()
read_image (Aegypt1, 'egypt1')
get_image_size (Aegypt1, Width, Height)
dev_open_window (0, 0, Width, Height, 'black', WindowID)
set_display_font (WindowID, 14, 'mono', 'true', 'false')
dev_set_colored (6)
dev_clear_window ()
Sigma := 4
auto_threshold (Aegypt1, Regions, Sigma)
gray_histo (Aegypt1, Aegypt1, AbsoluteHisto, RelativeHisto)
disp_continue_message (WindowID, 'black', 'true')
stop ()
dev_clear_window ()
create_funct_1d_array (AbsoluteHisto, Function)
smooth_funct_1d_gauss (Function, Sigma, SmoothedFunction)
dev_set_color ('red')
funct_1d_to_pairs (SmoothedFunction, XValues, YValues)
gen_region_histo (Histo1, YValues, 255, 255, 1)
dev_display (Aegypt1)
dev_set_color ('white')
gen_region_histo (Histo2, RelativeHisto, 255, 255, 1)
三、MFC源代码
1.头文件主要代码
public:
MyAssist myassist;
HTuple picture1_WindowID,picture2_WindowID;
CWnd *pWnd;
// Local iconic variables
HObject ho_Aegypt1, ho_Regions, ho_Histo1, ho_Histo2;
// Local control variables
HTuple hv_Width, hv_Height, hv_WindowID, hv_Sigma;
HTuple hv_AbsoluteHisto, hv_RelativeHisto, hv_Function;
HTuple hv_SmoothedFunction, hv_XValues, hv_YValues;
public:
afx_msg void OnBnClickedButtonReadImg();
afx_msg void OnBnClickedButtonProcessImg();
2.源文件主要代码
void CHalconMFCDlg::OnBnClickedButtonReadImg()
{
// TODO: 在此添加控件通知处理程序代码
CRect rect;
pWnd = GetDlgItem(IDC_STATIC_ORIGINAL_IMG);
picture1_WindowID = (Hlong)pWnd->m_hWnd;
pWnd->GetWindowRect(&rect);
ReadImage(&ho_Aegypt1, "egypt1");
GetImageSize(ho_Aegypt1, &hv_Width, &hv_Height);
SetWindowAttr("background_color", "black");
OpenWindow(0, 0, rect.Width(), rect.Height(), picture1_WindowID, "visible", "", &hv_WindowID);
HDevWindowStack::Push(hv_WindowID);
if (HDevWindowStack::IsOpen())
SetDraw(HDevWindowStack::GetActive(), "margin");
if (HDevWindowStack::IsOpen())
SetLineWidth(HDevWindowStack::GetActive(), 3);
if (HDevWindowStack::IsOpen())
DispObj(ho_Aegypt1, HDevWindowStack::GetActive());
}
void CHalconMFCDlg::OnBnClickedButtonProcessImg()
{
// TODO: 在此添加控件通知处理程序代码
CRect Rect;
pWnd = GetDlgItem(IDC_STATIC_PROCESS_IMAGE);
picture2_WindowID = (Hlong)pWnd->m_hWnd;
pWnd->GetWindowRect(&Rect);
OpenWindow(0, 0, Rect.Width(), Rect.Height(), picture2_WindowID, "visible", "", &hv_WindowID);
HDevWindowStack::Push(hv_WindowID);
myassist.set_display_font(hv_WindowID, 14, "mono", "true", "false");
if (HDevWindowStack::IsOpen())
SetColored(HDevWindowStack::GetActive(), 6);
if (HDevWindowStack::IsOpen())
ClearWindow(HDevWindowStack::GetActive());
hv_Sigma = 4;
AutoThreshold(ho_Aegypt1, &ho_Regions, hv_Sigma);
GrayHisto(ho_Aegypt1, ho_Aegypt1, &hv_AbsoluteHisto, &hv_RelativeHisto);
//myassist.disp_continue_message(hv_WindowID, "black", "true");
// stop(); only in hdevelop
if (HDevWindowStack::IsOpen())
ClearWindow(HDevWindowStack::GetActive());
CreateFunct1dArray(hv_AbsoluteHisto, &hv_Function);
SmoothFunct1dGauss(hv_Function, hv_Sigma, &hv_SmoothedFunction);
if (HDevWindowStack::IsOpen())
SetColor(HDevWindowStack::GetActive(), "red");
Funct1dToPairs(hv_SmoothedFunction, &hv_XValues, &hv_YValues);
GenRegionHisto(&ho_Histo1, hv_YValues, 255, 255, 1);
DispObj(ho_Histo1, HDevWindowStack::GetActive());
/*if (HDevWindowStack::IsOpen())
DispObj(ho_Aegypt1, HDevWindowStack::GetActive());*/
if (HDevWindowStack::IsOpen())
SetColor(HDevWindowStack::GetActive(), "white");
GenRegionHisto(&ho_Histo2, hv_RelativeHisto, 255, 255, 1);
if (HDevWindowStack::IsOpen())
DispObj(ho_Histo2, HDevWindowStack::GetActive());
}
分析总结
当图像的灰度值存在差异,可通过阈值分割的方法提取目标所在的区域。常用算子有:1.auto_threshold — 使用从直方图确定的阈值对图像进行分割。
2.bin_threshold — 使用自动确定的阈值分割图像。
bin_threshold 使用自动确定的阈值分割单通道灰度值图像。 首先,确定灰度值的相对直方图。 然后,从直方图中提取相关的最小值,用作阈值操作的参数。 为了减少最小值的数量,直方图用高斯平滑,如 auto_threshold。 掩模尺寸被放大,直到平滑的直方图中只有一个最小值。 所选区域包含灰度值从 0 到最小值的像素,或者对于真实图像从最小值到各自的最小值。 例如,此运算符可用于分割浅色纸上的深色字符。
3.char_threshold — 执行阈值分割以提取字符。
char_threshold 的主要应用是在亮纸上分割暗字符的单通道图像。该运算符的工作原理如下:首先,针对 HistoRegion{} 区域中的点计算图像 Image 中灰度值的直方图。为了消除噪声,直方图使用给定的 Sigma(高斯平滑)进行平滑。在直方图中,背景(白纸)在高灰度值处对应一个大峰,而字符在低灰度值处形成一个小峰。与定位两个峰值之间最小值的操作符 binary_threshold(带有 'Method'='smooth_histo')相比,这里的分割阈值是相对于直方图的最大值(即背景)确定的以下条件: histogram[threshold] * 100.0 < histogram[maximum] * (100.0 - Percent) 例如,如果您选择百分比 = 95,则操作员会定位频率最多为最大频率 5% 的灰度值。因为 char_threshold 假设字符比背景更暗,所以搜索阈值最大值的“左边”。 与 binary_threshold 相比,如果在分别对应于字符和背景的直方图峰值之间没有明确的最小值,或者根本没有对应于字符的峰值,则应使用此运算符。这可能发生,例如,如果图像只包含几个字符或在非均匀照明的情况下。
4.check_difference — 逐像素比较两个图像。
check_difference 从输入图像 Image 中选取那些像素 (g_{o} = g_{Image}),其灰度值与 Pattern 中对应像素的差值在区间 [DiffLowerBound,DiffUpperBound] 的内部(外部)。 Pattern 的像素由 (AddRow,AddCol) 相对于 Image 进行转换。 令 g_{p} 是由 (AddRow,AddCol) 相对于 g_{o} translated Pattern 的灰度值。
如果选择的模式 Mode 是 'diff_inside',则选择一个像素 g_{o},如果
如果模式设置为 'diff_outside',则选择一个像素 g_{o},如果 g_o - g_p - GrayOffset < DiffLowerBound 或 g_o - g_p - GrayOffset > DiffUpperBound。 该测试是针对 Image 域(区域)的所有点执行的,与转换后的 Pattern 域相交。 满足上述条件的所有点都聚集在输出区域中。 两个图像的大小可能不同。 通常,Pattern 小于 Image。
5.dual_threshold — 签名图像的阈值运算符。
dual_threshold 将输入图像分割为灰度值 >= Threshold 的区域(“正”区域)和灰度值 <= -Threshold 的区域(“负”区域)。仅考虑大小大于 MinSize 的“正”或“负”区域。并且最大灰度值绝对值小于MinGray的区域被抑制。 执行的分割不完整,即“正”和“负”区域一起不一定覆盖整个图像:灰度值分别介于 -Threshold 和 Threshold、-MinGray 和 MinGray 之间的区域不被考虑. dual_threshold 通常在将拉普拉斯算子(laplace、laplace_of_gauss、derivate_gauss 或 diff_of_gauss)应用于图像或两个图像的差异(sub_image)后调用。 拉普拉斯图像的零交叉点对应于图像的边缘,是拉普拉斯图像中“正”和“负”区域的分隔区域。它们可以通过使用 Threshold = 1 调用 dual_threshold 来确定,然后使用补码创建补码区域。参数 MinGray 决定了噪声不变性,而 MinSize 决定了边缘检测的分辨率。 使用字节图像,仅应用运算符的正部分。因此,dual_threshold 的行为类似于具有连续连接和 select_gray 的标准阈值运算符(阈值)。
6.dyn_threshold — 使用局部阈值分割图像。
1394 / 5000
翻译结果
dyn_threshold 从输入图像中选择像素满足阈值条件的那些区域。设 g_{o} = g_{OrigImage},并且 g_{t} = g_{ThresholdImage}。那么 LightDark = 'light' 的条件是:
对于 LightDark = 'dark' 条件是:
对于 LightDark = 'equal',它是:
最后,对于 LightDark = 'not_equal',它是:
通常,阈值图像是原始图像的平滑版本(例如,通过应用 mean_image、binomial_filter、gauss_filter 等)。然后 dyn_threshold 的效果类似于将阈值应用于原始图像的高通滤波版本(参见 highpass_image)。
使用 dyn_threshold,可以提取对象的轮廓,其中对象的大小(直径)由低通滤波器的掩码大小和对象边缘的幅度决定:
选择的掩模尺寸越大,找到的区域就越大。根据经验,遮罩大小应该是要提取的对象直径的两倍左右。重要的是不要将参数 Offset 设置为零,因为在这种情况下会发现太多小区域(噪声)。 5 到 40 之间的值是一个有用的选择。选择的 Offset 越大,提取的区域就越小。
满足上述条件的输入图像的所有点共同存储在一个区域中。如果需要,可以通过调用connection来获取连接的组件。