前面已经写了四篇博文介绍图像的阈值化技术了,四篇博文的标题和链接如下:
使用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是取有限个值的随机变量,
则随机变量X的熵定义为:
其中的对数底数a可为任何正数,并且规定当时,
对数的底数a决定了熵的单位。当a=2时,熵的单位为bit;当a=时,熵的单位为nat;当a=10时,熵的单位为Harteley。
可以证明,当且仅当时,熵有最大值。
也就是说,当随机变量的所有事件等概率分布时,系统的不确定性最大。
这个很好理解嘛,比如下面这两种情况:
第一种情况:
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;
}
运行结果如下图所示: