第一次尝试写博客,希望能坚持下去。。。
最近在做红外小目标检测,用到一个最大熵分割法,ok,下面介绍一下。
最大熵分割法
现在主要用的熵算法有 P 氏熵算法,KSW 熵算法、JM 熵算法下面以经典的 KSW 熵算法为例介绍其原理和计算过程。
KSW熵算法
设分割阈值为设分割阈值为t,
T为{0,1,2,...t}的灰度分布,B为{t+1,t+2,...L-1}的灰度分布,则概率分布为:
式中
则这两个概率密度相关的熵为:
定义函数φ(t)为H(T)和H(B)的和,则
求出φ(t)最大时的灰度级t即为所求的最佳阈值。
灰度直方图的求法参见 http://blog.csdn.net/xiaowei_cqu/article/details/7600666
代码如下:
#include <cv.h>
#include <opencv2/opencv.hpp>
#include <opencv2/legacy/legacy.hpp>
using namespace cv;
float calc_entropy(CvHistogram *hist, int begin, int end)
{
float total = 0; // 总概率
// 得到总的Pi
for(int i = begin; i < end; i++)
{
total += cvQueryHistValue_1D(hist,i);
}
float entropy = 0; // 熵
for(int i = begin; i < end; i++)
{
float probability = cvQueryHistValue_1D(hist, i);
if(probability == 0)
continue;
probability /= total;
entropy += -probability*log(probability);
}
return entropy;
}
int ksw_entropy(IplImage *img)
{
assert(img != NULL);
assert(img->depth == 8);
assert(img->nChannels == 1);
float range[2] = {0,255};
float *ranges[1] = {&range[0]};
int sizes = 256;
// 创建直方图
CvHistogram *hist = cvCreateHist(1, &sizes, CV_HIST_ARRAY, ranges, 1);
// 直方图计算
cvCalcHist(&img, hist, 0, 0);
// 直方图归一化
cvNormalizeHist(hist, 1.0);
int threshold = 0;
float max_entropy = 0;
// 循环计算,得到做大熵以及分割阈值
for(int i = 0; i < sizes; i++)
{
float entropy = calc_entropy(hist, 0, i) + calc_entropy(hist, i+1, sizes);
if(entropy > max_entropy)
{
max_entropy = entropy;
threshold = i;
}
}
return threshold;
}
int main(int argc, char **argv)
{
IplImage *img = cvLoadImage("1.bmp", CV_LOAD_IMAGE_GRAYSCALE);
IplImage *reimg = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 1);
int threshold = ksw_entropy(img);
cvThreshold(img, reimg, threshold, 255, CV_THRESH_BINARY);
cvNamedWindow("img");
cvShowImage("img", img);
cvNamedWindow("reimg");
cvShowImage("reimg", reimg);
cvWaitKey(0);
return 0;
}
在学习获取灰度直方图的时候,有一个函数让我费解了半天,cvNormalizeHist,灰度归一化,刚开始就是搞不懂这个灰度归一化是指的什么,
opencv给出的函数解释如下:
什么叫the sum of the bins becomes equal to factor,好吧,后来明白了,其实意思就是说,把直方图纵坐标的值相加的和等于第二个参数,所以如果设为1的话,那纵坐标的值就是该灰度级的概率了。
下面是实验图片:
图1 原图 图2 最大熵阈值分割后图片
效果很不理想吧,所以在做阈值分割前需要加入tophat,后面博客会讲到。
全文完。