|
详解什么叫二维直方图,并利用OpenCV的函数calcHist()绘制图像的H-S二维直方图
很多书籍与技术网文在对二维直方图的介绍中,都是说函数calcHist()可以计算图像的H-S二维直方图,然后给个用函数calcHist()计算图像H-S二维直方图的代码就完事了,看完之后,大家还是不知道图像的二维直方图究竟是怎么回事。
本篇博文昊虹君为大家详细介绍究竟图像的二维直方图的概念是什么,以及二维直方图矩阵中每个数值究竟是怎样计算得到的。
图像的二维直方图的概念:图像的二维直方图是图像在两个维度上的直方图联合统计的结果。
上面这句话相信大家看了还是不明白图像的二维直方图是怎么计算得到的。
举个简单的例子来说明这个问题。
设图像A有两个通道,其两个通道的数据分别如下:
![](http://pic1.hhai.cc/pic1/2023/2023-01/001/23.png)
利用OpenCV的函数calcHist()计算上面两个通道的二维直方图的C++代码如下:
[C++] 纯文本查看 复制代码 //出处:昊虹AI笔记网(hhai.cc)
//用心记录计算机视觉和AI技术
//博主微信/QQ 2487872782
//QQ群 271891601
//欢迎技术交流与咨询
//OpenCV版本 OpenCV3.0
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main()
{
Mat Image1(2, 3, CV_8UC2, cv::Scalar(0, 0));
cv::Mat A1 = (cv::Mat_<uchar>(2, 3) << 1, 1, 1,
2, 2, 2);
cv::Mat A2 = (cv::Mat_<uchar>(2, 3) << 3, 3, 3,
2, 2, 2);
std::vector<cv::Mat> A_plane;
A_plane.push_back(A1);
A_plane.push_back(A2);
cv::merge(A_plane, Image1);
int channels[] = { 0, 1 };
int histSize[] = { 2, 3 };
float Ranges_1[] = { 0, 4 };
float Ranges_2[] = { 0, 6 };
const float *ranges[] = { Ranges_1, Ranges_2 };
Mat dstHist;
calcHist(&Image1, 1, channels, Mat(), dstHist, 2, histSize, ranges, true, false);
cout << dstHist << endl << endl;
return 0;
}
运行结果如下:
![](http://pic1.hhai.cc/pic1/2023/2023-01/001/24.png)
现在的问题是上面这个二维直方图矩阵dstHist的每一个元素的数据值是怎么计算得到的?很多书籍与技术网文并未提及这个问题。
接下来昊虹君就给大家详细讲解下。
各位朋友,很抱歉,
因为靠写博文为生,
所以如果需要了解二维直方图矩阵是怎样计算得来的朋友,请您破费10元付费阅读。
我把这个问题的答案用简单明了的语言写在了下面这个页面中,
https://www.hhai.cc/thread-167-1-1.html
上面这个页面需要密码才能阅读。
阅读密码购买链接如下:
https://shop.autofaka.com//details/154549E2
接下来我们利用OpenCV的函数calcHist()绘制图像的H-S二维直方图。
对于彩色图像,我们通常需要同时考虑图像的色调(hue)和饱和度(saturation),所以有时候我们需要计算图像的“色调-饱和度二维直方图”,即H-S直方图。
关于HSV空间的介绍,可参见下面这个链接:
https://www.hhai.cc/thread-212-1-1.html
下面是利用OpenCV的函数calcHist()绘制彩色图像H-S直方图的C++示例代码。
代码中用到的图像下载链接:https://pan.baidu.com/s/1I23cagDGV08jp3F-pCizMQ?pwd=a7r7
[C++] 纯文本查看 复制代码 //出处:昊虹AI笔记网(hhai.cc)
//用心记录计算机视觉和AI技术
//博主微信/QQ 2487872782
//QQ群 271891601
//欢迎技术交流与咨询
//OpenCV版本 OpenCV3.0
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main()
{
Mat srcImage = imread("E:/material/images/2022/2022-12/view1.jpg");
imshow("【原图】", srcImage);
Mat hsvImage;
//因为要计算H-S的直方图,所以需要先进行颜色空间的转换
cvtColor(srcImage, hsvImage, CV_BGR2HSV);
//计算H-S二维直方图的参数配置
int channels[] = { 0, 1 };
Mat dstHist;
int histSize[] = { 30, 32 };
float HRanges[] = { 0, 181 };
float SRanges[] = { 0, 256 };
const float *ranges[] = { HRanges, SRanges };
//参数配置好后,就可以调用calcHis函数计算直方图数据了
calcHist(&hsvImage, 1, channels, Mat(), dstHist, 2, histSize, ranges, true, false);
//calcHist函数调用结束后,dstHist变量中将储存直方图数据
///接下来绘制直方图
//首先先创建一个黑底的图像,为了可以显示彩色,所以该绘制图像是一个8位的3通道图像
Mat drawImage = Mat::zeros(Size(300, 320), CV_8UC3);
//因为数据矩阵中的某个值的总个数可能会超出所定义的图像的尺寸,所以要对个数进行归一化处理
//先用 minMaxLoc函数来得到计算直方图中的最大数据,这个函数的使用方法大家一看函数原型便知
double g_dHistMaxValue;
minMaxLoc(dstHist, 0, &g_dHistMaxValue, 0, 0);
//遍历直方图数据,对数据进行归一化和绘图处理
for (int i = 0; i < 30; i++)
{
for (int j = 0; j < 32; j++)
{
int value = cvRound(dstHist.at<float>(i, j) * 255/ g_dHistMaxValue);
rectangle(drawImage, Point(10 * i, j * 10), Point((i + 1) * 10 - 1, (j + 1) * 10 - 1), Scalar(0,0,value), -1);
}
}
imshow("【H-S二维联合直方图】", drawImage);
waitKey(0);
return 0;
}
代码中写了详细的注释,大家应该不难看懂,只是对下面两句代码补充说明一下:
- int value = cvRound(dstHist.at<float>(i, j) * 255/ g_dHistMaxValue);
- rectangle(drawImage, Point(10 * i, j * 10), Point((i + 1) * 10 - 1, (j + 1) * 10 - 1), Scalar(0,0,value), -1);
复制代码
1 这两句代码把hist里的数据归一化到0~255。
2 这两句代码把二维直方图进行了按比例放大绘制,本来hist的大小是30×32的,但在绘制的时候我们把其放大到了300×320,这是为了便于我们观察。
3 我们是用三通道的彩色图绘制二维直方图的,这样做也是便于我们肉眼观察,每一块矩形我们都用不同程度的红色来绘制,其红色的程度值就是hist里对应点进行归一化后的值。
上面代码的运行结果如下:
![](http://pic1.hhai.cc/pic1/2023/2023-01/001/26.png)
在绘图方面,还是Python的Matplotlib方便,所以利用Python-OpenCV计算并绘制图像的H-S二维直方图的代码就很简单了。
代码如下:
代码中用到的图像下载链接:https://pan.baidu.com/s/1I23cagDGV08jp3F-pCizMQ?pwd=a7r7
[Python] 纯文本查看 复制代码 # -*- coding: utf-8 -*-
# 出处:昊虹AI笔记网(hhai.cc)
# 用心记录计算机视觉和AI技术
# 博主微信/QQ 2487872782
# QQ群 271891601
# 欢迎技术交流与咨询
# OpenCV的版本为4.4.0
import cv2 as cv
from matplotlib import pyplot as plt
import sys
if __name__ == '__main__':
# 读取图像road.jpg
image = cv.imread('E:/material/images/2022/2022-12/view1.jpg')
# 判断图片是否读取成功
if image is None:
print('Failed to read image.')
sys.exit()
# 将图像的颜色空间从BGR转为HSV
image_hsv = cv.cvtColor(image, cv.COLOR_BGR2HSV)
# 计算2D直方图
image_hist = cv.calcHist([image_hsv], [0, 1], None, [181, 256], [0, 181, 0, 256])
# 展示图像及直方图结果
cv.imshow('Origin Image', image)
plt.imshow(image_hist, cmap='Reds', interpolation='nearest', origin='lower')
plt.show()
cv.waitKey(0)
cv.destroyAllWindows()
运行结果如下:
![](http://pic1.hhai.cc/pic1/2023/2023-01/001/27.png)
之所以背景不是白色,而是略带红色,是因为颜色映射空间设置为红色,所以背景色为最浅的红色。详情见Matplotlib的使用介绍博文:https://www.hhai.cc/thread-210-1-1.html |
|