1. 计算图像直方图
图像是由像素组成的,在一个单通道的灰度图像中,每个像素的值介于0到255之间,而直方图就是一个简单的表,给出了一幅或者一组图像中拥有给定数值的像素数量。当然直方图也可以归一化,归一化后的所有项的和为1,在这种情况下,每一项给出的都是拥有特定数值的像素在图像中占的比例。
下面我们看看如何用opencv计算单通道图像的直方图,我用一个Histogram1D封装了与单通道直方图操作相关的变量和函数:
#ifndef HISTOGRAM1D_H
#define HISTOGRAM1D_H
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace cv;
class Histogram1D{
private:
int histSize[1]; //项的数量
float hranges[2]; //像素的最小及最大值
const float* ranges[1];
int channels[1]; //仅用到一个通道
public:
Histogram1D(){
//准备1D直方图的参数
histSize[0] =256;
hranges[0] = 0.0;
hranges[1] = 255.0;
ranges[0] = hranges;
channels[0] = 0; //默认情况,我们考察0号通道
}
//计算1D直方图
MatND getHistogram(const Mat&image)
{
MatND hist;
//计算直方图
calcHist(&image,
1, //计算单张图像的直方图
channels, //通道的数量
Mat(), //不使用图像作为掩码
hist, //返回的直方图
1, //这是1D的直方图
histSize, //项的数量
ranges //像素值的范围
);
return hist;
}
//计算1D直方图,并返回一幅图像
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));
//设置最高点为nbins的90%
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;
}
};
#endif // HISTOGRAM1D_H
main函数如下,直接调用getHistogramImage即可得到直方图:
#include <QCoreApplication>
#include "Histogram1D.h"
int main(int argc, char *argv[])
{
namedWindow( "src1", WINDOW_AUTOSIZE );
namedWindow( "src2", WINDOW_AUTOSIZE );
Mat image = imread( "test.jpg",0 ); //读取灰度图
Histogram1D h;
while(1)
{
imshow( "src1", image);
imshow( "src2", h.getHistogramImage(image));
char c = waitKey(30);
if( 27==c )
return 0;
}
}
效果如下:
原始灰度图像
直方图
当然我们还可以用calcHist函数来计算彩色BGR图像的直方图,这时候直方图变成三维的了:
#ifndef COLORHISTOGRAM_H
#define COLORHISTOGRAM_H
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include<QDebug>
using namespace cv;
class ColorHistogram{
private:
int histSize[3]; //项的数量
float hranges[2]; //像素的最小及最大值
const float* ranges[3];
int channels[3]; //用到三个通道
public:
ColorHistogram(){
//准备彩色直方图的参数
histSize[0] = histSize[1]= histSize[2]=256;
hranges[0] = 0.0;
hranges[1] = 255.0;
ranges[0] =ranges[1]=ranges[2]=hranges; //所有通道都有相同的范围
channels[0] = 0; //3个通道
channels[1] = 1;
channels[2] = 2;
}
//计算三维直方图
MatND getHistogram(const Mat&image)
{
MatND hist;
//计算直方图
calcHist(&image,
1, //计算单张图像的直方图
channels, //通道的数量
Mat(), //不使用图像作为掩码
hist, //返回的直方图
3, //这是三维的直方图
histSize, //项的数量
ranges //像素值的范围
);
qDebug()<<hist.dims;
return hist;
}
};
#endif // COLORHISTOGRAM_H
这时候getHistogram返回的是一个256*256*256的矩阵,包含了超过1600万个元素,这计算代价是非常大的,可以参考opencv操作像素中所述的方法减少颜色的数量,或者可以使用cv::SparseMat数据结构,用于存储大型的稀疏矩阵,这样可以极大地减少内存的消耗。
PS:至于绘制三维直方图的绘制比较麻烦,以后有时间看看用qt实现个界面,感兴趣的话可以先参看下这篇文章http://blog.csdn.net/foolpanda1168/article/details/6078463
2. 查找表的使用
查找表实际上就是一个简单的一对一(或者多对一)的函数,定义了如何将像素值转换为新的值,本质上就是一个一维数组。
openCV中用cv::LUT的方法对图像应用查找表生成新图像,我们将这个功能加到Histogram1D类中:
//应用查找表,image为输入图像,lookup是1*256的unchar矩阵,代表查找表
Mat applyLookUp(const Mat&image,const Mat&lookup)
{
Mat result;
//应用查找表
LUT(image,lookup,result);
return result;
}
这时我们可以做个简单的实验,比如将原先每个像素强度进行反转,即x变成255-x:
int dim(256);
Mat lut(1,&dim,CV_8U);
for(int i=0;i<256;i++)
{
lut.at<uchar>(i) = 255-i;
}
Mat reverse = h.applyLookUp(image,lut);
效果如下:
3. 直方图均衡化
直方图均衡化其实就是为了让直方图更加的平坦,能够大幅改善图像的外观。
openCV中提供了一个简单易用的函数cv::equalizeHist来执行直方图均衡化。
//直方图均衡化
Mat equalize(const Mat &image)
{
Mat result;
equalizeHist(image,result);
return result;
}
应用与之前的图像,效果如下:
、
可以看出图像的对比度增强了,直方图也更加的平坦。
参考书籍
《openCV2计算机视觉编程手册》
(转载请注明作者和出处:Shawn-HT http://blog.csdn.net/shawn_ht)