原文作者:aircraft

原文地址:https://www.cnblogs.com/DOMLX/p/8672489.html

 文中的一些图片以及思想很多都是参考https://www.cnblogs.com/My-code-z/p/5712524.html 大佬的思想 以及自己做一些个人理解的补充

  若想下载指静脉识别入门代码:https://github.com/lmskyle/process

  细化算法原理理解起来并不难,借助矩阵九宫格来实现。将九宫格定义并且编号成如下格式。

  在讲解之前有必要先看看书中是怎么说的:

  书中说的还是比较简洁的,毕竟是大牛写的,他们觉得很简单容易理解的东西,我们看起来就未必是这样了。好了闲话不多说 进入主题。

  第一步:为了不影响原图像的一些其他操作,先将原图像拷贝一份用来细化处理,在将细化后图片返回出去。

  第二步:就跟书里看的那样,需要满足四个条件来才能进行删除该点像素。这里进行的是沿着东南边界开始删除

      1: a. 2<= p2+p3+p4+p5+p6+p7+p8+p9<=6 

            大于等于2会保证p1点不是端点或孤立点,因为删除端点和孤立点是不合理的,小于等于6保证p1点是一个边界点,而不是一个内部点。等于0时候,周围没有等于1的像素,所以p1为孤立点,等于1的时候,周围只            有1个灰度等于1的像素,所以是端点(注:端点是周围有且只能有1个值为1的像素)。

          

       2:这里需要满足T(p1)=1  这里的T(p1)指的是以p2,p3...p8p9 就是p1邻居点进行轮转

          这里的轮转就是从p2开始不断的与后面的点进行组成元组的格式  比如(p2,p3)(p3,p4) (p4,p5)。。。(p9,p2) 看看这样组成的所有元组为(0,1)格式的是否恰好为1个

          假如为1个并且同时满足另外三个条件,那么他的样子大概会是这样的[[0,1,1]  这样轮转中就恰好有一个(0,1) 并且与1相邻的点必然还有像素值1 这样就是一个联通的区域 这时候p1就是边界点可以删除。

                                         [0,1,0]

                                         [0,0,0]]  

           大概的意思就是这样,我语文不好,不能说的很清楚,不过你们用本子画画就能理解我的意思了  见谅哈!!

       3:   P2*p4*p6 = 0

     4: p4*p6*p8 = 0

      这里 p4,p6出现了两次  在加上面的轮转判断 如果满足边界点条件 那么p4,p6中必然会有一个为0  至于为什么是p4,p6 就是因为这里是先沿着东南边界进行细化 

      将满足的点的索引值存入一个数组中,根据这个数组中点的索引值坐标  将图像中相应位置的点值置为0 完成一次边缘细化

  第三步:这里是沿着西北方向进行细化

      跟上面一步条件几乎一样,唯一改变就是第三和第四个条件,因为这里是为了沿着西北方向细化所以要调整为:p2*p4*p8 = 0    p2*p6*p8 = 0  这里的p2,p8出现两次的原因和 上面一步的p4,p6一样

       将满足的点的索引值存入一个数组中,根据这个数组中点的索引值坐标  将图像中相应位置的点值置为0 完成一次边缘细化

  最后:反复执行 第二步和第四步,不断的进行 左右的细化  直到没有点在可以细化  那么我们就得到了 细化后的骨架结构

现在原理已经解释完毕,那么就来看看python 是如何实现细化算法的

