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.
上面两个参数很好理解因为数组参数我们是不知道size 的,所以传递 images 要加上一个 nimages 说明有多少个image, *image++ till to *image[nimages-1]
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]
ranges[0] to rangs[dims-1] 也就是每一个channel 都有自己的范围,比如H is from 0 -179 ,S from 0-255 . 因此
ranges[i] 的每一个元素也是一个数组,一个二维数组,指明每一个channel 的范围。
#include
#include
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<
for(int r=0;r
for(int c=0;c
float histValue=hist.at
if(histValue>0)
cout<
}
//可以看到打印出来的结果: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:"<
cout<<"hValue"<
//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"<
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
cout<<")";
cout<
// cout<<(*it)<
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 element
for(int i = 0; i < 1000; i++)
{
int idx[dims];
for(int k = 0; k < dims; k++)
idx[k] = rand()
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
}
Mat result;
cv::LUT(image,lut,result);
cout<
(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<
break;
}
// find right extremity of the histogram
int imax= histSize[0]-1;
for( ; imax >= 0; imax-- ) {
if (hist.at
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
else if (i > imax) lookup.at
// linear mapping
else lookup.at
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]