OpenCV—calcHist()函数

void calcHist( const Mat* images, int nimages,
                          const int* channels, InputArray mask,
                          SparseMat& hist, int dims,
                          const int* histSize, const float** ranges,
                          bool uniform = true, bool accumulate = false );

images

输入的数据指针,要具备相同的尺寸和数据类型,可以有不同的通道数量,如果要计算n 个Mat数据,可以这么做:Mat images[n];images[0]=imread(),images[1]=imread()......

nimages

就是上面的n,指明传入了多少个Mat数据

channels

通道列表,看看下面这张图,传入了这样2个Mat数据,每个Mat各自包含3个通道,通道的索引依次是0,1,2,3,4,5。const int channels[3]={0,2,4},表示索引序号是0,2,4的三个通道会参与计算。

mask

表示掩码,与images尺寸相同的8bits 数组,传入一个空Mat即可。如果不是空的, 所有掩码非零数据对应的源数据才会参与计算。

hist

表示计算结果,用一个Mat保存便可。1维的结果是一个n行1列的矩阵,2维的结果是个n×m的 矩阵......,具体还要看下面这个维度参数。

dims

结果数据的维数,1维、2维.....32维,要跟其他参数结合起来。

histSize

是一个列表,对应每个维度的数据分组。如果dims=1,结果是1维的,这里可以这么做: const int histSize[1]={32},表示1维被划分成32份。如果dims=2,结果是2维的,可以这么 做:const int histSize[2]={32,64},表示1维被划分成32份,2维被划分成64份。

ranges

是数组的数组,数组中的每个元素是一个指针,这个指针指向一个数组。举例说明如下:

cpp 复制代码
//读取一张图像
    Mat image[1];
    image[0] = imread("../Image/apple.jpg");                 

    //取图像的0通道数据
    const int channels[1] = {0};
    
    //最终结果会有8行1列
    const int histSize[1] = {8};
    
    //参与计算的数据范围[0,256),也就是uchar类型的全部数据
    //如果范围是[m,n),范围外的数据不会参与计算
    const float range[2] = {0, 256};
    const float *ranges[1] = {range};

    //计算直方图
    Mat hist;
    calcHist(image, 1, channels, Mat(), hist, 1, histSize, ranges);

    //输出直方图
cout << hist << endl;

输出结果是:[7281; 137833; 90340; 25579; 990; 80; 31; 10]

7281+ 137833+90340+25579+990+80+31+ 10=262144=512×512,这些数量加起来就是图片所有的像素。

如果把const float range[2] = {0, 256};改成const float range[2] = {50, 200};

结果变成了[95967; 51565; 39066; 6193; 961; 294; 51; 26]

95967+51565+39066+ 6193+ 961+294+ 51+ 26=194123<512×512,说明{50, 200}范围外的一些像素没参与计算。

Uniform

这个参数表示是否均匀。也就是const float range[2] = {0, 256}这个范围是否被均匀分成8份

const int histSize[1] = {8})。如果不是均匀的,就应该这样const float range[9] =

{0,50,75,100,120,160,200,220,256},用9个数表示8份数据的边界。

cpp 复制代码
//读取一张图像
    Mat image[1];
    image[0] = imread("../Image/apple.jpg");

    //取图像的0通道数据
    const int channels[1] = {0};

    //最终结果会有8行1列
    const int histSize[1] = {8};

    //参与计算的数据范围[0,256),也就是uchar类型的全部数据
    //如果范围是[m,n),范围外的数据不会参与计算
    const float range[9] = {0,50,75,100,120,160,200,220,256};
    const float *ranges[1] = {range};

    //计算直方图
    Mat hist;
    calcHist(image, 1, channels, Mat(), hist, 1, histSize, ranges, false);

    //输出直方图
    cout << hist << endl;

结果是这样[67991; 113817; 62353; 15946; 1916; 91; 14; 16]

Accumulate

表示是否累积计算,如果true,就不会清空hist,会把上次结果累积到下次。

当Accumulate=flase 时:

cpp 复制代码
//计算直方图
    Mat hist;
    calcHist(image, 1, channels, Mat(), hist, 1, histSize, ranges, true, false);
    //输出直方图
    cout << hist << endl;
    calcHist(image, 1, channels, Mat(), hist, 1, histSize, ranges, true, false);
    //输出直方图
    cout << hist << endl;

两次输出结果是:

[7281; 137833; 90340; 25579; 990; 80; 31; 10]

[7281; 137833; 90340; 25579; 990; 80; 31; 10]

