OpenCV直方图(直方图、直方图均衡,直方图匹配,原理、实现)

本文详细介绍了OpenCV中如何进行图像直方图的统计,包括灰度直方图、H-S直方图以及非均匀直方图的计算。接着讨论了直方图均衡化,这是一种通过改变图像灰度分布来增强图像细节的方法。最后探讨了直方图匹配技术,可以将图像转换为具有指定直方图形状。示例代码展示了具体实现过程。

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

1 直方图

灰度级范围为 [0,L1] 的数字图像的直方图是离散函数 h(rk)=nk , 其中 rk 是第k级灰度值,nk 是图像中灰度为 rk 的像素个数。在实践中,经常用乘积 MN 表示的图像像素的总数除它的每个分量来归一化直方图,通常 MN 是图像的行和列的位数。因此,归一化后的直方图由 p(rk)=nk/MN 给出,其中 k=0,1,...,L1 。简单地说, p(rk) 是灰度级 rk 在图像中出现的概率的一个估计。归一化直方图的所有分量之和应等于1。

在OPENCV3.0中有相关函数如下,其中有3个版本,这是经常用到的一个:

CV_EXPORTS 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, 是要求的Mat的指针,这里可以传递一个数组,可以同时求很多幅图片的直方图,前提是他们的深度相同,CV_8U或者CV_32F,尺寸相同。通道数可以不同;

nimages, 源图像个数;

channels, 传递要加入直方图计算的通道。该函数可以求多个通道的直方图。通道序号从0开始依次递增。假如第一幅图像有3个通道,第二幅图像有两个通道。则:images[0]的通道序号为0、1、2,images[1]的通道序号则为3、4;如果想通过5个通道计算直方图,则传递的通道channels为int channels[5] = {0, 1, 2, 3, 4, 5}。

mask, 掩码矩阵,没有掩码,则传递空矩阵就行了。如果非空则掩码矩阵大小必须和图像大小相同,在掩码矩阵中非空元素将被计算到直方图内。

hist, 输出直方图;

dims, 直方图维度,必须大于0,并小于CV_MAX_DIMS(32);

histSize, 直方图中每个维度级别数量,比如灰度值(0-255),如果级别数量为4,则灰度值直方图会按照[0, 63],[64,127,[128,191],[192,255],也称为bin数目,这里是4个bin。如果是多维的就需要传递多个。每个维度的大小用一个int来表示。所以histSize是一个数组;

ranges, 一个维度中的每一个bin的取值范围。如果uniform == true,则range可以用一个具有2个元素(一个最小值和一个最大值)的数组表示。如果uniform == false,则需要用一个具有histSize + 1个元素(每相邻的两个元素组成的取值空间对应的bin的取值范围)的数组表示。如果统计多个维度则需要传递多个数组。所以ranges,是一个二维数组。如下代码是uniform == false时的情况:

int nHistSize[] = { 5 };
// range有6个元素,每个元素,组成5个bin的取值范围
float range[] = { 0, 70,100, 120, 200,255 };
const float* fHistRanges[] = { range };
Mat histR, histG, histB;
// 这里的uniform == false
calcHist(&matRGB[1], 1, &nChannels, Mat(), histB, 1, nHistSize, fHistRanges, false, false);

uniform, 表示直方图中一个维度中的各个bin的宽度是否相同,详细解释见ranges中介绍;

accumulate, 在计算直方图时是否清空传入的hist。true,则表示不清空,false表示清空。该参数一般设置为false。只有在想要统计多个图像序列中的累加直方图时才会设置为true。例如:

calcHist(mat1, 1, &nChannels, Mat(), hist, 1, nHistSize, fHistRanges, true, false);
calcHist(mat2, 1, &nChannels, Mat(), hist, 1, nHistSize, fHistRanges, true, true);

以上代码统计了mat1和mat2中图像数据的直方图到hist中。也就是说hist中的直方图数据是从matRGB1和mat2中统计出来的,不单单是mat1的数据,也不单单是mat2的数据。如果第二行中最后一个参数为false则hist中统计的数据单单是mat2的,mat1的数据被清空了。

1.1 统计灰度直方图
#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <string>

using namespace cv;

int main()
{
  std::string strPath = "D:\\MyDocuments\\My Pictures\\OpenCV\\";
    Mat matSrc = imread(strPath + "panda.jpg");

    Mat matRGB[3];
    split(matSrc, matRGB);
    int Channels[] = { 0 };
    int nHistSize[] = { 256 };
    float range[] = { 0, 255 };
    const float* fHistRanges[] = { range };
    Mat histR, histG, histB;
    // 计算直方图
    calcHist(&matRGB[0], 1, Channels, Mat(), histB, 1, nHistSize, fHistRanges, true, false);
    calcHist(&matRGB[1], 1, Channels, Mat(), histG, 1, nHistSize, fHistRanges, true, false);
    calcHist(&matRGB[2], 1, Channels, Mat(), histR, 1, nHistSize, fHistRanges, true, false);

    // 创建直方图画布
    int nHistWidth = 800;
    int nHistHeight = 600;
    int nBinWidth = cvRound((double)nHistWidth / nHistSize[0]);
    Mat matHistImage(nHistHeight, nHistWidth, CV_8UC3, Scalar(255, 255, 255));
    // 直方图归一化
    normalize(histB, histB, 0.0, matHistImage.rows, NORM_MINMAX, -1, Mat());
    normalize(histG, histG, 0.0, matHistImage.rows, NORM_MINMAX, -1, Mat());
    normalize(histR, histR, 0.0, matHistImage.rows, NORM_MINMAX, -1, Mat());
    // 在直方图中画出直方图
    for (int i = 1; i < nHistSize[0]; i++)
    {
        line(matHistImage,
            Point(nBinWidth * (i - 1), nHistHeight - cvRound(histB.at<float>(i - 1))),
            Point(nBinWidth * (i), nHistHeight - cvRound(histB.at<float>(i))),
            Scalar(255, 0, 0),
            2,
            8,
            0);
        line(matHistImage,
            Point(nBinWidth * (i - 1), nHistHeight - cvRound(histG.at<float>(i - 1))),
            Point(nBinWidth * (i), nHistHeight - cvRound(histG.at<float>(i))),
            Scalar(0, 255, 0),
            2,
            8,
            0);
        line(matHistImage,
            Point(nBinWidth * (i - 1), nHistHeight - cvRound(histR.at&
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值