I'm wondering how to plot a 2d histogram of an HSV Mat in opencv c++. My current code attempting to display it fails miserably. I've looked around on how to plot histograms and all the ones I've found were those plotting them as independent 1d histograms.
Here's my current output with the number of hue bins being 30 and saturation bins being 32:
Here's another output with the number of hue bins being 7 and saturaation bins being 5:
I would like it to look more like the result here
I also noticed whenever I do cout << Hist.size it gives me 50x50. Am I to understand that just means the first dimension of the array is 250 in size?
Also, how does one sort the histogram from highest to lowest (or vice versa) value frequency? That is another problem I am trying to solve.
My current function is as follows.
void Perform_Hist(Mat& MeanShift, Mat& Pyramid_Result, Mat& BackProj){
Mat HSV, Hist;
int histSize[] = {hbins, sbins};
int channels[] = {0, 1};
float hranges[] = {0, 180};
float sranges[] = {0, 256};
const float* ranges[] = {hranges, sranges};
cvtColor(MeanShift, HSV, CV_BGR2HSV);
Mat PyrGray = Pyramid_Result.clone();
calcHist(&HSV, 1, channels, Mat(), Hist, 2, histSize, ranges, true, false);
normalize(Hist, Hist, 0, 255, NORM_MINMAX, -1, Mat());
invert(Hist, Hist, 1);
calcBackProject(&PyrGray, 1, channels, Hist, BackProj, ranges, 1, true);
double maxVal = 0; minMaxLoc(Hist, 0, &maxVal, 0, 0);
int scale = 10;
Mat histImage = Mat::zeros(sbins*scale, hbins*10, CV_8UC3);
for(int i = 1; i < hbins * sbins; i++){
line(histImage,
Point(hbins*sbins*(i-1), sbins - cvRound(Hist.at<float>(i-1))),
Point(hbins*sbins*(i-1), sbins - cvRound(Hist.at<float>(i))),
Scalar(255,0,0), 2, 8, 0);
}
imshow (HISTOGRAM, histImage);
}
Did you mean something like this?
V
is ignored to get to 3D (otherwise it would be 4D graph ...)if yes then this is how to do it (I do not use OpenCV so adjust it to your needs):
draw the graph
Here is the C++ code I did this with:
pic0
(rose), output image ispic1
(histogram graph)pic2
is thepic0
converted to HSV for histogram computationzed
is the Zed buffer for 3D display avoiding Z sorting ...I use my own picture class for images so some members are:
xs,ys
size of image in pixelsp[y][x].dd
is pixel at (x,y) position as 32 bit integer typeclear(color)
- clears entire imageresize(xs,ys)
- resizes image to new resolutionrgb2hsv()
andhsv2rgb()
... guess what it does :)[edit1] your 2D histogram
It looks like you have color coded into 2D array. One axis is
H
and second isS
. So you need to calculateH,S
value from array address. If it is linear then forHSV[i][j]
:H=h0+(h1-h0)*i/maxi
S=s0+(s1-s0)*j/maxj
i,j
reversedh0,h1,s0,s1
are the color rangesmaxi,maxj
are the array sizeAs you can see you also discard
V
like me so now you haveH,S
for each cell in histogram 2D array. Where probability is the cell value. Now if you want to draw an image you need to know how to output this (as a 2D graph, 3D, mapping,...). For unsorted 2D graph draw graph where:x=i+maj*i
y=HSV[i][j]
color=(H,S,V=200);
If you want to sort it then just compute the x axis differently or loop the 2D array in sort order and x just increment
[edit2] update of code and some images
I have repaired the C++ code above (wrong Z value sign, changed Z buffer condition and added bigger points for nicer output). Your 2D array colors can be as this:
Where one axis/index is
H
, the otherS
andValue
is fixed (I choose 200). If your axises are swapped then just mirror it byy=x
I think ...The color sorting is really just an order in which you pick all the colors from array. for example:
This is the incrementing way. You can compute
x
fromH,S
instead to achieve different sorting or swap thefors
(x++
must be in the inner loop)If you want RGB histogram plot instead see: