精华内容
下载资源
问答
  • 基于openmv的自适应阈值,openmv受环境光影响较大,有可能提前设置好的阈值并不适合实际比赛或者应用场地,所以开发出这种自适应阈值的取阈值方式,没有设置初始化阈值,通过将想要追踪的颜色或者混合颜色目标对准...
  • Canny边缘检是在在1986年提出来的,到今天已经30多年过去了,但Canny算法仍然是图像边缘检测算法中最经典、先进的算法之一。 相比Sobel、Prewitt等算子,Canny算法更为优异。...双阈值的滞后阈值处理。
  • 根据分解层次不同,对小波系数进行自适应阈值处理;将缩放后的小波系数重构,得到去噪后的EEG信号。以信噪比、均方根误差作为去噪效果的定量指标,将改进算法与硬阈值法、软阈值法、Garrote阈值法进行比较,结果表明,...
  • 基于MATLAB的图像自适应阈值分割代码
  • a)实验图像见文件夹 Segmentation_data;b)设定三个不同阈值,直接观察分割结果;c)利用统计直方图,得到一个...d)以报告形式(pdf 格式)阐述对所采用的基于直方图的自适应阈值法、实验结果以及对实验结果的分析。
  • 针对复杂运动背景中慢速小目标检测误检率高,实时性差等问题,提出了基于自适应阈值分割的慢速小目标检测算法。首先计算连续两帧图像特征点的金字塔光流场,对光流场进行滤波,获取匹配特征点集合。然后对图像运动...
  • python+numpy实现自适应阈值分割函数OSTU,巩固编程基础以及图像处理基础
  • 自适应阈值FAST特征点检测算法的FPGA实现.pdf
  • 小波边缘检测自适应阈值-小波边缘检测(自适应阈值).part3.rar 小波边缘检测(自适应阈值) 管理员注明: 已经重复
  • 自适应阈值

    2021-08-09 13:10:18
    自适应阈值化 微信公众号:幼儿园的学霸 目录 文章目录自适应阈值化目录背景介绍及原理原理权重选择说明自定义实现结果对比参考资料 背景介绍及原理 原理 图像阈值化的一般目的是从灰度图像中分离出目标区域和背景...

    自适应阈值化

    微信公众号:幼儿园的学霸

    目录

    背景介绍及原理

    原理

    图像阈值化的一般目的是从灰度图像中分离出目标区域和背景区域,然而仅仅通过设定全局固定阈值(对图像中的每个点其二值化的阈值都是相同的)的方法很难达到理想的分割效果。那么就需要一种方法来应对这样的情况。

    这种办法就是自适应阈值法(adaptiveThreshold),它的思想不是计算图像的全局阈值,而是根据图像不同区域亮度分布,计算其局部阈值,对于图像不同区域,能够自适应计算不同的阈值,因此被称为自适应阈值法(其实就是局部阈值法)。这种方法能够保证图像中各个像素的阈值会随着其周围邻域像素的变化而变化。

    这样做的好处:
    1.每个像素位置处的二值化阈值不是固定不变的,而是由其周围邻域像素的分布来决定的
    2.亮度较高的图像区域的二值化阈值通常会较高,而亮度低的图像区域的二值化阈值则会相适应的变小
    3.不同亮度、对比度、纹理的局部图像区域将会拥有相对应的局部二值化阈值

    权重选择

    那么自适应阈值化处理中是如何确定局部阈值呢?一种思路是,可以计算该像素其邻域(局部)的均值、中值、高斯加权平均(高斯滤波)来确定阈值。比这个阈值大,该位置像素置为255,比这个阈值小,则置为255,如此就实现了局部阈值分割。

    接下来的问题就是,既然每个点都要取周边像素的值,那么应该如何分配权重呢?
    如果使用简单平均,显然不是很合理,因为图像都是连续的,越靠近待处理位置的点关系越密切,越远离的点关系越疏远。因此,加权平均更合理,距离越近的点权重越大,距离越远的点权重越小。

    cv::adaptiveThreshold()支持两种自适应方法,即cv::ADAPTIVE_THRESH_MEAN_C(平均)和cv::ADAPTIVE_THRESH_GAUSSIAN_C(高斯)。在两种情况下,自适应阈值T(x, y)。通过计算每个像素周围bxb大小像素块的加权均值并减去常量C得到。其中,b由blockSize给出,大小必须为奇数;如果使用平均的方法,则所有像素周围的权值相同;如果使用高斯的方法,则(x,y)周围的像素的权值则根据其到中心点的距离通过高斯方程得到。

    OpenCV中已经实现有该函数,

    void adaptiveThreshold(InputArray src, OutputArray dst, double maxValue,
        int adaptiveMethod, int thresholdType, int blockSize, double C)
    

    参数说明如下:

    src:源图像,必须是8位的灰度图
    
    dst:处理后的目标图像,大小和类型与源图像相同
    maxValue:用于指定满足条件的像素设定的灰度值
    adaptiveMethod:使用的自适应阈值算法,有2种类型ADAPTIVE_THRESH_MEAN_C算法(局部邻域块均值)或ADAPTIVE_THRESH_GAUSSIAN_C(局部邻域块高斯加权和),
    ADAPTIVE_THRESH_MEAN_C的计算方法是计算出邻域的平均值再减去第六个参数C的值,
    ADAPTIVE_THRESH_GAUSSIAN_C的计算方法是计算出邻域的高斯均匀值再减去第六个参数C的值。
    
    thresholdType:阈值类型,只能是THRESH_BINARY或THRESH_BINARY_INV二者之一,具体参考上面“图像阈值处理”的表格
    blockSize:表示邻域块大小,用来计算区域阈值,一般选择3、5、7……
    C:表示常数,它是一个从均匀或加权均值提取的常数,通常为正数,但也可以是负数或零
    
    返回值dst:处理后的图像
    

    说明

    一点说明:
    由于在灰度图像中,灰度值变化明显的区域往往是物体的轮廓,所以将图像分成一小块一小块的去计算阈值往往会得出图像的轮廓。因此函数adaptiveThreshold除了将灰度图像二值化,也可以进行边缘提取,当block很小时,如block_size=3 or 5 or 7时,“自适应”的程度很高,即容易出现block里面的像素值都差不多,这样便无法二值化,而只能在边缘等梯度大的地方实现二值化,结果显得它是边缘提取函数
    当把blockSize设为比较大的值时,如blockSize=21 or 31 or 41时,adaptiveThreshold便是二值化函数

    自定义实现

    基于OpenCV中函数操作和自适应阈值的思想,自己实现了该函数,并和OpenCV实现进行对比。

    #include <opencv2/opencv.hpp>
    /*!
     * 自定义实现自适应阈值二值化.
     * OpenCV源码函数:cv::adaptiveThreshold(...)
     * @param src 输入图像.CV_8UC1
     * @param dst CV_8UC1
     * @param maxValue 满足自适应阈值后的新像素值
     * @param blockSize 邻域大小.在该邻域内计算自适应阈值
     * @param C 自适应阈值调整量.利用高斯滤波或者均值滤波计算自适应阈值T后,(T+C)为最终的二值化阈值
     * @param adaptiveMethod 自适应阈值计算方法.和OpenCV 中 AdaptiveThresholdTypes 保持一致(均值滤波或这高斯滤波方式计算阈值)
     * @author liheng
     */
    void AdaptiveThreshold(const cv::Mat &src, cv::Mat &dst, unsigned char maxValue, int blockSize, unsigned char C, int adaptiveMethod)
    {
        CV_Assert( src.type() == CV_8UC1 );
        CV_Assert( blockSize % 2 == 1 && blockSize > 1 );
        CV_Assert(cv::ADAPTIVE_THRESH_MEAN_C== adaptiveMethod || cv::ADAPTIVE_THRESH_GAUSSIAN_C==adaptiveMethod );
    
        dst.create(src.size(),src.type());
        dst.setTo(cv::Scalar(0));
    
        cv::Mat mean;//保存计算的自适应阈值
    
        //计算自适应阈值:均值滤波或高斯滤波
        if (cv::ADAPTIVE_THRESH_MEAN_C == adaptiveMethod)
            cv::boxFilter( src, mean, src.type(), cv::Size(blockSize, blockSize),
                           cv::Point(-1,-1), true, cv::BORDER_REPLICATE|cv::BORDER_ISOLATED );
        else if (cv::ADAPTIVE_THRESH_GAUSSIAN_C == adaptiveMethod)
        {
            cv::Mat srcfloat,meanfloat;
            src.convertTo(srcfloat,CV_32F);
            meanfloat=srcfloat;
            GaussianBlur(srcfloat, meanfloat, cv::Size(blockSize, blockSize), 0, 0, cv::BORDER_REPLICATE|cv::BORDER_ISOLATED);
            meanfloat.convertTo(mean, src.type());
        }
    
    
        //======================================//
        //利用常规的遍历思想进行处理
        for(int r=0;r<src.rows; ++r)
        {
            for(int c=0;c<src.cols;++c)
            {
                const auto T = mean.at<uchar>(r,c)+C;
                if( src.at<uchar>(r,c)-T>0 )
                    dst.at<uchar>(r,c) = maxValue;
                //else
                //    dst.at<uchar>(r,c) = 0;
            }
        }
        //======================================//
    
    
    //    //======================================//
    //    //利用查找表的思想加速计算
    //    unsigned char tab[512]={0};//512来源:后面有公式sdata[j] - mdata[j] + 255的最大值为(255-0+255)=511
    //    for(int i=0;i<512;++i)
    //        tab[i] = i-255 > -C ? maxValue : 0;
    //
    //    cv::Size size(src.size());
    //    if( src.isContinuous() && mean.isContinuous() && dst.isContinuous() )
    //    {
    //        size.width *= size.height;
    //        size.height = 1;
    //    }
    //
    //    for(int i = 0; i < size.height; i++ )
    //    {
    //        const uchar* sdata = src.ptr(i);
    //        const uchar* mdata = mean.ptr(i);
    //        uchar* ddata = dst.ptr(i);
    //
    //        for( int j = 0; j < size.width; j++ )
    //            ddata[j] = tab[sdata[j] - mdata[j] + 255];
    //    }
    //    //======================================//
    }
    
    int main()
    {
        cv::Mat src;
        cv::imread("./src.png",cv::IMREAD_GRAYSCALE);
        cv::imshow("src",src);
        
        
        cv::GaussianBlur(src,src,cv::Size(5,5),0);//建议进行一次滤波
    
        cv::Mat cvdst,mydst;
        int blockSize = 61;//建议尝试邻域大小为3和61分别进行尝试
        
        cv::adaptiveThreshold(src,cvdst,255,cv::ADAPTIVE_THRESH_MEAN_C,cv::THRESH_BINARY,blockSize,0);
        AdaptiveThreshold(src,mydst,255,blockSize,0,cv::ADAPTIVE_THRESH_MEAN_C);
    
        
        cv::imshow("cv adaptive",cvdst);
        cv::imshow("my adaptive",mydst);
        
        cv::waitKey(0);
        
        return 0;
    }
    
    

    结果对比

    输入图像
    src
    blocksizeOpenCV结果自定义实现结果
    33 cv实现3 自定义实现
    6161 cv实现61 自定义实现

    参考资料

    1.opencv自适应阈值函数adaptiveThreshold() 剖析
    2.自适应阈值(adaptiveThreshold)分割原理及实现



    下面的是我的公众号二维码图片,按需关注。
    图注:幼儿园的学霸

    展开全文
  • 自适应阈值分割matlab代码 image-restoration-traditionalway homework for ZJU: :hollow_red_circle: 图像修复 :cross_mark:TODO: 噪声生成 习题3 - 图像恢复 3.1 实验内容 图像是一种非常常见的信息载体,但是在...
  • 自适应阈值分割matlab代码 迁移学习和传统机器学习在手写数字识别问题上的性能差异分析报告 工作概述: 运用matlab读取手写数字识别数据集,对其分别进行传统机器学习建模和迁移学习建模。对比分析两者性能差异。 ...
  • 针对相控阵三维摄像声纳阈值选取的问题,提出了一种新的自适应阈值方法。该方法首先根据检测到的各个波束所属的边缘模式,统计各个波束的邻域对称性,并利用该对称性对原始的基于波束强度的置信度图像进行修正。再...
  • 用于图像处理的自适应阈值分割算法,在matlab上实现的
  • 结合在线掌纹的特点,对模板...然后对匹配之后的数值矩阵采用各向异性的自适应阈值处理方法,以克服光照不均匀及光的方向性等问题,有效提取掌纹线特征;最后应用本算法对不同掌纹图像进行识别,验证了方法的有效性。
  • 利用基于直方图的自适应阈值方法实现分割前景与背景 a)设定三个不同阈值,直接观察分割结果; b) 利用统计直方图,得到一个自适应的阈值,再观察实验结果; c) 以报告形式(word)阐述对所采用的基于直方图的...
  • 基于自适应阈值的彩色图像分割算法研究,王博,宋苗,图像分割是由图像处理转换到图像分析的关键,人脸图像分割是人脸检测最重要的环节,好的人脸分割算法将会使得后续的人脸检测定位
  • 自适应阈值

    2020-02-24 13:12:10
    自适应阈值: ​ 当同一幅图像上的不同部分的具有<u>不同亮度</u>时。这种情况下我们需要采用自适应阈值。此时的阈值是根据图像上的每一个小区域计算与其对应的阈值。因此在同一幅图像上的不同区域采用...

    自适应阈值:

    ​ 当同一幅图像上的不同部分的具有<u>不同亮度</u>时。这种情况下我们需要采用自适应阈值。此时的阈值是根据图像上的每一个小区域计算与其对应的阈值。因此在同一幅图像上的不同区域采用的是不同的阈值,从而使我们能在亮度不同的情况下得到更好的结果。

    cv2.adaptiveThreshold(src,x,adaptive_method, threshold_type,block_size,param1)
    

    参数:

    src:指原图像,原图像应该是灰度图。

    x :指当像素值高于(有时是小于)阈值时应该被赋予的新的像素值

    adaptive_methodCV_ADAPTIVE_THRESH_MEAN_CCV_ADAPTIVE_THRESH_GAUSSIAN_C

    threshold_type : 指取阈值类型:

    ​ 必须是 CV_THRESH_BINARY, CV_THRESH_BINARY_INV

    block_size: 指用来计算阈值的象素邻域大小: 3, 5, 7, ...

    param1 :指与方法有关的参数。对方法CV_ADAPTIVE_THRESH_MEAN_CCV_ADAPTIVE_THRESH_GAUSSIAN_C, 它是一个从均值或加权均值提取的常数, 尽管它可以是负数。

    自适应阈值:

    • 对方法CV_ADAPTIVE_THRESH_MEAN_C,先求出块中的均值,再减掉param1
    • 对方法 CV_ADAPTIVE_THRESH_GAUSSIAN_C ,先求出块中的加权和(gaussian), 再减掉param1

    例:

    import cv2
    import numpy as np
    import  matplotlib.pyplot as plt
    img = cv2.imread("text1.png",0)
    
    # img = cv2.resize(img,(0,0),fx=0.5,fy=0.5)
    # 全局阈值
    (t,thresh) = cv2.threshold(img,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
    # 自适应阈值
    thresh0 = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,cv2.THRESH_BINARY,5,4)
    thresh1 = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY,5,4)
    
    plt.figure(figsize=(8,10))
    plt.subplot(221)
    plt.imshow(img,cmap="gray")
    plt.xlabel("原图",fontproperties='SimHei')
    plt.subplot(222)
    plt.imshow(thresh,cmap="gray")
    plt.xlabel("OTSU阈值",fontproperties='SimHei')
    plt.subplot(223)
    plt.imshow(thresh0,cmap="gray")
    plt.xlabel("自适应高斯阈值",fontproperties='SimHei')
    plt.subplot(224)
    plt.imshow(thresh1,cmap="gray")
    plt.xlabel("自适应均值阈值",fontproperties='SimHei')
    plt.show()
    

    输出结果:

    14657665-16ec3747cbc13c53.png
    自适应阈值.png
    展开全文
  • 基于分块级的模式噪声,提出一种基于最大似然估计的自适应阈值视频被动取证方法.它采用小波去噪和维纳滤波提取传感器的模式噪声,并通过固定大小的滑动窗口,计算分块级的能量梯度、信噪比和相邻帧相同位置块模式噪声的...
  • 实现opencv的canny算子及其改进,可以自适应生成阈值,包括了几种阈值处理算法
  • 针对语音信号去噪问题,提出小波熵自适应阈值去噪法。首先利用小波变换分解带噪语音信号,计算小波分解后信号子带区间的小波熵,然后将小波熵和自适应阈值相结合确定各层高频系数的阈值门限,采用折中指数阈值函数对...
  • 基于FPGA实现了一种自适应阈值Harris角点检测,用于解决低成本ARM处理器无法实时检测到目标角点的问题。该算法首先对整帧像素点进行预筛选,将筛选通过的点进行Harris角点检测,通过设置容忍距离剔除伪角点,得到...
  • 支持自定义阈值和自适应阈值两种方法,每次运行只能选择使用其中的一种方法。基于opencv2.3,自己编的,能跑过,自适应阈值得到的结果也不错。
  • MATLAB自适应阈值分割

    热门讨论 2011-10-28 14:50:33
    一个比较好的程序,MATLAB自适应阈值分割方法
  • 本文对图像分割方法进行了研究,给出了一种基于模糊逻辑的自适应阈值图像分割方法,并将其应用于车牌图像中,在MATLAB环境下对两幅典型图像通过Otsu方法、脉冲耦合神经网络算法和本文所提算法进行仿真分析,结果对比...
  • 提出了一种基于空间多项式曲面拟合的自适应阈值选取方法,该方法初步考虑了图像上空间目标、背景及像素的空间分布特征,拟合了图像及其空间分布结构。
  • 自适应阈值图像二值化

    千次阅读 2020-08-13 16:08:24
    一、二值化 关于二值化的介绍,以前...图像进行二值化,且做到自适应阈值参数,有4种自适应阈值二值化方法;先从自适应阈值的作用范围来区分,自适应阈值分为: 全局阈值 使用自适应全局阈值的全局二值化方法有:大津

    一、二值化

    关于二值化的介绍,以前的博客中有介绍,这里就不再描述了,二值化介绍;二值化分为固定阈值二值化和自适应阈值二值化,固定阈值二值化方式是我们常用的二值化方式,需要自己摸索一个经验阈值,不断调整,直到找到最佳阈值,这种方式在刚刚的链接中已经介绍;而这篇文档主要介绍的就是另一种二值化方式:自适应阈值二值化。

    二、自适应阈值二值化

    图像进行二值化,且做到自适应阈值参数,有4种自适应阈值二值化方法;先从自适应阈值的作用范围来区分,自适应阈值分为:

    • 全局阈值

    使用自适应全局阈值的全局二值化方法有:大津法图像二值化、三角法图像二值化;

    • 局部阈值

    使用自适应局部阈值的局部二值化方法有:局部均值处理、局部高斯处理;

    三、大津法图像二值化

    OTSU算法也称最大类间差法,有时也称之为大津算法,由大津于1979年提出,被认为是图像分割中阈值选取的最佳算法,计算简单,不受图像亮度和对比度的影响,因此在数字图像处理上得到了广泛的应用。它是按图像的灰度特性,将图像分成背景和前景两部分。因方差是灰度分布均匀性的一种度量,背景和前景之间的类间方差越大,说明构成图像的两部分的差别越大,当部分前景错分为背景或部分背景错分为前景都会导致两部分差别变小。因此,使类间方差最大的分割意味着错分概率最小。

    大津法二值化适用于图像直方图中存在双峰的图像(直方图中的双峰就是指背景像素和前景像素),最佳阈值就是双峰之间的某个参数,即将背景像素和前景像素分割开,其原理就是最大类间方差法。

    大津法二值化的大致算法思路如下:

    1. 计算灰度图像的直方图,计算出0 - 255 每个像素值所占的像素个数;
    2. 遍历阈值 0 - 255,小于或等于阈值的像素为背景,大于阈值的像素为前景;
    3. 计算背景像素个数所占总像素个数的比例、背景像素的平均值;
    4. 计算前景像素个数所占总像素个数的比例、前景像素的平均值;
    5. 计算类间方差或类内方差,当使类间方差最大或者使类内方差最小的阈值,即为最佳阈值;
    6. 使用最佳阈值,对图像进行二值化处理。

    思路细化:

    1. 图像宽[w],图像高度[h],灰度阈值[T];遍历T,从0 - 255 ;
    2. 小于阈值T的像素个数[c0],c0为背景像素个数;大于阈值T的像素个数[c1],c1为前景像素个数;c0 + c1 = w * h ;
    3. 背景像素个数占总像素个数的比例[w0],w0 = c0 / (w * h) ; 前景像素个数占总像素个数的比例[w1],w1 = c1 / (w * h) ; 且w0 + w1 = 1 ;
    4. 背景的平均像素灰度值[u0],u0 = c0个背景像素灰度值之和 / c0 ;前景的平均像素灰度值[u1],u1 = c1个前景像素灰度值之和 / c1 ;
    5. 整张图像的像素灰度平均值[u],u = (c0个背景像素灰度值之和 + c1个前景像素灰度值之和) / (w * h) ;
    6. 类间方差[g],g = w0 * (u0 - u)^2 + w1 * (u1 - u)^2 ;类间方差指的是前景和背景之间的差异,显然该差异越大,说明分离度越好。
    7. 根据第(6)步,推导后类间方差g = w0 * w1 * (u0 - u1) ^ 2 ;
    8. 找到最大类间方差对应的灰度阈值T,即是最佳阈值。

    除了最大类间方差,也可以通过计算最小类内方差来得到最佳阈值,这里有篇博客介绍到:链接

    对于一些噪声较多的图像,可以先使用高斯滤波去噪,再用大津法对图像进行二值化,这样会使二值化的图像效果更好

    OpenCV中有大津法二值化的接口:

    double threshold(InputArray src, OutputArray dst, double thresh, double maxVal, int thresholdType)

    将第5个参数 thresholdType 设置成 THRESH_OTSU 即可,可以将THRESH_OTSU 和THRESH_BINARY等类型配合使用;当使用了THRESH_OTSU,函数threshold()返回值即是找到的最佳阈值,且函数中第三个参数thresh将不起作用。

     

    最大类间方差法 实现代码:

    //二值化处理,自适应阈值 大津法
    int Binarization::BinaryProcessing_OTSU(Mat& srcImg)
    {
    	//【1】安全性检查
    	if (!srcImg.data || srcImg.data == NULL) 
    	{
    		cout << "BinaryProcessing_OTSU() --> srcImg读取失败" << endl;
    		return MTC_FAIL;
    	}
    
    	//【2】图像灰度化
    	Mat grayImg;
    	cvtColor(srcImg, grayImg, CV_BGR2GRAY);
    
    	//【3】获取最佳二值化阈值
    	int nBestTH = 0;
    	int nRet = GetBestTH_OTSU(grayImg, nBestTH);
    	if (nRet != MTC_SUCCESS) 
    	{
    		cout << "BinaryProcessing_OTSU() --> 获取最佳二值化阈值 失败" << endl;
    		return MTC_FAIL;
    	}
    	cout << "BinaryProcessing_OTSU() --> 最佳二值化阈值 = " << nBestTH << endl;
    
    	//【4】图像二值化
    	Mat binaryImg;
    	threshold(grayImg, binaryImg, nBestTH, 255, CV_THRESH_BINARY);
    
    	//【5】显示图像
    	imshow("二值化图像", binaryImg);
    
    	return MTC_SUCCESS;
    }
    
    //获取最佳阈值,自适应阈值 大津法(最大类间差法)
    int Binarization::GetBestTH_OTSU(Mat& grayImg, int& nBestTH)
    {
    	//【1】安全性检查
    	if (!grayImg.data || grayImg.data == NULL)
    	{
    		cout << "GetBestTH_OTSU() --> grayImg读取失败" << endl;
    		return MTC_FAIL;
    	}
    
    	if (grayImg.channels() != 1) 
    	{
    		cout << "GetBestTH_OTSU() --> grayImg不是灰度图像" << endl;
    		return MTC_FAIL;
    	}
    
    	//【2】参数准备
    	double sum = 0.0;			//所有像素灰度之和
    	double w0 = 0.0;			//背景像素所占比例
    	double w1 = 0.0;			//前景像素所占比例
    	double u0_temp = 0.0;
    	double u1_temp = 0.0;
    	double u0 = 0.0;			//背景平均灰度
    	double u1 = 0.0;			//前景平均灰度
    	double delta_temp = 0.0;	//类间方差
    	double delta_max = 0.0;		//最大类间方差
    	const int GrayScale = 256;
    
    	//src_image灰度级  
    	int pixel_count[GrayScale] = { 0 };		//每个灰度级的像素数目
    	float pixel_pro[GrayScale] = { 0 };		//每个灰度级的像素数目占整幅图像的比例  
    
    	int height = grayImg.rows;
    	int width = grayImg.cols;
    	//统计每个灰度级中像素的个数  
    	for (int i = 0; i < height; i++)
    	{
    		for (int j = 0; j < width; j++)
    		{
    			int index = i * width + j;
    			pixel_count[(int)grayImg.data[index]]++;		//每个灰度级的像素数目
    			sum += (int)grayImg.data[index];				//灰度之和
    		}
    	}
    	cout << "平均灰度:" << sum / (height * width) << endl;
    
    	//计算每个灰度级的像素数目占整幅图像的比例  
    	int imgArea = height * width;
    	for (int i = 0; i < GrayScale; i++)
    	{
    		pixel_pro[i] = (float)pixel_count[i] / imgArea;
    	}
    
    	//遍历灰度级[0,255],寻找合适的threshold  
    	for (int i = 0; i < GrayScale; i++)
    	{
    		w0 = w1 = u0_temp = u1_temp = u0 = u1 = delta_temp = 0;
    		for (int j = 0; j < GrayScale; j++)
    		{
    			if (j <= i)   //背景部分  
    			{
    				w0 += pixel_pro[j];			//背景像素比例
    				u0_temp += j * pixel_pro[j];
    			}
    			else		 //前景部分  
    			{
    				w1 += pixel_pro[j];			//前景像素比例
    				u1_temp += j * pixel_pro[j];
    			}
    		}
    		u0 = u0_temp / w0;		//背景像素点的平均灰度
    		u1 = u1_temp / w1;		//前景像素点的平均灰度
    
    		delta_temp = (float)(w0 * w1 * pow((u0 - u1), 2));		//类间方差 g=w0*w1*(u0-u1)^2
    
    		//当类间方差delta_temp最大时,对应的i就是阈值T
    		if (delta_temp > delta_max)
    		{
    			delta_max = delta_temp;
    			nBestTH = i;
    		}
    	}
    
    	return MTC_SUCCESS;
    }
    

    OpenCV接口,实现代码:

    //二值化处理,自适应阈值 大津法 opencv自带接口
    int Binarization::BinaryProcessing_OTSU_OpenCV(Mat& srcImg)
    {
    	//【1】安全性检查
    	if (!srcImg.data || srcImg.data == NULL)
    	{
    		cout << "BinaryProcessing_OTSU() --> srcImg读取失败" << endl;
    		return MTC_FAIL;
    	}
    
    	//【2】图像灰度化
    	Mat grayImg;
    	cvtColor(srcImg, grayImg, CV_BGR2GRAY);
    
    
    	//【3】图像二值化
    	Mat binaryImg;
    	double dBestTH = threshold(grayImg, binaryImg, 0, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);		//CV_THRESH_OTSU
    	cout << "BinaryProcessing_OTSU_OpenCV() --> dBestTH = " << dBestTH << endl;
    
    	//【4】显示图像
    	imshow("二值化图像-opencv", binaryImg);
    
    	return MTC_SUCCESS;
    }

     

    四、三角法图像二值化

    三角法二值化,适用于图像直方图中存在单峰的图像,这是一种纯几何的方法来寻找最佳阈值,它的成立条件是假设直方图最大波峰在靠近最亮的一侧,然后通过三角形求得最大直线距离,根据最大直线距离对应的直方图灰度等级即为分割阈值,图示如下:

    在直方图上从最高峰处bmx到最暗对应直方图bmin(p=0)%构造一条直线,从bmin处开始计算每个对应的直方图b到直线的垂直距离,直到bmax为止,其中最大距离对应的直方图位置即为图像二值化对应的阈值T。

     

    三角法二值化算法步骤:

    (1)图像转灰度

    (2)计算图像灰度直方图

    (3)寻找直方图中两侧边界

    (4)寻找直方图最大值

    (5)检测是否最大波峰在亮的一侧,否则翻转

    (6)求解到直线的最大值

    设灰度级别为L,频率为α,当频率αmax最大的时候设L=L_αmax,当Lmin时,α=α_Lmin

    • 求解直线方程:根据点(Lmin,α_Lmin)和点(L_αmax,αmax)可以确定直线l的方程;
    • 求解各点到直线的距离:各点(L,α)到直线l的距离d,根据点到直线的距离公式可以求得,用一个列表去存放所有的距离d,然后利用max函数即可求得dmax;
    • 找到当点(L,α)到直线l的距离d最大时,灰度级别L的值即为最佳阈值;

    (7)确定最佳阈值T,如果翻转则最佳阈值为255 - T

    (8)使用最佳阈值,对图像进行二值化处理。

     

    OpenCV中有三角法二值化的接口:

    double threshold(InputArray src, OutputArray dst, double thresh, double maxVal, int thresholdType)

    将第5个参数 thresholdType 设置成 THRESH_TRIANGLE即可,可以将THRESH_TRIANGLE 和THRESH_BINARY等类型配合使用;当使用了THRESH_TRIANGLE,函数threshold()返回值即是找到的最佳阈值,且函数中第三个参数thresh将不起作用。

    几何法 实现代码:

    //二值化处理,自适应阈值 三角法
    int Binarization::BinaryProcessing_Triangle(Mat& srcImg)
    {
    	//【1】安全性检查
    	if (!srcImg.data || srcImg.data == NULL)
    	{
    		cout << "BinaryProcessing_Triangle() --> srcImg读取失败" << endl;
    		return MTC_FAIL;
    	}
    
    	//【2】图像灰度化
    	Mat grayImg;
    	cvtColor(srcImg, grayImg, CV_BGR2GRAY);
    
    	//【3】获取最佳二值化阈值
    	int nBestTH = 0;
    	int nRet = GetBestTH_Triangle(grayImg, nBestTH);
    	if (nRet != MTC_SUCCESS)
    	{
    		cout << "BinaryProcessing_Triangle() --> 获取最佳二值化阈值 失败" << endl;
    		return MTC_FAIL;
    	}
    	cout << "BinaryProcessing_Triangle() --> 最佳二值化阈值 = " << nBestTH << endl;
    
    	//【4】图像二值化
    	Mat binaryImg;
    	threshold(grayImg, binaryImg, nBestTH, 255, CV_THRESH_BINARY);
    
    	//【5】显示图像
    	imshow("二值化图像", binaryImg);
    
    
    	return MTC_SUCCESS;
    }
    
    //获取最佳阈值,自适应阈值 三角法
    int Binarization::GetBestTH_Triangle(Mat& grayImg, int& nBestTH)
    {
    	//【1】安全性检查
    	if (!grayImg.data || grayImg.data == NULL)
    	{
    		cout << "GetBestTH_Triangle() --> grayImg读取失败" << endl;
    		return MTC_FAIL;
    	}
    
    	if (grayImg.channels() != 1)
    	{
    		cout << "GetBestTH_Triangle() --> grayImg不是灰度图像" << endl;
    		return MTC_FAIL;
    	}
    
    	//【2】参数准备
    	const int GrayScale = 256;
    	int pixel_count[GrayScale] = { 0 };		//每个灰度级的像素数目
    	int height = grayImg.rows;
    	int width = grayImg.cols;
    	int left_bound = 0;						//最左边零的位置
    	int right_bound = 0;					//最右边零的位置
    	int max_mid = 0;						//像素数量最多的灰度级位置
    	bool bIsFlipped = false;				//是否将直方图左右翻转
    
    	//【3】统计每个灰度级的像素数目
    	for (int i = 0; i < height; i++)
    	{
    		for (int j = 0; j < width; j++)
    		{
    			int index = i * width + j;
    			pixel_count[grayImg.data[index]]++;
    		}
    	}
    
    	//【4】找到最左边零的位置
    	for (int i = 0; i < GrayScale; i++)
    	{
    		if (pixel_count[i] > 0) 
    		{
    			left_bound = i;
    			break;
    		}
    	}
    
    	//位置再移动一个步长,即为最左侧零位置
    	if (left_bound > 0)
    		left_bound--;
    
    	//【5】找到最右边零的位置
    	for (int i = GrayScale - 1; i >= 0; i--)
    	{
    		if (pixel_count[i] > 0)
    		{
    			right_bound = i;
    			break;
    		}
    	}
    
    	//位置再移动一个步长,即为最右侧零位置
    	if (right_bound < GrayScale - 1)
    		right_bound++;
    
    	//【6】找到像素数量最多的灰度级位置
    	int maxNum = 0;
    	for (int i = 0; i < GrayScale; i++)
    	{
    		if (pixel_count[i] > maxNum) 
    		{
    			maxNum = pixel_count[i];
    			max_mid = i;
    		}
    	}
    
    	//【7】如果最大值(max_mid)位置落在靠左侧这样就无法满足三角法求阈值,所以要检测是否最大值(max_mid)位置是否靠近左侧
            
    	//如果靠近左侧则通过翻转到右侧位置
    	if (max_mid - left_bound < right_bound - max_mid) 
    	{
    		int i = 0;
    		int j = GrayScale - 1;
    		int temp = 0;
    		while (i < j)
    		{
    			temp = pixel_count[i];
    			pixel_count[i] = pixel_count[j];
    			pixel_count[j] = temp;
    			i++;
    			j--;
    		}
    
    		bIsFlipped = true;
    		left_bound = GrayScale - 1 - right_bound;
    		max_mid = GrayScale - 1 - max_mid;
    	}
    	  
    	//【8】计算求得阈值
    	nBestTH = left_bound;
    	int a = maxNum;
    	int b = left_bound - max_mid;
    	float maxDist = 0;
    	for (int i = left_bound + 1; i <= max_mid; i++)
    	{
    		//计算距离(点到直线的距离 (Ax + Bx + C) / 根号[A的平方 + B的平方] 
    		//因为只有 Ax+Bx 是变化的,而我们的目的是比较距离大小,所以只计算 Ax+Bx 的值)
    		float tempDist = a * i + b * pixel_count[i];
    		if (tempDist > maxDist) 
    		{
    			maxDist = tempDist;
    			nBestTH = i;
    		}
    	}
    	nBestTH--;
    
    	//【9】对已经得到的最佳阈值,如果前面已经翻转了,则阈值要用 255 - nBestTH
    	if (bIsFlipped)
    		nBestTH = GrayScale - 1 - nBestTH;
    
    	return MTC_SUCCESS;
    }
    

    OpenCV接口,实现代码:

    //二值化处理,自适应阈值 三角法 opencv自带接口
    int Binarization::BinaryProcessing_Triangle_OpenCV(Mat& srcImg)
    {
    	//【1】安全性检查
    	if (!srcImg.data || srcImg.data == NULL)
    	{
    		cout << "BinaryProcessing_Triangle_OpenCV() --> srcImg读取失败" << endl;
    		return MTC_FAIL;
    	}
    
    	//【2】图像灰度化
    	Mat grayImg;
    	cvtColor(srcImg, grayImg, CV_BGR2GRAY);
    
    
    	//【3】图像二值化
    	Mat binaryImg;
    	double dBestTH = threshold(grayImg, binaryImg, 0, 255, CV_THRESH_BINARY | CV_THRESH_TRIANGLE);		//CV_THRESH_TRIANGLE
    	cout << "BinaryProcessing_Triangle_OpenCV() --> dBestTH = " << dBestTH << endl;
    
    	//【4】显示图像
    	imshow("二值化图像-opencv", binaryImg);
    
    	return MTC_SUCCESS;
    }

     

    五、自适应局部阈值图像二值化

    全局阈值图像二值化 只可以对整张图像使用同一个阈值进行二值化,如果图像中亮度分布不均匀,每个区域亮度都有差别,那么再使用全局阈值图像二值化,会导致部分信息缺失。

    而自适应局部阈值化能够根据图像不同区域亮度分布,来改变阈值。

     

    OpenCV中集成了这样的方法,接口如下:

    void adaptiveThreshold(InputArray src, OutputArray dst, double maxValue, int adaptiveMethod, int thresholdType, int blockSize, double C)

    参数介绍:

    src参数 表示输入图像(8位单通道图像);

    maxValue参数 表示使用 THRESH_BINARY 和 THRESH_BINARY_INV 的最大值;

    adaptiveMethod参数 表示自适应阈值算法,平均 (ADAPTIVE_THRESH_MEAN_C)或高斯(ADAPTIVE_THRESH_GAUSSIAN_C);

    thresholdType参数表示阈值类型,必须为THRESH_BINARY或THRESH_BINARY_INV的阈值类型;

    blockSize参数 表示块大小(奇数且大于1,比如3,5,7........ );

    C参数是常数,表示从平均值或加权平均值中减去的数。通常情况下,这是正值,但也可能为零或负值。

    (1)局部均值法图像二值化

    将参数adaptiveMethod 设置为ADAPTIVE_THRESH_MEAN_C,自适应阈值T(x, y),通过计算像素(x, y)周围blockSize x blockSize大小像素块的平均值并减去常量 C 得到。

    (2)局部高斯处理图像二值化

    将参数adaptiveMethod 设置为ADAPTIVE_THRESH_GAUSSIAN_C,自适应阈值T(x, y),通过计算像素(x, y)周围blockSize x blockSize大小像素块的加权求和(与高斯窗口相关)并减去常量 C 得到。

    如果使用平均的方法,则所有像素周围的权值相同;

    如果使用高斯的方法,则每个像素周围像素的权值则根据其到中心点的距离通过高斯方程得到。

    OpenCV接口 实现代码: 

    //自适应阈值二值化  均值
    int Binarization::AdaptiveThreshold_Mean(Mat& srcImg)
    {
    	//【1】安全性检查
    	if (!srcImg.data || srcImg.data == NULL)
    	{
    		cout << "AdaptiveThreshold_Mean() --> srcImg读取失败" << endl;
    		return MTC_FAIL;
    	}
    
    	//【2】图像灰度化
    	Mat grayImg;
    	cvtColor(srcImg, grayImg, CV_BGR2GRAY);
    
    	//【3】自适应阈值二值化
    	Mat binaryImg;
    	adaptiveThreshold(grayImg, binaryImg, 255, CV_ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY, 11, 2);
    
    	//【4】显示图像
    	imshow("二值化图像", binaryImg);
    
    	return MTC_SUCCESS;
    }
    
    
    
    //自适应阈值二值化  高斯
    int Binarization::AdaptiveThreshold_GAUSSIAN(Mat& srcImg)
    {
    	//【1】安全性检查
    	if (!srcImg.data || srcImg.data == NULL)
    	{
    		cout << "AdaptiveThreshold_GAUSSIAN() --> srcImg读取失败" << endl;
    		return MTC_FAIL;
    	}
    
    	//【2】图像灰度化
    	Mat grayImg;
    	cvtColor(srcImg, grayImg, CV_BGR2GRAY);
    
    	//【3】自适应阈值二值化
    	Mat binaryImg;
    	adaptiveThreshold(grayImg, binaryImg, 255, CV_ADAPTIVE_THRESH_GAUSSIAN_C, CV_THRESH_BINARY, 11, 2);
    
    	//【4】显示图像
    	imshow("二值化图像", binaryImg);
    
    	return MTC_SUCCESS;
    }

    总结

    (1)大津法的优点在于可以快速有效的找到类间分割阈值,但其缺点也很明显,就是只能针对单一目标分割,或者感兴趣的目标都属于同一灰度范围,若需探测目标灰度范围分布较大,则必将有一部分目标探测丢失。

    (2)局部分割的优点在于可以进行多目标分割,缺点在于基于局部阈值分割出的目标连结性较差,包含噪声。

     

    关于二值化,这里有几篇从OpenCV官网找到的介绍供参考:

    链接1 链接2 链接3

    展开全文

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 24,261
精华内容 9,704
关键字:

自适应阈值