OpenCV 直方图统计函数 cv::calcHist算是彻底弄明白了

文章详细解析了OpenCV中的calcHist函数,用于计算图像的直方图。函数参数包括图像数组、通道选择、直方图尺寸和像素值范围等,支持一维和二维颜色直方图的计算。示例展示了如何统计RGB图像中各通道的一维直方图以及如何构建二维颜色直方图。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参数说明

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 );
  • images 图像数组。每个图像的大小要一致,depth要一致,即数据类型要一致,但通道数可以不一致。
  • nimages 图像数组的大小,即images数组的大小
  • channels 参与计算的各个通道的索引。由于各图像的通道数并不一定一致,并且此函数也不强制要求所有通道都参与计算,因此此参数用于指定要参与计算的图像的通道索引。索引值包含了图像标识,以及图像的通道标识。具体方法为:
    第一个图像的索引标识为 [0,  images[0].channels()), 
    第二个图像的索引标识为 [mages[0].channels(),  mages[0].channels()+mages[1].channels())
    之后以此类推。
    例如:
    图像mages[0]有三个通道,其通道索引为{0, 1, 2};
    图像mages[1] 有两个通道,其通过索引为 {3, 4};
    图像mages[2] 有三个通道,其通过索引为 {5, 6, 7}
  • mask 此函数支持对图像中指定区域进行直方图统计
  • hist 直方图统计结果
  • dims 直方图维度。使用最多的也就是1维、2维,更高维应该很少吧。本文后续再详细说明。
  • histSize 指定各个通道的的直方图中分类数。此函数的设计是,多个像素取值可以划分为同个分类,例如:像素灰度值取值范围是[0, 255],但不一定要分成256类,可以为了8类,就可以通过histSize 这个参数指定。
  • ranges 指定各个通道的像素灰度值的取值范围。也就是说,此函数的设计并不要求各个通道的像素灰度值取值范围一致。例如:可以第一个通道取值是[0, 255], 第二个通道取值是[0, 127]。
    此外,此参数地用于与uniform的取值有关。
    uniform取值为true时,表示将像素取值平均分配为 histSize[i]个区域(此处的i表示,参与计算的第i个通道),此时,ranges[i]数组的大小通道是2,即ranges[i][0] 表示像素值的最小值,ranges[i][1]表示像素取值的最大值。
    uniform取值为false时,表示不平均分配区间。此时,此函数就不知道如何划分区间了,就需要调用者指定。此时,ranges[i]数组的大小为区间个数加1,即histSize[i]+1。

一维颜色直方图

如果要统计一个RGB图像,三个通道的各自的直方图,需要将dims设置为1,并调用三次calcHist函数。

{
    READ_IMG_DEF_ERR_RET(img, "img1.jpg");

    cv::imshow("原始图像", img);


    int channels[1] = { 0 }; 
    int histSize[1] = { 256 };
    float range[2] = { 0, 256 };
    const float* ranges[1] = { range}; // 指定每个通道的取值范围

    // 计算B通道的颜色直方图
    cv::Mat bHist;
    cv::calcHist(&img, 1, channels, cv::Mat(), bHist, 1, histSize, ranges);
    PRINT_IMG_INFO(bHist);
    // 计算G通道的颜色直方图
    cv::Mat gHist;
    cv::calcHist(&img, 1, channels, cv::Mat(), gHist, 1, histSize, ranges);
    PRINT_IMG_INFO(gHist);
    // 计算R通道的颜色直方图
    cv::Mat rHist;
    cv::calcHist(&img, 1, channels, cv::Mat(), rHist, 1, histSize, ranges);
    PRINT_IMG_INFO(rHist);

    cv::Mat bHistImg  = ImgTools->buildHistImg(bHist, 600, 600, cv::Scalar(255, 0, 0));
    cv::imshow("颜色直方图 B", bHistImg);
    cv::Mat gHistImg  = ImgTools->buildHistImg(gHist, 600, 600, cv::Scalar(0, 255, 0));
    cv::imshow("颜色直方图 G", gHistImg);
    cv::Mat rHistImg  = ImgTools->buildHistImg(rHist, 600, 600, cv::Scalar(0, 0, 255));
    cv::imshow("颜色直方图 R", rHistImg);

    cv::waitKey(0);
}

二维颜色直方图

此时需要将dims参数设置为2。

{
    cv::Mat imgs[2];
    imgs[0] = (cv::Mat_<uchar>(2, 4) << 0, 1,  3,  3, 3,  5,  6, 7);
    imgs[1] = (cv::Mat_<uchar>(2, 4) << 8, 9, 10, 11, 11, 13, 14, 15);

    ImgTools->dumpImg(imgs[0], "imags[0]: ");
    ImgTools->dumpImg(imgs[1], "imags[1]: ");

    cv::Mat img;
    cv::merge(imgs, sizeof(imgs) / sizeof(imgs[0]), img);
    ImgTools->dumpImg(img, "imag: ");

    int channels[3] = { 0, 1  }; 
    
    int histSize[3] = { 8, 8 };  
    float rRange[2] = { 0, 8 };
    float gRange[2] = { 8, 16 };
    const float* ranges[2] = { rRange, gRange}; // 指定每个通道的取值范围

    cv::Mat hist;
    cv::calcHist(&img, 1, channels, cv::Mat(), hist, 2, histSize, ranges);
    ImgTools->dumpImg(hist, "hist info:");

}

运行结果如下图所示:

 在代码中,计算两个通道,通过参数histSize指定,每个通道都分成8个分区,因此可能有64种组合结果,因此二维直方图的大小为8*8的矩阵。

在图像中(3,10)这个组合的数据只有一个,对应的分区编号是(3,2),因此二维直方图中(3,2)位置的数据值为1,即上图中绿色标记的位置。
在图像中 (3, 11)这个组合的数据有两个, (3, 11)对应的分区编号是(3,3),因此上图中的红色标记的位置的统计值为2

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值