图像最大熵阈值分割的详细原理及基于OpenCV的C++代码实现

前面已经写了四篇博文介绍图像的阈值化技术了,四篇博文的标题和链接如下:

使用OpenCV的函数threshold()对图像进行二值化或阈值化处理
https://www.hhai.cc/thread-162-1-1.html

使用OpenCV的函数createTrackbar()创建窗口滑动条查找图像二值化的最优阈值
https://www.hhai.cc/thread-228-1-1.html

图像的OTSU阈值化、双阈值化、半阈值化的原理及OpenCV代码实现
https://www.hhai.cc/thread-165-1-1.html

详解OpenCV的函数adaptiveThreshold(),并利用它实现图像的自适应二值化阈值分割
https://www.hhai.cc/thread-166-1-1.html

从上面四篇博文中我们可以看出,图像的阈值化技术的关键在于找寻合适的阈值然后用这个阈值对图像进行二值化处理。

找寻阈值的方法有很多,上面三篇博文就提供了四种方法。

本文介绍利用图像图像直方图的最大熵找寻阈值的方法,并附相关代码。

先介绍原理:

1、要理解最大熵就不得不先了解熵的概念。熵的概念用于表示系统的不确定性,系统的熵越大则系统的不确定性越大。所以取系统的最大熵就是取系统的最大不确定性,从信息论的角度来讲就是取最大的信息量。

2、熵的数学定义如下:

设X是取有限个值的随机变量,

\large p_{i}=P\left ( X=x_{i} \right ),i=1,2,\cdots,n

则随机变量X的熵定义为:

\large H\left ( X \right )=\sum_{i=1}^{n}p_{i}log_{a}\frac{1}{p_{i}}=-\sum_{i=1}^{n}p_{i}log_{a}p_{i}

其中的对数底数a可为任何正数,并且规定当p_{i}=0时,

\large p_{i}log_{a}\frac{1}{p_{i}}=-p_{i}log_{a}p_{i}=0

对数的底数a决定了熵的单位。当a=2时,熵的单位为bit;当a=时,熵的单位为nat;当a=10时,熵的单位为Harteley。

可以证明,当且仅当p_{1}=p_{2}=\cdots =p_{n}=1/n时,熵有最大值log_{a}n
也就是说,当随机变量的所有事件等概率分布时,系统的不确定性最大。
这个很好理解嘛,比如下面这两种情况:
第一种情况:
A股票涨的概率为0.3
B股票涨的概率为0.5
C股票涨的概率为0.1
D股票涨的概率为0.1
第二种情况:
A股票、B股票、C股票、D股票涨的概率都为0.25
现在让你从这四支股票中选一支,你觉得是哪种情况好选呢?
显然是第一种情况好选嘛。

本文前面说熵用来衡量系统的不确定性,熵越大系统的不确定性越高,理解了这个数学定义,也就更加理解这句话了。

3、下面介绍利用最大熵进行图像阈值化的原理和思路。

关于利用最大熵进行图像阈值化的原理和思路,
请大家访问本博文的原文查看,
本博文的原文链接如下:

https://www.hhai.cc/thread-229-1-1.html

原理和思路就介绍完了,接下来上代码:

借助图像直方图计算图像阈值分割最大熵,并对图像进行阈值分割的C++代码如下(基于OpenCV写成):

代码中用到的图像下载链接:

https://pan.baidu.com/s/1k13r2DdhEXuXWlxV-IbxNA 提取码:kwgo 

//出处:昊虹AI笔记网(hhai.cc)
//用心记录计算机视觉和AI技术

//博主微信/QQ 2487872782
//QQ群 271891601
//欢迎技术交流与咨询

//OpenCV版本 OpenCV3.0

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace std;
using namespace cv;

 
// 利用图像的直方图计算各阈值分割图像时图像的熵值
float caculateCurrentEntropy(cv::Mat hist, int threshold)
{
    float BackgroundSum = 0, targetSum = 0;
	const float* pDataHist = (float*) hist.ptr<float>(0);
	for (int i = 0; i < 256; i++)
	{
		// 累计背景值
		if( i < threshold )
		{
			BackgroundSum += pDataHist[i];
		}else // 累计前景值
	    {
            targetSum += pDataHist[i];
	    }
	}
	
	//接下来计算图像在阈傎threshold分割情况下的熵值
    float BackgroundEntropy = 0, targetEntropy = 0;
	for (int i = 0; i < 256; i++)
	{
		// 计算图像熵的背景熵分量
		if( i < threshold )
        {
        	if( pDataHist[i] == 0 )
				continue;
			float ratio1 = pDataHist[i] / BackgroundSum;
			// 当循环完成后,BackgroundEntropy中存储的便是图像熵的背景熵分量
			BackgroundEntropy += - ratio1 * logf(ratio1); 
        }else // 计算图像熵的前景熵分量
        {
         	if( pDataHist[i] == 0 )
			 	continue;
			float ratio2 = pDataHist[i] / targetSum;
			// 当循环完成后,targetEntrop中存储的便是图像熵的前景熵分量
			targetEntropy += -ratio2 * logf(ratio2); 
        }
	}
	return ( targetEntropy + BackgroundEntropy );
}
// 找寻最大熵及最大熵对应的阈值
cv::Mat maxEntropySegMentation(cv::Mat inputImage)
{
    // 初始化直方图参数
    const int channels[1]={0};
    const int histSize[1]={256};
	float pranges[2]={0, 256};
	const float* ranges[1]={pranges};
	cv::Mat hist;
	// 计算直方图
	cv::calcHist(&inputImage,1,channels,
		cv::Mat(),hist,1,histSize,ranges);
	float maxentropy = 0;
	int    max_index  = 0;
	cv::Mat result; 
	// 遍历得到最大熵阈值分割的最佳阈值
	for(int i = 0; i < 256; i++)	
	{
		float cur_entropy = 
			caculateCurrentEntropy(hist,i);
		// 计算当前最大值的位置
		if(cur_entropy > maxentropy)
		{
			maxentropy = cur_entropy;
			max_index = i;
		}
	}
	cout<<"利用最大熵法找到的阈值为:"<<max_index<<endl;
	//  二值化分割
	threshold(inputImage, result, max_index, 255,
           CV_THRESH_BINARY); //max_index就是满足最大熵情况下的阈值
	return result;
}
int main()
{
	//cv::Mat srcImage=cv::imread("hand1.jpg");
	cv::Mat srcImage=cv::imread("E:/material/images/P0027-coins-01.png");
	if(!srcImage.data)
		return 0;
	cv::Mat grayImage;
	cv::cvtColor(srcImage, grayImage, CV_BGR2GRAY); 
    // 最大熵阈值分割实现    
    cv::Mat result = maxEntropySegMentation(grayImage);
    cv::imshow("grayImage", grayImage);
    cv::imshow("result" , result);
    cv::waitKey(0);
	return 0;
}

运行结果如下图所示:

  • 7
    点赞
  • 63
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值