直方图(Histogram)又称质量分布图。是一种统计报告图,由一系列高度不等的纵向条纹或线段表示数据分布的情况。一般用横轴表示数据类型,纵轴表示分布情况。众所周知,一幅图像是由不同颜色值的像素组成,因此像素值在图像中的分布情况是这幅图像的一个重要特征,因此直方图广泛应用在数字图像处理中。
拍照是现实生活中必不可少的一部分,由于环境亮度、图像拍摄过程中透视光圈设置错误等影响,经常会拍出一些“过暗”的照片,此时美图、PS等美化工具可以派上用场。但是这些工具的算法通常都是不公开的,鉴于研究所需,这里利用图像直方图的一些性质来探索一些图像增强方法。开发平台为OpenCV2.4.9+Qt5.3.2。
一、灰度图像的直方图
在一幅单通道的灰度图像中,每个像素的取值区间为[0,255],因此灰度图像的直方图包含256个容器,各个容器给出当前值的像素个数。简单来说,直方图统计出每种像素取值的个数,它可以被归一化,即算出图像的像素总数,然后将各容器的像素个数除去总数,归一化后所有项的和为1。
在OpenCV中,使用cv::calcHist函数以计算直方图,这里创建一个Qt控制台应用并定义一个类(注意直方图中的数据类型为float):
histgram1d.h:
#ifndef HISTOGRAM1D_H
#define HISTOGRAM1D_H
#include
#include
#include
class Histogram1D
{
public:
Histogram1D()
{
histSize[0] = 256;
hranges[0] = 0.0;
hranges[1] = 255.0;
ranges[0] = hranges;
channels[0] = 0;
}
cv::MatND getHistogram(const cv::Mat &image);
cv::Mat getHistogramImage(const cv::Mat &image);
private:
int histSize[1];
float hranges[2];
const float *ranges[1];
int channels[1];
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
histgram1d.cpp:
#include "histogram1d.h"
#include
#include
#include
#include
#include
cv::MatND Histogram1D::getHistogram(const cv::Mat &image)
{
cv::MatND hist;
cv::calcHist(&image,
1,
channels,
cv::Mat(),
hist,
1,
histSize,
ranges);
return hist;
}
cv::Mat Histogram1D::getHistogramImage(const cv::Mat &image)
{
cv::MatND hist = getHistogram(image);
double minVal = 0;
double maxVal = 0;
cv::minMaxLoc(hist, &minVal, &maxVal, 0, 0);
cv::Mat hist_img(histSize[0], histSize[0], CV_8U, cv::Scalar(255));
int highpoint = static_cast<int>(0.9*histSize[0]);
for(int h = 0; h < histSize[0]; h++)
{
float binVal = hist.at<float>(h);
int intensity = static_cast<int>(binVal*highpoint/maxVal);
cv::line(hist_img, cv::Point(h, histSize[0]),
cv::Point(h, histSize[0]-intensity),
cv::Scalar::all(0));
}
return hist_img;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
注意直方图数据为浮点类型!接着修改main函数:
#include "histogram1d.h"
#include
#include
#include
#include
#include
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
cv::Mat image = cvLoadImage("c:/Fig6.35(5).jpg", -1);
Histogram1D h;
cv::MatND histo = h.getHistogram(imt);
for(int i=0;i<256;i++)
{
std::cout << "Value" << i << "=" << histo.at<float>(i) << std::endl;
}
cv::imshow("Image",imt);
cv::imshow("Histgram", h.getHistogramImage(imt));
return a.exec();
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
这是一幅亮度不足的图片,这里提供三种提高对比度的方法增强图像的方法:
一、使用查找表增强图像
首先在histgram1d.h中添加:
public:
cv::Mat stretch(const cv::Mat &image, int);
然后在histgram1d.cpp中编写该函数如下:
cv::Mat Histogram1D::stretch(const cv::Mat &image, int minValue = 0)
{
cv::MatND hist = getHistogram(image);
int imin = 0;
for(;imin < 256; imin++)
{
if(hist.at<float>(imin) > minValue)
break;
}
int imax = 255;
for(;imax >= 0 ;imax--)
{
if(hist.at<float>(imax) > minValue)
break;
}
std::cout<< imin << ";" << imax << std::endl;
cv::Mat lookup(1, 256, CV_8U);
for(int i=0; i<256; i++)
{
if(i(i)=0;
else if(i>imax) lookup.at(i)=255;
else
lookup.at(i)=static_cast(255.0*(i-imin)/(imax-imin));
}
cv::Mat result;
cv::LUT(image, lookup, result);
return result;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
接着修改main函数:
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
cv::Mat image = cvLoadImage("c:/Fig2.19(a).jpg", -1);
if(image.channels()==1)
{
Histogram1D h;
cv::Mat imt ;
imt = h.stretch(image, 4500);
cv::MatND histo = h.getHistogram(imt);
for(int i=0;i<256;i++)
{
std::cout << "Value" << i << "=" << histo.at<float>(i) << std::endl;
}
cv::imshow("Image", image);
cv::imshow("Stretched Image", imt);
cv::imshow("Histgram", h.getHistogramImage(image));
cv::imshow("Histgram1", h.getHistogramImage(imt));
else qDebug() << "error!" ;
return a.exec();
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
输出效果图如下,该方法的优点是算法复杂度低,设计简单,同时缺点也比较明显,那就是stretch函数的第二个参数选取需要根据不同图像进行调整,若参数选取不当,将造成图像细节的丢失,以下给出两种不同参数的输出结果,效果差别还是挺大的:
二、使用直方图均衡方法增强图像
通过拉伸直方图的方法可以简单有效地提升图像质量,但是在多数情况下,一些图像的缺陷并不是因为使用过窄的强度范围,而是由于某些颜色值出现的概率远高于其他值。因此直方图均衡成为一种常用的手段,它的思想是试图使一幅图像平均地使用所有像素的强度值,后面还给出一种改进的直方图均衡方法。
普通的直方图均衡方法在OpenCV中十分常用,原则上直方图均衡方法仅适用于灰度图像,但事实上使用直方图处理彩色图像同样可行,只是可能需要一些额外的处理。下面给出直方图均衡算法调用的基本步骤:
1、在histgram1d.h中public: 下添加:cv::Mat equalize(const cv::Mat &image); //使用OpenCV自带的直方图均衡算法
2、在histgram1d.cpp中添加:
cv::Mat equalize(const cv::Mat &image)
{
cv::Mat result;
cv::equalizeHist(image, result);
return result;
}
最后简单修改main函数即可,测试下效果~
结果表明,图像经直方图均衡后,对比度有所提升,但其中其实隐含了一些问题,继续测试:
问题很明显,直方图均衡化带来了对比度的提升,但由于该算法使用了像素值的映射关系,造成了灰度级的减少,这对于很多图像来说意味着细节的丢失。因此寻找一种改进的直方图均衡化算法很有必要。
三、图像增强之限制对比度自适应直方图均衡
这里介绍一种有用的、同时OpenCV已有的新算法,即限制对比度自适应直方图均衡(Contrast Limited Adaptive histgram equalization/CLAHE)。这个博客 http://www.cnblogs.com/Imageshop/archive/2013/04/07/3006334.html 给出了该算法的详细解释,我也是从中深受启发,下面是关于CLAHE的简单解释:
CLAHE同普通的直方图均衡不同的地方主要是其对比度限幅。在CLAHE中,对将要进行处理的图像进行区域划分,并对每个小区域使用对比度限幅。CLAHE主要是用来克服传统自适应直方图均衡化算法的过度放大噪音和丢失细节的问题。
这主要是通过限制自适应直方图均衡化算法的对比提高程度来达到的。在指定的像素值周边的对比度放大主要是由变换函数的斜度决定的。这个斜度和领域的累积直方图的斜度成比例。CLAHE通过在计算累积直方图函数前用预先定义的阈值来裁剪直方图以达到限制放大幅度的目的。这限制了累积直方图函数的斜度因此,也限制了变换函数的斜度。直方图被裁剪的值,也就是所谓的裁剪限幅,取决于直方图的分布因此也取决于领域大小的取值。
通常,直接忽略掉那些超出直方图裁剪限幅的部分是不好的,而应该将这些裁剪掉的部分均匀的分布到直方图的其他部分。如下图所示。
事实上,CLAHE算法能够从整体上改善输入图像的灰度分布范围, 提供更佳的视觉效果,以下给出一个例子,在这里加上了对彩色图像的直方图均衡化处理。
直接修改main函数:
#include "histogram1d.h"
#include
#include
#include
#include
#include
#include
#include
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
cv::Mat image = cvLoadImage("c:/Fig6.35(5).jpg", -1);
if(image.channels()==1)
{
Histogram1D h;
cv::Mat imt;
cv::MatND histo = h.getHistogram(imt);
for(int i=0;i<256;i++)
{
std::cout << "Value" << i << "=" << histo.at<float>(i) << std::endl;
}
cv::Mat imt1;
cv::Ptr clahe = cv::createCLAHE();
clahe->setClipLimit(3);
clahe->setTilesGridSize(cv::Size(8,8));
clahe -> apply(image, imt1);
cv::imshow("Input Histogram", h.getHistogramImage(image));
cv::imshow("Input Image",image);
cv::imshow("Output Histogram", h.getHistogramImage(imt1));
cv::imshow("CLAHE Output", imt1);
}
else if(image.channels()==3)
{
std::vector out;
cv::Mat kk;
cv::cvtColor(image, kk, CV_RGB2YUV);
cv::split(kk, out);
cv::Mat colorimt;
cv::Ptr clahe = cv::createCLAHE();
clahe->setClipLimit(3);
clahe->setTilesGridSize(cv::Size(8,8));
kk = out[0];
clahe -> apply(kk, colorimt);
out.at(0) = colorimt;
cv::Mat cc;
cv::merge(out, cc);
cv::cvtColor(cc, colorimt, CV_YUV2RGB);
cv::imshow("Input",image);
cv::imshow("Output",colorimt);
}
else qDebug() << "error!" ;
return a.exec();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
结果大大改善~
对于彩色图像,有几种较为主流的直方图均衡方法:基于RGB颜色通道的独立均衡方法、基于RGB三通道联合概率的均衡方法、基于HSI空间的I分量均衡法和基于YUV空间的Y分量均衡法。这篇论文:http://www.docin.com/p-350697910.html 介绍了各方法及其优缺点。这里使用的是基于YUV空间的Y分量均衡法,基本步骤是将输入的RGB图像转换到YUV空间,取其亮度通道进行直方图均衡,再转换回RGB图像即可。该方法运行效率较高,处理后的图像效果也更佳!
欢迎转载或分享,但请务必声明文章出处~
转自 http://blog.csdn.net/liyuefeilong/article/details/43709359