跳转至

07 区域测量与结果可视化#

一句话总结

测量用 area_center(面积 + 质心)和 orientation_region(主轴方向);把结果画到窗口上用 dev_disp_text / disp_message(文字)和 disp_arrow(箭头)。核心坑:坐标是 Row/Column,"image" 与 "window" 两套坐标系别用混。

源文件: note/05曲别针练习.hdevnote/04套环检测.hdev

一、基本测量算子#

1.1 area_center:面积 + 质心(最常用)#

area_center (SelectedRegions, Area, Row, Column)

签名: area_center(Regions : : : Area, Row, Column)

输出 类型 含义
Area number-array 每个区域的面积(像素数)
Row number-array 每个区域质心的行坐标
Column number-array 每个区域质心的列坐标

输出顺序是 Area → Row → Column

源码里写成 area_center (SelectedRegions, Area, X, Y),变量名 X/Y随手起的,实际 X 装的是 RowY 装的是 Column。 后面 disp_arrow (..., X, Y, ...) 之所以能画对,正是因为 disp_arrow 的前两个坐标参数也是 Row, Column,顺序刚好对上。一旦某处按 (x, y) 理解去用,就会画反。

  • AreaRowColumn 都是元组,长度 = 区域个数,下标一一对应:Area[i] 对应 Row[i]Column[i]
  • 区域个数:num := |Area|

1.2 orientation_region:区域方向#

orientation_region (SelectedRegions, Phi)

签名: orientation_region(Regions : : : Phi)

说明
输出 Phi 区域的主轴方向,单位是弧度(rad)
范围 −π ≤ Phi < π(≈ −180° ~ 180°)
算法 基于 elliptic_axis(等效椭圆)的二阶矩主轴;再用"离质心最远的轮廓点"的列坐标确定方向的正负
前置算子 thresholdconnection
后继算子 disp_arrow

必须记住的两件事

  1. Phi 是弧度不是角度!直接显示会得到 −3.14 ~ 3.14 的数字,看起来完全不对。要用 deg (Phi) 转成角度。
  2. 正负号与"等效椭圆"有关,等价方向可能一正一负。例如两条物理上平行、只差一个"反向"的区域,可能一个返回 18°、另一个返回 −174.8°(相差 180°)。同一个方向有两种弧度表示,写判断逻辑时要注意归一化。

1.3 角度与弧度的互相转换#

AngleDeg := deg (Phi)        * 弧度 -> 角度
AngleRad := rad (45)         * 角度 -> 弧度

配合格式化输出:

disp_message (WindowHandle, deg(Phi)$'3.2f' + 'deg', 'window', X, Y, 'black', 'false')
  • $'3.2f':保留 2 位小数
  • + 'deg':拼上单位后缀,显示成 45.32deg

二、把主轴画出来(画箭头的正确姿势)#

dev_set_line_width (3)
dev_set_color ('yellow')
disp_arrow (WindowHandle, X, Y, X-100*sin(Phi), Y+100*cos(Phi), 3)

签名: disp_arrow(WindowHandle, Row1, Column1, Row2, Column2, Size)

参数 含义
Row1, Column1 起点(这里是质心)
Row2, Column2 终点(沿主轴方向延长 100 像素)
Size 箭头头部的大小(像素)

终点公式怎么来的#

要把"从质心出发、方向为 Phi、长度 100 像素"的向量换算成 (Row, Column)

\[ \begin{cases} \text{Column}_2 = \text{Column}_1 + L \cdot \cos(\Phi) \\ \text{Row}_2 = \text{Row}_1 - L \cdot \sin(\Phi) \end{cases} \]
* X 就是 RowY 就是 Column
X - 100*sin(Phi)      * Row2Row 轴向下所以取负号
Y + 100*cos(Phi)      * Column2Column 轴向右取正号

为什么 Row 那里要有负号

图像坐标里 Row 向下增大,而角度 Phi 是按"数学惯例(y 向上为正、逆时针为正)"定义的。两者方向相反,所以 Row 方向的增量要取负。 一句话:Column 跟 cos 同向,Row 跟 sin 反向。

与官方示例完全一致

HALCON 自带示例 clip.hdev 的写法是:

Length := 80
disp_arrow (WindowID, Row, Column, Row - Length * sin(Phi), Column + Length * cos(Phi), 4)
与源码里的 X-100*sin(Phi), Y+100*cos(Phi) 完全同构,只是长度取 100。把变量名换成 Row/Column 会更清晰。

三、把结果显示到窗口#

3.1 dev_disp_text:开发环境专用文字显示#

dev_disp_text (Text, CoordSystem, Row, Column, Color, GenParamName, GenParamValue)
参数 说明
Text 要显示的内容(可以是元组,逐个显示)
CoordSystem 'image' 用图像坐标;'window'窗口像素坐标
Row, Column 显示位置(配合 CoordSystem 解释)
Color 文字颜色
GenParamName/Value 附加样式,如 ['border_radius'], [1] 加圆角背景框
* 在窗口固定位置左上角 100,100显示统计个数
dev_disp_text ('个数'+num, 'window', 100, 100, 'blue', ['border_radius'], [1])

* 在每个区域的质心处显示其面积
dev_disp_text (Area[Index1], 'image', Row[Index1], Column[Index1], 'black', [], [])

3.2 disp_message:传统绘制文字#

