灰度图像--图像分割 阈值处理之谷底阈值、峰顶平均

学习DIP第54天

转载请标明本文出处:http://blog.csdn.net/tonyshengtan ,出于尊重文章作者的劳动,转载请标明出处!文章代码已托管,欢迎共同开发:https://github.com/Tony-Tan/DIPpro

开篇废话

废话开始,这篇介绍两种基于直方图的方法,前面介绍的几种阈值处理方法,可以使用直方图作为处理工具,也可以不使用直方图,直接操作图像也可以,不过建议使用直方图,因为直方图只进行一次计算,免去后续多次的访问全图像素。

今天介绍的算法有意个前提条件,就是直方图必须是一个双峰图,我们通过找到双峰之间的谷底,或者双峰值的平均值作为阈值。

其次是对直方图进行加工,怎么加工?平滑,通过对直方图的平滑使不标准的直方图变得平滑,最终达到双峰的效果。

算法描述

算法过程详解

1. 计算图像直方图

2. 使用14[1,2,1]平滑直方图,直到直方图平滑成双峰图。

3. 计算直方图的一阶导数,按照符号(正负)将一阶导数“格式化”–一阶导数大于0的设置为1,等于0的设置为0,小于0的设置为-1

4. 寻找峰顶和谷底,从1 -> 0 -> -1的为峰顶从-1 -> 0 -> 1的为谷底

5. 谷底为阈值,或者使用双峰的峰值的平均值作为阈值,进行阈值处理。

解释下第2步,如何判断是否是双峰,同样使用第3步的方法,得出“格式化”的直方图,如果整个“格式化”直方图的模式是:

1,1,1,1,1,1,1,0,(0,0,0,0)-1,-1,-1,-1,-1,-1,-1,0(0,0,0,0),1,1,1,1,1,1,1,1,1,0(0,0,0,0)-1,-1,-1,-1,-1,-1,-1

满足这种模式的直方图就是一个双峰图,否则不是。粗体处为谷底。

本算法难度不大,所以直接上代码。

代码实现

/*********************************************************************************/
/*********************************************************************************/
/*双峰型直方图,经过直方图平滑后呈现出双峰后找出谷底,以此值为阈值划分灰度值
 *平滑直方图采用1/4[1 2 1]的模板
 *判断是否是双峰采用1阶微分,判断正负性
 *直方图非双峰不能使用该方法。
 */

