opencv-calcHist直方图计算

什么是直方图:

直方图是对数据集合的统计

看一个例子:假设有一个矩阵包含一张图像的信息 (灰度值 0-255):数字的范围包含 256个值,我们可以将这个范围分割成子区域(称作 bins), 如:

    然后再统计每一个 bin_{i} 的像素数目,采用这一方法来统计上面的数字矩阵,我们可以得到下图( x轴表示 bin, y轴表示各个bin中的像素个数)

    

   直方图可以统计的不仅仅是颜色灰度, 它可以统计任何图像特征 (如 梯度, 方向等等) 

直方图的一些具体细节: 

1.dims: 需要统计的特征数目, 在上例中, dims = 1 因为我们仅仅统计了灰度值(灰度图像) 

2.bins: 每个特征空间 子区段 的数目,在上例中, bins = 16

3.range: 每个特征空间的取值范围,在上例中, range = [0,255]

怎样去统计两个特征呢? 在这种情况下, 直方图就是3维的了,x轴和y轴分别代表一个特征, z轴是子区段 (bin_{x}, bin_{y}) 组合中的样本数目。 同样的方法适用于更高维的情形 (当然会变得很复杂) 

OpenCV提供了一个简单的计算数组集(通常是图像或分割后的通道)的直方图函数 calcHist 。 支持高达 32 维的直方图

直方图理解

#include<opencv2/opencv.hpp>
#include<iostream>

int main(int argc, char** argv) {

    cv::Mat M(4, 4, CV_8UC1, cv::Scalar(0));
    M.at<uchar>(0, 1) = 1;
    M.at<uchar>(0, 2) = 2;
    M.at<uchar>(0, 3) = 3;
    M.at<uchar>(1, 0) = 4;
    M.at<uchar>(1, 1) = 5;
    M.at<uchar>(1, 2) = 6;
    M.at<uchar>(1, 3) = 7;
    M.at<uchar>(2, 0) = 8;
    M.at<uchar>(2, 1) = 9;
    M.at<uchar>(2, 2) = 10;
    M.at<uchar>(2, 3) = 11;
    M.at<uchar>(3, 0) = 8;
    M.at<uchar>(3, 1) = 9;
    M.at<uchar>(3, 2) = 14;
    M.at<uchar>(3, 3) = 15;

    std::cerr << "M=" << std::endl << M << std::endl << std::endl;

    cv::Mat hist; //保存直方图
    int bins = 4;  //设定bins数目

    float range[] = { 0, 16 };//设定取值范围
    //注意:取值范围[0,16)
    //说明:把[0,16)分成4个区段:[0,3],[4,7],[8,11],[12,16)
    const float* histRange = { range };

    calcHist(&M, 1, 0, cv::Mat(), hist, 1, &bins, &histRange, true, false);
    std::cerr << "hist=" << std::endl << hist << std::endl << std::endl;  //输出直方图


    cv::waitKey(0);
    return 0;
}

 

 在[0,3]区域有4个,在[4,7]区域有4个,在[8,11]区域有6个,在[12,15]区域有2个

 

实例源码 

tiantan.png

 

#include<opencv2/opencv.hpp>
#include<iostream>


int main(int argc, char** argv) {

    cv::Mat src = cv::imread("D:/bb/tu/tiantan.png");//原图
    std::vector<cv::Mat> rgb_planes;
    split(src, rgb_planes);//分割成3个单通道图像 ( B, G 和 R )
    int histSize = 256;  //设定bins数目
    float range[] = { 0, 255 };//设定取值范围,【 [0,255)  】 
    const float* histRange = { range };

    bool uniform = true; bool accumulate = false;

    cv::Mat r_hist, g_hist, b_hist;

    // 计算直方图:
    calcHist(&rgb_planes[0], 1, 0, cv::Mat(), b_hist, 1, &histSize, &histRange, uniform, accumulate);
    /*
    参数1:Mat* images:输入的图像或数组,它们的深度必须为CV_8U, CV_16U或CV_32F中的一类,尺寸必须相同
    参数2:输入数组个数,也就是第一个参数中存放了几张图像,有几个原数组
    参数3:需要统计的通道channels索引,会按照索引顺序依次计算图像直方图(注意,第一张图片索引为 0,以此类推)
      双通道计算实例看:https://www.cnblogs.com/liming19680104/p/15808731.html 参数4:mask: 可选的操作掩码。如果此掩码不为空,那么它必须为8位并且尺寸要和输入图像images[i]一致。非 零掩码用于标记出统计直方图的数组元素数据 参数5:输出的目标直方图,一个二维数组【bins行1列bins行,float类型】 参数6:dims: 需要计算直方图的维度(特征数目),必须是正数且并不大于CV_MAX_DIMS(在opencv中等于32)
      【需要计算的通道数目】 参数7:histSize: 子区段的数目bins,x 轴将被分成 histSize 份 参数8:每个维度中bin的取值范围(range),x的取值范围 参数9:uniform: 直方图是否均匀的标识符,有默认值true 参数10:accumulate: 累积标识符,有默认值false,若为true,直方图再分配阶段不会清零。此功能主要是允许 从多个阵列中计算单个直方图或者用于再特定的时间更新直方图
*/ calcHist(&rgb_planes[1], 1, 0, cv::Mat(), g_hist, 1, &histSize, &histRange, uniform, accumulate); calcHist(&rgb_planes[2], 1, 0, cv::Mat(), r_hist, 1, &histSize, &histRange, uniform, accumulate); // 创建直方图画布 int hist_w = 512; int hist_h = 400; int bin_w = cvRound(hist_w / histSize); //横坐标间隔--->每个bin的宽度 cv::Mat histImage(hist_w, hist_h, CV_8UC3, cv::Scalar(0, 0, 0)); // 将直方图归一化到范围 [ 0, histImage.rows ] normalize(r_hist, r_hist, 0, histImage.rows, cv::NORM_MINMAX, -1, cv::Mat()); normalize(g_hist, g_hist, 0, histImage.rows, cv::NORM_MINMAX, -1, cv::Mat()); normalize(b_hist, b_hist, 0, histImage.rows, cv::NORM_MINMAX, -1, cv::Mat()); // 在直方图画布上画出直方图 for (int i = 1; i < histSize; i++) { line(histImage, cv::Point(bin_w * (i - 1), hist_h - cvRound(r_hist.at<float>(i - 1))), cv::Point(bin_w * (i), hist_h - cvRound(r_hist.at<float>(i))), cv::Scalar(0, 0, 255), 2, 8, 0); /* y坐标用hist_h - 原因:y的0坐标在上面,这样数据小的人眼看起来在上头;为了数据小的看起来 在下面,所以用hist_h - */ line(histImage, cv::Point((i - 1) * bin_w, hist_h - cvRound(b_hist.at<float>(i - 1))), cv::Point((i)*bin_w, hist_h - cvRound(b_hist.at<float>(i))), cv::Scalar(255, 0, 0), 2, cv::LINE_AA); line(histImage, cv::Point((i - 1) * bin_w, hist_h - cvRound(g_hist.at<float>(i - 1))), cv::Point((i)*bin_w, hist_h - cvRound(g_hist.at<float>(i))), cv::Scalar(0, 255, 0), 2, cv::LINE_AA); } imshow("直方图", histImage); cv::waitKey(0); return 0; }

 

  

 

posted @ 2022-01-14 16:08  天子骄龙  阅读(157)  评论(0编辑  收藏  举报