opencv 颜色直方图(灰度图,均衡化,对比,描绘颜色直方图)

时间:2022-03-31 22:43:19

参考:

《OpenCV3编程入门》

《学习OpenCV(中文版)》


#################################################################33


最近上课需要将颜色直方图的内容,所以就把直方图一些常用的知识点复习了一遍,包括灰度图,均衡化,对比等。

#include "hist.h"

/**
* @function : 显示灰度图和均衡化后的灰度图
* @param : img_address - const string 图片地址
*/
void show_gray(const string img_address)
{
Mat img, dst;
//加载源图像
img = imread(img_address, 1);
if (img.empty())
{
cout<<"读取图片错误\n";
return ;
}

//转为灰度图
cvtColor(img, img, COLOR_BGR2GRAY);

//直方图均衡化
equalizeHist(img, dst);

imshow("show", img);
//显示结果
imshow("show_equalize", dst);

waitKey(0);
}

/**
* @function : 提取灰度颜色直方图并展示
* @param : color_address - string 图像地址
*/
void show_gray_color_histogram(const string color_address)
{
//载入原图并显示
Mat srcImage = imread(color_address, 0);
//imshow("原图", srcImage);
if (!srcImage.data)
{
cout<<"fail to load image"<<endl;
return ;
}
Mat dst;
equalizeHist(srcImage, dst);
imshow("均衡化", dst);

//定义变量
MatND dstHist;
int dims = 1;
float hranges[] = {0,255};
const float *ranges[] = {hranges};
int size = 256;
int channels = 0;

//计算直方图
calcHist(&dst, 1, &channels, Mat(), dstHist, dims, &size, ranges);
int scale = 1;

Mat dstImage(size*scale, size, CV_8U, Scalar(0));
//获取最大值和最小值
double maxValue = 0;
double minValue = 0;
minMaxLoc(dstHist, &minValue, &maxValue, 0, 0);

//绘制直方图
int hpt = saturate_cast<int>(0.9 * size);
for (int i=0; i<256; i++)
{
float binValue = dstHist.at<float>(i);
int realValue = saturate_cast<int>(binValue * hpt / maxValue);
rectangle(dstImage, Point(i*scale, size-1), Point((i+1)*scale-1, size-realValue), Scalar(255));
}
imshow("灰度直方图", dstImage);
waitKey(0);
}

/**
* @function : 绘制RGB颜色直方图
* @param : img_address - const string 图片地址
*/
void show_RGB_histogram(const string img_address)
{
//载入图片
Mat srcImage;
srcImage = imread(img_address);
imshow("原图", srcImage);

//参数准备
int bins = 256;
int hist_size[] = {bins};
float range[] = {0, 256};
const float *ranges[] = {range};
MatND redHist, greenHist, blueHist;
int channels_r[] = {0};

//进行直方图的计算(红色部分)
calcHist(&srcImage, 1, channels_r, Mat(), redHist, 1, hist_size, ranges, true, false);

//绿色分量计算
int channels_g[] = {1};
calcHist(&srcImage, 1, channels_g, Mat(), greenHist, 1, hist_size, ranges, true, false);

//蓝色分量计算
int channels_b[] = {1};
calcHist(&srcImage, 1, channels_b, Mat(), blueHist, 1, hist_size, ranges, true, false);

//绘制rgb颜色直方图
//参数准备
double maxValue_red, maxValue_green, maxValue_blue;
minMaxLoc(redHist, 0, &maxValue_red, 0, 0);
minMaxLoc(greenHist, 0, &maxValue_green, 0, 0);
minMaxLoc(blueHist, 0, &maxValue_blue, 0, 0);
int scale = 1;
int histHeight = 256;
Mat histImage = Mat::zeros(histHeight, bins*3, CV_8UC3);

//开始绘制
for (int i=0; i<bins; i++)
{
//参数准备
float binValue_red = redHist.at<float>(i);
float binValue_green = greenHist.at<float>(i);
float binValue_blue = blueHist.at<float>(i);

int intensity_red = cvRound(binValue_red * histHeight / maxValue_red); //计算绘制高度
int intensity_green = cvRound(binValue_green * histHeight / maxValue_green); //计算绘制高度
int intensity_blue = cvRound(binValue_blue * histHeight / maxValue_blue); //计算绘制高度

//绘制红色部分直方图
rectangle(histImage, Point(i*scale, histHeight-1), Point((i+1)*scale-1, histHeight-intensity_red), Scalar(0, 0, 255));

//绘制绿色部分直方图
rectangle(histImage, Point((i+bins)*scale, histHeight-1), Point((i+bins+1)*scale-1, histHeight-intensity_green), Scalar(0, 255, 0));

//绘制蓝色部分直方图
rectangle(histImage, Point((i+bins*2)*scale, histHeight-1), Point((i+bins*2+1)*scale-1, histHeight-intensity_blue), Scalar(255, 0, 0));
}

//在窗口中显示
imshow("RGB", histImage);
waitKey(0);
}

/**
* @function : 直方图对比
* @param : srcImg_address - const string 原图地址
* @param : comp1_address - const string 对比图1地址
* @param : comp2_address - const string 对比图2地址
*/
void compare_hist(const string srcImg_address, const string comp1_address, const string comp2_address)
{
//声明存储基准图像和另外两张对比图像的矩阵(RGB 和 HSV)
Mat srcImage_base, hsvImage_base;
Mat srcImage_test1, hsvImage_test1;
Mat srcImage_test2, hsvImage_test2;
Mat hsvImage_halfDown;

//载入基准图像(srcImage_base)和两张测试图像srcImage_test1,srcImage_test2,并显示
srcImage_base = imread(srcImg_address, 1);
srcImage_test1 = imread(comp1_address, 1);
srcImage_test2 = imread(comp2_address, 1);
//显示
imshow("基准图像", srcImage_base);
imshow("测试图像1", srcImage_test1);
imshow("测试图像2", srcImage_test2);

//将图像由RGB色彩空间转换到HSV色彩空间
cvtColor(srcImage_base, hsvImage_base, COLOR_BGR2HSV);
cvtColor(srcImage_test1, hsvImage_test1, COLOR_BGR2HSV);
cvtColor(srcImage_test2, hsvImage_test2, COLOR_BGR2HSV);

//创建包含基准图像下半身的半身图像(HSV格式)
hsvImage_halfDown = hsvImage_base(Range(hsvImage_base.rows/2, hsvImage_base.rows-1), Range(0, hsvImage_base.cols-1));

//初始化计算直方图需要的实参
//对hue通道使用30个bin,对saturation通道使用32个bin
int h_bins = 50; int s_bins = 60;
int histSize[] = {h_bins, s_bins};
//hue的取值范围从0到256,saturation取值范围从0到180
float h_ranges[] = {0, 256};
float s_ranges[] = {0, 180};
const float* ranges[] = {h_ranges, s_ranges};
//使用第0和第1通道
int channels[] = {0, 1};

//创建存储直方图的MatND
MatND baseHist;
MatND halfDownHist;
MatND testHist1;
MatND testHist2;

//计算基准图像,两张测试图像,半身基准图像的HSV直方图
calcHist(&hsvImage_base, 1, channels, Mat(), baseHist, 2, histSize, ranges, true, false);
normalize(baseHist, baseHist, 0, 1, NORM_MINMAX, -1, Mat());

calcHist(&hsvImage_halfDown, 1, channels, Mat(), halfDownHist, 2, histSize, ranges, true, false);
normalize(halfDownHist, halfDownHist, 0, 1, NORM_MINMAX, -1, Mat());

calcHist(&hsvImage_test1, 1, channels, Mat(), testHist1, 2, histSize, ranges, true, false);
normalize(testHist1, testHist1, 0, 1, NORM_MINMAX, -1, Mat());

calcHist(&hsvImage_test2, 1, channels, Mat(), testHist2, 2, histSize, ranges, true, false);
normalize(testHist2, testHist2, 0, 1, NORM_MINMAX, -1, Mat());

//按顺序使用4种对比标准将基准图像的直方图与其余各直方图进行对比
for (int i=0; i<4; i++)
{
//进行图像直方图的对比
int compare_method = i;
double base_base = compareHist(baseHist, baseHist, compare_method);
double base_half = compareHist(baseHist, halfDownHist, compare_method);
double base_test1 = compareHist(baseHist, testHist1, compare_method);
double base_test2 = compareHist(baseHist, testHist2, compare_method);

//输出结果
cout<<"方法["<<i<<"]的匹配结果如下:"<<endl;
cout<<"【基准图 - 基准图】:"<<base_base<<" 【基准图 - 半身图】:"<<base_half<<" 【基准图 - 测试图1】:"<<base_test1<<" 【基准图 - 测试图2】:"<<base_test2<<endl;
}

cout<<"endl..."<<endl;
waitKey(0);
}

上面代码都是使用全局颜色直方图进行操作。在实际应用中,如果想要进行直方图对比,仅使用全局颜色直方图并不能得到很好的结果,因为全局颜色直方图忽略了图像的空间信息。改进方法如下:

将图像分成若干局域,比如上下左右四等分为4个区域,然后分别提取颜色直方图,计算相似度;

在上一步的基础上还可以增加权重值,对于比较重要的区域赋以高权重值;

还可以使用颜色直方图和其他特征向量进行联合对比,比如纹理特征,形状特征等。