halcon 入门教程(二)Blob分析(二值化,联通区域,分割区域,提取区域)保姆级教程
原文作者:aircraft
原文链接:https://www.cnblogs.com/DOMLX/p/18781187
有兴趣可以多看其他的halcon教程
OK,今天讲halcon入门教程第二篇,比较常用的一篇Blob分析(今天主要讲简单的,好理解的为主),本篇的预处理就不详细讲了,想了解预处理的参考第一篇入门教程----------
halcon 入门教程(一) 预处理图像 (图像平滑,图像增强,二值化,形态学分析)
最后也有两个实例代码,一个超简单的车牌字符的分割,一个入门最经典的豆子分割案例,而且每行代码我都会打上注释!!! 上来就是这么简单好学的示例就是让大家学起来有信心,不吃力,免得看的一脸懵逼就开始打退堂鼓觉得自己学不会那就蛋糕咯~。
主要的示例代码都在halcon的实例程序集Blob分析里的车牌分割 threshold.hdev 和 豆子分割计数 count_pellets.hdev
一.Blob分析简介
Blob分析的步骤
预处理:
- 去噪(如高斯滤波、中值滤波)。
- 增强对比度或调整光照不均。
二值化:
- 将图像转换为黑白二值图(例如通过阈值分割),使目标区域(Blob)与背景分离。
连通区域标记:
- 扫描图像,为每个连通区域分配唯一标签(如使用种子填充法或两次扫描算法)。
特征提取:
- 计算每个Blob的特征,例如:
- 几何特征:面积、周长、质心、外接矩形、长宽比、圆形度。
- 灰度/颜色特征:平均灰度、颜色分布。
- 高阶特征:方向(主轴)、矩(用于形状描述)。
- 计算每个Blob的特征,例如:
筛选与分析:
- 根据特征过滤无关区域(例如排除面积过小的噪声点)。
- 分类或统计目标Blob的数量、位置、形状等。
Blob分析的优缺点
1.优点:
灵活性高:Blob分析可以应用于多种类型的图像,包括高对比度图像、二维目标图像等。
特征丰富:通过Blob分析,可以提取出多种特征,为后续的图像处理和分析提供丰富的信息。
直观易懂:Blob分析的结果通常以图像的形式呈现出来,便于直观地了解和分析。
2.缺点:
计算量大:Blob分析需要对图像中的每个像素进行逐点扫描和处理,因此计算量较大,处理速度相对较慢。
对光源敏感:Blob分析的结果容易受到光源的影响,如光照强度、光照方向等。因此,在实际应用中需要确保光源的稳定性和一致性。
局限性:Blob分析主要适用于具有明显灰度突变或颜色差异的图像区域。对于低对比度或颜色相近的图像区域,Blob分析的准确性可能会降低。
二.Blob分析简单车牌分割实例和常用的算子学习
先看示例,这样理解起来才比较快(图片可直接拿去用):
提取车牌字符:
* 读取图像文件'audi2'到变量Audi2
read_image (Audi2, 'E:/学习测试/halcon/Blob分析/1.bmp') * 修复隔行扫描图像的奇数行伪影(消除交錯扫描效应) 图像增强 用一些其他滤波也可以 哪个效果好 用哪个要灵活
fill_interlace (Audi2, ImageFilled, 'odd') * 对图像进行阈值分割(0-90灰度范围),提取较暗区域
threshold (ImageFilled, Region, 0, 90) * 将连通区域分割为独立区域(分离不连通的像素块)
connection (Region, ConnectedRegions) * 筛选宽度在30-70像素之间的区域(过滤过宽/过窄的干扰区域)
select_shape (ConnectedRegions, SelectedRegions, 'width', 'and', 30, 70) * 进一步筛选高度在60-110像素之间的区域(聚焦字母特征)
select_shape (SelectedRegions, Letters, 'height', 'and', 60, 110) * 将筛选后的多个独立区域合并为一个整体区域
union1 (Letters, RegionUnion) * 清空图形窗口准备显示结果
dev_clear_window () * 设置显示颜色为12色模式(多色高亮显示)
dev_set_colored (12) * 显示预处理后的图像背景
dev_display (ImageFilled) * 在背景图上叠加显示最终筛选的字母区域
dev_display (Letters)
OK,非常简短的代码用来入门最好不过了。首先第一行我们先读取车牌图片进来
read_image (Audi2, 'E:/学习测试/halcon/Blob分析/1.bmp')(这是我保存图片的路径,你们填你们自己的好吧!)
第二行使用图像增强算子fill_interlace(),这里增强也可以换起来的算子比如高斯,直方图均衡化和一些其他的都行。即使你把这行注释掉也没关系也不影响后面的分割字符。锦上添花罢了。
第三行我们使用全局阈值分割算子threshold对图像中灰度分布在0-90之间的图像区域,这边在halcon的HDevelop软件上把鼠标放在图片内就可以看到图像鼠标位置的灰度分布了,移动一下确定字符区域的灰度基本是在这个范围就直接提取他们出来。
看一下提取后的区域范围是:
可以看到提取了很多其他区域一起进来,而这时候我们就要对其进行分割选取,真正得到我们想要的一个字符区域。
先使用connection()算子,这个算子的作用是在阈值提取后,将一个总的区域划分成一个个不连通小区域,只要字母在图上与其他区域没有连接在一起,经过这个算子函数后都会变成一个个单独的小区域,存放在区域数组里。将一个个小区域联通起来就是union算子了。
接下来我们使用select_shape()算子,这个算子主要就是用来筛选作用的,筛选出符合你需要特征的区域。这里选择的是width宽度属性,宽度在30-70个像素点之间的区域。
宽度筛选后还多了个圆圈的东西在上面,那我们就要进行二次筛选,在第一次筛选后的区域数组里继续使用select_shape()算子筛选height高度在60-110个像素之间的区域提取。不知道怎么看高度宽度的,鼠标放在图上右下角有鼠标位置的行列坐标,移动一下就知道了。
OK,经过了两次筛选,以及将字符区域都提取出来了。最后用union1()算子合并成整体显示一下,该示例就结束了,非常简单好理解。
上面的程序的大概意思理解一下就行了,就是为了学习起来有信心,现在详细的看看一些常用的算子:
1.全局阈值分割算子threshold(Image : Region : MinGray, MaxGray : )详解:
- 输入参数:
Image
:输入图像(单通道灰度图像)。MinGray
,MaxGray
:灰度阈值范围,闭区间[MinGray, MaxGray]
。
- 输出参数:
Region
:输出区域,包含所有灰度值在阈值范围内的像素。白话将就是提取灰度在MinGray
到MaxGray
之间的像素区域

