文章目录
- opencv图像的直方图,二维直方图,直方图均衡化
- 一、图像的直方图
- 1、什么是图像的直方图:
- 2、直方图的作用:
- 3、如何绘制图像的直方图:
- (1)cv::calcHist()函数原型:
- (2)代码示例:
- cv::cvRound()函数:将浮点数值四舍五入为最接近的整数;
- 灰度图像直方图:
- 彩色图像直方图:
- 二、二维直方图
- 1、什么是二维直方图:
- 2、二维直方图与直方图有哪些不同:
- 3、二维直方图的作用:
- 4、如何绘制二维直方图:
- 三、直方图均衡化
- 1、直方图均衡化的原理:
- 2、opencv中,可以使用 `cv::equalizeHist()`函数来实现直方图均衡化:
- (1)cv::equalizeHist()函数原型:
- (2)代码示例:
- 灰度直方图均衡化:
- 彩色直方图均衡化:
opencv图像的直方图,二维直方图,直方图均衡化
一、图像的直方图
1、什么是图像的直方图:
- 要理解直方图,绕不开“亮度”这个概念,人们把亮度分为0到255共256个数值,数值越大,代表的亮度越高,其中0代表纯黑色,最暗区域,255表示最亮,纯白色,而中间的数字就是不同亮度的灰色;
- 图像的直方图是一种统计图,它显示了图像中各个灰度级别的分布情况(也就是统计一幅图某个亮度像素的数量),通常它的横轴代表像素的灰度级别(从0到255),而纵轴代表该灰度级别在图像中出现的频次或概率;
2、直方图的作用:
- 可视化图像亮度分布:直方图能够帮助我们直观地了解图像的亮度分布情况,从而为图像处理提供重要参考;
- 对比度调整:通过观察直方图,我们可以确定图像的对比度是否合适,从而决定是否需要进行对比度调整;
- 灰度级别选择:直方图可以帮助我们选择合适的灰度级别,以使图像的细节更加清晰;
3、如何绘制图像的直方图:
在opencv中,可以使用
cv::calcHist()
函数来计算图像的直方图,这个函数可以接受一个通道的图像(灰度图像)或多个通道的图像(彩色图像);
(1)cv::calcHist()函数原型:
void cv::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}表示统计第一个通道,{0, 1, 2}表示统计所有三个通道;
mask:掩码图像,用于指定计算直方图的区域(必须是一个8位(CV_8U)的数组并且和images的数组大小相同);
hist:直方图计算的输出值,是一个cv::Mat对象;
dims:输出直方图的维度(
由channels值决定dims的数值;
对于灰度图像dims为1,因为我们只考虑了一个通道(亮度通道);
对于彩色图像,通常会考虑多个通道,比如在HSV色彩空间中,dims为2(H和S两个通道)
);
histSize:指定每个dims维度需要分成多少个区间(用于控制直方图的精细度);
ranges:用于指定每个dims维度(通道)的取值范围,包含了每个通道的最小值和最大值(
对于灰度图像,单通道只有1个range,灰度级别的范围从0到255;
对于彩色图像有多个通道就有多个range,对于每个通道,ranges指定了取值范围,通常在彩色图像中,H(色相)的范围是0到180,S(饱和度)和V(明度)的范围是0到255 );
uniform:是否对得到的直方图数组进行归一化处理;
accumulate:在多个图像时,是否累积计算像素值的个数;
(2)代码示例:
cv::cvRound()函数:将浮点数值四舍五入为最接近的整数;
// 函数在图像处理过程中经常用于处理像素值,特别是当需要将浮点数转换为整数时,可以保留最接近的整数值
int cvRound(double value)
参数解释:
value:待四舍五入的浮点数;
灰度图像直方图:
#include <opencv2\opencv.hpp>
#include <iostream>
#include <demo.h>
using namespace cv;
using namespace std;
int main() {
// 读取灰度图像
cv::Mat image = cv::imread("C:\\cpp\\image\\suzy2.jpg", cv::IMREAD_GRAYSCALE);
if (image.empty()) {
std::cerr << "Error: 无法读取图像文件." << std::endl;
return -1;
}
// 直方图计算的输出值,是一个cv::Mat对象
cv::Mat hist;
// 亮度级别的个数
int histSize = 256;
// 亮度值的范围
float range[] = { 0, 256 };
// 在C++中,数组名本身就代表该数组的首地址,无需使用取地址符号&,
const float* histRange = range;
cv::calcHist(&image, 1, 0, cv::Mat(), hist, 1, &histSize, &histRange);
// 待绘制的目标图像,将直方图计算的输出值hist,绘制到histImage图像上
int histWidth = 512, histHeight = 400;
int binWidth = cvRound( (double)histWidth/histSize );
cv::Mat histImage(histHeight, histWidth, CV_8UC3, cv::Scalar(0, 0, 0));
// 使用normalize()函数将直方图的值缩放到图像的高度范围内
normalize(hist, hist, 0, histImage.rows, cv::NORM_MINMAX, -1, cv::Mat());
// 使用cv::line()函数绘制直线
for ( int i=1; i<histSize; i++ ) {
cv::line(
histImage,
Point( binWidth*(i-1), histHeight-cvRound(hist.at<float>(i-1)) ),
Point( binWidth*(i), histHeight-cvRound(hist.at<float>(i)) ),
Scalar(255, 0, 0),
2,
LINE_8,
0
);
}
// 显示图像和直方图
cv::imshow("Image", image);
cv::imshow("Histogram", histImage);
waitKey();
return 0;
}
运行结果:
彩色图像直方图:
对于彩色图像,你可以分别计算其颜色通道(蓝色、绿色、红色)的直方图,或者将其转换为灰度图像后计算整体的亮度直方图;
在这个示例中,我们首先读取了一个彩色图像,然后使用 cv::split()
函数将图像分离成蓝色、绿色和红色通道,接着,我们分别计算了每个通道的直方图,最后我们绘制了各个通道的直方图并显示了原始图像和直方图;
#include <opencv2\opencv.hpp>
#include <iostream>
#include <demo.h>
using namespace cv;
using namespace std;
int main() {
// 读取彩色图像
cv::Mat image = cv::imread("C:\\cpp\\image\\suzy2.jpg");
if (image.empty()) {
std::cerr << "Error: 无法读取图像文件." << std::endl;
return -1;
}
// 分离通道
std::vector<cv::Mat> bgrPlanes;
cv::split(image, bgrPlanes);
// 计算直方图
int histSize = 256; // 亮度级别的个数
float range[] = { 0, 256 };
const float* histRange = range;
cv::Mat bHist, gHist, rHist;
cv::calcHist(&bgrPlanes[0], 1, 0, cv::Mat(), bHist, 1, &histSize, &histRange);
cv::calcHist(&bgrPlanes[1], 1, 0, cv::Mat(), gHist, 1, &histSize, &histRange);
cv::calcHist(&bgrPlanes[2], 1, 0, cv::Mat(), rHist, 1, &histSize, &histRange);
// 绘制直方图
int histWidth=512, histHeight=400;
int binWidth = cvRound( (double)histWidth/histSize );
cv::Mat histImage(histHeight, histWidth, CV_8UC3, cv::Scalar(0, 0, 0));
normalize(bHist, bHist, 0, histImage.rows, cv::NORM_MINMAX, -1, cv::Mat());
normalize(gHist, gHist, 0, histImage.rows, cv::NORM_MINMAX, -1, cv::Mat());
normalize(rHist, rHist, 0, histImage.rows, cv::NORM_MINMAX, -1, cv::Mat());
for (int i=1; i<histSize; i++) {
// 绘制蓝色分量直方图
cv::line(
histImage,
cv::Point( binWidth*(i-1), histHeight-cvRound(bHist.at<float>(i-1)) ),
cv::Point( binWidth*(i), histHeight-cvRound(bHist.at<float>(i)) ),
cv::Scalar(255, 0, 0),
2,
LINE_8,
0
);
// 绘制绿色分量直方图
cv::line(
histImage,
cv::Point( binWidth*(i-1), histHeight-cvRound(gHist.at<float>(i-1)) ),
cv::Point( binWidth*(i), histHeight-cvRound(gHist.at<float>(i)) ),
cv::Scalar(0, 255, 0),
2,
LINE_8,
0
);
// 绘制红色分量直方图
cv::line(
histImage,
cv::Point( binWidth*(i-1), histHeight-cvRound(rHist.at<float>(i-1)) ),
cv::Point( binWidth*(i), histHeight-cvRound(rHist.at<float>(i)) ),
cv::Scalar(0, 0, 255),
2,
LINE_8,
0
);
}
// 显示图像和直方图
cv::imshow("Image", image);
cv::imshow("Histogram", histImage);
cv::waitKey(0);
return 0;
}
运行结果:
二、二维直方图
1、什么是二维直方图:
二维直方图是对彩色图像进行分析时的一个重要工具,与一维直方图不同,它同时考虑了两个通道的信息,通常是颜色空间中的两个分量,例如在HSV色彩空间中的H(色相)和S(饱和度);
2、二维直方图与直方图有哪些不同:
- 维度:直方图是一维的,它只考虑了图像的亮度或色彩信息;而二维直方图考虑了两个通道的信息,因此是二维的;
- 通道:直方图通常只针对一个通道(灰度图只有亮度通道);而二维直方图可以同时考虑多个通道,通常是颜色空间中的两个分量;
3、二维直方图的作用:
- 颜色分布分析:通过二维直方图,我们可以了解图像中各个颜色组合的分布情况,有助于理解图像的颜色特性;
- 图像分割与对象识别:在图像分割和对象识别任务中,二维直方图可用于将图像的颜色特征映射到特定的空间,从而实现对图像中的对象进行定位和识别;
- 颜色调整与匹配:通过分析二维直方图,我们可以进行颜色调整,使图像的颜色分布更加符合预期;
4、如何绘制二维直方图:
首先读取了一个彩色图像,然后将其从BGR颜色空间转换为HSV颜色空间,接着我们分离了H和S通道,并指定了直方图的维度、通道数、亮度级别个数和范围,然后我们使用 cv::calcHist()
计算了二维直方图并使用 cv::rectangle()
绘制;
#include <opencv2\opencv.hpp>
#include <iostream>
#include <demo.h>
using namespace cv;
using namespace std;
int main() {
// 读取彩色图像
cv::Mat image = cv::imread("C:\\cpp\\image\\suzy2.jpg");
if (image.empty()) {
std::cerr << "Error: 无法读取图像文件." << std::endl;
return -1;
}
// 将图像从BGR颜色空间转换为HSV颜色空间
cv::Mat hsvImage;
cv::cvtColor(image, hsvImage, cv::COLOR_BGR2HSV);
// 分离H和S通道
std::vector<cv::Mat> channels;
cv::split(hsvImage, channels);
int channelNumbers[] = { 0, 1 };
int histSize[] = { 256, 256 };
float hueRanges[] = { 0, 256 };
float satRanges[] = { 0, 256 };
const float* ranges[] = { hueRanges, satRanges };
// 计算直方图
cv::Mat hist;
cv::calcHist(&channels[0], 1, channelNumbers, cv::Mat(), hist, 2, histSize, ranges, true, false);
// 绘制直方图
int histWidth= 512, histHeight = 512;
int binWidth = histWidth/histSize[0];
int binHeight = histHeight/histSize[1];
cv::Mat histImage(histHeight, histWidth, CV_8UC3, cv::Scalar(0, 0, 0));
cv::normalize(hist, hist, 0, 255, cv::NORM_MINMAX);
for (int i = 0; i < histSize[0]; i++) {
for (int j = 0; j < histSize[1]; j++) {
int x = i * binWidth;
int y = j * binHeight;
int w = (i + 1) * binWidth - x;
int h = (j + 1) * binHeight - y;
cv::rectangle(histImage, cv::Rect(x, y, w, h), cv::Scalar(hist.at<float>(i, j), 0, 0), -1);
}
}
// 显示图像和直方图
cv::imshow("Image", image);
cv::imshow("2D Histogram", histImage);
cv::waitKey(0);
return 0;
}
运行结果:
三、直方图均衡化
直方图均衡化是一种用于增强图像对比度的图像处理技术,它通过重新分配图像的像素值,使得整个亮度范围得到充分利用,从而使图像看起来更清晰和具有更好的对比度;
1、直方图均衡化的原理:
- 计算直方图:首先计算原始图像的灰度直方图,这个直方图描述了图像中各个灰度级别的分布情况;
- 计算累积分布函数(CDF):将灰度直方图转换为累积分布函数,CDF表示了每个灰度级别的累积概率;
- 映射新的像素值:对于每个像素,将其原始灰度值映射到新的值,使得新的值在整个亮度范围内均匀分布;
2、opencv中,可以使用 cv::equalizeHist()
函数来实现直方图均衡化:
(1)cv::equalizeHist()函数原型:
void cv::equalizeHist(
InputArray src,
OutputArray dst
);
参数解释:
src:输入图像(灰度图像);
dst:输出图像,用于存储均衡化后的结果;
(2)代码示例:
灰度直方图均衡化:
#include <opencv2\opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main() {
// 读取灰度图像
cv::Mat image = cv::imread("C:\\cpp\\image\\suzy2.jpg", cv::IMREAD_GRAYSCALE);
if (image.empty()) {
std::cerr << "Error: 无法读取图像文件." << std::endl;
return -1;
}
// 均衡化灰度直方图
cv::Mat equalizedImage;
cv::equalizeHist(image, equalizedImage);
cv::imshow("Original Gray Image", grayImage);
cv::imshow("Equalized Gray Image", equalizedImage);
cv::waitKey(0);
return 0;
}
运行结果:
彩色直方图均衡化:
首先将彩色图像转换为HSV色彩空间,然后对亮度(Value/V通道)进行均衡化,最后将其转换回BGR色彩空间以显示
#include <opencv2\opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main() {
// 读取彩色图像
cv::Mat image = cv::imread("C:\\cpp\\image\\suzy2.jpg", cv::IMREAD_COLOR);
if (image.empty()) {
std::cerr << "Error: 无法读取图像文件." << std::endl;
return -1;
}
// 将图像从BGR色彩空间转换为HSV色彩空间
cv::Mat hsvImage;
cv::cvtColor(image, hsvImage, cv::COLOR_BGR2HSV);
// 均衡化HSV通道的直方图
std::vector<cv::Mat> channels;
cv::split(hsvImage, channels);
cv::equalizeHist(channels[2], channels[2]);
cv::Mat equalizedHSV;
cv::merge(channels, equalizedHSV);
cv::cvtColor(equalizedHSV, equalizedHSV, cv::COLOR_HSV2BGR);
cv::imshow("Original Color Image", image);
cv::imshow("Equalized Color Image", equalizedHSV);
cv::waitKey(0);
return 0;
}
运行结果: