Hilditch 细化算法是经典的二值图像细化算法
关于手写汉字切分方法的思考

A Survey of Methods in Handwritten Chinese Character Segmentation
SHAO J ie ,CHEN G Yu
(Nanjing University of Aeronautics and Astronautics ,Nanjing 210016 , China)
竖直方向上可能存在多个笔画的问题 ,但对于粘连过紧密 的字符仍不能正确切分 (如图 2 所示) 。
3 基于汉字笔画结构的分割方法 虽然以上两类方法较好地解决了无粘连汉字的分割 ,
但粘连汉字的分割依然是困扰人们的难题 。汉字是由具 有一定排列规律的笔画组合而成的 ,通常情况下 ,使用以 识别为基础的判别规则可以将不属于同一汉字的笔画剔 出 ,以及区分粘连笔画和单一笔画 。因此 ,采用笔画提取 再合并的方法可以从另一个角度解决笔画粘连问题 。
图 3 笔画连接盒算法效果 另一种基于笔画的分割方法[12 ]通过黑游程跟踪法提 取笔画 ,笔画提取前计算了黑像素游程宽度 、字符的平均 宽度 (通过垂直投影法得到) 和高度 。首先从图像中寻找 到一条黑游程 ,作为笔画的开始 ,然后对该黑游程进行逐 行跟踪 ,在当前黑游程的下一行左右的一定范围内 ,找到 所有的黑游程 ,并根据已有的游程平均宽度和游程直线拟 合得到的笔画方向 ,确定归入该笔画的黑游程 ,并确定出 下一行的跟踪范围 ,直到找不到新的游程 ,跟踪结束 ,得到 一个笔画 。从图像中提取的笔画分别用外接矩形和凸包 描述 ,采用一定的算法合并 。在文中 ,由于分割对象是信 函地址 ,笔者将此方法作为预切分的手段 ,之后采用基于 识别的最优路径动态规划算法决定分割路径 。 基于笔画提取的分割方法在很大程度上依赖于笔画 的提取优劣程度 ,至今 ,笔画提取主要有 3 种方法 ,分别基 于二值图像 、细化图像和汉字轮廓 。基于二值图像的笔画 提取省略了对图像的进一步处理 ,所以分割时大多采用这 种笔画提取法 。然而 ,这种笔画提取的方法还没有达到很 高的水平 ,对于横不平竖不直的汉字提取效果不佳 。因 此 ,这种方法的分割正确率也受到限制 ,适用范围不够广 。 此外 ,笔画先提取后合并使算法过于复杂 ,将其作为垂直 投影后的细切分可以更简单省时 。
Hilditch 细化算法是经典的二值图像细化算法