disp_message (WindowHandle, String, CoordSystem, Row, Column, Color, Box)
参数 说明
WindowHandle 窗口句柄(dev_disp_text 不需要,这是两者最大区别)
String 字符串或字符串元组
CoordSystem 'image''window'
Box 'true' 带背景框(看得清)、'false' 不带
disp_message (WindowHandle, deg(Phi)$'3.2f'+'deg', 'window', X, Y, 'black', 'false')

dev_disp_text vs disp_message

dev_disp_text disp_message
需要 WindowHandle 不需要(HDevelop 里更省事) 需要
位置含义 文字左上角在 (Row, Column) 文字左上角在 (Row, Column)
适用场景 HDevelop 调试 导出到 C# / C++ 等语言时用得更多

两者不能混用在同一个程序里做精细对齐,因为它们对文本边界的处理略有差异。学阶段统一用 dev_disp_text 即可。

3.3 image 与 window 两套坐标系#

CoordSystem 含义 什么时候用
'image' 坐标按图像坐标系解释,会随图像缩放/平移一起动 标注目标本身的位置,如"在这个区域上写面积"
'window' 坐标按窗口像素解释,固定不动 显示状态信息,如左上角的"个数:37"

源码中的典型搭配

* 统计信息 —— 固定在窗口左上角不随图像变化
dev_disp_text ('个数'+num, 'window', 100, 100, 'blue', ['border_radius'], [1])

* 单体信息 —— 跟着每个区域走
for Index1 := 0 to num-1 by 1
    dev_disp_text (Area[Index1], 'image', Row[Index1], Column[Index1], 'black', [], [])
endfor
统计类用 window,标注类用 image —— 记住这条就够了。

四、其他常用绘制算子#

算子 作用
disp_circle (WindowHandle, Row, Column, Radius) 画圆
disp_cross (WindowHandle, Row, Column, Size, Angle) 画十字标记(标中心点很好用)
disp_line (WindowHandle, Row1, Column1, Row2, Column2) 画线段
disp_rectangle1 (WindowHandle, Row1, Column1, Row2, Column2) 画正矩形
disp_rectangle2 (WindowHandle, Row, Column, Phi, Len1, Len2) 画带角度矩形
disp_region (Region, WindowHandle) 把区域画到指定窗口
set_display_font (WindowHandle, Size, Font, Bold, Slant) 设置字体,如 (WindowID, 14, 'mono', 'true', 'false')
dev_set_line_width (3) 设置线宽(箭头、轮廓加粗)

五、完整源码解读(05曲别针练习.hdev#

read_image (Image, 'clip')
* 获取图片大小
get_image_size (Image, Width, Height)
dev_close_window ()
dev_open_window (0, 0, Width, Height, 'black', WindowHandle)
dev_display (Image)

* 二值化处理曲别针比背景暗所以取暗段
threshold (Image, Region, 0, 100)
* 分割每个曲别针变成独立区域
connection (Region, ConnectedRegions)
* 筛选按面积留下真正的曲别针
select_shape (ConnectedRegions, SelectedRegions, 'area', 'and', 3500, 8107.97)

* 看效果
dev_set_color ('blue')
dev_clear_window ()
dev_display (Image)
dev_display (SelectedRegions)

* 获取坐标和面积注意X 实际是 Row, Y 实际是 Column
area_center (SelectedRegions, Area, X, Y)

* 获取角度弧度
orientation_region (SelectedRegions, Phi)
* 显示角度deg 转成角度$'3.2f' 保留两位小数
disp_message (WindowHandle, deg(Phi)$'3.2f'+'deg', 'window', X, Y, 'black', 'false')

* 每个曲别针的中心画一条表示方向的箭头
dev_set_line_width (3)
dev_set_color ('yellow')
disp_arrow (WindowHandle, X, Y, X-100*sin(Phi), Y+100*cos(Phi), 3)

数据结构对照表#

此时代码里有 4 个长度相同的元组,下标一一对应:

元组 长度 含义 第 i 个元素
SelectedRegions N 区域对象数组 第 i 个曲别针
Area N 面积 Area[i]
X(实为 Row) N 质心行坐标 X[i]
Y(实为 Column) N 质心列坐标 Y[i]
Phi N 主轴弧度 Phi[i]

\"元组下标对齐\"是 HALCON 最核心的思维

HALCON 用平行元组代替"数组 of 结构体"。只要你清楚"第 i 个元素属于第 i 个区域",就能用 for 循环自由组合任意特征。这是写 HALCON 代码的底层直觉。

六、坑位清单#

Danger

  1. Phi 当角度用 —— 必须 deg() 转换。
  2. Area, Row, Column 当成 Area, X, Y —— 名字不重要,位置才重要。
  3. disp_arrow 的 Row 增量忘了负号 —— 箭头会朝反方向(相当于镜像)。
  4. 统计文字用 'image' 坐标 —— 图像一缩放文字就跑到画面外;统计信息请用 'window'
  5. 对筛选前的 ConnectedRegions 做测量 —— 测到的数量、坐标与显示的目标不匹配。测量对象必须是 SelectedRegions
  6. |Area| 当成面积 —— |Area|元组长度,即区域个数,不是面积总和。

上一步:形态学运算 · 下一步:实战 套环检测

续篇:区域测量只能到\"像素级质心\"

area_center 给的是整块区域的像素级质心,量不出"一条边在哪""一个圆的半径是多少"。 要亚像素级地量直线 / 圆 / 矩形,用的是 2D 计量(Metrology):画一个近似形状 → 沿边界自动摆一排卡尺 → RANSAC 拟合。 见 [[15 测量模型 2D Metrology]]。