一、图像直方图的统计与绘制
如果直方图各个数字之间分布比较均衡,则其对比度就相对较低,反之亦然。同时也可以通过直方图可以大致了解图像的亮暗等。
calcHist(const Mat * images, int nimages, const int * channels, lnputArray mask, OutputArray hist, int dims, const int* histSize, const float ** ranges, bool accumullate = false)
images:待统计直方图的图像数组。
nimages:输入的图像数量。
channels:需要统计的通道索引数组。
mask:可选的操作掩码。
hist:输出的统计直方图结果,是一个dims维度的数组。
dims:需要计算直方图的维度,必须是整数。
histSize:存放每个维度直方图的数组的尺寸。
ranges:每个图像通道中灰度值的取值范围。
uniform:直方图是否均匀的标志符,默认状态下为均匀(true) 。
accumulate:是否累积统计直方图的标志。
直方图使用案例:
int main() {
//读取图片
Mat src1 = imread("1.png");
if (!src1.data)
{
printf("不能打开空图片");
return -1;
}
//将彩色图像转化为灰度图像
Mat gray;
cvtColor(src1, gray, COLOR_BGR2GRAY);
//定义存放直方图计算结果的变量
Mat hist;
//定义通道的索引
const int channels[1] = { 0 };
//定义直方图的维度,为灰度图像的最大值
const int bins[1] = { 256 };
float inRanges[2] = { 0,255 };
//像素灰度值的取值范围
const float* ranges[1] = { inRanges };
//计算直方图
calcHist(&gray, 1, channels, Mat(), hist, 1, bins, ranges);
//绘制直方图
//直方图背景图的大小
int hist_w = 512;
int hist_h = 400;
//直方图矩形柱的宽度
int width = 2;
Mat histImage = Mat::zeros(hist_h, hist_w, CV_8UC3);
//分别绘制直方图的矩形柱
//for (int i = 1; i < hist.rows; i++)
//{
// //将原始数据缩放为15倍:hist.at<float>(i - 1) / 15)
// rectangle(histImage, Point(width*(i - 1), hist_h - 1), Point(width*i - 1, hist_h - cvRound(hist.at<float>(i - 1) / 15)), Scalar(255, 255, 255), -1);
//}
//将直方图数据进行归一化操作
Mat hist_INF;
normalize(hist, hist_INF, 1, 0, NORM_INF, -1, Mat());
for (int i = 1; i < hist_INF.rows; i++)
{
//将原始数据缩放为15倍:hist.at<float>(i - 1) / 15)
rectangle(histImage, Point(width*(i - 1), hist_h - 1), Point(width*i - 1, hist_h - cvRound(hist_h*hist_INF.at<float>(i - 1)) -1), Scalar(255, 255, 255), -1);
}
imshow("histImage", histImage);
imshow("gray", gray);
waitKey(0);
return 0;
}
二、直方图均衡化
图像像素拉伸:
略
直方图均衡化函数:
equalizeHist(lnputArray src, OutputArray dst)
src:需要直方图均衡化的CV_8UC1图像。
dst:直方图均衡化后的输出图像,与src具有相同尺寸和数据类型。
使用样例如下:
void drawHist(Mat &hist, int type, string name)
{
//直方图背景图的大小
int hist_w = 512;
int hist_h = 400;
//直方图矩形柱的宽度
int width = 2;
Mat histImage = Mat::zeros(hist_h, hist_w, CV_8UC3);
//归一化
normalize(hist, hist, 1, 0, NORM_INF, -1, Mat());
for (int i = 1; i < hist.rows; i++)
{
rectangle(histImage, Point(width*(i - 1), hist_h - 1), Point(width*i - 1, hist_h - cvRound(hist_h*hist.at<float>(i - 1)) - 1), Scalar(255, 255, 255), -1);
}
imshow(name, histImage);
}
int main() {
//读取图片
Mat src1 = imread("1.png");
if (!src1.data)
{
printf("不能打开空图片");
return -1;
}
Mat gray, hist, hist2;
//将彩色图像转化为灰度图像
cvtColor(src1, gray, COLOR_BGR2GRAY);
Mat equalImg;
//将图像均衡化
equalizeHist(gray, equalImg);
//定义通道的索引
const int channels[1] = { 0 };
//定义直方图的维度,为灰度图像的最大值
const int bins[1] = { 256 };
float inRanges[2] = { 0,255 };
//像素灰度值的取值范围
const float* ranges[1] = { inRanges };
//计算直方图
calcHist(&gray, 1, channels, Mat(), hist, 1, bins, ranges);
calcHist(&equalImg, 1, channels, Mat(), hist2, 1, bins, ranges);
//绘制直方图
drawHist(hist, NORM_INF, "hist");
drawHist(hist2, NORM_INF, "hist2");
imshow("1", gray);
imshow("2", equalImg);
waitKey(0);
return 0;
}
三、直方图匹配
具体实现细节如下:
void drawHist(Mat &hist, int type, string name)
{
//直方图背景图的大小
int hist_w = 512;
int hist_h = 400;
//直方图矩形柱的宽度
int width = 2;
Mat histImage = Mat::zeros(hist_h, hist_w, CV_8UC3);
//归一化
normalize(hist, hist, 1, 0, type, -1, Mat());
for (int i = 1; i < hist.rows; i++)
{
rectangle(histImage, Point(width*(i - 1), hist_h - 1), Point(width*i - 1, hist_h - cvRound(20*hist_h*hist.at<float>(i - 1)) - 1), Scalar(255, 255, 255), -1);
}
imshow(name, histImage);
}
int main() {
//读取图片
Mat src = imread("1.png");
Mat src1 = imread("1.png");
if (src.empty() || src1.empty())
{
printf("不能打开空图片");
return -1;
}
Mat hist, hist2;
//定义通道的索引
const int channels[1] = { 0 };
//定义直方图的维度,为灰度图像的最大值
const int bins[1] = { 256 };
float inRanges[2] = { 0,255 };
//像素灰度值的取值范围
const float* ranges[1] = { inRanges };
//计算直方图
calcHist(&src, 1, channels, Mat(), hist, 1, bins, ranges);
calcHist(&src1, 1, channels, Mat(), hist2, 1, bins, ranges);
//绘制直方图
drawHist(hist, NORM_INF, "hist");
drawHist(hist2, NORM_INF, "hist2");
//计算两张直方图的累计概率
float hist1_cdf[256] = { hist.at<float>(0) };
float hist2_cdf[256] = { hist2.at<float>(0) };
for (int i = 0; i < 256; i++)
{
hist1_cdf[i] = hist1_cdf[i - 1] + hist.at<float>(i);
hist2_cdf[i] = hist2_cdf[i - 1] + hist2.at<float>(i);
}
//构建累计概率误差矩阵
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]);
}
}
//生成LUT映射表
Mat lut(1, 256, CV_8U);
for (int i = 0; i < 256; i++)
{
//查找源灰度级为1的映射灰度和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) = (uchar)index;
}
Mat result, hist3;
LUT(src, lut, result);
imshow("待匹配图像", src);
imshow("匹配的模板图像", src1);
imshow("直方图匹配结果", result);
//计算直方图
calcHist(&result, 1, channels, Mat(), hist3, 1, bins, ranges);
drawHist(hist3, NORM_INF, "hist3");
waitKey(0);
return 0;
}