Hilditch 细化算法是经典的二值图像细化算法,然而,在网上却很难找到一个详细、正确的介绍和实现。
可以找到一辆个 Hilditch 算法的C实现,但缺乏注释,代码可读性也很差。
在期刊网上找到几篇论文,提及了Hilditch 算法,结果一篇说的罗哩罗嗦根本看不懂,另一篇说的说的易懂,却是错误的!拿来主义是行不通了,于是只好结合着这几个论文和代码,从头写 Hilditch 细化算法。
假设像素p的3×3邻域结构为:Hilditch 细化算法的步骤为:对图像从左向右从上向下迭代每个像素,是为一个迭代周期。
在每个迭代周期中,对于每一个像素p,如果它同时满足6个条件,则标记它。
在当前迭代周期结束时,则把所有标记的像素的值设为背景值。
如果某次迭代周期中不存在标记点(即满足6个条件的像素),则算法结束。
假设背景值为0,前景值为1,则:6个条件为:(I):p 为1,即p不是背景;(2):x1,x3,x5,x7不全部为1(否则把p标记删除,图像空心了);(3):x1-x8 中,至少有2个为1(若只有1个为1,则是线段的端点。
若没有为1的,则为孤立点);(4):p的8连通联结数为1;联结数指在像素p的3*3邻域中,和p连接的图形分量的个数:上图中,左图的4连通联结数是2,8连通联结数是1,而右图的4联通联结数和8联通联结数都是2。
4连通联结数计算公式是:8连通联结数计算公式是:其中,至于公式怎么来的就不管了,直接用就行了。
(5)假设x3已经标记删除,那么当x3为0时,p的8联通联结数为1;(6)假设x5已经标记删除,那么当x5为0时,p的8联通联结数为1。
======在程序中,我使用的是这样的邻域编码:为了方便计算联结数,以0作为前景,1作为背景。
程序如下(完整程序见:/svn/trunk/src/mon/UnmanagedI mage/ImageU8.cs):/// <summary>/// 计算八联结的联结数,计算公式为:/// (p6 - p6*p7*p0) + sigma(pk - pk*p(k+1)*p(k+2)), k = {0,2,4)/// </summary>/// <param name="list"></param>/// <returns></returns>private unsafe Int32 DetectConnectivity(Int32* list){Int32 count = list[6] - list[6] * list[7] * list[0];count += list[0] - list[0] * list[1] * list[2];count += list[2] - list[2] * list[3] * list[4];count += list[4] - list[4] * list[5] * list[6];return count;}private unsafe void FillNeighbors(Byte* p, Int32* list, Int32 width, Byte foreground = 255){// list 存储的是补集,即前景点为0,背景点为1,以方便联结数的计算list[0] = p[1] == foreground ? 0 : 1;list[1] = p[1 - width] == foreground ? 0 : 1;list[2] = p[-width] == foreground ? 0 : 1;list[3] = p[-1 - width] == foreground ? 0 : 1;list[4] = p[-1] == foreground ? 0 : 1;list[5] = p[-1 + width] == foreground ? 0 : 1;list[6] = p[width] == foreground ? 0 : 1;list[7] = p[1 + width] == foreground ? 0 : 1;}/// <summary>/// 使用 hilditch 算法进行细化/// </summary>public unsafe void Thinning(Byte foreground = 255){Byte* start = this.Start;Int32 width = this.Width;Int32 height = this.Height;Int32* list = stackalloc Int32[8];Byte background = (Byte)(255 - foreground);Int32 length = this.Length;using (ImageU8 mask = new ImageU8(this.Width, this.Height)) {mask.Fill(0);Boolean loop = true;while (loop == true){loop = false;for (Int32 r = 1; r < height - 1; r++){for (Int32 c = 1; c < width - 1; c++){Byte* p = start + r * width + c;// 条件1:p 必须是前景点if (*p != foreground) continue;// p3 p2 p1// p4 p p0// p5 p6 p7// list 存储的是补集,即前景点为0,背景点为1,以方便联结数的计算FillNeighbors(p, list, width, foreground);// 条件2:p0,p2,p4,p6 不皆为前景点if (list[0] == 0 && list[2] == 0 && list[4] == 0 && list[6] == 0)continue;// 条件3: p0~p7至少两个是前景点Int32 count = 0;for (int i = 0; i < 8; i++){count += list[i];}if (count > 6) continue;// 条件4:联结数等于1if (DetectConnectivity(list) != 1) continue;// 条件5: 假设p2已标记删除,则令p2为背景,不改变p的联结数 if (mask[r - 1, c] == 1){list[2] = 1;if (DetectConnectivity(list) != 1)continue;list[2] = 0;}// 条件6: 假设p4已标记删除,则令p4为背景,不改变p的联结数 if (mask[r, c - 1] == 1){list[4] = 1;if (DetectConnectivity(list) != 1)continue;}mask[r, c] = 1; // 标记删除loop = true;}}for (int i = 0; i < length; i++){if (mask[i] == 1){this[i] = background;}}}}}。
手写数字体自动识别技术的研究现状