* 读取图像
read_image(Image, 'metal_part.jpg') * 设定阈值范围(假设目标灰度在120-220之间)
threshold(Image, Region, 120, 220) * 显示分割结果
dev_display(Image)
dev_display(Region)

2.局部阈值分割算子dyn_threshold(OrigImage, ThresholdImage : RegionDynThresh : Offset, LightDark : )详解:
- 输入参数:
OrigImage
:原始输入图像(单通道灰度图像)。ThresholdImage
:参考图像(通常为模糊后的图像,如高斯滤波或均值滤波结果)。Offset
:阈值偏移量(用于放宽或收紧比较条件)。LightDark
:比较模式,可选'light'
、'dark'
或'equal'
。
- 输出参数:
RegionDynThresh
:满足条件的区域。
LightDark
参数为'light'
时意思就是:提取原图中相对于均值图像灰度值还要大Offset
的灰度值像素区域。LightDark
参数为'dark'
时意思就是:提取原图中相对于均值图像灰度值还要小
Offset
的灰度值像素区域。LightDark
参数为'equal'
时意思就是:提取原图中相对于均值图像灰度值灰度差值在Offset范围内
的灰度值像素区域。
read_image(OrigImage, 'scratched_metal.jpg')
* 生成参考图像(高斯模糊)
gauss_filter(OrigImage, ThresholdImage, 15)
* 动态阈值分割(提取暗缺陷)
dyn_threshold(OrigImage, ThresholdImage, Scratches, 10, 'dark')
* 显示结果
dev_display(OrigImage)
dev_display(Scratches)
3.自动阈值分割算子binary_threshold(Image : Region : Method, LightDark : UsedThreshold)详解:
- 输入参数:
Image
:单通道灰度图像(需处理图像)。Method
:阈值选择方法(如'smooth'
或'max_separability'
)。LightDark
:提取区域类型('light'
提取亮区域,'dark'
提取暗区域)。
- 输出参数:
Region
:二值化后的目标区域。UsedThreshold
:算法自动计算的实际阈值(可输出用于调试)。
核心功能
- 自动阈值分割:
- 根据图像直方图特性,自动选择最佳阈值分离目标与背景。
- 适用场景:
- 工业检测:分离零件与背景(如金属表面缺陷检测)。
- OCR预处理:提取深色文字或浅色背景。
- 生物医学:分割细胞或组织区域。
参数详解
1. **Method
(阈值选择方法)**
方法 | 原理 | 适用场景 |
---|---|---|
'max_separability' |
最大化前景与背景的类间方差(Otsu算法) | 高对比度图像,双峰直方图 |
'smooth' |
平滑直方图后寻找最大曲率点(类似Renyi熵法) | 渐變背景,目标边缘清晰 |
'between_clusters' |
基于聚类分析选择阈值(类似k-means) | 多目标复杂场景 |
2. **LightDark
(区域极性)**
'light'
:提取高于阈值的亮区域(如白色零件、反光缺陷)。'dark'
:提取低于阈值的暗区域(如黑色文字、深色污渍)。
与其他阈值算子对比
算子 | 优点 | 缺点 |
---|---|---|
binary_threshold |
全自动,适合快速部署 | 依赖直方图分布 |
threshold |
手动控制范围,灵活 | 需人工干预,效率低 |
auto_threshold |
多阈值分割 | 计算复杂,适用性受限 |
示例代码:
* 读取图像并自动分割亮区域(如金属零件)
read_image(Image, 'metal_part.jpg')
binary_threshold(Image, Region, 'max_separability', 'light', UsedThreshold) * 显示结果及实际阈值
dev_display(Image)
dev_set_color('red')
dev_display(Region)
disp_message('Used Threshold: ' + UsedThreshold, 'window', 12, 12, 'black', 'true')
4.分割联通区域算子connection(Region : ConnectedRegions : : )详解:
- 输入参数:
Region
:输入的二值化区域(可能包含多个连通或非连通的部分)。
- 输出参数:
ConnectedRegions
:输出的一组独立连通区域(每个连通区域作为一个单独对象存储)。
功能与作用
- 区域分离:
- 将粘连或重叠的目标分离为独立区域(如分离接触的零件或字符)。一般配合阈值提取分割threshold算子后使用
- 噪声过滤:
- 通过后续操作(如
select_shape
)筛选有效区域,去除孤立噪点。
- 通过后续操作(如
- 应用场景:
- OCR:分割图像中的独立字符。
- 目标计数:统计图像中的物体数量(如药片、细胞)。
- 缺陷检测:分离缺陷区域以便单独分析。
示例代码:
* 读取图像并二值化
read_image(Image, 'particles.jpg')
threshold(Image, Region, 128, 255) * 分割连通区域
connection(Region, ConnectedRegions) * 筛选面积大于100像素的区域
select_shape(ConnectedRegions, LargeRegions, 'area', 'and', 100, 99999) * 显示结果
dev_display(Image)
dev_set_color('red')
dev_display(LargeRegions)
5.筛选区域算子select_shape(Regions : SelectedRegions : Features, Operation, Min, Max : )详解:
- 输入参数:
Regions
:待筛选的输入区域(通常来自connection
或threshold
等操作)。Features
:筛选依据的几何特征(支持单特征或多特征组合)。Operation
:逻辑运算符('and'
、'or'
),用于多特征筛选时的逻辑组合。Min
,Max
:特征值的允许范围(闭区间[Min, Max]
)。
- 输出参数:
SelectedRegions
:符合筛选条件的区域集合。
核心功能
- 特征筛选:
- 根据形状、尺寸或位置特征过滤区域,保留有效目标,去除噪声或干扰区域。
- 应用场景:
- 工业检测:筛选符合尺寸标准的零件。
- OCR:提取特定宽高比的字符区域。
- 医学成像:分离符合生物学特征的细胞或组织。
以下参数都是举例常用的 算子内部还有很多参数可以去halcon算子手册里看
1. **Features
(特征列表)**
特征名称 | 描述 | 计算公式/说明 |
---|---|---|
'area' |
区域面积(像素数) | 直接统计像素数量 |
'width' |
区域外接矩形的宽度 | width=c2−c1+1 |
'height' |
区域外接矩形的高度 | height=r2−r1+1 |
'roundness' |
圆度(1表示完美圆,值越小越不规则) | perimeter24π×area |
'compactness' |
紧凑度(类似圆度,计算方式不同) | widthheight |
'row' , 'column' |
区域中心坐标 | 基于区域重心计算 |
2. **Operation
(逻辑运算)**
'and'
:所有特征条件同时满足(交集)。'or'
:任意特征条件满足(并集)。
3. **Min
和 Max
(范围阈值)**
- 定义特征值的有效区间,例如:
- 筛选面积在 100~500 像素的区域:
Features='area', Min=100, Max=500
- 筛选宽度≥30且高度≤80的区域:
Features=['width','height'], Operation='and', Min=[30,0], Max=[9999,80]
- 筛选面积在 100~500 像素的区域:
示例1:筛选面积在50-200像素的圆形区域
* 分割连通区域
connection(Region, ConnectedRegions) * 筛选圆形区域(圆度≥0.85)
select_shape(ConnectedRegions, Circles, 'roundness', 'and', 0.85, 1.0) * 进一步筛选面积范围
select_shape(Circles, SelectedRegions, 'area', 'and', 50, 200)
6.联合区域算子union1(Region : RegionUnion : : )详解:
- 输入参数:
Regions
:输入的区域集合(多个独立区域组成的数组)。
- 输出参数:
RegionUnion
:合并后的单一区域,包含所有输入区域的并集。
核心功能
- 区域合并:
- 将多个离散区域合并为一个整体区域,忽略区域之间的空间关系(无论是否重叠)。
- 应用场景:
- OCR预处理:将分割后的字符区域合并为文本行。
- 缺陷检测:聚合分散的缺陷区域,便于统一分析。
- 目标增强:合并同一物体的多个碎片化区域,提升后续处理效率。
示例代码片段:合并分散的缺陷区域
* 检测金属表面的划痕(可能分散为多个小区域)
threshold(Image, Scratches, 0, 90)
connection(Scratches, ConnectedScratches) * 合并所有划痕区域,计算总缺陷面积
union1(ConnectedScratches, TotalScratches)
area_center(TotalScratches, Area, Row, Column) * 判断是否超出允许阈值
if (Area > 1000)
dev_set_color('red')
dev_display(TotalScratches)
endif
三.豆子分割实例代码
此程序演示基础形态学算子,联通区域分割,二值化算子等综合的使用。
流程为:原始图像 → 阈值分割 → 开运算去噪 → 腐蚀分离 → 连通分析 → 膨胀恢复 → 结果计数
豆子分割计数 count_pellets.hdev示例代码:
* 此程序演示基础形态学算子的使用,联通区域分割,二值化算子等综合的使用。
* 目标:检测图像中的每个颗粒(深色背景上的亮色颗粒数)
*
* 关闭设备更新(提升执行效率)
dev_update_off () * 读取颗粒图像到Image变量
read_image (Image, 'pellets') * 关闭可能存在的旧图形窗口
dev_close_window () * 获取图像尺寸用于新窗口设置
get_image_size (Image, Width, Height) * 打开与图像同尺寸的黑色背景窗口
dev_open_window (0, 0, Width, Height, 'black', WindowID) * 设置显示区域为完整图像尺寸(避免缩放)
dev_set_part (0, 0, Height - 1, Width - 1) * 设置显示字体为等宽字体,字号16
set_display_font (WindowID, 16, 'mono', 'true', 'false') * 使用6种颜色高亮显示不同区域
dev_set_colored (6) * 设置区域显示模式为轮廓(节省显示资源)
dev_set_draw ('margin') * 设置轮廓线宽为3像素(更显眼)
dev_set_line_width (3) * 显示原始图像
dev_display (Image) * 在窗口左上角显示提示信息(黑色文字)
disp_message (WindowID, 'Detect each single pellet', 'window', 12, 12, 'black', 'true') * 显示继续提示并等待用户按键
disp_continue_message (WindowID, 'black', 'true')
stop () * 【步骤1:颗粒区域分割】
* 使用最大可分性阈值分割亮色区域(自动计算最佳阈值) 使用其他阈值分割也可以 自己可以尝试一下
binary_threshold (Image, LightRegion, 'max_separability', 'light', UsedThreshold) * 使用半径3.5的圆形结构元素做开运算(平滑边界,去除小噪点)
opening_circle (LightRegion, Region, 3.5) * 显示预处理后的区域
dev_display (Region)
disp_message (WindowID, 'First, segment the pellets', 'window', 12, 12, 'black', 'true')
disp_continue_message (WindowID, 'black', 'true')
stop () * 【步骤2:直接连通域分析(会失败)】
* 尝试直接计算连通区域(因颗粒粘连导致部分区域无法分离)
connection (Region, ConnectedRegionsWrong) * 显示失败结果:粘连区域被识别为同一区域
dev_display (Image)
dev_display (ConnectedRegionsWrong)
disp_message (WindowID, 'Simple connection fails', 'window', 12, 12, 'black', 'true')
disp_continue_message (WindowID, 'black', 'true')
stop () * 【步骤3:形态学腐蚀分离粘连】
* 使用半径7.5的圆形结构元素腐蚀区域(缩小颗粒以分离粘连)
erosion_circle (Region, RegionErosion, 7.5) * 显示腐蚀后的分离效果
dev_display (Image)
dev_display (RegionErosion)
disp_message (WindowID, 'Erosion of the pellet regions', 'window', 12, 12, 'black', 'true')
disp_continue_message (WindowID, 'black', 'true')
stop () * 【步骤4:正确连通域分析】
* 对腐蚀后的区域进行连通域分析(此时颗粒已分离)
connection (RegionErosion, ConnectedRegions) * 显示正确的独立连通区域
dev_display (Image)
dev_display (ConnectedRegions)
disp_message (WindowID, 'Perform connection now', 'window', 12, 12, 'black', 'true')
disp_continue_message (WindowID, 'black', 'true')
stop () * 【步骤5:恢复原始颗粒尺寸】
* 使用半径7.5的圆形结构元素膨胀区域(恢复原始颗粒大小)
dilation_circle (ConnectedRegions, RegionDilation, 7.5) * 统计并显示最终检测到的颗粒数量
count_obj (RegionDilation, Number)
dev_display (Image)
dev_display (RegionDilation)
disp_message (WindowID, Number + ' pellets detected', 'window', 12, 12, 'black', 'true')
1.先读取图片
2.利用自动阈值分割算子binary_threshold()提取图片中偏亮色的区域
3.使用connection()算子分割成小区域集合数组
4.使用腐蚀erosion_circle()算子腐蚀掉相互连接的区域,让豆子区域独立无连接
5.在分割小区域connection()算子,这个时候的豆子都是各自独立无连接的,这时候计算数目就对了
6.在使用dilation_circle()膨胀算子,同样的膨胀腐蚀滤波参数,将豆子区域大小还原。在使用区域数目统计算子count_obj()得出结果
OK,本篇的教程就到这里了,有兴趣慢慢学习halcon的入门教程的可以关注一下我后面的更新,会一步步由浅入深的更新。
halcon 入门教程(二)Blob分析(二值化,联通区域,分割区域,提取区域)保姆级教程的更多相关文章
- 第二步:将LAD结果的属性值二(多)值化,投入计算模型
一文详解LDA主题模型 - 达观数据 - SegmentFault 思否 https://segmentfault.com/a/1190000012215533 SELECT COUNT(1) FRO ...
- RocketMQ保姆级教程
大家好,我是三友~~ 上周花了一点时间从头到尾.从无到有地搭建了一套RocketMQ的环境,觉得还挺easy的,所以就写篇文章分享给大家. 整篇文章可以大致分为三个部分,第一部分属于一些核心概念和工作 ...
- 保姆级教程——Ubuntu16.04 Server下深度学习环境搭建:安装CUDA8.0,cuDNN6.0,Bazel0.5.4,源码编译安装TensorFlow1.4.0(GPU版)
写在前面 本文叙述了在Ubuntu16.04 Server下安装CUDA8.0,cuDNN6.0以及源码编译安装TensorFlow1.4.0(GPU版)的亲身经历,包括遇到的问题及解决办法,也有一些 ...
- 强大博客搭建全过程(1)-hexo博客搭建保姆级教程
1. 前言 本人本来使用国内的开源项目solo搭建了博客,但感觉1核CPU2G内存的服务器,还是稍微有点重,包括服务器内还搭建了数据库.如果自己开发然后搭建,耗费时间又比较多,于是乎开始寻找轻量型的博 ...
- 自建本地服务器,自建Web服务器——保姆级教程!
搭建本地服务器,Web服务器--保姆级教程! 本文首发于https://blog.chens.life/How-to-build-your-own-server.html. 先上图!大致思路就是如此. ...
- Eclipse for C/C++ 开发环境部署保姆级教程
Eclipse for C/C++ 开发环境部署保姆级教程 工欲善其事,必先利其器. 对开发人员来说,顺手的开发工具必定事半功倍.自学编程的小白不知道该选择那个开发工具,Eclipse作为一个功能强大 ...
- ElasticSearch入门篇(保姆级教程)
本章将介绍:ElasticSearch的作用,搭建elasticsearch的环境(Windows/Linux),ElasticSearch集群的搭建,可视化客户端插件elasticsearch-he ...
- nifi从入门到实战(保姆级教程)——环境篇
背景: 公司领导决定将各种基础数据的导入从代码中分离出来,用Apache Nifi替换.使开发者们更关注在业务上,而不用关心基础的由来. Apache Nifi对于整个团队都是一个全新的工具,之前大家 ...
- nifi从入门到实战(保姆级教程)——身份认证
上一篇我们搭建好了nifi的运行环境了 但是每次登陆那一串随机字符串的用户名和密码是不是让人很头疼,那是人类能记住的吗?当然不是!!!! 那么今天我们就来消灭这些难看又难记的字符串. windows( ...
- nifi从入门到实战(保姆级教程)——flow
本文章首发于博客园,转载请标明出处 经过前两篇文章(环境篇,身份验证),我们已经有了nifi可以运行的基础,今天就来实现一个案例吧. 假设我们要从ftp上获取一个zip包,里面有两个csv文件,一个是 ...
随机推荐
- 关于Qt选择qml还是widget的深度思考
在Qt界始终有两大阵营产生激烈的纷争,那就是选用qml还是widget好,大量初学者也会问这个问题,有以下几点总结. widget属于传统界面开发,和VB/VC/Delphi等拖曳控件开发类似,走CP ...
- IM开发干货分享:IM客户端不同版本兼容运行的技术思路和实践总结
本文由巩鹏军分享,原题"IM兼容性基建",本文有修订. 1.引言 一个成熟的IM成品,在运营过程中随着时间的推移,会发布不同的版本,但为了用户体验并不能强制要求用户必须升级到最新版 ...
- PHP 安装启用imagick(解决 word press可选的模组imagick未被安装或已被禁用)
本教程仅适用Windows Servier IIS网站服务器. 我的博客使用IIS搭建,相比Linux,相关的教程格外少.因此让以后的小伙伴也能马上解决问题,分享此方法. 首先需要下载php对应版本的 ...
- Linux 压缩命令集合
01. tar格式 解包:tar xvf FileName.tar 打包:tar cvf FileName.tar DirName(注:tar是打包,不是压缩!) 02. gz格式 解压1:gunzi ...
- Solution -「NOI Simu.」逆天题
\(\mathscr{Description}\) 对于 \(r=0,1,\cdots,n-1\), 设 \(\{1,2,\cdots,nm\}\) 中有 \(f_r\) 个子集满足子集内元素之和 ...
- Flutter的一些概念(二)
注:本文同步发布于微信公众号:stringwu的互联网杂谈 Flutter的一些概念(二) 1 flutter的核心渲染模块 当应用启动时flutter 会遍历所有的Widget 形成Widget 树 ...
- CDS标准视图:维护包描述 I_MaintPackageTextData
视图名称:维护包描述 I_MaintPackageTextData 视图类型:基础 视图代码: 点击查看代码 @EndUserText.label: 'Maintenance Package - Te ...
- 探索自联接(SELF JOIN):揭示数据间复杂关系的强大工具
title: 探索自联接(SELF JOIN):揭示数据间复杂关系的强大工具 date: 2025/1/11 updated: 2025/1/11 author: cmdragon excerpt: ...
- javascript对象学习笔记
目前所见的,在普通的JavaScript应用中和绝大多数人一样使用的是全局的方式.大概是因为JavaScript所定义的变量或函数默认就是全局的.为了是代码不至于太凌乱,变量不至于稀里糊涂的就被人重定 ...
- Windows 激活
参考:链接1 链接2 链接3 链接4