def neighbours(x,y,image):
"Return 8-neighbours of image point P1(x,y)
img = image
x_1, y_1, x1, y1 = x-1, y-1, x+1, y+1
return [ img[x_1][y], img[x_1][y1], img[x][y1], img[x1][y1], # P2,P3,P4,P5
img[x1][y], img[x1][y_1], img[x][y_1], img[x_1][y_1] ] # P6,P7,P8,P9 def transitions(neighbours):
n = neighbours + neighbours[0:1] # P2, P3, ... , P8, P9, P2
return sum( (n1, n2) == (0, 1) for n1, n2 in zip(n, n[1:]) ) # (P2,P3), (P3,P4), ... , (P8,P9), (P9,P2)
#将白色静脉区域细化成骨架结构  
def Refine(image):
Image_Thinned = image.copy() # deepcopy to protect the original image
changing1 = changing2 = 1 # the points to be removed (set as 0)
while changing1 or changing2: # iterates until no further changes occur in the image
# Step 1
changing1 = []
rows, columns = Image_Thinned.shape # x for rows, y for columns
for x in range(1, rows - 1): # No. of rows
for y in range(1, columns - 1): # No. of columns
P2,P3,P4,P5,P6,P7,P8,P9 = n = neighbours(x, y, Image_Thinned)
if (Image_Thinned[x][y] == 1 and # Condition 0: Point P1 in the object regions
2 <= sum(n) <= 6 and # Condition 1: 2<= N(P1) <= 6   The guarantee is not an isolated point and an endpoint or an internal point
transitions(n) == 1 and # Condition 2: S(P1)=1  (0,1)The number of rotation of the structure is 1, and the boundary point can be determined by adding other conditions
P2 * P4 * P6 == 0 and # Condition 3 Remove the southeast boundary point
P4 * P6 * P8 == 0): # Condition 4
changing1.append((x,y))
for x, y in changing1:
Image_Thinned[x][y] = 0
# Step 2
changing2 = [] for x in range(1, rows - 1):
for y in range(1, columns - 1):
P2,P3,P4,P5,P6,P7,P8,P9 = n = neighbours(x, y, Image_Thinned)
if (Image_Thinned[x][y] == 1 and # Condition 0
2 <= sum(n) <= 6 and # Condition 1
transitions(n) == 1 and # Condition 2
P2 * P4 * P8 == 0 and # Condition 3   remove the northwest border point
P2 * P6 * P8 == 0): # Condition 4
changing2.append((x,y))
for x, y in changing2:
Image_Thinned[x][y] = 0
return Image_Thinned

虽然我英文很差,但是我有百度翻译啊 ,就将我所有的注释都翻译成了英文。

这里我指给出了算法的函数原型,至于怎么调用中间的代码我就不给了,反正如果你需要用到这个算法,只要将归一化到(0,1)二值的话图片传入进来调用就行了

在看看细化后的效果图

原图:

细化后的图片:

原图的静脉是黑色的  后面我在处理的时候 将黑白二值化翻转了  白色代表静脉区域

看完这些有兴趣还可以看看我这篇对指静脉预处理提取纹理的博客:http://www.cnblogs.com/DOMLX/p/8989836.html

总结归纳:

      1,看的出来这个细化算法还是有不足的,没有那么的美观,图像在分叉点处存在像素的冗余,即非单像素点,这会使得以后对特征点的提取相当的麻烦。

      这就需要对细化算法进行改进了,这里可以采用一些模板算子对图像进行除去。

      2,至于原手指静脉图像中的噪声和阴影等会在骨架图像中产生各种毛刺,这些毛刺也会影响后期的处理。除去毛刺可以通过从每个端点开始沿着费零点搜索,直到

        交叉点时停止。在这个过程中,记录下每个端点上遍历的点数,然后取一个阈值,将小于阈值的那个端点搜索路径上的置为0。这样就完成了对图像的裁剪。

有兴趣还可以看看:

http://www.cnblogs.com/DOMLX/p/8989836.html 提取纹理特征

http://www.cnblogs.com/DOMLX/p/8672489.html 指静脉细化算法

http://www.cnblogs.com/DOMLX/p/8111507.html 指静脉切割过程

指静脉识别:

https://www.cnblogs.com/DOMLX/p/9491972.html

若有兴趣交流分享技术,可关注本人公众号,里面会不定期的分享各种编程教程,和共享源码,诸如研究分享关于c/c++,python,前端,后端,opencv,halcon,opengl,机器学习深度学习之类有关于基础编程,图像处理和机器视觉开发的知识

手指静脉细化算法过程原理解析 以及python实现细化算法的更多相关文章

  1. Web APi之过滤器执行过程原理解析【二】(十一)

    前言 上一节我们详细讲解了过滤器的创建过程以及粗略的介绍了五种过滤器,用此五种过滤器对实现对执行Action方法各个时期的拦截非常重要.这一节我们简单将讲述在Action方法上.控制器上.全局上以及授 ...

  2. Web APi之过滤器创建过程原理解析【一】(十)

    前言 Web API的简单流程就是从请求到执行到Action并最终作出响应,但是在这个过程有一把[筛子],那就是过滤器Filter,在从请求到Action这整个流程中使用Filter来进行相应的处理从 ...

  3. Twitter分布式自增ID算法snowflake原理解析

    以JAVA为例 Twitter分布式自增ID算法snowflake,生成的是Long类型的id,一个Long类型占8个字节,每个字节占8比特,也就是说一个Long类型占64个比特(0和1). 那么一个 ...

  4. Twitter分布式自增ID算法snowflake原理解析(Long类型)

    Twitter分布式自增ID算法snowflake,生成的是Long类型的id,一个Long类型占8个字节,每个字节占8比特,也就是说一个Long类型占64个比特(0和1). 那么一个Long类型的6 ...

  5. 一致性Hash算法的原理与实现(分布式映射算法)

    一致性Hash算法解决的问题: 解决分布式系统中的负载均衡问题 背景问题:有N台服务器提供缓存服务,需要对服务器进行负载均衡,将请求平均发到每台服务器上,每台服务器负载1/N的服务 硬Hash映射:将 ...

  6. 排序算法:快速排序解析及Python实现

    关键词:分而治之.递归.计算速度.基准值 1. 什么是分而治之? 1.1 分而治之(divide and conquer)一种递归式方法 1.2 找出基线条件,这种条件必须尽可能简单 1.3 不断将问 ...

  7. 【Socket通信】关于Socket通信原理解析及python实现

    Socket(套接字)通信{网络通信其实就是Socket间的通信},首先了解下概念:[来源于百度百科] "两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket.& ...

  8. 【python原理解析】python中分片的实现原理及使用技巧

    首先:说明什么是序列? 序列中的每一个元素都会被分配一个序号,即元素的位置,也称为索引:在python中的序列包含:字符串.列表和元组 然后是:什么是分片? 分片就是通过操作索引访问及获得序列的一个或 ...

  9. 【算法】(查找你附近的人) GeoHash核心原理解析及代码实现

    本文地址 原文地址 分享提纲: 0. 引子 1. 感性认识GeoHash 2. GeoHash算法的步骤 3. GeoHash Base32编码长度与精度 4. GeoHash算法 5. 使用注意点( ...

随机推荐

  1. 文字相对于 div 垂直居中

    通用方法 height  跟line-height div{ border: 1px solid black; text-align: left; height: 200px; line-height ...

  2. sql server重建系统数据库

    方法一:https://bbs.csdn.net/topics/100013082 方法二:http://blog.51cto.com/jimshu/1095780 *** 方法三:https://b ...

  3. C# 根据论文 像素差异算法【个人实验还是比较好使的】

    论文地址:http://www.docin.com/p-1081596986.html 具体代码: 我转YUV,再通过上面的论文的方式比较近. YVU 介绍:https://blog.csdn.net ...

  4. vSphere 安装操作系统

    0.找到 vSphere Client 安装文件并安装 1.创建完成EXSI.Openfiler - 磁盘创建 * - 网卡设置 2.openfiler LVM 3.EXSI of ISCSI 4.s ...

  5. loj #2538. 「PKUWC2018」Slay the Spire

    $ \color{#0066ff}{ 题目描述 }$ 九条可怜在玩一个很好玩的策略游戏:Slay the Spire,一开始九条可怜的卡组里有 \(2n\) 张牌,每张牌上都写着一个数字\(w_i\) ...

  6. linux的发行版

    Linux的不同版本以及应用领域 1.Linux内核及发行版介绍 <1>Linux内核版本 内核(kernel)是系统的心脏,是运行程序和管理像磁盘和打印机等硬件设备的核心程序,它提供了一 ...

  7. 蓝牙4.0BLE抓包(三) – 扫描请求和扫描响应

    版权声明:本文为博主原创文章,转载请注明作者和出处.    作者:强光手电[艾克姆科技-无线事业部] 1. 扫描请求和扫描响应 广播包含扫描请求SCAN_REQ和扫描响应SCAN_RSP. 扫描请求: ...

  8. HTML <form> target 属性

    浏览器支持 所有主流浏览器都支持 target 属性. 定义和用法 target 属性规定一个名称或一个关键词,指示在何处打开 action URL,即在何处显示提交表单后接收到的响应. target ...

  9. Luogu P1273 有线电视网 树形DP

    又重构了一下...当然当初的题一看就看懂了QAQ 设f[i][j]表示以i为根的子树,有j个客户的最大收益 方程:f[u][j+k]=max(f[u][j+k],f[u][j]+f[v][k]-w(u ...

  10. C++ 构造函数与默认构造函数

    构造函数:C++用于构建类的新对象时需要调用的函数,该函数无返回类型!(注意:是“无”! 不是空!(void)). 默认构造函数:未提供显式初始值时,用来穿件对象的构造函数. 以上是二者的定义,但是单 ...