浙江万里学院学报2015年3月手写数字体自动识别技术的研究现状胡玲琳,张若男,李培年,王仁芳(浙江万里学院,浙江宁波315100)摘要:脱机手写数字体自动识别一直以来是图像处理与模式识别领域中的研究热点及具有较高的实用价值。
文章对手写数字体研究现状进行概述,着重分析了预处理中的二值化、细化等方法,对特征提取中较重要的统计特征和结构特征分别进行阐述,讨论了分类识别中BP 神经网络、支持向量机等关键技术的各种主流方法。
通过分析,以期使读者对手写数字体识别的进展有较全面的了解,并对未来的研发有切实的帮助。
关键词:手写数字识别;预处理;特征提取;分类识别中图分类号:TP391文献标识码:A 文章编号:1671-2250(2015)02-0072-07收稿日期:2015-01-15基金项目:浙江省科技计划项目(项目编号:2012C21004);浙江省大学生科技创新活动计划(新苗计划)(项目编号:2014R419028)。
作者简介:胡玲琳(1989-),女,浙江建德人,浙江万里学院计算机与信息学院物流工程研究生,研究方向:物流信息技术应用。
通信作者:王仁芳(1974-),男,河南南阳人,浙江万里学院计算机与信息学院教授,研究方向:数字几何处理。
236Vo1.23No.62010年11月November 2010Journal of Zhejiang Wanli University浙江万里学院学报第28卷第2期Vo1.28No.22015年3月March 2015手写数字体识别(Handwritten digits recognition )是光学字符识别技术(Optical Character Recogni -tion ,OCR )的一个分支,其目的是让计算机自动识别出纸张上的手写数字[1]。
近几十年来,国内外学者对识别技术中的各个环节进行了广泛深入的研究,可见手写数字识别问题具有重要的学术意义和实用价值。
细化算法研究

论文中文摘要毕业设计说明书(论文)外文摘要1 绪论图像的细化是数字图像预处理中的重要的一个核心环节。
图像细化在图像分析和图像识别中如汉子识别、笔迹鉴别。
指纹分析中有着广泛的应用。
图像的细化主要有以下几个基本要求:(1)骨架图像必须保持原图像的曲线连通性。
(2)细化结果尽量是原图像的中心线。
(3)骨架保持原来图形的拓扑结构。
(4)保留曲线的端点。
(5)细化处理速度快。
(6)交叉部分中心线不畸变。
虽然现在人们对细化有着广泛的研究,细化算法的方法有很多。
然而大多数算法如Hilditch算法和Rosenfield算法存在着细化后图形畸变,断点过多,在复杂的情况下关键信息的缺失等问题。
基于上诉考虑,传统的细化算法已经无法满足如今的数字图像处理要求。
因此,需要提出新的一种算法,来解决相关问题。
1.1 相关定义1.1.1串行与并行从处理的过程来看,主要可以分为串行和并行两类,前者对图像中当前象素的处理依据其邻域内象素的即时化结果,即对某一元素进行检测,如果该点是可删除点,则立即删除,且不同的细化阶段采用不同的处理方法;后者对当前的象素处理依据该象素及其邻域内各象素的前一轮迭代处理的结果,至始至终采用相同的细化准则。
即全部检测完汉子图像边缘上的点后再删除可删除点。
1.1.2骨架对图像细化的过程实际上是求一图像骨架的过程。
骨架是二维二值目标的重要拓扑描述,它是指图像中央的骨架部分,是描述图像几何及拓扑性质的重要特征之一。
骨架形状描述的方法是Blum最先提出来的,他使用的是中轴的概念。
如果用一个形象的比喻来说明骨架的含义,那就是设想在t=0的时刻,讲目标的边界各处同时点燃,火焰以匀速向目标内部蔓延,当火焰的前沿相交时火焰熄灭,那么火焰熄灭点的集合就构成了中轴,也就是图像的骨架。
例如一个长方形的骨架是它的长方向上的中轴线,正方形的骨架是它的中心点,圆的骨架是它的圆心,直线的骨架是它自身,孤立点的骨架也是自身。
细化的目的就是在将图像的骨架提取出来的同时保持图像细小部分的连通性,特别是在文字识别,地质识别,工业零件识别或图像理解中,先对被处理的图像进行细化有助于突出形状特点和减少冗余信息量。
月球撞击坑分类检测研究

