C++OpenCV下绘制灰度直方图

这篇博客介绍了如何利用OpenCV的calcHist函数在C++中计算并绘制8位灰度图像的直方图。通过GetHist函数统计图像的灰度分布,并使用ShowHist函数在512x400的黑色背景图上绘制直方图。直方图可以帮助分析图像的亮度和对比度分布。
C++OpenCV下绘制直方图
直方图的定义:灰度直方图是对一幅灰度图像素分布的统计。对于一幅8Bit量化的图像来说。就是统计在0~255各个灰度级上,像素点的个数或者密度。
在OpenCV库提供了calcHist()方法用于得到图像的直方图。
具体函数的调用方法如下:
void cv::calcHist(const Mat*images,int nimages,const int *channels,InputArrary mask,OutputArrary hist,int dims,const int* histSize,const float **ranges,bool uniform=true,bool accumulate=false)
//参数说明
//images:带统计直方图的图像数组
//nimages:输入图像的数目
//channels:需要统计的通道索引数组 一般灰度图就是0通道
//mask:掩码 一般填Mat()
//hist:输出的统计直方图结果,是一个dims维度的数组。不过用OpenCV中Mat类型的变量存储
//dims:hist的列数、需要计算直方图的维度
//histSize:存放每个维度直方图数组的尺寸
//ranges:每个通道灰度值的取值范围
根据以上参数,设计了一个获取直方图Mat数组Hist的函数
//@para gray:需要统计的图   Hist:用于存放统计数据
void GetHist(Mat gray,Mat &Hist)    //统计8Bit量化图像的灰度直方图
{
    const int channels[1] = { 0 }; //通道索引
    float inRanges[2] = { 0,255 };  //像素范围
    const float* ranges[1] = {inRanges};//像素灰度级范围
    const int bins[1] = { 256 };   //直方图的维度
    calcHist(&gray, 1, channels,Mat(), Hist,1, bins, ranges);
}
这里得gray为测试灰度图。看下运行完calcHist函数后,Hist的维数。
可以看到Hist就是一个256行1列Mat类型的变量,可以看做一个数组索引。
这样我们就可以根据这个Hist画图。
这里设计一个ShowHist函数,主要的步骤就是申请一块背景为纯黑的图像,再根据Hist的值转化为点坐标从而画出一个个白色的矩形以完成直方图的绘制。
void ShowHist(Mat &Hist)
{
    //准备绘制直方图
    int hist_w = 512;
    int hist_h = 400;
    int width = 2;
    Mat histImage = Mat::zeros(hist_h,hist_w,CV_8UC3);   //准备histImage为全黑背景色,大小为512*400
    for (int i = 0; i < Hist.rows; i++)
    {
         rectangle(histImage,Point(width*(i),hist_h-1),Point(width*(i+1),hist_h-cvRound(Hist.at<float>(i)/20)),
            Scalar(255,255,255),-1);
    }
    namedWindow("histImage", WINDOW_AUTOSIZE);
    imshow("histImage", histImage);
    //waitKey(0);
}

首先准备一幅512*400的全黑背景图histImage,接下来通过Hist.rows遍历每个灰度级的像素个数。通过Rectangle函数画出白色的矩形。最后的-1代表绘制的矩形是填充矩形。对于Rectangle,重点分析下两个Point的含义。
@param pt1 Vertex of the rectangle.
@param pt2 Vertex of the rectangle opposite to pt1 .
上面是官方给出的注释,pt1为矩形的一个顶点,pt2为矩形对角线上的另一个顶点。
首先看横坐标,一个矩形的横坐标长度应该为一个像素级所占的长度,这里我们假定一个像素级占长度为width=2。pt1的横坐标值为width*(i),pt2的横坐标值为width*(i+1);
再看纵坐标,首先需要明白图像的坐标系,它是这样的:

pt1的纵坐标为:hist_h-1相当于图像最下面,也就是矩形的左下角。pt2的纵坐标应该是矩形的右上角才对,hist_h-cvRound(Hist.at<float>(i)/20),cvRound是4舍5入,Hist.at<float>(i)取出在该像素级上像素点的个数,这个数字可能远远大于hist_h(512),因为直方图我们只希望看到图像像素级分布的大致,所以我们除以一个20保证不会超出图像的边界。
最后给出我主函数的调用:
int main(int argc,char *argv)
{
    Mat src,gray,hist;   //hist用于统计gray的直方图
    src=imread("2.jpg");
    cvtColor(src,gray,CV_BGR2GRAY);
    GetHist(gray,hist);
    ShowHist(hist);
    namedWindow("gray");
    imshow("gray",gray);
    waitKey(0);
    return 0;
}
结果:

总结分析:其实图像的直方图只能告诉我们图像像素级别的分布如何,可以定性的帮助我们判断图像对比度,以及亮度的分布情况,如果想通过直方图进行操作,还是得借助calcHist得到的Hist数组对其进行定量的操作。
### 使用 OpenCVC++ 实现灰度图直方图绘制 为了实现这一功能,程序通常分为几个部分:读取图像、转换为灰度模式、计算直方图以及最终绘制直方图。 #### 1. 初始化环境与加载图片 首先需要导入必要的头文件,并初始化OpenCV环境。接着通过`imread()`函数载入待分析的彩色或灰度图像。如果输入的是彩色图像,则需将其转化为灰度形式以便后续操作[^2]。 ```cpp #include <opencv2/opencv.hpp> using namespace cv; int main() { Mat src = imread("example.jpg"); if (src.empty()) return -1; Mat gray; cvtColor(src, gray, COLOR_BGR2GRAY); } ``` #### 2. 计算灰度直方图 使用`calcHist()`方法可以方便地获取给定图像中各个亮度级别的频率分布情况。此过程涉及设置参数如通道数目、区间范围等,具体配置取决于所使用的版本和需求[^3]。 ```cpp Mat hist; int histSize = 256; // 灰度级数 float range[] = {0, 256}; const float* histRange = &range; bool uniform = true, accumulate = false; calcHist(&gray, 1, 0, Mat(), hist, 1, &histSize, &histRange, uniform, accumulate); ``` #### 3. 创建用于展示的空白画布 建立一个新的矩阵作为背景板,在其基础上描绘出实际的数据图形。这里设定了固定的宽高比例来适应大多数屏幕尺寸[^4]。 ```cpp int hist_w = 512, hist_h = 400; int bin_w = cvRound((double) hist_w / histSize); Mat histImage(hist_h, hist_w, CV_8UC3, Scalar(0,0,0)); ``` #### 4. 归一化处理并绘制柱状图 考虑到原始数值可能会非常大而无法直观呈现,因此先对这些值做适当缩放;之后遍历每一个bin位置,按照对应高度填充颜色条带完成整个图表构建[^5]。 ```cpp // Normalize the result to [0, histImage.rows] normalize(hist, hist, 0, histImage.rows, NORM_MINMAX, -1, Mat()); for (int i = 1; i < histSize; ++i) { line(histImage, Point(bin_w*(i-1), hist_h - cvRound(hist.at<float>(i-1))), Point(bin_w*i, hist_h - cvRound(hist.at<float>(i))), Scalar(255, 0, 0), 2, LINE_AA); } imshow("Histogram", histImage); waitKey(); ``` 上述代码片段展示了完整的流程,从准备阶段直到最后的结果输出。值得注意的是,不同应用场景下的细节设定会有所差异,比如窗口大小的选择或是色彩方案的设计都可以灵活调整以满足特定的要求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值