Ubuntu系统VScode实现opencv(c++)图像一维直方图
前言
在数字图像处理领域,直方图是一种极为重要的工具,而图像的一维直方图更是其中的基础与核心。一维直方图以简洁而直观的方式,将图像中像素的灰度值分布情况呈现出来,让我们能够快速了解图像的整体亮度、对比度等关键信息。通过对一维直方图的分析,我们可以轻松判断图像是否过亮或过暗,对比度是否合适,进而为后续的图像增强、分割、特征提取等一系列操作提供有力的依据。
直方图概念
实际上,一维直方图就是不同像素值出现频率的统计。如下图:
一维直方图一般是将多通道拆分为单通道,然后统计对应单通道像素出现频率,对于RGB图像来说一般有三个折线。
opencv中已经有相应的函数帮助我们生成直方图的数据,我们先了解每一个参数变量。
calcHist函数
1.函数原型:
void 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 );
2.参数介绍:
(1)const Mat* images
输入图像数组。通常传入图像的地址(如 &image
)。如果传入多个图像,它们必须具有相同的尺寸和通道数。
(2)int nimages
输入图像的数量。如果只有一个图像,传入 1
;如果有多个图像,传入图像数组的长度。
(3)const int* channels
对于灰度图像,通常传入 0
(表示单通道)。
对于彩色图像(如 BGR),可以传入 0
(蓝色通道)、1
(绿色通道)或 2
(红色通道)。
如果需要同时计算多个通道的直方图,可以传入一个数组(例如,int channels[] = {0, 1}
表示计算蓝色和绿色通道的直方图)。
(4)InputArray mask:
掩码图像,如果传入掩码,直方图仅计算掩码中非零像素对应的值。
(5)OutputArray hist
输出的直方图。直方图是一个一维或多维数组。
(6)int dims
直方图的维度。对于一维直方图(例如,单通道图像),传入 1。
对于多维直方图(例如,计算两个通道的联合直方图),传入相应的维度数(如 2
)。
(7)const int* histSize
每个维度的直方图大小。对于一维直方图,传入一个整数值(如 256
,表示灰度值范围为 0-255)。对于多维直方图,传入一个数组(例如,int histSize[] = {256, 256}
,表示两个通道的直方图大小均为 256)。
(8)const float** ranges
每个维度的值范围。对于一维直方图,传入一个包含两个值的数组(如 float range[] = {0, 256}
,表示灰度值范围为 0-255)。对于一维直方图,传入一个包含两个值的数组(如 float range[] = {0, 256}
,表示灰度值范围为 0-255)。
在了解每一个参数的作用之后就可以开始使用这个函数了。
函数使用
对于三通道RGB图像,我们做一维的就是提取每个通道的直方图。那么首先就是分离通道,这里应该不用多说,前面已经知道怎么做了。
vector<Mat> BGR;split(image,BGR);
接下来我们需要定义上述函数的参数,这里多看就会理解了。主要定义的有两个变量,分别是 const int* histSize和const float** ranges
const int bins[] = {256};
float hranges[] = {0,255};
const float* ranges[1] = {hranges};
其实 bins就是横坐标的取值长度,这里的像素是0-255,所以总共有256个灰度等级,而 ranges则是定义像素值范围的,这里之所以这么定义,是因为如果是多通道的,我们直接在ranges中加入另一个通道的范围值,例如这样:
float h_ranges[] = {0,180};float s_ranges[] = {0,256};const float* ranges[] = {h_ranges,s_ranges};
接下来,定义三个用于接受三个通道直方图的Mat变量;整体代码就是这样:
vector<Mat> BGR;split(image,BGR);const int bins[] = {256};float hranges[] = {0,255};const float* ranges[1] = {hranges};Mat b_hist,g_hist,r_hist;calcHist(&BGR[0],1,0,Mat(),b_hist,1,bins,ranges);calcHist(&BGR[1],1,0,Mat(),g_hist,1,bins,ranges);calcHist(&BGR[2],1,0,Mat(),r_hist,1,bins,ranges);
具体数据可视化的代码段我使用辅助工具生成:
// ---------- 1. 准备画布 ----------int hist_w = 512; // 画布宽度int hist_h = 400; // 画布高度int bin_w = cvRound(double(hist_w) / 256); // 每个 bin 的像素宽度Mat histImage(hist_h, hist_w, CV_8UC3, Scalar(0, 0, 0)); // 白底// ---------- 2. 归一化到 [0, hist_h] ----------normalize(b_hist, b_hist, 0, histImage.rows - 20, NORM_MINMAX);normalize(g_hist, g_hist, 0, histImage.rows - 20, NORM_MINMAX);normalize(r_hist, r_hist, 0, histImage.rows - 20, NORM_MINMAX);// ---------- 3. 绘制折线 ----------for (int i = 1; i < 256; ++i){line(histImage,Point(bin_w * (i - 1), hist_h - cvRound(b_hist.at<float>(i - 1))),Point(bin_w * i, hist_h - cvRound(b_hist.at<float>(i))),Scalar(255, 0, 0), 2); // B 通道,蓝色line(histImage,Point(bin_w * (i - 1), hist_h - cvRound(g_hist.at<float>(i - 1))),Point(bin_w * i, hist_h - cvRound(g_hist.at<float>(i))),Scalar(0, 255, 0), 2); // G 通道,绿色line(histImage,Point(bin_w * (i - 1), hist_h - cvRound(r_hist.at<float>(i - 1))),Point(bin_w * i, hist_h - cvRound(r_hist.at<float>(i))),Scalar(0, 0, 255), 2); // R 通道,红色}// ---------- 4. 显示 ----------imshow("BGR Histogram", histImage);waitKey(0); // 按任意键关闭窗口
整个Demo的代码如下:
void Demo::calicHist_Demo(Mat &image)
{vector<Mat> BGR;split(image,BGR);const int bins[] = {256};float hranges[] = {0,255};const float* ranges[1] = {hranges};Mat b_hist,g_hist,r_hist;calcHist(&BGR[0],1,0,Mat(),b_hist,1,bins,ranges);calcHist(&BGR[1],1,0,Mat(),g_hist,1,bins,ranges);calcHist(&BGR[2],1,0,Mat(),r_hist,1,bins,ranges);// ---------- 1. 准备画布 ----------int hist_w = 512; // 画布宽度int hist_h = 400; // 画布高度int bin_w = cvRound(double(hist_w) / 256); // 每个 bin 的像素宽度Mat histImage(hist_h, hist_w, CV_8UC3, Scalar(0, 0, 0)); // 白底// ---------- 2. 归一化到 [0, hist_h] ----------normalize(b_hist, b_hist, 0, histImage.rows - 20, NORM_MINMAX);normalize(g_hist, g_hist, 0, histImage.rows - 20, NORM_MINMAX);normalize(r_hist, r_hist, 0, histImage.rows - 20, NORM_MINMAX);// ---------- 3. 绘制折线 ----------for (int i = 1; i < 256; ++i){line(histImage,Point(bin_w * (i - 1), hist_h - cvRound(b_hist.at<float>(i - 1))),Point(bin_w * i, hist_h - cvRound(b_hist.at<float>(i))),Scalar(255, 0, 0), 2); // B 通道,蓝色line(histImage,Point(bin_w * (i - 1), hist_h - cvRound(g_hist.at<float>(i - 1))),Point(bin_w * i, hist_h - cvRound(g_hist.at<float>(i))),Scalar(0, 255, 0), 2); // G 通道,绿色line(histImage,Point(bin_w * (i - 1), hist_h - cvRound(r_hist.at<float>(i - 1))),Point(bin_w * i, hist_h - cvRound(r_hist.at<float>(i))),Scalar(0, 0, 255), 2); // R 通道,红色}// ---------- 4. 显示 ----------imshow("BGR Histogram", histImage);waitKey(0); // 按任意键关闭窗口
}
运行结果: