2018-08-08 09:56:56 su1041168096 阅读数 2044

下面论述FPGA在图像处理领域中的地位:
图像处理,它是一个非常广义的概念,它包含图像增强,图像复原,图像重建,图像分析,模式识别,计算机视觉等N多个应用方向。这些应用技术有许多在本质上是相通的,但是不同应用领域的关注点往往是不同的。 从网络上的开源情况来看,FPGA在图像处理方面的主要应用一直处于图像的预处理阶段。
什么叫图像的预处理?例如图像的畸变校正,滤波器处理,边缘检测、颜色检测和阈值处理等。这些预处理都有一些共同的特征,算法较为简单,操作重复性强等。但是,除了预处理,FPGA就不能做点别的吗?有的哦,图像处理类似一个三层金字塔,分为底层,中间层,高层。
这里写图片描述
图像处理金字塔有三层,分别针对的是像素级、特征级和目标级。一个成熟的图像处理应用应该同时涵盖这三层。
在像素层,我们可以对图像做一些变换,目的是增强图像的有用信息,同时抑制任何不相关的信息(如噪声)。然后通过对预处理后的图像做分割操作实现图像从像素级到特征级的过度,分割操作可以理解为检测图像中具有一些共同性质的区域。针对这些区域,依据一个或多个分类法则,将区域归类到一些预先设定的特征类型中作为后期识别的数据集。此时的数据已经不仅仅是图像了,其中包含了丰富的特征信息,如物体的位置信息等。在金字塔高层,依靠获取的特征,如有必要还可以将这些特征集作为学习的训练集来创建专用的模型,借助模型来实现识别,进而用来对实时采集的图像进行描述。

一言以蔽之,FPGA在图像处理中的应用尚处于未成熟状态,网络上可供借鉴的大多是预处理方面的资源,而关于使用FPGA去做特征和目标层次的处理还是十分复杂的,大多数高层次的应用还处于研发阶段,具体说吧,主要是高校和大公司会去做这方面的研究与应用,而且因为是研发阶段,所以使用的FPGA套件是十分昂贵的,不计成本。显然这种实现方式是不符合商用的。因此,当你真正用一个全新的“较低廉”的“高效”的硬件方式实现了一个效果十分出色的图像处理算法,那么你当真是非常了不起的了。

用FPGA做图像处理往往需要考虑除算法之外的更多问题,如时序约束,存储器带宽不足,资源不足,计算问题,这些问题都制约着FPGA在图像处理领域的发展。

FPGA主要用于通信,IC验证,高速接口,高速总线等。对于成熟的高速电路,比如路由芯片,交换机芯片一般内部结构比较稳定,使用专用IC。FPGA主要用于需要频繁修改的高速数字电路,当专用功能的数字电路成熟,并且有一定量上的市场需求,就会放弃FPGA,转而流片生产专用功能的IC。

2012-02-11 16:18:32 renshengrumenglibing 阅读数 49281

在图像处理时,受外界光线的干扰一般比较大,假如在阈值分割时采用固


定阈值,那么在环境改变时分割效果受影响极大,那么为了避免此影响就


必须采用动态阈值,自动求出合适的阈值进行分割。
本文的介绍几种主要的图像分割方法,并给出自动阈值分割的源代码




图像分割是图像处理与计算机视觉领域低层次视觉中最为基础和重要的领域之一,它是对图像进行视觉分析和模式识别的基本前提.阈值法是一种传统的图像分割方法,因其实现简单、计算量小、性能较稳定而成为图像分割中最基本和应用最广泛的分割技术.已被应用于很多的领域。本文是在阅读大量国内外相关文献的基础上,对阈值分割技术稍做总结,分三个大类综述阈值选取方法,然后对阈值化算法的评估做简要介绍。


1.引言
所谓图像分割是指根据灰度、彩色、空间纹理、几何形状等特征把图像划分成若干个互不相交的区域,使得这些特征在同一区域内,表现出一致性或相似性,而在不同区域间表现出明显的不同[37].简单的讲,就是在一幅图像中,把目标从背景中分离出来,以便于进一步处理。图像分割是图像处理与计算机视觉领域低层次视觉中最为基础和重要的领域之一,它是对图像进行视觉分析和模式识别的基本前提.同时它也是一个经典难题,到目前为止既不存在一种通用的图像分割方法,也不存在一种判断是否分割成功的客观标准。
阈值法是一种传统的图像分割方法,因其实现简单、计算量小、性能较稳定而成为图像分割中最基本和应用最广泛的分割技术.已被应用于很多的领域,例如,在红外技术应用中,红外无损检测中红外热图像的分割,红外成像跟踪系统中目标的分割;在遥感应用中,合成孔径雷达图像中目标的分割等;在医学应用中,血液细胞图像的分割,磁共振图像的分割;在农业工程应用中,水果品质无损检测过程中水果图像与背景的分割。在工业生产中,机器视觉运用于产品质量检测等等。在这些应用中,分割是对图像进一步分析、识别的前提,分割的准确性将直接影响后续任务的有效性,其中阈值的选取是图像阈值分割方法中的关键技术。


2.阈值分割的基本概念
图像阈值化分割是一种最常用,同时也是最简单的图像分割方法,它特别适用于目标和背景占据不同灰度级范围的图像[1]。它不仅可以极大的压缩数据量,而且也大大简化了分析和处理步骤,因此在很多情况下,是进行图像分析、特征提取与模式识别之前的必要的图像预处理过程。图像阈值化的目的是要按照灰度级,对像素集合进行一个划分,得到的每个子集形成一个与现实景物相对应的区域,各个区域内部具有一致的属性,而相邻区域布局有这种一致属性。这样的划分可以通过从灰度级出发选取一个或多个阈值来实现。
阈值分割法是一种基于区域的图像分割技术,其基本原理是:通过设定不同的特征阈值,把图像像素点分为若干类.常用的特征包括:直接来自原始图像的灰度或彩色特征;由原始灰度或彩色值变换得到的特征.设原始图像为f(x,y),按照一定的准则在f(x,y)中找到特征值T,将图像分割为两个部分,分割后的图像为


 
若取 :b0=0(黑),b1=1(白),即为我们通常所说的图像二值化。


   
          (原始图像)    (阈值分割后的二值化图像)


一般意义下,阈值运算可以看作是对图像中某点的灰度、该点的某种局部特性以及该点在图像中的位置的一种函数,这种阈值函数可记作
    T(x,y,N(x,y),f(x,y))
式中,f(x,y)是点(x,y)的灰度值;N(x,y)是点(x,y)的局部邻域特性.根据对T的不同约束,可以得到3种不同类型的阈值[37],即
    点相关的全局阈值T=T(f(x,y))  
(只与点的灰度值有关)
区域相关的全局阈值T=T(N(x,y),f(x,y))  
(与点的灰度值和该点的局部邻域特征有关)
    局部阈值或动态阈值T=T(x,y,N(x,y),f(x,y))
(与点的位置、该点的灰度值和该点邻域特征有关)


图像阈值化这个看似简单的问题,在过去的四十年里受到国内外学者的广泛关注,产生了数以百计的阈值选取方法[2-9],但是遗憾的是,如同其他图像分割算法一样,没有一个现有方法对各种各样的图像都能得到令人满意的结果,甚至也没有一个理论指导我们选择特定方法处理特定图像。
所有这些阈值化方法,根据使用的是图像的局部信息还是整体信息,可以分为上下文无关(non-contextual)方法(也叫做基于点(point-dependent)的方法)和上下文相关(contextual)方法(也叫做基于区域(region-dependent)的方法);根据对全图使用统一阈值还是对不同区域使用不同阈值,可以分为全局阈值方法(global thresholding)和局部阈值方法(local thresholding,也叫做自适应阈值方法adaptive thresholding);另外,还可以分为双阈值方法(bilever thresholding)和多阈值方法(multithresholding)
本文分三大类对阈值选取技术进行综述:
1) 基于点的全局阈值方法;
2) 基于区域的全局阈值方法
3) 局部阈值方法和多阈值方法




3.基于点的全局阈值选取方法
3.1  p-分位数法
1962年Doyle[10]提出的p-分位数法(也称p-tile法)可以说是最古老的一种阈值选取方法。该方法使目标或背景的像素比例等于其先验概率来设定阈值,简单高效,但是对于先验概率难于估计的图像却无能为力。
例如,根据先验知识,知道图像目标与背景象素的比例为PO/PB,则可根据此条件直接在图像直方图上找到合适的阈值T,使得f(x,y)>=T的象素为目标,f(x,y)<T的象素为背景。


3.2  迭代方法选取阈值[11]
初始阈值选取为图像的平均灰度T0,然后用T0将图像的象素点分作两部分,计算两部分各自的平均灰度,小于T0的部分为TA,大于T0的部分为TB
计算   ,将T1 作为新的全局阈值代替T0,重复以上过程,如此迭代,直至TK 收敛,即TK+1 =TK 
经试验比较,对于直方图双峰明显,谷底较深的图像,迭代方法可以较快地获得满意结果。但是对于直方图双峰不明显,或图像目标和背景比例差异悬殊,迭代法所选取的阈值不如最大类间方差法。


3.3  直方图凹面分析法
从直观上说,图像直方图双峰之间的谷底,应该是比较合理的图像分割阈值,但是实际的直方图是离散的,往往十分粗糙、参差不齐,特别是当有噪声干扰时,有可能形成多个谷底。从而难以用既定的算法,实现对不同类型图像直方图谷底的搜索。
Rosenfeld和Torre[12]提出可以构造一个包含直方图 的最小凸多边形 ,由集差 确定 的凹面。若 和 分别表示 与 在灰度级之处的高度,则 取局部极大值时所对应的灰度级可以作为阈值。也有人使用低通滤波的方法平滑直方图,但是滤波尺度的选择并不容易[13]。
但此方法仍然容易受到噪声干扰,对不同类型的图像,表现出不同的分割效果。往往容易得到假的谷底。但此方法对某些只有单峰直方图的图像,也可以作出分割。如:
 


3.4 最大类间方差法
由Otsu[14]于1978年提出的最大类间方差法以其计算简单、稳定有效,一直广为使用。从模式识别的角度看,最佳阈值应当产生最佳的目标类与北京类的分离性能,此性能我们用类别方差来表征,为此引入类内方差 、类间方差 和总体方差 ,并定义三个等效的准则测量:
 ,  ,  .                 (3)
鉴于计算量的考量,人们一般通过优化第三个准则获取阈值。此方法也有其缺陷,kittler和Illingworth[15]的实验揭示:当图像中目标与背景的大小之比很小时方法失效。
 在实际运用中,往往使用以下简化计算公式:
   (T) = WA(μa-μ)2  + Wb(μb-μ)2 
其中, 为两类间最大方差,WA 为A类概率,μa为A类平均灰度,Wb 为B类概率,μb为B类平均灰度,μ为图像总体平均灰度。
即阈值T将图像分成A,B两部分,使得两类总方差 (T)取最大值的T,即为最佳分割阈值。


3.5 熵方法
八十年代以来,许多学者将Shannon信息熵的概念应用于图像阈值化,其基本思想都是利用图像的灰度分布密度函数定义图像的信息熵,根据假设的不同或视角的不同提出不同的熵准则,最后通过优化该准则得到阈值。Pun[16]通过使后验熵的上限最大来确定阈值。Kapur等人[17]的方法假定目标和背景服从两个不同的概率分布 和 定义
                 (4)
使得熵
                           (5)
达到最大求得最佳阈值。
此方法又称为KSW熵方法。


3.6 最小误差阈值
此方法来源于Bayes最小误差分类方法。
 
Eb(T)是目标类错分到背景类的概率,Eo(T)是背景类错分到目标类的概率
总的误差概率 E(T) = Eb(T) + Eo(T)
使E(T)取最小值,即为最优分类方法。


在Kittler和Illingworth[18]于1986年提出的最小误差法中,直方图被视为目标与背景混合集概率密度函数 的估计
                 (9)
其中, 为先验概率, ,求解下列方程可得到Bayes最小误差阈值
                        (10)
遗憾的是上式中 , 和 通常是未知的,Nakagawa和Rosenfeld[19]提倡用拟合方法从直方图中估计这些参数,但是算法相当复杂,不易实现。




3.7 矩量保持法
矩量保持(moment-preserving)法[20] ,即矩守恒阈值法,是1985年提出的,其基本思想是最佳的阈值应该使分割前后图像的矩量保持不变,由此可以得到一组矩量保持方程,求解该方程组就可以得到最佳阈值。


3.8 模糊集方法
模糊集理论较好的描述了人类视觉中的模糊性和随机性,因此在图像阈值化领域受到了广泛的关注。模糊集阈值化方法的基本思想是,选择一种S状的隶属度函数定义模糊集,隶属度为0.5的灰度级对应了阈值,当然在上述隶属度函数的表达式中阈值是一个未知的参数;然后在此模糊集上定义某种准则函数(例如整个图像的总体模糊度),通过优化准则函数来确定最佳阈值。
Pal等[21]首先,他们把一幅具有 个灰度级的 图像看作一个模糊集 ,其中隶属函数 定义如下:
               (11)
参数 称之为交叉点(即 )。由此从图像 的空间 平面得到模糊特性 平面。然后,基于此模糊集定义了图像的线性模糊度 、二次模糊度 和模糊熵 ,使这三个量取最小值时的交叉点 即为最佳阈值。
文献[21]指出模糊隶属度函数在该算法中的作用仅在于将图像由灰度数据空间转换为模糊空间 ,其函数的形式对增强结果几乎没有影响。这就使我们有理由使用一些形式简单的函数形式。例如国内学者发表的一种模糊阈值方法[22]:
 
隶属度μ(x)表示灰度x具有明亮特性的程度,c为隶属函数窗宽,q对应隶属度为0.5的灰度级。设灰度级 的模糊率为:
  = min{μ(l),1-μ(l)}
则得到整幅图像的模糊率[44] 
 
其中,MN为图像尺寸,L为图像总灰度级, 图像中灰度为 的象素个数。
对应于不同的q值,就可以计算出相应的图像模糊率,选取使得 最小的q值,作为图像分割的最佳阈值即可。


3.9 小结
对于基于点的全局阈值选取方法,除上述主要几种之外还许多,但大多都是以上述基本方法为基础,做出的改进方法或者对算法的优化,如使用递推方法以降低算法复杂性。
例如在文献[42]中,提出一种使目标和背景差距最大的阈值求取方法,类似于最大类间方差阈值法。是它的一种简化算法。
又如1984年Dunn等人[23]提出了均匀化误差阈值选取方法,这种方法实质上是要使将背景点误分为目标点的概率等于将目标点误分为背景点的概率。类似于最小误差阈值法。
近年来有一些新的研究手段被引入到阈值选取中。比如人工智能,在文献[24] 中,描述了如何用人工智能的方法,寻找直方图的谷底点,作为全局阈值分割。其它如神经网络,数学形态学[39][46],小波分析与变换[40]等等。
总的来说,基于点的全局阈值算法,与其它几大类方法相比,算法时间复杂度较低,易于实现,适合应用于在线实时图像处理系统。由于我的研究方向为机器视觉,所作的项目要求算法具有良好的实时性,因此针对基于点的全局阈值方法,阅读了较多的文献,在综述里叙述也相对比较详细。


4 基于区域的全局阈值选取方法
对一幅图像而言,不同的区域,比如说目标区域或背景区域,同一区域内的象素,在位置和灰度级上同时具有较强的一致性和相关性。
而在上述基于点的全局阈值选取方法中,有一个共同的弊病,那就是它们实际上只考虑了直方图提供的灰度级信息,而忽略了图像的空间位置细节,其结果就是它们对于最佳阈值并不是反映在直方图的谷点的情况会束手无策,不幸我们通常遇到的很多图像恰恰是这种情况。另一方面,完全不同的两幅图片却可以有相同的直方图,所以即使对于峰谷明显的情况,这些方法也不能保证你得到合理的阈值。于是,人们又提出了很多基于空间信息的阈值化方法。
可以说,局域区域的全局阈值选取方法,是基于点的方法,再加上考虑点领域内象素相关性质组合而成,所以某些方法常称为“二维xxx方法”。由于考虑了象素领域的相关性质,因此对噪声有一定抑止作用[41]。
4.1 二维熵阈值分割方法[25]
使用灰度级-局域平均灰度级形成的二维灰度直方图[43]进行阈值选取,这样就得到二维熵阈值化方法。
 
(二维灰度直方图: 灰度-领域平均灰度)
如图,在0区和1区,象素的灰度值与领域平均灰度值接近,说明一致性和相关性较强,应该大致属于目标或背景区域;2区和3区一致性和相关性较弱,可以理解为噪声或边界部分。二维熵阈值分割,就是选择(S,T)对,使得目标类和背景类的后验熵最大。(具体方法是一维熵阈值分割的推广,可参见上一节)
Abutaleb[26],和Pal]结合Kapur]和Kirby的方法,分别提出了各自的二维熵阈值化方法,其准则函数都是使目标熵和背景熵之和最大化。Brink[27]的方法则是使这两者中的较小者最大化,该方法的计算复杂度为 ,后来有人改进为递推快速算法将时间复杂度降为 (其中 为最大灰度级数)。 


4.2  简单统计法
Kittler等人[28],[29]提出一种基于简单的图像统计的阈值选取方法。使用这种方法,阈值可以直接计算得到,从而避免了分析灰度直方图,也不涉及准则函数的优化。该方法的计算公式为
                         (19)
其中, 
    
    
因为e(x,y)表征了点(x,y)领域的性质,因此本方法也属于基于区域的全局阈值法。


4.3  直方图变化法
从理论上说,直方图的谷底是非常理想的分割阈值,然后在实际应用中,图像常常受到噪声等的影响而使其直方图上原本分离的峰之间的谷底被填充,或者目标和背景的峰相距很近或者大小差不多,要检测他们的谷底就很难了。
在上一节基于点的全局阈值方法中,我们知道直方图凹面分析法的弊病是容易受到噪声干扰,对不同类型的图像,表现出不同的分割效果。往往容易得到假的谷底。这是由于原始的直方图是离散的,而且含噪声,没有考虑利用象素领域性质。
而直方图变化法,就是利用一些象素领域的局部性质变换原始的直方图为一个新的直方图。这个新的直方图与原始直方图相比,或者峰之间的谷底更深,或者谷转变成峰从而更易于检测。这里的象素领域局部性质,在很多方法中经常用的是象素的梯度值。
 例如,由于目标区的象素具有一定的一致性和相关性,因此梯度值应该较小,背景区也类似。而边界区域或者噪声,就具有较大的梯度值。最简单的直方图变换方法,就是根据梯度值加权,梯度值小的象素权加大,梯度值大的象素权减小。这样,就可以使直方图的双峰更加突起,谷底更加凹陷。


4.4 其它基于区域的全局阈值法
松弛法利用邻域约束条件迭代改进线性方程系统的收敛特性,当用于图像阈值化时其思想是:首先根据灰度级按概率将像素分为“亮”和“暗”两类,然后按照领域像素的概率调整每个像素的概率,调整过程迭代进行,使得属于亮(暗)区域的像素“亮(暗)”的概率变得更大。
其它还有许多方法利用灰度值和梯度值散射图,或者利用灰度值和平均灰度值散射图。


5 局部阈值法和多阈值法


5.1 局部阈值(动态阈值)
当图像中有如下一些情况:有阴影,照度不均匀,各处的对比度不同,突发噪声,背景灰度变化等,如果只用一个固定的全局阈值对整幅图像进行分割,则由于不能兼顾图像各处的情况而使分割效果受到影响。有一种解决办法就是用与象素位置相关的一组阈值(即阈值使坐标的函数)来对图像各部分分别进行分割。这种与坐标相关的阈值也叫动态阈值,此方法也叫变化阈值法,或自适应阈值法。这类算法的时间复杂性可空间复杂性比较大,但是抗噪能力强,对一些用全局阈值不易分割的图像有较好的效果。
例如,一幅照度不均(左边亮右边暗)的原始图像为:

如果只选择一个全局阈值进行分割,那么将出现下面两种情况,都不能得到满意的效果。


                
(阈值低,对亮区效果好,则暗区差)          (阈值高,对暗区效果好,则亮区差)


若使用局部阈值,则可分别在亮区和暗区选择不同的阈值,使得整体分割效果较为理性。


 
(按两个区域取局部阈值的分割结果)
进一步,若每个数字都用不同的局部阈值,则可达到更理想的分割效果。

/************************************************************************/
/* 全局阈值分割  自动求取阈值        */
/************************************************************************/
//自动求取阈值,增加对场景的适应性
//只需求取一次,之后就可以一直使用
#include<cv.h>
#include <highgui.h>
#include <iostream>
#include <math.h>
using namespace std;
int main(){
	IplImage * image,* image2;
	image = cvLoadImage("E:\\image\\dowels.tif",0);
	cvNamedWindow("image",1);
	cvShowImage("image",image);
	image2 = cvCreateImage(cvSize(image->width,image->height),image->depth,1);
	double T = 0;
	double dT0 = 1.0;//阈值求取结束标志
	double dT = 255.0;

	//求取平均灰度,作为阈值T的初始值T0
	  int i, j;
     double T0 = 0,T1 = 0,T2 = 0;//初始阈值
	 int count1,count2;
	 unsigned char * ptr,*dst;
	 for (i = 0 ; i< image->height ; i++)
	 {
		 for (j =0 ; j < image->width;j++)
		 {
			 ptr = (unsigned char *)image->imageData + i*image->widthStep + j;
			 T0 += ((double)(*ptr))/image->width/image->height;
		 }
	 }
	 cout<<"T0:     "<<T0<<endl;
	 T = (int)(T0 + 0.5); 
	 //计算T两侧的灰度平均值,然后把二者的均值赋值给T
	 while (dT > dT0)
	 {

		 T1 = 0;
		 T2 = 0;
		 count1 = 0;
		 count2 = 0;
		 for (i = 0 ; i< image->height ; i++)
		 {
			 for (j =0 ; j < image->width;j++)
			 {
				 ptr = (unsigned char *)image->imageData + i*image->widthStep + j;
				if (*ptr > T)
				{
					T1 += ((double)(*ptr))/image->width/image->height;
					count1++;
				} 
				else if(*ptr < T)
				{
					T2 +=  ((double)(*ptr))/image->width/image->height;
					count2++;
				}
			 }
		 }

		 T1 = T1*image->width*image->height/count1;
		 T2 = T2*image->width*image->height/count2;
		 dT = fabs(T - (T1 + T2)/2);
		  
		 cout<<"T1"<<T1<<endl;
		 cout<<"T2"<<T2<<endl;
		 cout<<"dT  " << dT<<endl;
		 T = (T1 + T2)/2;
		 cout<<"T:     "<<T<<endl;
	 }
	 


	 //根据求取的阈值进行分割
	 for (i = 0 ; i< image2->height ; i++)
	 {
		 for (j =0 ; j < image2->width;j++)
		 {
			 ptr = (unsigned char *)image->imageData + i*image->widthStep + j;
			  dst = (unsigned char *)image2->imageData+i*image2->widthStep+j;
			if (*ptr > T)
			{
				*dst = 255;
			} 
			else
			{
				*dst =0;
			}
		 }
	 }

	 cvNamedWindow("image2",1);
	 cvShowImage("image2",image2);
	 cvSaveImage("E:\\image\\dowels2.tif",image2);
	 cvWaitKey(0);
	 return 0;
}




5.1.1 阈值插值法
 首先将图像分解成系列子图,由于子图相对原图很小,因此受阴影或对比度空间变化等带来的问题的影响会比较小。然后对每个子图计算一个局部阈值(此时的阈值可用任何一种固定阈值选取方法)。通过对这些子图所得到的阈值进行插值,就可以得到对原图中每个象素进行分割所需要的合理阈值。这里对应每个象素的阈值合起来构成的一个曲面,叫做阈值曲面。


5.1.2 水线阈值算法
水线(也称分水岭或流域,watershed)阈值算法可以看成是一种特殊的自适应迭代阈值方法,它的基本思想是:初始时,使用一个较大的阈值将两个目标分开,但目标间的间隙很大;在减小阈值的过程中,两个目标的边界会相向扩张,它们接触前所保留的最后像素集合就给出了目标间的最终边界,此时也就得到了阈值。


5.1.3 其它的局部阈值法
文献[30]提出了一种基于阈值曲面的二维遗传算法。遗传算法是基于进化论中自然选择机理的、并行的、统计的随机化搜索方法,所以在图像处理中常用来确定分割阈值。
 文献[31] [32]中提出一种基于局部梯度最大值的插值方法。首先平滑图像,并求得具有局部梯度最大值的像素点,然后利用这些像素点的位置和灰度在图像上内插,得到灰度级阈值表面。
除此之外,典型的局部阈值方法还有White和Rohrer[33]的加权移动平均阈值方法,Perez和Gonzalez[34]的适用于非均匀照射下图像的局部阈值方法以及Shio[35]的与照射无关的对比度度量阈值方法等。总的来说,这类算法的时间和空间复杂度都较大,但是抗噪能力强,对一些使用全局阈值法不宜分割的图像具有较好的效果。


5.2 多阈值法
很显然,如果图像中含有占据不同灰度级区域的几个目标,则需要使用多个阈值才能将它们分开。其实多域值分割,可以看作单阈值分割的推广,前面讨论的大部分阈值化技术,诸如Otsu的最大类间方差法, Kapur的最大熵方法、矩量保持法和最小误差法等等都可以推广到多阈值的情形。以下介绍另外几种多阈值方法。
5.2.1 基于小波的多域值方法。
小波变换的多分辨率分析能力也可以用于直方图分析[36],一种基于直方图分析的多阈值选取方法思路如下:首先在粗分辨率下,根据直方图中独立峰的个数确定分割区域的类数,这里要求独立峰应该满足三个条件:(1)具有一定的灰度范围;(2)具有一定的峰下面积;(3)具有一定的峰谷差。然后,在相邻峰之间确定最佳阈值,这一步可以利用多分辨的层次结构进行。首先在最低分辨率一层进行,然后逐渐向高层推进,直到最高分辨率。可以基于最小距离判据对在最低层选取的所有阈值逐层跟踪,最后以最高分辨率层的阈值为最佳阈值。
5.2.2 基于边界点的递归多域值方法。
这是一种递归的多阈值方法。首先,将象素点分为边界点和非边界点两类,边界点再根据它们的邻域的亮度分为较亮的边界点和较暗的边界点两类,然后用这两类边界点分别作直方图,取两个直方图中的最高峰多对应的灰度级作为阈值。接下去,再分别对灰度级高于和低于此阈值的像素点递归的使用这一方法,直至得到预定的阈值数。


5.2.3 均衡对比度递归多域值方法。
首先,对每一个可能阈值计算它对应于它的平均对比度
                                         
其中, 是阈值为 时图像总的对比度, 是阈值 检测到的边界点的数目。然后,选择 的直方图上的峰值所对应的灰度级为最佳阈值。对于多阈值情形,首先用这种方法确定一个初始阈值,接着,去掉初始阈值检测到的边界点的贡献再做一次 的直方图,并依据新的直方图选择下一个阈值。这一过程可以这样一直进行下去,直到任何阈值的最大平均对比度小于某个给定的限制为止。


6 阈值化算法评价简介
尽管人们在图像分割方面做了许多研究工作,但由于尚无通用的分割理论,现已提出的分割算法大都是针对具体问题的,并没有一种适合于所有图像的通用的分割算法。另一方面,给定一个实际图像分割问题要选择合用的分割算法也还没有标准的方法。为解决这些问题需要研究对图像分割的评价问题。分割评价是改进和提高现有算法性能、改善分割质量和指导新算法研究的重要手段。
 然而,如同所有的图像分割方法一样,阈值化结果的评价是一个比较困难的问题。事实上对图像分割本身还缺乏比较系统的精确的研究,因此对其评价则更差一些。人们先后已经提出了几十个评价准则。这些准则中又有定性的,也有定量的;有分析算法的,也有检测实验结果的,文献[37]将它们大致分为13类。
文献[4] 中选择摄影师、建筑物和模特三幅图像作为标准图像,并采用趋于一致性度量和形状参数对几种常用的全局阈值方法的分割结果进行了评价。结果表明对于这三幅图像,如果希望得到的二值图像比较均匀且目标的形状较好,推荐使用最大熵方法、矩量保持方法和最大类间方差法。
文献[38] 中以磁盘及鹤模型作标准图像,在噪声条件下用错分概率、形状和均匀性度量作为标准评估了五种常见的整体阈值选取方法的性能。这五种方法是四元树方法、矩量保持法、最大类间方差法、最大熵方法和简单统计法。结果表明各种方法的性能不仅与所处理的图像有关,而且也和所选用的准则有关。该文献也指出,对于一般实时应用来说,可以选择最大类间方差方法和简单统计法。
最后,评价的目的是为了能指导、改进和提高分割,如何把评价和分割应用联系起来尚有许多工作要做。一个可能的方法是结合人工智能技术,建立分割专家系统[45],以有效的利用评价结果进行归纳推理,从而把对图像的分割由目前比较盲目的试验阶段推进到系统地实现的阶段。
2018-07-02 20:12:16 zx520113 阅读数 289

6、阈值分割

      阈值分割主要是根据灰度值信息提取前景,所以对前景物体有较强对比度的图像的分割特别有用。在OpenCV中通过调用cv2.threshold()实现阈值分割。

 

      全阈值分割:将灰度值大于thresh的像素设为白色,小于或等于thresh的像素设为黑色;反之也行,只不过表现形式不同。

          

      局部阈值分割:针对输入矩阵的每一个位置的值都有相对应的阈值,这些阈值构成了和输入矩阵同等尺寸的矩阵thresh。

        

      熵阈值计算:通过计算所有分割阈值下的图像总熵,找到最大的熵,将最大熵对应的分割阈值作为最终的阈值。

首先计算图像I的累加概率直方图,又称零阶累积矩,记为:

        

      计算各个灰度级的熵,记为:

        

      计算使f(t)=f1(t)+f2(t)最大化的t的值,该值即为得到的阈值。

        
      Otsu阈值分割:

      首先计算灰度直方图的零阶累积矩,

        

      计算灰度直方图的一阶累积矩,

        

      计算图像I的总体灰度平均值mean,其实就是k=255时的一阶累积矩,mean=oneCumuMoment(255)

      计算每一个灰度级作为阈值时,前景区域的平均灰度、前景区域的平均灰度与整幅图像的平均灰度的方差。对方差的衡量采用以下度量:

        

      找到上述最大的,然后对应的k即为Otsu自动选取的阈值,

         

      自适应阈值:

      首先对图像进行平滑,平滑的结果记为,平滑一般采用高斯平滑,均值平滑,中值平滑。

通过自适应矩阵

        

        一般令ratio=0.15。


7、形态学处理:

      腐蚀:,结构元S与图像做与运算,如果都为1,则结果为1,否则为0。腐蚀的结果是图像较小一圈。在OpenCV中通过cv2.erode()实现。

      膨胀:,结构元S与图像做或运算,如果有一个为1,则结果为1,否则为0(都为0,结果为0)。膨胀的结果是图像扩大一圈。在OpenCV中通过cv2.dilate()实现。

      开运算:,先腐蚀在膨胀的过程。它具有消除亮度较高的细小区域、在纤细点处分离物体,对于较大物体可以在不明显改变其面积的情况下平滑其边界等作用。

      闭运算:,先膨胀在腐蚀的过程。它具有填充白色物体内细小黑色空洞的区域、连接临近物体、同一个结构元、多次迭代处理,也可以在不明显改变其面积的情况下平滑其边界等作用。

      顶帽变换:,图像减去开运算的结果。它可以消除暗背景下的较量其区域,如果用原图减去开运算结果,就可以得到原图中灰度较亮的区域,可以用来校正不均匀光照。

      底帽变换:,图像减去闭运算的结果。它可以删除较高亮度背景下的较暗区域,那么用原图减去闭运算结果,可以得到原图中灰度较暗的区域。

      形态学梯度:,膨胀后的图像减去腐蚀后的图像,从而提取物体的边界。

      以上5中形态学变换的功能实现可以通过调用OpenCV中的cv2.morphologyWx()实现。依次输入的是图像矩阵,选择何种形态学运算,形态学处理的结构元,结构元的锚点,迭代次数。不同的结构元选取,处理后的形态学效果不同。


2013-05-02 20:50:26 dyl_love98 阅读数 205

PS:今天上午,非常郁闷,有很多简单基础的问题搞得我有些迷茫,哎,代码几天不写就忘。目前又不当COO,还是得用心记代码哦!

    

言前

    上一篇文章,我们讲解了图像处理中的值阈数函,这一篇文章我们来做膨胀和腐蚀数函。

 

    

膨胀与腐蚀

    说念概可能很难释解,我们来看图,首先是原图:

    Original image

    膨胀后以会成变这样:

    Dilation result - Theory example

    腐蚀后以则会成变这样:

    Erosion result - Theory example

    看起来可能有些稀里糊涂,明显是膨胀,为什么字反而变细了,而明显是腐蚀,为什么字反而变粗了。

    实际上,所谓膨胀该应指:

    

    较亮色块膨胀。

    

    而所谓腐蚀该应指:

    

    较亮色块腐蚀。

    

    面上图面里,由于景背白色是较亮色块,所以膨胀时就把玄色较暗色块的字压扁了……相反腐蚀时,字就吸水膨胀了……

    用数学公式表现就是:

    

    

    说白了就是在指定巨细的内核里找最暗或者最亮的像素点,并用该点替换掉内核锚点上的像素。 

    

 

    

实现

    首先我们来实现膨胀dilate数函。

var dilate = function(__src, __size, __borderType, __dst){
    __src || error(arguments.callee, IS_UNDEFINED_OR_NULL/* {line} */);
    if(__src.type && __src.type == "CV_RGBA"){
        var width = __src.col,
            height = __src.row,
            size = __size || 3,
            dst = __dst || new Mat(height, width, CV_RGBA),
            dstData = dst.data;
        
        var start = size >> 1;
        var withBorderMat = copyMakeBorder(__src, start, start, 0, 0, __borderType),
            mData = withBorderMat.data,
            mWidth = withBorderMat.col;
        
        var newOffset, total, nowX, offsetY, offsetI, nowOffset, i, j;
        
        if(size & 1 === 0){
            error(arguments.callee, UNSPPORT_SIZE/* {line} */);
            return __src;
        }
        
        for(i = height; i--;){
            offsetI = i * width;
            for(j = width; j--;){
                newOffset = 0;
                total = 0;
                for(y = size; y--;){
                    offsetY = (y + i) * mWidth * 4;
                    for(x = size; x--;){
                        nowX = (x + j) * 4;
                        nowOffset = offsetY + nowX;
                        (mData[nowOffset] + mData[nowOffset + 1] + mData[nowOffset + 2] > total) && (total = mData[nowOffset] + mData[nowOffset + 1] + mData[nowOffset + 2]) && (newOffset = nowOffset);
                    }
                }
                dstData[(j + offsetI) * 4] = mData[newOffset];
                dstData[(j + offsetI) * 4 + 1] = mData[newOffset + 1];
                dstData[(j + offsetI) * 4 + 2] = mData[newOffset + 2];
                dstData[(j + offsetI) * 4 + 3] = mData[newOffset + 3];
            }
        }
        
    }else{
        error(arguments.callee, UNSPPORT_DATA_TYPE/* {line} */);
    }
    return dst;
};
    每日一道理
站在历史的海岸漫溯那一道道历史沟渠:楚大夫沉吟泽畔,九死不悔;魏武帝扬鞭东指,壮心不已;陶渊明悠然南山,饮酒采菊……他们选择了永恒,纵然谄媚诬蔑视听,也不随其流扬其波,这是执著的选择;纵然马革裹尸,魂归狼烟,也要仰天长笑,这是豪壮的选择;纵然一身清苦,终日难饱,也愿怡然自乐,躬耕陇亩,这是高雅的选择。在一番选择中,帝王将相成其盖世伟业,贤士迁客成其千古文章。

    (mData[nowOffset] + mData[nowOffset + 1] + mData[nowOffset + 2] > total) && (total = mData[nowOffset] + mData[nowOffset + 1] + mData[nowOffset + 2]) && (newOffset = nowOffset);

    这行代码面里,我们先用RGB的数值和与上一个最大值total比较,然后如果新的值比较大,就把新的值赋给total,并把新的偏移量newOffset赋值以后偏移量nowOffset。

    然后个整内核巨细的面积环循完了就能够找到一个最大的total以及对应的偏移量newOffset。就能够赋值了:

    dstData[(j + offsetI) * 4] = mData[newOffset];
dstData[(j + offsetI) * 4 + 1] = mData[newOffset + 1];
dstData[(j + offsetI) * 4 + 2] = mData[newOffset + 2];
dstData[(j + offsetI) * 4 + 3] = mData[newOffset + 3];

    

    那么腐蚀erode数函则相反,找一个最小的值就好了。

var erode = function(__src, __size, __borderType, __dst){
    __src || error(arguments.callee, IS_UNDEFINED_OR_NULL/* {line} */);
    if(__src.type && __src.type == "CV_RGBA"){
        var width = __src.col,
            height = __src.row,
            size = __size || 3,
            dst = __dst || new Mat(height, width, CV_RGBA),
            dstData = dst.data;
        
        var start = size >> 1;
        var withBorderMat = copyMakeBorder(__src, start, start, 0, 0, __borderType),
            mData = withBorderMat.data,
            mWidth = withBorderMat.col;
        
        var newOffset, total, nowX, offsetY, offsetI, nowOffset, i, j;
        
        if(size & 1 === 0){
            error(arguments.callee, UNSPPORT_SIZE/* {line} */);
            return __src;
        }
        
        for(i = height; i--;){
            offsetI = i * width;
            for(j = width; j--;){
                newOffset = 0;
                total = 765;
                for(y = size; y--;){
                    offsetY = (y + i) * mWidth * 4;
                    for(x = size; x--;){
                        nowX = (x + j) * 4;
                        nowOffset = offsetY + nowX;
                        (mData[nowOffset] + mData[nowOffset + 1] + mData[nowOffset + 2] < total) && (total = mData[nowOffset] + mData[nowOffset + 1] + mData[nowOffset + 2]) && (newOffset = nowOffset);
                    }
                }
                dstData[(j + offsetI) * 4] = mData[newOffset];
                dstData[(j + offsetI) * 4 + 1] = mData[newOffset + 1];
                dstData[(j + offsetI) * 4 + 2] = mData[newOffset + 2];
                dstData[(j + offsetI) * 4 + 3] = mData[newOffset + 3];
            }
        }
        
    }else{
        error(arguments.callee, UNSPPORT_DATA_TYPE/* {line} */);
    }
    return dst;
};
 

    

果效

    

    

 

    

系列目录

    Javascript图像处理系列

 

 参考资料

    Eroding and Dilating

文章结束给大家分享下程序员的一些笑话语录: 一个合格的程序员是不会写出 诸如 “摧毁地球” 这样的程序的,他们会写一个函数叫 “摧毁行星”而把地球当一个参数传进去。


2019-01-19 23:31:28 Andy_shenzl 阅读数 281

目录

一、阈值分割分类

1、二进制阈值化

2、反二进制阈值化

3、截断阈值化

4、反阈值化为0

5、阈值化为0

二、阈值函数处理

threshold函数

代码


一、阈值分割分类

首先想要说的一点是,阈念yu,四声

不是阀(fa)值,根本就没有阀值一说,阈是门槛的意思,阀值就像请稍后一样是误读,应该是请稍侯。。。

首先看下阈值的分类:

1、二进制阈值化

上图的2号

我们设置一个阈值,

大于阈值的数值设定为255,小于阈值的我们设定为0

阈值一般情况下我们设置为128,也就是256的一半

2、反二进制阈值化

上图的3号

与二进制阈值化正好相反

大于阈值的数值设定为0,小于阈值的我们设定为255

3、截断阈值化

上图的4号

首先我们设置一个阈值,

图像中大于阈值的像素点被设定为该阈值,小于该阈值的保持不变

4、反阈值化为0

上图的5号

首先我们设置一个阈值,

图像中大于阈值的像素点变为0,小于阈值的像素点值保持不变

5、阈值化为0

上图的6号

首先我们设置一个阈值,

大于等于阈值的像素点,值保持不变,小于该阀值的像素点,值变为0

二、阈值函数处理

threshold函数

ret, dst = cv2.threshold(src, thresh, maxval, type)

  • src: 输入图,只能输入单通道图像,通常来说为灰度图
  • dst: 输出图
  • thresh: 阈值=ret,一般情况指定128
  • maxval: 当像素值超过了阈值(或者小于阈值,根据type来决定),所赋予的值
  • type:二值化操作的类型,包含以下5种类型: cv2.THRESH_BINARY; cv2.THRESH_BINARY_INV; cv2.THRESH_TRUNC; cv2.THRESH_TOZERO;cv2.THRESH_TOZERO_INV

  • cv2.THRESH_BINARY 超过阈值部分取maxval(最大值),否则取0

  • cv2.THRESH_BINARY_INV THRESH_BINARY的反转
  • cv2.THRESH_TRUNC 大于阈值部分设为阈值,否则不变
  • cv2.THRESH_TOZERO_INV THRESH_TOZERO的反转
  • cv2.THRESH_TOZERO 大于阈值部分不改变,否则设为0

代码

为了方便对比我们用matplotlib里面的subplot进行绘图

import cv2
import matplotlib.pyplot as plt
img=cv2.imread("/Users/admin/Desktop/模型数据/testz.png",0)
ret, thresh1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
ret, thresh2 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV)
ret, thresh3 = cv2.threshold(img, 127, 255, cv2.THRESH_TRUNC)
ret, thresh4 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO_INV)
ret, thresh5 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO)

titles = ['Original Image', 'BINARY', 'BINARY_INV', 'TRUNC', 'TOZERO_INV', 'TOZERO']
images = [img, thresh1, thresh2, thresh3, thresh4, thresh5]

for i in range(6):
    plt.subplot(2, 3, i + 1), plt.imshow(images[i], 'gray')
    plt.title(titles[i])
    plt.xticks([]), plt.yticks([])
plt.show()

 

 

Python与图像处理2

阅读数 5022

没有更多推荐了,返回首页