图像二值化方法--OTSU(最大类间方差法)

前面学习了直方图双峰法:图像二值化方法中的阈值法

最大类间方差法(OTSU)是找到自适应阈值的常用方法。原理参考了冈萨雷斯的《数字图像处理》。

以下是自己写的函数:

//----获取灰度图in的OTSU阈值--
int Segment::otsuMat(Mat in)
{
	int i,j;
	int temp;
	//第一类均值,第二类均值,全局均值,mk=p1*m1, 第一类概率,第二类概率
	double m1,m2,mG,mk,p1,p2;

	int hist[256] = {0};
	double pro_hist[256] = {0.0};

	double cov;
	double maxcov=0.0;
	int maxthread=0;

	int row = in.rows;
	int col = in.cols;

	//统计每个灰度的数量
	for(i=0; i(i,j);
			hist[temp]++;
		}
	}

	//计算每个灰度级占图像的概率
	for(i=0; i<256; ++i)
		pro_hist[i] = (double)hist[i]/(double)(row*col);

	//计算平均灰度值
	mG = 0.0;
	for(i=0; i<256; ++i)
		mG += i * pro_hist[i];

	//统计前景和背景的平均灰度值,并计算类间方差
	for(i=0; i<256; ++i)
	{
		m1=0.0; m2=0.0; mk=0.0; p1=0.0; p2=0.0;
		for(j=0; jmaxcov) {
			maxcov = cov;
			maxthread = i;
		}
	}
	//cout<
每次需要的时候就直接复制粘贴,效果常常很满意,就感觉要二值化OTSU法总可以用上,但是今天发现不好用了。我需要把一组图片分离出目标和背景,选取了Lab空间中的L通道,直接使用OTSU法。本来这一组图背景很相似,但是结果发现分割后有一些图片错误太明显。查看了阈值,发现相差蛮多的。例如下面的四张图。

灰度图:

 图像二值化方法--OTSU(最大类间方差法)_第1张图片 图像二值化方法--OTSU(最大类间方差法)_第2张图片

图像二值化方法--OTSU(最大类间方差法)_第3张图片 图像二值化方法--OTSU(最大类间方差法)_第4张图片

对应的二值图:

图像二值化方法--OTSU(最大类间方差法)_第5张图片 图像二值化方法--OTSU(最大类间方差法)_第6张图片

图像二值化方法--OTSU(最大类间方差法)_第7张图片 图像二值化方法--OTSU(最大类间方差法)_第8张图片

图1,2,4的结果都可以接受,但是图3的结果突然就差了很远,即使我在OTSU法算得的阈值基础上加加减减也没办法兼顾到每一张图。

我列出了灰度图的均值和方差,以及OTSU算得的阈值

这四张图的均值和方差很接近,但是OTSU算得的阈值却变化较大,尤其是图3,导致二值化结果不正确。

觉得自己对“最大类间方差”理解有误,至少,它不能解决这一类问题。它的结果 out of my control.  琢磨琢磨,到底是怎么回事?

最后通过均值和标准差设置了阈值,效果可控。


另外,要使用OTSU,也可以直接采用opencv 库函数:

double threshold(InputArray src, OutputArray dst, double thresh, double maxval, int type)

当 type = CV_THRESH_OTSU 时,   thresh 的值会被忽略,所以可以任取一个值。

在手册上,type 中没有列出OTSU方法,但是在函数源代码中是有的:

double cv::threshold( InputArray _src, OutputArray _dst, double thresh, double maxval, int type )
{
    Mat src = _src.getMat();
    bool use_otsu = (type & THRESH_OTSU) != 0;
    type &= THRESH_MASK;

    if( use_otsu )
    {
        CV_Assert( src.type() == CV_8UC1 );
        thresh = getThreshVal_Otsu_8u(src);
    }

    _dst.create( src.size(), src.type() );
    Mat dst = _dst.getMat();

    if( src.depth() == CV_8U )
    {
        int ithresh = cvFloor(thresh);
        thresh = ithresh;
        int imaxval = cvRound(maxval);
        if( type == THRESH_TRUNC )
            imaxval = ithresh;
        imaxval = saturate_cast(imaxval);

        if( ithresh < 0 || ithresh >= 255 )
        {
            if( type == THRESH_BINARY || type == THRESH_BINARY_INV ||
                ((type == THRESH_TRUNC || type == THRESH_TOZERO_INV) && ithresh < 0) ||
                (type == THRESH_TOZERO && ithresh >= 255) )
            {
                int v = type == THRESH_BINARY ? (ithresh >= 255 ? 0 : imaxval) :
                        type == THRESH_BINARY_INV ? (ithresh >= 255 ? imaxval : 0) :
                        /*type == THRESH_TRUNC ? imaxval :*/ 0;
                dst.setTo(v);
            }
            else
                src.copyTo(dst);
            return thresh;
        }
        thresh = ithresh;
        maxval = imaxval;
    }
    else if( src.depth() == CV_16S )
    {
        int ithresh = cvFloor(thresh);
        thresh = ithresh;
        int imaxval = cvRound(maxval);
        if( type == THRESH_TRUNC )
            imaxval = ithresh;
        imaxval = saturate_cast(imaxval);

        if( ithresh < SHRT_MIN || ithresh >= SHRT_MAX )
        {
            if( type == THRESH_BINARY || type == THRESH_BINARY_INV ||
               ((type == THRESH_TRUNC || type == THRESH_TOZERO_INV) && ithresh < SHRT_MIN) ||
               (type == THRESH_TOZERO && ithresh >= SHRT_MAX) )
            {
                int v = type == THRESH_BINARY ? (ithresh >= SHRT_MAX ? 0 : imaxval) :
                type == THRESH_BINARY_INV ? (ithresh >= SHRT_MAX ? imaxval : 0) :
                /*type == THRESH_TRUNC ? imaxval :*/ 0;
                dst.setTo(v);
            }
            else
                src.copyTo(dst);
            return thresh;
        }
        thresh = ithresh;
        maxval = imaxval;
    }
    else if( src.depth() == CV_32F )
        ;
    else
        CV_Error( CV_StsUnsupportedFormat, "" );

    parallel_for_(Range(0, dst.rows),
                  ThresholdRunner(src, dst, thresh, maxval, type),
                  dst.total()/(double)(1<<16));
    return thresh;
}
其中 type 取值:

/* Threshold types */
enum
{
    CV_THRESH_BINARY      =0,  /* value = value > threshold ? max_value : 0       */
    CV_THRESH_BINARY_INV  =1,  /* value = value > threshold ? 0 : max_value       */
    CV_THRESH_TRUNC       =2,  /* value = value > threshold ? threshold : value   */
    CV_THRESH_TOZERO      =3,  /* value = value > threshold ? value : 0           */
    CV_THRESH_TOZERO_INV  =4,  /* value = value > threshold ? 0 : value           */
    CV_THRESH_MASK        =7,
    CV_THRESH_OTSU        =8  /* use Otsu algorithm to choose the optimal threshold value;
                                 combine the flag with one of the above CV_THRESH_* values */
};
其中 getThreshVal_Otsu_8u(): 

static double
getThreshVal_Otsu_8u( const Mat& _src )
{
    Size size = _src.size();
    if( _src.isContinuous() )
    {
        size.width *= size.height;
        size.height = 1;
    }
    const int N = 256;
    int i, j, h[N] = {0};
    for( i = 0; i < size.height; i++ )
    {
        const uchar* src = _src.data + _src.step*i;
        j = 0;
        #if CV_ENABLE_UNROLLED
        for( ; j <= size.width - 4; j += 4 )
        {
            int v0 = src[j], v1 = src[j+1];
            h[v0]++; h[v1]++;
            v0 = src[j+2]; v1 = src[j+3];
            h[v0]++; h[v1]++;
        }
        #endif
        for( ; j < size.width; j++ )
            h[src[j]]++;
    }

    double mu = 0, scale = 1./(size.width*size.height);
    for( i = 0; i < N; i++ )
        mu += i*(double)h[i];

    mu *= scale;
    double mu1 = 0, q1 = 0;
    double max_sigma = 0, max_val = 0;

    for( i = 0; i < N; i++ )
    {
        double p_i, q2, mu2, sigma;

        p_i = h[i]*scale;
        mu1 *= q1;
        q1 += p_i;
        q2 = 1. - q1;

        if( std::min(q1,q2) < FLT_EPSILON || std::max(q1,q2) > 1. - FLT_EPSILON )
            continue;

        mu1 = (mu1 + i*p_i)/q1;
        mu2 = (mu - q1*mu1)/q2;
        sigma = q1*q2*(mu1 - mu2)*(mu1 - mu2);
        if( sigma > max_sigma )
        {
            max_sigma = sigma;
            max_val = i;
        }
    }

    return max_val;
}






你可能感兴趣的:(opencv,c++)