最近不是很有空,因为要忙着复习期中考试。考完试又在写之前落下的作业,可能速度有点慢。今天我学得是关于直方图的内容。
一、图像直方图
图像直方图是用来表现图像中亮度分布的直方图,给出的是图像中某个亮度或者某个范围亮度下共有几个像素,即统计一幅图某个亮度像素数量。
我们要用到calcHist()函数计算每个像素块在图像中的数量。(之所以是像素块,因为下面可以传入Bin改变一块中像素值个数)
void calcHist( const Mat* images, int nimages,
const int* channels, InputArray mask,
OutputArray hist, int dims, const int* histSize,
const float** ranges, bool uniform=true, bool accumulate=false );
参数解释:
• images:输入的图像的指针;
• nimages:输入图像个数;
• channels:通道索引,灰度图像可填入0;
• mask:掩模,mask必须是一个8位(CV_8U)的数组并且和images的数组大小相同;Mat()
• hist:直方图计算的输出值;
• dims:需要统计直方图通道的个数;
• histSize:直方图中每个dims维度需要分成多少个区间(如果把直方图看作一个一个竖条的话,就是竖条的个数);全尺寸用 [256] 就行;
• ranges:像素范围,一般设为 [0, 256];
• uniform=true:是否对得到的直方图数组进行归一化处理;
• accumulate=false:在多个图像时,是否累积计算像素值的个数;
除此之外,若需要绘制直方图,还需要在图像上对0~256中每个点绘制直线,需要用到line函数:
void line(Mat& img, Point pt1, Point pt2, const Scalar& color, int thickness=1, int lineType=8, int shift=0)
参数:
img: 要绘制线段的图像。
pt1: 线段的起点。
pt2: 线段的终点。
color: 线段的颜色,通过一个Scalar对象定义。
thickness: 线条的宽度。
lineType: 线段的类型。可以取值8, 4, 和CV_AA, 分别代表8邻接连接线,4邻接连接线和反锯齿连接线。默认值为8邻接。为了获得更好地效果可以选用CV_AA(采用了高斯滤波)。
shift: 坐标点小数点位数
还需了解cvRound():返回跟参数最接近的整数值,即四舍五入;
cvRound(hist.at<float>(i)):将某一亮度下的像素值取整;
#include"iostream"
#include"opencv2/opencv.hpp"
using namespace cv;
using namespace std;
int main()
{
Mat image, image_gray, hist, image_enhanced;//定义输入,输出,灰度图像
image = imread("D:\\Backup\\Documents\\My Pictures\\1.jpg");
cvtColor(image, image_gray, COLOR_BGR2GRAY);//灰度化
imshow("image_gray",image_gray);
//创建直方图显示图像
int histsize = 256;
float ranges[] = {0, 256};
const float*histRanges = {ranges};
calcHist(&image_gray, 1, 0, Mat(), hist, 1, &histsize, &histRanges,true, false);
imshow("hist",hist);
//创建直方图显示图像
int hist_h = 300; //直方图图像的高
int hist_w = 512; //直方图图像的宽
int bin_w = hist_w / histsize; //直方图的等级
Mat histImage(hist_h,hist_w,CV_8UC3, Scalar(0,0,0)); //绘制直方图显示的图像
//绘制并显示直方图
normalize(hist, hist, 0, hist_h, NORM_MINMAX, -1, Mat());//将直方图归一化
for(int i=1; i < histsize ; i++)
{
line(histImage, Point((i-1)*bin_w, hist_h - cvRound(hist.at<float>(i-1))),
Point((i)*bin_w, hist_h - cvRound(hist.at<float>(i))),Scalar(255, 0, 0), 2, 8, 0);
}
imshow("histImage",histImage);
waitKey(0);
return 0;
}
效果图如下:
当我改变line中的参数:
line(histImage, Point((i)*bin_w,hist_h),Point((i)*bin_w, hist_h - cvRound(hist.at<float>(i))),Scalar(255, 0, 0), 2, 8, 0);
图像变成了:
二、直方图均衡化
直方图均衡化是图像处理领域中利用图像直方图对对比度进行调整的方法。
这种方法通常用来增加许多图像的全局对比度,尤其是当图像的有用数据的对比度相当接近的时候。通过这种方法,亮度可以更好地在直方图上分布。这样就可以用于增强局部的对比度而不影响整体的对比度,直方图均衡化通过有效地扩展常用的亮度来实现这种功能。这种方法对于背景和前景都太亮或者太暗的图像非常有用,这种方法尤其是可以带来X光图像中更好的骨骼结构显示以及曝光过度或者曝光不足照片中更好的细节。
equalizeHist函数用于直方图均衡化,用于提高图像的质量;
equalizeHist(image_gray, image_enhanced);//直方图均衡化
imshow("image_enhanced", image_enhanced);//显示增强图像
图像前后对比为:
三、直方图匹配
直方图匹配又称直方图规定化。是指将一幅图像的直方图变成规定形状的直方图而进行的图像增强方法。 其原理是:对两个直方图都做均衡化,变成相同的归一化的均匀直方图,以此均匀直方图为媒介,再对参考图像做均衡化的逆运算。
我们常常用到直方图均衡化,直方图均衡化是在整个灰度阶范围内对图像进行拉升;但是有的时候,这种整个范围内的拉升也许并不是最好的,我们可能会需要其按照某个灰度分布去进行拉升,也就需要用到上面提到的直方图均衡化/规定化。
1.LUT函数
CV_EXPORTS_W void LUT(InputArray src, InputArray lut, OutputArray dst);
//参数 src : 8位元素的输入矩阵
//参数 lut : 256个元素的查找表.如果输入通道是多通道,查找表应该具有一个单一的通道(这种情况下同样的表被用于所有的通道)或者和输入矩阵具有同样的通道数。
//参数dst : 输出矩阵,和src具有同样的大小和通道数,和lut具有同样的深度。
2.具体步骤可以参考下面博客:
https://blog.csdn.net/mmmmmk_/article/details/82927411
代码如下:
#include "iostream"
#include "opencv2/opencv.hpp"
using namespace std;
using namespace cv;
int main()
{
Mat image1, image1_gray, hist1, image2, image2_gray, hist2, image_enhanced;
image1 = imread("D:\\Backup\\Documents\\My Pictures\\1.jpg");
cvtColor(image1,image1_gray, COLOR_BGR2GRAY);
imshow("image1_gray", image1_gray);
image2 = imread("D:\\Backup\\Documents\\My Pictures\\2.jpg");
cvtColor(image2,image2_gray, COLOR_BGR2GRAY);
imshow("image2_gray", image2_gray);
//均衡化处理
equalizeHist(image2_gray,image2_gray);
equalizeHist(image1_gray,image1_gray);
//获取两个图像的直方图
int histsize = 256;
float ranges[] = {0,256};
const float *histRanges = {ranges};
calcHist(&image1_gray,1,0,Mat(),hist1,1,&histsize,&histRanges,true,false);
calcHist(&image2_gray,1,0,Mat(),hist2,1,&histsize,&histRanges,true,false);
//计算两个均衡化直方图的累计概率
float hist1_cdf[256] = {hist1.at<float>(0)};
float hist2_cdf[256] = {hist2.at<float>(0)};
for (int i=1;i<256;i++)
{
hist1_cdf[i] = hist1_cdf[i-1]+hist1.at<float>(i);
hist2_cdf[i] = hist2_cdf[i-1]+hist2.at<float>(i);
}
for (int i =0;i<256;i++)
{
hist1_cdf[i] = hist1_cdf[i] / (image1_gray.rows*image1_gray.cols);
hist2_cdf[i] = hist2_cdf[i] / (image2_gray.rows*image2_gray.cols);
}
//计算源图像累计直方图各个灰度阶到规定图像的累计直方图各个灰度阶的差的绝对值
float diff_cdf[256][256];
for (int i =0;i<256;i++)
{
for (int j=0;j<256;j++)
{
diff_cdf[i][j]=fabs(hist1_cdf[i]-hist2_cdf[j]);//fabs是求绝对值函数
}
}
Mat lut(1,256,CV_8U);
for (int i=0;i<256;i++)
{
//求出中各阶中绝对值对应的最小值,最小值对应的灰度阶即为映射后的值
float min = diff_cdf[i][0];
int index = 0;
for (int j=0;j<256;j++)
{
if (min>diff_cdf[i][j])
{
min = diff_cdf[i][j];
index =j;
}
}
lut.at<uchar>(i) =index;
}
LUT(image1_gray,lut,image_enhanced);//图像中进行映射
imshow("image_enhanced", image_enhanced);
waitKey(0);
return 0;
}
运行结果(我后面添加了三个图的灰度直方图):
加油~~