//直方图从int转换为double
void Hist_int2double(int *hist,double *hist_d){
    for(int i=0;i<GRAY_LEVEL;i++)
        hist_d[i]=(double)hist[i];
}
//平滑直方图,是指呈现双峰形状
void SmoothHist(double *hist,double *dsthist){
    double *histtemp=(double *)malloc(sizeof(double)*GRAY_LEVEL);
    histtemp[0]=0.0;
    histtemp[GRAY_LEVEL]=0.0;
    for(int i=0;i<GRAY_LEVEL;i++)
        histtemp[i]=hist[i];
    for(int i=1;i<GRAY_LEVEL-1;i++){
        histtemp[i]=0.25*histtemp[i-1]+0.5*histtemp[i]+0.25*histtemp[i+1];
    }
    for(int i=0;i<GRAY_LEVEL;i++)
        dsthist[i]=histtemp[i];
    free(histtemp);
}
//判断是否是双峰直方图,如果是返回谷底,否则返回0
//#define DOUBLEHUMP_BOTTOM 1
//#define DOUBLEHUMP_MEANHUMP 2
int isDoubleHump(double *hist,int returnvalue){
    double * diffHist=(double *)malloc(sizeof(double)*GRAY_LEVEL);
    int * statusHist=(int *)malloc(sizeof(int)*GRAY_LEVEL);
    diffHist[0]=0.0;
    for(int i=1;i<GRAY_LEVEL-1;i++){
        diffHist[i]=hist[i+1]-hist[i];
    }
    for(int i=1;i<GRAY_LEVEL;i++){
        if(diffHist[i]>0)
            statusHist[i]=1;
        else if(diffHist[i]<0)
            statusHist[i]=-1;
        else if(diffHist[i]==0&&statusHist[i-1]>=0)
            statusHist[i]=1;
        else if(diffHist[i]==0&&statusHist[i-1]<0)
            statusHist[i]=-1;
    }
/*1st order:
 *______________                ________________
 *              |              |
 *              |              |
 *              |______________|
 *status:       1             -1
 *hist:
 *0 0 0 0 0 0 0 1 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0
 */
    for(int i=1;i<GRAY_LEVEL-1;i++)
        if(statusHist[i]*statusHist[i+1]<0){
            if(statusHist[i]>0)
                statusHist[i]=1;
            else if(statusHist[i]<0)
                statusHist[i]=-1;
        }else{
            statusHist[i]=0;
        }
    statusHist[GRAY_LEVEL-1]=0;
 /*double hump diff:
  *______________                _______________
  *              |              |               |
  *              |              |               |
  *              |______________|               |______________
  *status:       1             -1               1
  *             top           bottom           top
  *0 0 0 0 0 0 0 1 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 1 0 0 0 0
  *the arry test store nonzero
  */
    int test[4]={0,0,0,0};
    int test_num=0;
    for(int i=1;i<GRAY_LEVEL;i++){
        if(statusHist[i]!=0){
            test[test_num]=statusHist[i];
            if(test_num>=3){
                free(diffHist);
                free(statusHist);
                return 0;
            }
            test_num++;
        }
    }

    if(test_num==3&&test[0]==1&&test[1]==-1&&test[2]==1){
        if(returnvalue==DOUBLEHUMP_BOTTOM){
            for(int i=0;i<GRAY_LEVEL;i++)
                if(statusHist[i]==-1){
                    free(diffHist);
                    free(statusHist);
                    return i;
                }
        }else if(returnvalue==DOUBLEHUMP_MEANHUMP){
            int hump[2];
            for(int i=0,k=0;i<GRAY_LEVEL;i++)
                if(statusHist[i]==1){
                    hump[k]=i;
                    k++;
                }
            free(diffHist);
            free(statusHist);
            return (hump[0]+hump[1])/2;
        }
    }
    free(diffHist);
    free(statusHist);
    return 0;
}
//谷底法阈值分割,适用于直方图是双峰的。
void ValleyBottomThreshold(double *src,double *dst,int width,int height,int type){
    int *hist=(int *)malloc(sizeof(int)*GRAY_LEVEL);
    double *hist_d=(double *)malloc(sizeof(double)*GRAY_LEVEL);
    setHistogram(src, hist, width, height);
    Hist_int2double(hist, hist_d);
    double threshold=0.0;
#define MAXLOOP 1000
    for(int i=0;i<MAXLOOP;i++){
        SmoothHist(hist_d, hist_d);
        if(0.0!=(threshold = (double)isDoubleHump(hist_d,DOUBLEHUMP_BOTTOM))){
            Threshold(src, dst, width, height, threshold, type);
            printf("smooth times:%d   threshold:%g\n",i,threshold);
            break;
        }
    }

}
//与谷底法类似,不是使用最小谷底值,而是使用峰值位置平均值
void MeanDoubleHumpThreshold(double *src,double *dst,int width,int height,int type){
    int *hist=(int *)malloc(sizeof(int)*GRAY_LEVEL);
    double *hist_d=(double *)malloc(sizeof(double)*GRAY_LEVEL);
    setHistogram(src, hist, width, height);
    Hist_int2double(hist, hist_d);
    double threshold=0.0;
    for(int i=0;i<MAXLOOP;i++){
        SmoothHist(hist_d, hist_d);
        if(0.0!=(threshold = (double)isDoubleHump(hist_d,DOUBLEHUMP_MEANHUMP))){
            Threshold(src, dst, width, height, threshold, type);
            break;
        }
    }

}

上面包括谷底阈值和双峰平均值的阈值处理

结果观察

原图:

直方图和平滑后的直方图,以及格式化的直方图:

阈值处理结果

原图

直方图和平滑后的直方图,以及格式化的直方图:

阈值处理结果

结论

针对双峰图像的谷底阈值和峰顶平均阈值算法是纯粹的基于直方图的阈值处理方法,本算法对图像的唯一要求是原直方图是双峰的,或者经过平滑可以形成双峰的,这样就可以利用一阶导数得到谷底和峰顶位置,找到合适的阈值进行阈值处理,本算法对噪声不敏感,但鲁棒性不好,容易无法给出阈值。所以需要谨慎使用。

待续。。。

时间: 2024-10-15 05:12:06

灰度图像--图像分割 阈值处理之谷底阈值、峰顶平均的相关文章

灰度图像--图像分割 阈值处理综述

学习DIP第51天 转载请标明本文出处:http://blog.csdn.net/tonyshengtan ,出于尊重文章作者的劳动,转载请标明出处!文章代码已托管,欢迎共同开发:https://github.com/Tony-Tan/DIPpro 开篇废话 新年第一篇博客,图像分割进行到阈值处理,前面学的边缘检测,然后将边缘连接起来,达到分割区域的目的,用到的基础原理是图像灰度的变化,而阈值处理用到的是阈值的不变,也就是把具有相同或相似的灰度的一类像素定义为一个区域,根据这个特点来定义并区分一

灰度图像--图像分割 阈值处理之平均阈值

学习DIP第52天 转载请标明本文出处:http://blog.csdn.net/tonyshengtan ,出于尊重文章作者的劳动,转载请标明出处!文章代码已托管,欢迎共同开发:https://github.com/Tony-Tan/DIPpro 开篇废话 好久没写博客了,已然不熟练了,过完年整个人都不好了,哈哈,到刚才为止算是把图像分割学习了一下,这两天把学习结果和代码简单总结一下. 前面已经介绍了边缘检测,和边缘修复,阈值处理的基本概念也进行了一定介绍.阈值处理速度快,算法简单,所以应用比

灰度图像--图像分割 阈值处理之局部阈值

学习DIP第57天 转载请标明本文出处:http://blog.csdn.net/tonyshengtan ,出于尊重文章作者的劳动,转载请标明出处!文章代码已托管,欢迎共同开发:https://github.com/Tony-Tan/DIPpro 开篇废话 废话开始,今天说下区域阈值(局部阈值),前面介绍的阈值都是全局阈值,也就是阈值根据全局信息产生,而作用对象也是整幅图像的全部像素,而局部阈值的产生是一个中心像素c(x,y)的邻域的一些属性来计算出一个或多个阈值以及阈值的判别式.这句话比较难

灰度图像--图像分割 阈值处理之补充说明

学习DIP第56天 转载请标明本文出处:http://blog.csdn.net/tonyshengtan ,出于尊重文章作者的劳动,转载请标明出处!文章代码已托管,欢迎共同开发:https://github.com/Tony-Tan/DIPpro 开篇废话 在前面的介绍中,说到过,影响阈值处理的两个主要问题是目标和背景的大小关系,和噪声对目标的影响,补充说明就是来解决下这两个问题. 算法原理 首先来解决噪声影响,在图像增强的时候提到过,低通滤波和平滑能够减少图像噪声,通过减少噪声,可以一定程度

灰度图像--图像分割 阈值处理之P-Tile阈值

学习DIP第53天 转载请标明本文出处:http://blog.csdn.net/tonyshengtan ,出于尊重文章作者的劳动,转载请标明出处!文章代码已托管,欢迎共同开发:https://github.com/Tony-Tan/DIPpro 开篇废话 废话不多说,因为刚才(上一篇)已经说过了,p-tile可能听起来挺可怕,没关系,说个它的对象–中位数,这个都知道吧,数值排排站,然后选出中间那个,或者说,假如数据一共有N个,那么中位数就是排在第N?0.5的那个数:p位数,也叫p分位,可以理

灰度图像--图像分割 综合介绍

学习DIP第40天 转载请标明本文出处:http://blog.csdn.net/tonyshengtan,欢迎大家转载,发现博客被某些论坛转载后,图像无法正常显示,无法正常表达本人观点,对此表示很不满意.有些网站转载了我的博文,很开心的是自己写的东西被更多人看到了,但不开心的是这段话被去掉了,也没标明转载来源,虽然这并没有版权保护,但感觉还是不太好,出于尊重文章作者的劳动,转载请标明出处!!!! 文章代码已托管,欢迎共同开发:https://github.com/Tony-Tan/DIPpro

灰度图像--图像分割 Robert算子

学习DIP第43天 转载请标明本文出处:http://blog.csdn.net/tonyshengtan,欢迎大家转载,发现博客被某些论坛转载后,图像无法正常显示,无法正常表达本人观点,对此表示很不满意.有些网站转载了我的博文,很开心的是自己写的东西被更多人看到了,但不开心的是这段话被去掉了,也没标明转载来源,虽然这并没有版权保护,但感觉还是不太好,出于尊重文章作者的劳动,转载请标明出处!!!! 文章代码已托管,欢迎共同开发:https://github.com/Tony-Tan/DIPpro

灰度图像--图像分割 Canny边缘检测

学习DIP第48天 转载请标明本文出处:http://blog.csdn.net/tonyshengtan ,出于尊重文章作者的劳动,转载请标明出处!文章代码已托管,欢迎共同开发: https://github.com/Tony-Tan/DIPpro 开篇废话 废话开始,Canny大名鼎鼎,大家都称之为Canny算子,包括wiki上也是写的Canny detector,但是按照我的理解,我觉得叫做Canny算法比较合适,但如果叫做算子,那也应该叫做复合算子,因为Canny本身并不是一个线性模板(

灰度图像--图像分割 Marr-Hildreth算子(LoG算子)

学习DIP第49天 转载请标明本文出处:http://blog.csdn.net/tonyshengtan ,出于尊重文章作者的劳动,转载请标明出处!文章代码已托管,欢迎共同开发: https://github.com/Tony-Tan/DIPpro 开篇废话 今天介绍二阶微分算子,二阶微分算子典型的是Laplace算子,LoG可以看成是一个高斯模板的拉普拉斯变换,但是也可以从根源上推导出LoG算子,而后面要介绍的DoG则是为了纯粹的减少计算,模拟LoG的一种方法. LoG原理 LoG最底层的原