OpenCV—calcHist()函数

发布于:2024-11-04 ⋅ 阅读:(140) ⋅ 点赞:(0)
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

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

//读取一张图像
    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份数据的边界。

//读取一张图像
    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 时:

//计算直方图
    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时:

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维数据。

//读取一张图像
    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,仍然是总的像素数量。这些数据太大了,为了用图像显示,将数据处理一下:

//数据太大了,处理一下
    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显示能直观的看出像素分布状况。更高维度的计算结果,不知该如何以直观的方式显示了。