当Accumulate=true时:

cpp 复制代码
Accumulate=true
//计算直方图
Mat hist;
calcHist(image, 1, channels, Mat(), hist, 1, histSize, ranges, true, false);
//输出直方图
cout << hist << endl;
calcHist(image, 1, channels, Mat(), hist, 1, histSize, ranges, true, false);
//输出直方图
cout << hist << endl;

结果变成了这样:

[7281; 137833; 90340; 25579; 990; 80; 31; 10]

[14562; 275666; 180680; 51158; 1980; 160; 62; 20]

也就是下次的结果在上次结果基础上累积。

2维数据

上面都是1维数据的情况,看一下2维数据。

cpp 复制代码
//读取一张图像
    Mat image[1];
    image[0] = imread("../Image/apple.jpg");

    //取图像的0、1通道数据
    const int channels[2] = {0, 1};

    //最终结果会有8行6列
    const int histSize[2] = {8, 6};

    //参与计算的数据范围[0,256),也就是uchar类型的全部数据
    //如果范围是[m,n),范围外的数据不会参与计算
    const float range0[2] = {0, 256};
    const float range1[2] = {0, 256};
    const float *ranges[2] = {range0, range1};

    //计算直方图
    Mat hist;
    calcHist(image, 1, channels, Mat(), hist, 2, histSize, ranges);
    //输出直方图
cout << hist << endl;

结果就是这个*++const++* ++int histSize[2] = {8, 6}++维度数据:

[1309, 1219, 3754, 762, 237, 0;

484, 30587, 94245, 11228, 997, 292;

0, 2518, 33548, 45328, 8108, 838;

0, 0, 676, 14186, 10072, 645;

0, 0, 0, 399, 155, 436;

0, 0, 0, 0, 26, 54;

0, 0, 0, 0, 3, 28;

0, 0, 0, 0, 0, 10]

这些数据加起来还是521×512=262144,仍然是总的像素数量。这些数据太大了,为了用图像显示,将数据处理一下:

cpp 复制代码
//数据太大了,处理一下
    hist += 1;
    log(hist, hist);
    Mat dst;
    normalize(hist, dst, 0, 255, NORM_MINMAX);

    Mat convert;
    dst.convertTo(convert, CV_8UC1);

    //输出直方图
    cout << convert << endl;


    namedWindow("convert", WINDOW_NORMAL);
    imshow("convert", convert);

用图像表示就是上图,channel[0]被划分成8份,channel[1]被划分成6份,然后对image[0]中每个像素进行统计。比如位置(i,j)对应的0通道数据属于上图的第3行,1通道数据属于上图的5通道,那么上图中的格子(3,5)就会计数加1。

处理之后的数据已经无法反应像素的准确数量,但是像素分布状况还是正确的

[160, 158, 183, 148, 122, 0;

138, 230, 255, 208, 154, 126;

0, 174, 232, 239, 200, 150;

0, 0, 145, 213, 205, 144;

0, 0, 0, 133, 112, 135;

0, 0, 0, 0, 73, 89;

0, 0, 0, 0, 31, 75;

0, 0, 0, 0, 0, 53]

上面的数据用3D显示能直观的看出像素分布状况。更高维度的计算结果,不知该如何以直观的方式显示了。

相关推荐
小码贾16 分钟前
评估 机器学习 回归模型 的性能和准确度
人工智能·机器学习·回归·scikit-learn·性能评估
不是AI16 分钟前
【持续更新】【NLP项目】【自然语言处理】智能聊天机器人——“有问必答”【Chatbot】第2章、《模式一:问候模式》
人工智能·自然语言处理·机器人
YRr YRr34 分钟前
深度学习:Transformer 详解
人工智能·深度学习·transformer
UCloud_TShare1 小时前
融合虚拟化与容器技术,打造灵活又安全的AI算力服务
人工智能·安全
决战春招1 小时前
人工智能之人脸识别(人脸采集人脸识别)
人工智能·opencv·学习·计算机视觉
皓7412 小时前
敏捷开发新助力:超越传统的10大知识库工具
运维·网络·人工智能·安全·零售·敏捷流程
知来者逆2 小时前
使用 GPT-4V 全面评估泛化情绪识别 (GER)
人工智能·gpt·语言模型·自然语言处理·gpt-4v
深度学习实战训练营2 小时前
roberta融合模型创新中文新闻文本标题分类
人工智能·深度学习
angleboy83 小时前
【LLM Agents体验 1】Dify框架的安装指南
人工智能·语言模型·大模型·nlp