这一篇我们来学习下直方图的应用,主要有直方图的拉伸、直方图均衡化以及利用直方图寻找相似图像。
1. 直方图拉伸
图像对比度增强分为两类:直接对比度增强和间接对比度增强。直方图拉伸和直方图均衡化是两种最常用的间接对比度增强方法。直方图拉伸是通过对比度拉伸对直方图进行调整,从而扩大“前景”和背景灰度的差别,以达到增强对比度的目的。
公式如下:
设f(x,y)为输入图像,则
最小灰度级A=min[f(x,y)]
最大灰度级B=max[f(x,y)]
将A和B分别线性映射到0和255,最终得到的图像为: g(x,y)=(255/(B-A))[f(x,y)-A]
以下为代码实现:
/*------------------------------------【程序说明】-------------------------------------
描述:直方图拉伸
2015/10/16 by czp
2015/10/18 【修改】由于现有的图片像素区间在【0,255】,无法进行拉伸,故进行反向运算,得到sky-lancer1.jpg
---------------------------------------------------------------------------------------*/
/*--------------------------------【程序头文件部分】----------------------------------
描述:包含程序所依赖的文件
--------------------------------------------------------------------------------------*/
#include <iostream>
#include "highgui.h"
#include <opencv2/opencv.hpp>
/*--------------------------------【命名空间部分】-------------------------------------
描述:包含程序所使用的命名空间
--------------------------------------------------------------------------------------*/
using namespace cv;
using namespace std;
/*--------------------------------【类Histogram1D】-------------------------------------
描述:计算一维直方图
--------------------------------------------------------------------------------------*/
class Histogram1D{
private:
int histSize[1];//项的数量
float hranges[2];
const float*ranges[1];
int channels[1];
public:
Histogram1D(){
histSize[0] = 256;
hranges[0] = 0.0;
hranges[1] = 255.0;
ranges[0] = hranges;
channels[0] = 0;
}
MatND getHistogram(const Mat &image){
MatND hist;
calcHist(&image,
1,
channels,
Mat(),
hist,
1,
histSize,
ranges
);
return hist;
}
Mat getHistogramImage(const Mat &image){
MatND hist = getHistogram(image);
double maxVal = 0;
double minVal = 0;
minMaxLoc(hist, &minVal, &maxVal, 0, 0);
Mat histImg(histSize[0], histSize[0], CV_8U, Scalar(255));
int hpt = 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*hpt / maxVal);
line(histImg, Point(h, histSize[0]), Point(h, histSize[0] - intensity), Scalar::all(0));
}
return histImg;
}
};
/*--------------------------------【main()函数】---------------------------------------
描述:控制台应用程序入口
--------------------------------------------------------------------------------------*/
int main(){
int PixMax_array[3] = { 0, 0, 0 }, PixMin_array[3] = { 255, 255, 255 }; //存储像素的最大值和最小值
float DifferenceOfMaxMin[3];
int i, j, nl, nc;
Mat srcImg = imread("sky-lancer1.jpg", 1); //读入原图像,这里只研究彩色图像
if (!srcImg.data){ printf("读取图片错误!!\n"); return false; }
Mat dstImg = srcImg.clone();
//遍历图像,寻找最大和最小值
nl = dstImg.rows; //行数
nc = dstImg.cols; //列数
for (i = 0; i < nl; i++)
{
for (j = 0; j < nc; j++)
{
if (dstImg.at<Vec3b>(i, j)[0] < PixMin_array[0]){ PixMin_array[0] = dstImg.at<Vec3b>(i, j)[0]; }
if (dstImg.at<Vec3b>(i, j)[1] < PixMin_array[1]){ PixMin_array[1] = dstImg.at<Vec3b>(i, j)[1]; }
if (dstImg.at<Vec3b>(i, j)[2] < PixMin_array[2]){ PixMin_array[2] = dstImg.at<Vec3b>(i, j)[2]; }
if (dstImg.at<Vec3b>(i, j)[0] > PixMax_array[0]){ PixMax_array[0] = dstImg.at<Vec3b>(i, j)[0]; }
if (dstImg.at<Vec3b>(i, j)[1] > PixMax_array[1]){ PixMax_array[1] = dstImg.at<Vec3b>(i, j)[1]; }
if (dstImg.at<Vec3b>(i, j)[2] > PixMax_array[2]){ PixMax_array[2] = dstImg.at<Vec3b>(i, j)[2]; }
}
}
//遍历图像,进行直方图拉伸
for (i = 0; i < 3; i++)
{
DifferenceOfMaxMin[i] = 255.0/(float)(PixMax_array[i] - PixMin_array[i]);
}
for (i = 0; i < nl; i++)
{
for (j = 0; j < nc; j++)
{
dstImg.at<Vec3b>(i, j)[0] = (int)(DifferenceOfMaxMin[0] * (dstImg.at<Vec3b>(i, j)[0] - PixMin_array[0]));
dstImg.at<Vec3b>(i, j)[1] = (int)(DifferenceOfMaxMin[1] * (dstImg.at<Vec3b>(i, j)[1] - PixMin_array[1]));
dstImg.at<Vec3b>(i, j)[2] = (int)(DifferenceOfMaxMin[2] * (dstImg.at<Vec3b>(i, j)[2] - PixMin_array[2]));
}
}
namedWindow("srcImg");
namedWindow("dstImg");
imshow("srcImg", srcImg);
waitKey(5000);
imshow("dstImg", dstImg);
waitKey(5000);
//查看各个通道的直方图是否有变化(以通道0为例子)
vector<Mat> mv,nv;
split(srcImg,mv);
split(dstImg, nv);
Histogram1D hm, hn;
namedWindow("Histogram_m");
imshow("Histogram_m", hm.getHistogramImage(mv.at(0)));
waitKey(5000);
namedWindow("Histogram_n");
imshow("Histogram_n", hn.getHistogramImage(nv.at(0)));
waitKey(5000);
}
效果图:
参考文章:http://blog.csdn.net/guoyk1990/article/details/8104130
2.直方图均衡化
如果一幅图像的像素占有很多的灰度级而且分布均匀,那么这样的图像往往有高对比度和多变的灰度色调。直方图均衡化就是为了达到这种效果。它的基本思想是对图像中像素个数多的灰度级进行展宽,而对图像中像素个数少的灰度进行压缩,从而扩展像原取值的动态范围,提高了对比度和灰度色调的变化,使图像更加清晰。【百度百科】
简单的说,均衡化就是“把一个分布(给定的直方图)映射到另一个分布(一个更宽更统一的强度值分布),所以强度值分布会在整个范围内展开”。直方图均衡化是通过累计分布函数来实现的。
详细的原理参见: 直方图原理OpenCV提供了一个简单易用的函数来执行直方图均衡化。
cv::equalizeHist(Mat &srcImage,Mat &dstImage);
其中输入输出均为单通道图像。
代码及其实现:
//------------------------------------【程序功能】-----------------------------------------------
// 描述:直方图均衡化
//-----------------------------------------------------------------------------------------------
//----------------------------------【头文件包含部分】-------------------------------------------
// 描述:包含程序所依赖的文件
//-----------------------------------------------------------------------------------------------
#include <iostream>
#include "highgui.h"
#include <opencv2/opencv.hpp>
//-----------------------------------【命名空间声明】--------------------------------------------
// 描述:包含程序所使用的命名空间
//-----------------------------------------------------------------------------------------------
using namespace cv;
using namespace std;
// 定义直方图的类
class Histogram1D{
private:
int histSize[1];//项的数量
float hranges[2];
const float*ranges[1];
int channels[1];
public:
Histogram1D(){
histSize[0] = 256;
hranges[0] = 0.0;
hranges[1] = 255.0;
ranges[0] = hranges;
channels[0] = 0;
}
MatND getHistogram(const Mat &image){
MatND hist;
calcHist(&image,
1,
channels,
Mat(),
hist,
1,
histSize,
ranges
);
return hist;
}
Mat getHistogramImage(const Mat &image){
MatND hist = getHistogram(image);
double maxVal = 0;
double minVal = 0;
minMaxLoc(hist, &minVal, &maxVal, 0, 0);
Mat histImg(histSize[0], histSize[0], CV_8U, Scalar(255));
int hpt = 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*hpt / maxVal);
line(histImg, Point(h, histSize[0]), Point(h, histSize[0] - intensity), Scalar::all(0));
}
return histImg;
}
};
//-------------------------------------【main()函数】-----------------------------------------------
//描述:控制台应用程序的入口
//--------------------------------------------------------------------------------------------------
int main(){
Mat srcImage = imread("sky-lancer.jpg", 0), dstImage; //读入灰度图像
if (!srcImage.data) {printf("读取图像错误!"); return false;}
equalizeHist(srcImage, dstImage);
//处理前和处理后的图像对比
namedWindow("srcImage");
imshow("srcImage", srcImage);
waitKey(5000);
namedWindow("dstImage");
imshow("dstImage", dstImage);
waitKey(5000);
//处理前和处理后的直方图对比
Histogram1D h;
namedWindow("srcHistogram");
/*split(srcImage, nv);*/
imshow("srcHistogram", h.getHistogramImage(srcImage));
waitKey(5000);
namedWindow("EqualizationOfHistogram");
imshow("EqualizationOfHistogram", h.getHistogramImage(dstImage));
waitKey(5000);
}