地理空间信息GEOSPATIAL INFORMATION2015年4月第13卷第2期Apr.,2015V ol.13,No.2doi:10.3969/j.issn.1672-4623.2015.02.收稿日期:2014-05-06。
项目来源:国家高技术研究发展计划资助项目(2010AA12202)。
月球撞击坑分类检测研究马新凡1,苗 放2,杨文晖3,孟庆凯3(1.成都理工大学 信息科学与技术学院,四川 成都 610059;2.成都理工大学 地球探测与信息技术教育部重点实验室,四川 成都 610059;3.成都理工大学 空间信息技术研究所,四川 成都 610059)摘 要:根据嫦娥二号采集的月球CCD 影像,基于分类检测的方法,对撞击坑进行识别检测研究。
针对简单撞击坑、撞击盆地和复杂撞击坑的不同形态特征和影像特征,采用不同的滤波和检测算子进行识别。
结果表明,对直径在1.5 km 以上且光照条件较好的简单撞击坑和撞击盆地识别效果较好;对于复杂撞击坑,由于其形态结构相对复杂,造成图像边缘特征不突出, 因此识别率相对较低,有待进一步改进。
关键词:撞击坑;撞击坑分类;边缘检测;月球中图分类号:P237.3 文献标志码:B 文章编号:1672-4623(2015)02-0036-03014月球撞击坑是月球表面最显著的特征[1,2]。
对撞击坑进行正确的识别和提取,可为研究月球现状和演化历史提供直接证据。
欧阳自远曾做了月表直径>4 km 的撞击坑分布密度和月表地质年龄的关系图;冯军华等结合梯度信息,利用嫦娥一号CCD 图像, 采用最小二乘法拟合边缘椭圆的方法实现对撞击坑的检测[3]。
目前对于月球撞击坑的检测方法都各有优势,但由于月球撞击坑大小形状和深度不仅与撞击体的大小、密度、结构、撞击速度和撞击角度有密切关系,还受被撞击体的引力场和被撞击体处的岩性影响。
要完成对撞击坑的检测,就必须对撞击坑有一个清晰的认识,对每一类撞击坑特征有明确了解和掌握,这样有利于更准确地检测边缘[4,5]。
二值图像的细化算法

首先 , 对二 值 图像 进 行 设 定 。这 里 假 定 二值 图像 以 白 色 为 底 , 化黑 色 部 分 , 给 出 的 黑 色 部 分 的 图像 为 连 通 细 且
以下 是 对 链 表 的遍 历 ( 中 的 边 界 点 、 通 点 的 判 断 , 其 连 都 留 到下 面 的算 法 中具 体 的介 绍 ) 在 一 轮 轮 的遍 历 中 , , 逐
我设 定 了一 些 不 同 的颜 色 值 , 作 不 同 情 况 的 判 断 。 而 一 以 轮需 作 两 次 的遍 历 。第 一 次 遍 历 判 断 是 否 为 边 界点 , 考 需
摘 要 : 二值 图像是只有黑 白两种颜 色的 图像 , 二值 图像 的细化是 讨论将一 个图像 中的 黑色部分沿着 它的 中心轴 线
将 其 细化 为一 个像 素 宽 的线 条 的处 理 过 程 , 细化 的 结 果 能基 本 保 留 图形 中 黑 色部 分 的拓 扑 结 构 。介 绍 了一 个 二 值 图 像 的 细化 算 法 , 想 是从 原 图像 的 边 界 逐 层 消 除 黑 色像 素 点 , 同 时 保 持 黑 色 部 分 的 连 通 性 , 到 最 后 得 到 细 化 结 思 但 直
0 引 言
在 计 算 机 领 域 , 像 处 理 的 问题 越来 越 成 为 重 要 的一 图
图像 区域 会 失 去 连 通性 , 体 判 断要 考 虑 该 点 的 8个 方 向 具 的像 素 的颜 色 值 。因 为 在 后 面 的 判 断 中有 两 个 黑 点 去 掉 后 , 色 图 像 区 域 是 否 会 失 去 连 通 性 的 , 们 在 此 定 义 使 黑 我
第 1R 第7 o 期 2 1年 7 01 月
一种简单高效的二值图像并行细化算法PABIT
扭曲变形的情况. 步骤 2 保形腐蚀 在标记完所有非骨架边缘点后, 如果一次性
删除, 部分区域会出现断点. 仔细分析其原因, 发 现在判断边缘点满足指定模板的时候, 忽略了一 种情况, 即: 当笔划周边都满足边缘点条件 (但不 满足图 4 模板) 时, 会删去所有的外围点, 导致笔 划与笔划的断裂, 如图 5 所示.
提出一种简单易行的二值图像并行细化算法pab该算法通过模板匹配的方式层层剥离原始图像的边缘像素使具有像素点宽度为偶数的笔划保留双像素的中心骨架使像素点宽度为奇数的笔划只保留单像素的中心骨架线最终在此准骨架的基础上通过进一步处理得到最后的笔划宽度为1的细化图案
第 28 卷 第 2 期 2004 年 4 月
© 1995-2006 Tsinghua Tongfang Optical Disc Co., Ltd. All rights reserved.
第 2 期
陈庆虎等: 一种简单高效的二值图像并行细化算法 PAB IT
·271·
个像素称为 P 的八邻域.
P3 P2 P1 P4 P P0
合图 4 中任何一模板, 则该点属于应被删除的边 缘点, 将其标记为 2; 如果是边缘点, 且图 4 中有 至少一个模板与之匹配, 则该点属于图形的端点
5) 若 P 为黑点且 P 0, P 2, P 4, P 6 中仅有一白 点, 称 P 为边缘点或轮廓点.
6) 若删除黑点 P 会破坏图形的连续性, 则称
图 4 保留模板
用数学语言描述上述思想如下: 设 f (P 0) f (P 7) 分 别 代 表 P 点 的 八 邻 域 的 像 素 值, 且
1 当像素为前景点时 f (P i) = 0 当像素为背景点时 (其中 i = 0, …, 7)
hilditch 细化算法 python
Hilditch细化算法是一种用于二值图像细化(或骨架化)的算法。
以下是一个简单的Python实现:
def hilditch(image):
# 获取图像的宽度和高度
width, height = image.shape
# 创建一个与原图大小相同的掩模,所有值初始化为1
mask = image.copy()
# 遍历原图像
for y in range(height):
for x in range(width):
# 如果当前像素是1,则进行细化操作
if image[x][y] == 1:
# 判断上下左右四个方向是否为0
if (x > 0 and image[x-1][y] == 0) or (x < width-1 and image[x+1][y] == 0):
mask[x][y] = 0
if (y > 0 and image[x][y-1] == 0) or (y < height-1 and image[x][y+1] == 0):
mask[x][y] = 0
# 返回细化后的图像
return mask
这个函数接受一个二值图像作为输入,并返回一个细化后的图像。
在函数中,我们首先创建一个与原图大小相同的掩模,并将所有值初始化为1。
然后,我们遍历原图像中的每个像素,如果当前像素是1,则检查其上下左右四个方向是否为0。
如果是,则将当前像素的值设置为0,表示该像素应该被细化掉。
最后,我们返回细化后的图像。
hilditch细化算法python
Hilditch细化算法是一种用于图像处理的算法,旨在对二值化图像进行细化处理,以消除图像中不必要的细节,从而得到更加清晰的边缘轮廓。
该算法在数字图像处理领域具有广泛的应用,可以帮助我们提取出图像中的有效信息,并且在计算机视觉、模式识别等领域有着重要的作用。
Hilditch细化算法的原理比较复杂,主要包括以下几个步骤:1. 定义细化算法的结构元素:Hilditch细化算法中使用了一个3x3的结构元素,该结构元素用于检测图像中的特定模式,并对其进行处理。
2. 识别图像中的特征点:在细化算法中,首先需要识别图像中的特征点,这些特征点通常指的是图像中的边缘像素点,通过对这些特征点进行分析和处理,可以实现对图像的细化处理。
3. 进行细化处理:一旦特征点被识别出来,就可以开始对图像进行细化处理。
这通常包括对结构元素进行滑动操作,利用结构元素与图像进行匹配,并根据预先设定的条件对结构元素进行处理,以使得图像的边缘轮廓更加清晰。
4. 迭代处理:细化算法通常需要进行多次迭代处理,直到图像中的所有特征点都被处理完毕,并且不再发生变化为止。
Hilditch细化算法虽然在图像处理中有着重要的作用,但是在实际应用中也存在一些问题和挑战。
算法本身的复杂性较高,需要较高的计算和存储资源;另外在处理过程中可能会出现一些误差和不确定性,这也需要我们在实际应用中做出一些改进和调整。
在Python中,我们可以利用一些开源的图像处理库来实现Hilditch 细化算法的功能,比如OpenCV、Pillow等。
这些库提供了丰富的API和功能,可以帮助我们实现图像的二值化处理、特征点的识别以及细化处理等操作。
Python本身具有简洁清晰的语法结构,也使得我们可以比较方便地编写和调试相关的算法代码。
以OpenCV为例,我们可以通过以下步骤来实现Hilditch细化算法:1. 导入OpenCV库:首先需要导入OpenCV库,并读取需要处理的图像数据。
opencv实现二值图像细化的算法
opencv实现⼆值图像细化的算法opencv实现⼆值图像细化的算法细化算法通常和⾻骼化、⾻架化算法是相同的意思,也就是thin算法或者skeleton算法。
虽然很多图像处理的教材上不是这么写的,具体原因可以看这篇论⽂,Louisa Lam, Seong-Whan Lee, Ching Y. Suen,“Thinning Methodologies-A Comprehensive Survey ”,IEEE TRANSACTIONS ON PATTERN ANALYSIS AND MACHINE INTELLIGENCE, VOL. 14, NO. 9, SEPTEMBER 1992 ,总结了⼏乎所有92年以前的经典细化算法。
函数:void cvThin( IplImage* src, IplImage* dst, int iterations=1)功能:将IPL_DEPTH_8U型⼆值图像进⾏细化参数:src,原始IPL_DEPTH_8U型⼆值图像dst,⽬标存储空间,必须事先分配好,且和原图像⼤⼩类型⼀致iterations,迭代次数参考⽂献:T. Y. Zhang and C. Y. Suen, “A fast parallel algorithm for thinning digital patterns,” Comm. ACM, vol. 27, no. 3, pp. 236-239, 1984.void cvThin( IplImage* src, IplImage* dst, int iterations=1){CvSize size = cvGetSize(src);cvCopy(src, dst);int n = 0,i = 0,j = 0;for(n=0; n<iterations; n++){IplImage* t_image = cvCloneImage(dst);for(i=0; i<size.height; i++){for(j=0; j<size.width; j++){if(CV_IMAGE_ELEM(t_image,byte,i,j)==1){int ap=0;int p2 = (i==0)?0:CV_IMAGE_ELEM(t_image,byte, i-1, j);int p3 = (i==0 || j==size.width-1)?0:CV_IMAGE_ELEM(t_image,byte, i-1, j+1);if (p2==0 && p3==1){ap++;}int p4 = (j==size.width-1)?0:CV_IMAGE_ELEM(t_image,byte,i,j+1);if(p3==0 && p4==1){ap++;}int p5 = (i==size.height-1 || j==size.width-1)?0:CV_IMAGE_ELEM(t_image,byte,i+1,j+1);if(p4==0 && p5==1){ap++;}int p6 = (i==size.height-1)?0:CV_IMAGE_ELEM(t_image,byte,i+1,j);if(p5==0 && p6==1){ap++;}int p7 = (i==size.height-1 || j==0)?0:CV_IMAGE_ELEM(t_image,byte,i+1,j-1);if(p6==0 && p7==1){ap++;}int p8 = (j==0)?0:CV_IMAGE_ELEM(t_image,byte,i,j-1);if(p7==0 && p8==1){ap++;}int p9 = (i==0 || j==0)?0:CV_IMAGE_ELEM(t_image,byte,i-1,j-1);if(p8==0 && p9==1){ap++;}if(p9==0 && p2==1){ap++;}if((p2+p3+p4+p5+p6+p7+p8+p9)>1 && (p2+p3+p4+p5+p6+p7+p8+p9)<7){if(ap==1){if(!(p2 && p4 && p6)){if(!(p4 && p6 && p8)){CV_IMAGE_ELEM(dst,byte,i,j)=0;}}}}}}}cvReleaseImage(&t_image);t_image = cvCloneImage(dst);for(i=0; i<size.height; i++){for(int j=0; j<size.width; j++){if(CV_IMAGE_ELEM(t_image,byte,i,j)==1){int ap=0;int p2 = (i==0)?0:CV_IMAGE_ELEM(t_image,byte, i-1, j);int p3 = (i==0 || j==size.width-1)?0:CV_IMAGE_ELEM(t_image,byte, i-1, j+1);if (p2==0 && p3==1){ap++;}int p4 = (j==size.width-1)?0:CV_IMAGE_ELEM(t_image,byte,i,j+1);if(p3==0 && p4==1){ap++;}int p5 = (i==size.height-1 || j==size.width-1)?0:CV_IMAGE_ELEM(t_image,byte,i+1,j+1);if(p4==0 && p5==1){ap++;}int p6 = (i==size.height-1)?0:CV_IMAGE_ELEM(t_image,byte,i+1,j);if(p5==0 && p6==1){ap++;}int p7 = (i==size.height-1 || j==0)?0:CV_IMAGE_ELEM(t_image,byte,i+1,j-1); if(p6==0 && p7==1){ap++;}int p8 = (j==0)?0:CV_IMAGE_ELEM(t_image,byte,i,j-1);if(p7==0 && p8==1){ap++;}int p9 = (i==0 || j==0)?0:CV_IMAGE_ELEM(t_image,byte,i-1,j-1);if(p8==0 && p9==1){ap++;}if(p9==0 && p2==1){ap++;}if((p2+p3+p4+p5+p6+p7+p8+p9)>1 && (p2+p3+p4+p5+p6+p7+p8+p9)<7) {if(ap==1){if(p2*p4*p8==0){if(p2*p6*p8==0){CV_IMAGE_ELEM(dst, byte,i,j)=0;}}}}}}}cvReleaseImage(&t_image);}}//使⽤举例#include "cxcore.h"#include "cv.h"#include "highgui.h"int main(int argc, char* argv[]){if(argc!=2){return 0;}IplImage *pSrc = NULL,*pDst = NULL,*pTmp = NULL;//传⼊⼀个灰度图像pSrc = cvLoadImage(argv[1],CV_LOAD_IMAGE_GRAYSCALE);if(!pSrc){return 0;}pTmp = cvCloneImage(pSrc);pDst = cvCreateImage(cvGetSize(pSrc),pSrc->depth,pSrc->nChannels);cvZero(pDst);cvThreshold(pSrc,pTmp,128,1,CV_THRESH_BINARY_INV);//做⼆值处理,将图像转换成0,1格式 //cvSaveImage("c://Threshold.bmp",pTmp,0);cvThin(pTmp,pDst,8);//细化,通过修改iterations参数进⼀步细化cvNamedWindow("src",1);cvNamedWindow("dst",1);cvShowImage("src",pSrc);//将⼆值图像转换成灰度,以便显⽰int i = 0,j = 0;CvSize size = cvGetSize(pDst);for(i=0; i<size.height; i++){for(j=0; j<size.width; j++){if(CV_IMAGE_ELEM(pDst,uchar,i,j)==1){CV_IMAGE_ELEM(pDst,uchar,i,j) = 0;}else{CV_IMAGE_ELEM(pDst,uchar,i,j) = 255;}}}//cvSaveImage("c://thin.bmp",pDst);cvShowImage("dst",pDst);cvWaitKey(0);cvReleaseImage(&pSrc);cvReleaseImage(&pDst);cvReleaseImage(&pTmp);cvDestroyWindow("src");cvDestroyWindow("dst");return 0;}。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Hilditch 细化算法是经典的二值图像细化算法,然而,在网上却很难找到一个详细、正确的介绍和实现。
可以找到一辆个 Hilditch 算法的C实现,但缺乏注释,代码可读性也很差。
在期刊网上找到几篇论文,提及了Hilditch 算法,结果一篇说的罗哩罗嗦根本看不懂,另一篇说的说的易懂,却是错误的!拿来主义是行不通了,于是只好结合着这几个论文和代码,从头写 Hilditch 细化算法。
假设像素p的3×3邻域结构为:
Hilditch 细化算法的步骤为:
对图像从左向右从上向下迭代每个像素,是为一个迭代周期。
在每个迭代周期中,对于每一个像素p,如果它同时满足6个条件,则标记它。
在当前迭代周期结束时,则把所有标记的像素的值设为背景值。
如果某次迭代周期中不存在标记点(即满足6个条件的像素),则算法结束。
假设背景值为0,前景值为1,则:
6个条件为:
(I):p 为1,即p不是背景;
(2):x1,x3,x5,x7不全部为1(否则把p标记删除,图像空心了);
(3):x1-x8 中,至少有2个为1(若只有1个为1,则是线段的端点。
若没有为1的,则为孤立点);
(4):p的8连通联结数为1;
联结数指在像素p的3*3邻域中,和p连接的图形分量的个数:
上图中,左图的4连通联结数是2,8连通联结数是1,而右图的4联通联结数和8联通联结数都是2。
4连通联结数计算公式是:
8连通联结数计算公式是:
其中,
至于公式怎么来的就不管了,直接用就行了。
(5)假设x3已经标记删除,那么当x3为0时,p的8联通联结数为1;
(6)假设x5已经标记删除,那么当x5为0时,p的8联通联结数为1。
======
在程序中,我使用的是这样的邻域编码:
为了方便计算联结数,以0作为前景,1作为背景。
程序如下(完整程序见:
/svn/trunk/src/mon/UnmanagedI mage/ImageU8.cs):
/// <summary>
/// 计算八联结的联结数,计算公式为:
/// (p6 - p6*p7*p0) + sigma(pk - pk*p(k+1)*p(k+2)), k = {0,2,4)
/// </summary>
/// <param name="list"></param>
/// <returns></returns>
private unsafe Int32 DetectConnectivity(Int32* list)
{
Int32 count = list[6] - list[6] * list[7] * list[0];
count += list[0] - list[0] * list[1] * list[2];
count += list[2] - list[2] * list[3] * list[4];
count += list[4] - list[4] * list[5] * list[6];
return count;
}
private unsafe void FillNeighbors(Byte* p, Int32* list, Int32 width, Byte foreground = 255)
{
// list 存储的是补集,即前景点为0,背景点为1,以方便联结数的计算
list[0] = p[1] == foreground ? 0 : 1;
list[1] = p[1 - width] == foreground ? 0 : 1;
list[2] = p[-width] == foreground ? 0 : 1;
list[3] = p[-1 - width] == foreground ? 0 : 1;
list[4] = p[-1] == foreground ? 0 : 1;
list[5] = p[-1 + width] == foreground ? 0 : 1;
list[6] = p[width] == foreground ? 0 : 1;
list[7] = p[1 + width] == foreground ? 0 : 1;
}
/// <summary>
/// 使用 hilditch 算法进行细化
/// </summary>
public unsafe void Thinning(Byte foreground = 255)
{
Byte* start = this.Start;
Int32 width = this.Width;
Int32 height = this.Height;
Int32* list = stackalloc Int32[8];
Byte background = (Byte)(255 - foreground);
Int32 length = this.Length;
using (ImageU8 mask = new ImageU8(this.Width, this.Height)) {
mask.Fill(0);
Boolean loop = true;
while (loop == true)
{
loop = false;
for (Int32 r = 1; r < height - 1; r++)
{
for (Int32 c = 1; c < width - 1; c++)
{
Byte* p = start + r * width + c;
// 条件1:p 必须是前景点
if (*p != foreground) continue;
// p3 p2 p1
// p4 p p0
// p5 p6 p7
// list 存储的是补集,即前景点为0,背景点为1,以方便联结数的计算
FillNeighbors(p, list, width, foreground);
// 条件2:p0,p2,p4,p6 不皆为前景点
if (list[0] == 0 && list[2] == 0 && list[4] == 0 && list[6] == 0)
continue;
// 条件3: p0~p7至少两个是前景点
Int32 count = 0;
for (int i = 0; i < 8; i++)
{
count += list[i];
}
if (count > 6) continue;
// 条件4:联结数等于1
if (DetectConnectivity(list) != 1) continue;
// 条件5: 假设p2已标记删除,则令p2为背景,不改变p的联结数 if (mask[r - 1, c] == 1)
{
list[2] = 1;
if (DetectConnectivity(list) != 1)
continue;
list[2] = 0;
}
// 条件6: 假设p4已标记删除,则令p4为背景,不改变p的联结数 if (mask[r, c - 1] == 1)
{
list[4] = 1;
if (DetectConnectivity(list) != 1)
continue;
}
mask[r, c] = 1; // 标记删除
loop = true;
}
}
for (int i = 0; i < length; i++)
{
if (mask[i] == 1)
{
this[i] = background;
}
}
}
}
}。