Couting the Pixels with Histograms

来源:互联网 发布:linux设置开机启动程序 编辑:程序博客网 时间:2024/05/16 01:42

The distribution of pixels value across the image constitutes an important characteristic of this image. that's the concept discript as histograms.A histogram is a simple table that gives the numbeer of pixes that have a give value in an images or sometimes a set of image. 也就是一张图片中每个颜色的像素有多少的统计表。 比如gray-level. will have 256 entries (or bins),Bin 0 give the number of pixel having value 0, bin 1 number of pixes have value1 and so on. Obvisously, if you sum all of these bins of a histogram, you should get the total number of pixels. Histograms can also be normalized such that sum of the bins equals 1, each bin give the percentage of pixels haveing specific value in the image.



Calculates a histogram of a set of arrays.

C++: voidcalcHist(const Mat*images, intnimages, const int*channels, InputArraymask, OutputArrayhist, intdims, const int*histSize, const float**ranges, booluniform=true, boolaccumulate=false )

C++: voidcalcHist(const Mat*images, intnimages, const int*channels, InputArraymask, SparseMat&hist, intdims, const int*histSize, const float**ranges, booluniform=true, boolaccumulate=false )
  • images – Source arrays. They all should have the same depth,CV_8U orCV_32F , and the same size. Each of them can have an arbitrary number of channels.
  • nimages – Number of source images.

     上面两个参数很好理解因为数组参数我们是不知道size 的,所以传递 images 要加上一个 nimages 说明有多少个image, *image++ till to *image[nimages-1]

  • channels – List of the dims channels used to compute the histogram. The first array channels are numerated from 0 toimages[0].channels()-1 , the second array channels are counted fromimages[0].channels() toimages[0].channels() + images[1].channels()-1, and so on.

      Mat 本身就有channels()函数可以获得自己有多少个channel的,所以这个参数是用来指示我们用那些channels 去计算histogram, ie int chnels[]={0,1}; means we calculate each image's 0th and 1st channel, if

int channels[]={1,2};will calculate channles[1] and channels[2]


  • mask – Optional mask. If the matrix is not empty,it must be an 8-bit array of the same size asimages[i] . The non-zero mask elements mark the array elements counted in the histogram.  (need to do a experiment to get understand it ,so let it pass first)


  • hist – Output histogram, which is a dense or sparsedims -dimensional array.
  • dims – Histogram dimensionality that must be positive and not greater thanCV_MAX_DIMS (equal to 32 in the current OpenCV version).只计算一个channel 就是 1D, 如果是2个channel就是2D ,

  

  • histSize – Array of histogram sizes in each dimension. 比如我们计算HSV H,S 两个channel,H channel 最大去到30也就是Bins (Bar)  数目是30, but S channel can till to 32 Bins.  我们想象MAT 里面看到直方图,那里是gray -level 的,也就是一个channel 的,把这个想个一个unit,1 channel on unit,他的水平轴就是hitsSize 的每个值。

      


  • ranges – Array of the dims arrays of the histogram bin boundaries in each dimension. When the histogram is uniform (uniform =true), then for each dimensioni it is enough to specify the lower (inclusive) boundaryL_0 of the 0-th histogram bin and the upper (exclusive) boundaryU_{\texttt{histSize}[i]-1} for the last histogram binhistSize[i]-1 . That is, in case of a uniform histogram each ofranges[i] is an array of 2 elements. When the histogram is not uniform (uniform=false ), then each ofranges[i] containshistSize[i]+1 elements:L_0, U_0=L_1, U_1=L_2, ..., U_{\texttt{histSize[i]}-2}=L_{\texttt{histSize[i]}-1}, U_{\texttt{histSize[i]}-1} . The array elements, that are not between L_0 andU_{\texttt{histSize[i]}-1} , are not counted in the histogram.

      ranges[0] to rangs[dims-1] 也就是每一个channel 都有自己的范围,比如H is from 0 -179 ,S from 0-255 . 因此

     ranges[i] 的每一个元素也是一个数组,一个二维数组,指明每一个channel 的范围。



  • uniform – Flag indicating whether the histogram is uniform or not (see above).
  • accumulate – Accumulation flag. If it is set, the histogram is not cleared in the beginning when it is allocated. This feature enables you to compute a single histogram from several sets of arrays, or to update the histogram in time.

#include <cv.h>#include <highgui.h>using namespace cv;int main( int argc, char** argv ){    Mat src, hsv;    if( argc != 2 || !(src=imread(argv[1], 1)).data )        return -1;    cvtColor(src, hsv, CV_BGR2HSV);    // Quantize the hue to 30 levels    // and the saturation to 32 levels    int hbins = 30, sbins = 32;    int histSize[] = {hbins, sbins};    // hue varies from 0 to 179, see cvtColor    float hranges[] = { 0, 180 };//Note need to plus +1 for the max rangs    // saturation varies from 0 (black-gray-white) to    // 255 (pure spectrum color)    float sranges[] = { 0, 256 };    const float* ranges[] = { hranges, sranges };    MatND hist;    // we compute the histogram from the 0-th and 1-st channels    int channels[] = {0, 1};显然这里30 个 Bin 去表达180 个value的intensity. ie:Bin0 就是0-5 的H value. calcHist( &hsv, 1, channels, Mat(), // do not use mask             hist, 2, histSize, ranges,             true, // the histogram is uniform             false );    double maxVal=0;    minMaxLoc(hist, 0, &maxVal, 0, 0);    int scale = 10;    Mat histImg = Mat::zeros(sbins*scale, hbins*10, CV_8UC3);    for( int h = 0; h < hbins; h++ )        for( int s = 0; s < sbins; s++ )        {            float binVal = hist.at<float>(h, s); //Note that the value stored as float in the histogram            int intensity = cvRound(binVal*255/maxVal);            rectangle( histImg, Point(h*scale, s*scale),                        Point( (h+1)*scale - 1, (s+1)*scale - 1),                        Scalar::all(intensity),                        CV_FILLED );        }    namedWindow( "Source", 1 );    imshow( "Source", src );    namedWindow( "H-S Histogram", 1 );    imshow( "H-S Histogram", histImg );    waitKey();} 



=======My Test version

#include "openCV.h"
int main()

{
    const string imagePath="c://image//test.png";
    cv::Mat image(3,4,CV_8UC3,Scalar(50,100,150));
    const string sourceImageWindowName="Source Image";
//    namedWindow(sourceImageWindowName,CV_WINDOW_AUTOSIZE);
     imshow(sourceImageWindowName,image);
//    imwrite(imagePath,image);


    int channels[3]={0,1,2};
    int hSize[3]={256,256,256};
    float range[2]={0,255};
    const float * hRange[3]={range,range,range};

    MatND hist;
    calcHist(&image,1,channels,Mat(),hist,2,hSize,hRange,true,false);
    cout<<hist.rows<<" "<<hist.cols<<endl;
    
    for(int r=0;r<hist.rows;++r)
    {
       for(int c=0;c<hist.cols;++c)
       {
           float histValue=hist.at<float>(r,c);
           if(histValue>0)
           cout<<r<<" "<<c<<" "<< histValue<<endl;
       }
    
    }

//可以看到打印出来的结果:50 100 12,也就是 channel[0]=50 &channel[1]=100 的pixel 有12 个,

2 demension Mat 有256*256 ,calcHist will calculate each possible 组合,然后算出这些组合有多少个像素。 注意我用的是像素而不是channel,calcHist 不是bye channel去计算,比如channel[0] 0-255 每一个value有多少个,从而得到

256*2 的Mat 结果,,之前的这个想法是错的。 同理3 dimension will calculate 256*256*256 个组合所有的像素个数

所以会得出很多0 , as follow show:

    MatND hist3D;
    calcHist(&image,1,channels,Mat(),hist3D,3,hSize,hRange,true,false);

    cout<<"total:"<<hist3D.total()<<endl;
    float histValu=hist3D.at<float>(50,100,150);// get 50,100,150出的value,因为我理解这个计算,So I do it jus at here.
    cout<<"hValue"<<histValu<<endl; //print 12

//because will generate lot's of zero, that will wast memroy. we use sparseMat

    SparseMat sparsemat(3,hSize,CV_32FC1);
    calcHist(&image,1,channels,Mat(),sparsemat,3,hSize,hRange);
    cout<<"sparseMat size"<<sparsemat.size()[0]<<" "<<sparsemat.size()[1]<<" "<< sparsemat.size()[2]<<endl;
    //ptrdiff_t pDiff;.

    int dims=sparsemat.dims();
    SparseMatConstIterator
        it=sparsemat.begin(),
        itEnd=sparsemat.end();
   for(;it!=itEnd;++it)
   {
      const SparseMat::Node * node=it.node();
       cout<<"(";
       for(size_t i=0;i<dims;i++)
           cout<<node->idx[i]<<",";
       cout<<")";
       cout<<it.value<float>()<<endl;
  ///   (50,100,150,)12,虽然SparseMat 不存0,但他的order 不边,so 12 will store at 50 100 150
      // cout<<(*it)<<endl;
   }
        


    waitKey(0);
return 0;
}

==========print result=======

256 256
50 100 12
total:16777216
hValue12
sparseMat size256 256 256
(50,100,150,)12



=========SparseMat study==============

The class SparseMat represents multi-dimensional sparse numerical arrays. Such a sparse array can store elements of any type thatMat can store. Sparse means that only non-zero elements are stored (though, as a result of operations on a sparse matrix, some of its stored elements can actually become 0. It is up to you to detect such elements and delete them using SparseMat::erase ). The non-zero elements are stored in a hash table that grows when it is filled so that the search time is O(1) in average (regardless of whether element is there or not). Elements can be accessed using the following methods:

  • Query operations (SparseMat::ptr and the higher-levelSparseMat::ref,SparseMat::value andSparseMat::find), for example:

    const int dims = 5;int size[] = {10, 10, 10, 10, 10};SparseMat sparse_mat(dims, size, CV_32F);//5 维,每一维是10个元素,so will have 10^5 elementfor(int i = 0; i < 1000; i++){    int idx[dims];    for(int k = 0; k < dims; k++)        idx[k] = rand()
  •         //idx 其实就相当于这个5维空间的坐标拉,,,
  •  sparse_mat.ref<float>(idx) += 1.f; //assign value to this point}
  • Sparse matrix iterators. They are similar toMatIterator but different fromNAryMatIterator. That is, the iteration loop is familiar to STL users:

    // prints elements of a sparse floating-point matrix// and the sum of elements.SparseMatConstIterator_<float>    it = sparse_mat.begin<float>(),    it_end = sparse_mat.end<float>();double s = 0;int dims = sparse_mat.dims();for(; it != it_end; ++it){    // print element indices and the element value    const SparseMat::Node* n = it.node();    printf("(");    for(int i = 0; i < dims; i++)        printf("%d%s", n->idx[i], i < dims-1 ? ", " : ")");    printf(": %g\n", it.value<float>());  //打印非零元素的坐标and its value    s += *it;}printf("Element sum is %g\n", s);

    If you run this loop, you will notice that elements are not enumerated in a logical order (lexicographical, and so on). They come in the same order as they are stored in the hash table (semi-randomly). You may collect pointers to the nodes and sort them to get the proper ordering. Note, however, that pointers to the nodes may become invalid when you add more elements to the matrix. This may happen due to possible buffer reallocation.

  • Combination of the above 2 methods when you need to process 2 or more sparse matrices simultaneously. For example, this is how you can compute unnormalized cross-correlation of the 2 floating-point sparse matrices:

    double cross_corr(const SparseMat& a, const SparseMat& b){    const SparseMat *_a = &a, *_b = &b;    // if b contains less elements than a,    // it is faster to iterate through b    if(_a->nzcount() > _b->nzcount())        std::swap(_a, _b);    SparseMatConstIterator_<float> it = _a->begin<float>(),                                   it_end = _a->end<float>();    double ccorr = 0;    for(; it != it_end; ++it)    {        // take the next element from the first matrix        float avalue = *it;        const Node* anode = it.node();        // and try to find an element with the same index in the second matrix.        // since the hash value depends only on the element index,        // reuse the hash value stored in the node        float bvalue = _b->value<float>(anode->idx,&anode->hashval);        ccorr += avalue*bvalue;    }    return ccorr;}

Applying look-up tables to modify   image appearance


 look-up table is a  simple mapping function, to modify the pixel values of an image.

其实就是一个Array, 定义了all pixel value will map to what new value. ie. for 2 D image. we deine a

Mat (256*256),each element represent a pixel, ie (50,100)=(new value),means color channel 0,1 at value(50,100), will change to new value. 显然each pixel's position 没考虑,just focus on the pixel value itself.


A look-up table is a simple one-to-one (or many-to-one) function that defines how pixel values
are transformed into new values.




// Create an image inversion table

int dim(256);
cv::Mat lut(1, // 1 dimension
&dim, // 256 entries
CV_8U); // uchar
for (int i=0; i<256; i++) {
lut.at<uchar>(i)= 255-i;
}


  Mat result;
       cv::LUT(image,lut,result);
  cout<<result<<endl;


(50,100,150,)12 , all the 50 100 150 change to[ 205 155 105 ] ,虽然只是定义了一个1 dimesional 的mapping table,

but all the channel on the image will apply on the LUT


[205, 155, 105, 205, 155, 105, 205, 155, 105, 205, 155, 105;
  205, 155, 105, 205, 155, 105, 205, 155, 105, 205, 155, 105;
  205, 155, 105, 205, 155, 105, 205, 155, 105, 205, 155, 105]



You can also define a look-up table that tries to improve an image's contrast.

For example, if  you observe the original histogram of the previous image shown in the first recipe, it is easy
to notice that the full range of possible intensity values is not used (in particular, for this
image, the brighter intensity values are not used in the image).
One can therefore stretch the
histogram in order to produce an image with an expanded contrast.


The procedure is designed  to detect the lowest (imin) and the highest (imax) intensity value with non-zero count in the   image histogram.(找到最小于最大像素 (数量)) The intensity values can then be remapped such that the imin value is   repositioned at intensity 0, and the imax is assigned value 255. The in-between intensities i
are simply linearly remapped as follows:
255.0*(i-imin)/(imax-imin)+0.5);


cv::Mat stretch(const cv::Mat &image, int minValue=0) {
// Compute histogram first
cv::MatND hist= getHistogram(image);
// find left extremity of the histogram
int imin= 0;
for( ; imin < histSize[0]; imin++ ) {
std::cout<<hist.at<float>(imin)<<std::endl;
if (hist.at<float>(imin) > minValue)
break;

}
// find right extremity of the histogram
int imax= histSize[0]-1;
for( ; imax >= 0; imax-- ) {
if (hist.at<float>(imax) > minValue)
break;
}
// Create lookup table
int dim(256);
cv::Mat lookup(1, // 1 dimension
&dim, // 256 entries
CV_8U); // uchar
// Build lookup table
for (int i=0; i<256; i++) {
// stretch between imin and imax
if (i < imin) lookup.at<uchar>(i)= 0;
else if (i > imax) lookup.at<uchar>(i)= 255;
// linear mapping
else lookup.at<uchar>(i)= static_cast<uchar>(
255.0*(i-imin)/(imax-imin)+0.5);
}
// Apply lookup table
cv::Mat result;
result= applyLookUp(image,lookup);
return result;
}


他的算法思想其实是这样, 在histogram image 上给定一个intensity value ie=100, then y=100  f(x1)=100. f(x2)=100

那么在[x1  x2] region curve, 进行线性拉伸到[0-255]



// ignore starting and ending bins with less than 100 pixels
cv::Mat streteched= h.stretch(image,100);// 



Equalizing the image histogram

In  fact, one can think that a good-quality image should make equal use of all available pixel
intensities. This is the idea behind the concept of histogram equalization, that is making the
image histogram as flat as possible
.


equalizeHist

Equalizes the histogram of a grayscale image.

C++: void equalizeHist(InputArray src, OutputArray dst)
C: void cvEqualizeHist(const CvArr* src, CvArr* dst)
    Parameters:    

        src – Source 8-bit single channel image.
        dst – Destination image of the same size and type as src .

The function equalizes the histogram of the input image using the following algorithm:

    Calculate the histogram H for src .

    Normalize the histogram so that the sum of histogram bins is 255.

    Compute the integral of the histogram:

    H'_i = \sum _{0 \le j < i} H(j)

    Transform the image using H' as a look-up table: \texttt{dst}(x,y) = H'(\texttt{src}(x,y))

The algorithm normalizes the brightness and increases the contrast of the image.













GRAY2BGR 时候,所有的BGR channel will just apply same gray level value

Source image [50 100 150 ]

===========convert to Gray level
[109, 109, 109, 109;
  109, 109, 109, 109;
  109, 109, 109, 109]
=========== change back to RGB
[109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109; 
  109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109;
  109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109]











       
 














原创粉丝点击