直方图匹配的原理:
//图像直方图匹配
#include <stdio.h>
#include <iostream>
#include <string>
#include <opencv2\opencv.hpp>
using namespace std;
using namespace cv;
void drawHist(Mat &hist, int hist_w, int hist_h, int type, string name);
int main()
{
/*
直方图匹配与直方图均衡相似,但是直方图均衡不能指定往哪个方向均衡,而直方图匹配则是根据输入的模板函数进行均衡
*/
Mat img_dst = imread("pp1.jpg", IMREAD_GRAYSCALE);//待匹配的图像
Mat img_mask = imread("person1.jpeg", IMREAD_GRAYSCALE);//用来匹配的模板图像
//统计0~255各个灰度值的像素个数
const int channels[1] = {
0 };
Mat hist_dst, hist_mask;
const int histSize[1] = {
256 };
float a[2] = {
0,255 };
const float * ranges[1] = {
a };
//得到的hist是一个单通道,width=1,height=256的Mat变量
calcHist(&img_dst, 1, channels, Mat(), hist_dst, 1, histSize, ranges);
calcHist(&img_mask, 1, channels, Mat(), hist_mask, 1, histSize, ranges);
//归一化的直方图
drawHist(hist_dst, 512, 400, NORM_INF, "hist_dst");
drawHist(hist_mask, 512, 400, NORM_INF, "hist_mask");
//计算两幅图像的累积概率
float hist_dst_cdf[256] = {
hist_dst.at<float>(0) };
float hist_mask_cdf[256] = {
hist_mask.at<float>(0) };
for (int i = 1; i < 256; i++)//这里采用的是累积小于该像素灰度值的比例,如上面的表格所示
{
hist_dst_cdf[i] = hist_dst_cdf[i - 1] + hist_dst.at<float>(i);
hist_mask_cdf[i] = hist_mask_cdf[i - 1] + hist_mask.at<float>(i);
}
//构建累计概率的误差矩阵,diff_cdf[0][0..255]表示hist_dst[0]分别与与hist_mask[0..255]的差值
float diff_cdf[256][256];
for (int i = 0; i < 256; i++)
{
for (int j = 0; j < 256; j++)
{
diff_cdf[i][j] = fabs(hist_dst_cdf[i] - hist_mask_cdf[j]);
}
}
//映射灰度值
Mat lut(1, 256, CV_8U);
for (int i = 0; i < 256; i++)
{
float min = diff_cdf[i][0];
int index = 0;
//找到diff_cdf[i][0..255]中的最小值,此时的j即为要用的灰度值,用来替换i
for (int j = 0; j < 256; j++)
{
if (min > diff_cdf[i][j])
{
min = diff_cdf[i][j];
index = j;
}
}
//替换i,注意i,j不仅是数组下标,也是灰度值
lut.at<uchar>(i) = (uchar)index;
}
Mat img_result, hist_result;
LUT(img_dst, lut, img_result);
namedWindow("待匹配的图像", WINDOW_NORMAL);
namedWindow("用来匹配的模板图像", WINDOW_NORMAL);
namedWindow("匹配后的图像", WINDOW_NORMAL);
imshow("待匹配的图像", img_dst);
imshow("用来匹配的模板图像", img_mask);
imshow("匹配后的图像", img_result);
calcHist(&img_result, 1, channels, Mat(), hist_result, 1, histSize, ranges);
drawHist(hist_result, 512, 400, NORM_INF, "hist_result");
waitKey(0);
return 0;
}
//画直方图函数,传入参数:hist,图像宽,图像高,归一化方式,窗口名称
void drawHist(Mat &hist, int hist_w, int hist_h,int type, string name)//这个函数适合用NORM_INF方法
{
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(hist_h*hist.at<float>(i - 1)) - 1),
Scalar(255, 255, 255), -1);
}
namedWindow(name, WINDOW_NORMAL);
imshow(name, histImage);
}