Ubuntu系统VScode实现opencv(c++)图像一维直方图

发布于:2025-08-05 ⋅ 阅读:(12) ⋅ 点赞:(0)

前言

在数字图像处理领域,直方图是一种极为重要的工具,而图像的一维直方图更是其中的基础与核心。一维直方图以简洁而直观的方式,将图像中像素的灰度值分布情况呈现出来,让我们能够快速了解图像的整体亮度、对比度等关键信息。通过对一维直方图的分析,我们可以轻松判断图像是否过亮或过暗,对比度是否合适,进而为后续的图像增强、分割、特征提取等一系列操作提供有力的依据。

直方图概念

实际上,一维直方图就是不同像素值出现频率的统计。如下图:

一维直方图一般是将多通道拆分为单通道,然后统计对应单通道像素出现频率,对于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);   // 按任意键关闭窗口
}

运行结果: 


网站公告

今日签到

点亮在社区的每一天
去签到