Related
I am using OpenCV 3.1 with VS2012 C++/CLI.
I have stored the result of a finContours call into:
std::vector<std::vector<Point>> Contours;
Thus, Contours[0] is a vector of the contour points of the first contour.
Contours[1] is a vector of the contour points of the second vector, etc.
Now, I want to load one of the contours into a Mat Based on Convert Mat to vector <float> and Vector<float> to mat in opencv I thought something like this would work.
Mat testMat=Mat(Images->Contours[0].size(),2,CV_32FC1);
memcpy(testMat.data,Images->Contours[0].data(),Images->Contours[0].size()*CV_32FC1);
I specified two columns because I each underlying pint must be composed of both an X point and a Y point and each of those should be a float. However, when I access the Mat elements, I can see that the first element is not the underlying data but the total number of contour points.
Any help on the right way to accomplish this appreaciated.
You can do that with:
Mat testMat = Mat(Images->Contours[0]).reshape(1);
Now testMat is of type CV_32SC1, aka of int. If you need float you can:
testMat.convertTo(testMat, CV_32F);
Some more details and variants...
You can simply use the Mat constructor that accepts a std::vector:
vector<Point> v = { {0,1}, {2,3}, {4,5} };
Mat m(v);
With this, you get a 2 channel matrix with the underlying data in v. This means that if you change the value in v, also the values in m change.
v[0].x = 7; // also 'm' changes
If you want a deep copy of the values, so that changes in v are not reflected in m, you can use clone:
Mat m2 = Mat(v).clone();
Your matrices are of type CV_32SC2, i.e. 2 channels matrices of int (because Point uses int. Use Point2f for float). If you want a 2 columns single channel matrix you can use reshape:
Mat m3 = m2.reshape(1);
If you want to convert to float type, you need to use convertTo:
Mat m4;
m2.convertTo(m4, CV_32F);
Here some working code as a proof of concept:
#include <opencv2\opencv.hpp>
#include <vector>
using namespace std;
using namespace cv;
int main()
{
vector<Point> v = { {0,1}, {2,3}, {4,5} };
// changes in v affects m
Mat m(v);
// changes in v doesn't affect m2
Mat m2 = Mat(v).clone();
// m is changed
v[0].x = 7;
// m3 is a 2 columns single channel matrix
Mat m3 = m2.reshape(1);
// m4 is a matrix of floats
Mat m4;
m2.convertTo(m4, CV_32F);
return 0;
}
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
http://docs.opencv.org/doc/tutorials/imgproc/histograms/histogram_calculation/histogram_calculation.html
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?
it is HSV histogram showed as 3D graph
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):
convert source image to HSV
compute histogram ignoring V value
all colors with the same H,S are considered as single color no matter what the V is
you can ignore any other but the V parameter looks like the best choice
draw the graph
first draw ellipse with darker color (HSV base disc)
then for each dot take the corresponding histogram value and draw vertical line with brighter color. Line size is proportional to the histogram value
Here is the C++ code I did this with:
picture pic0,pic1,pic2,zed;
int his[65536];
DWORD w;
int h,s,v,x,y,z,i,n;
double r,a;
color c;
// compute histogram (ignore v)
pic2=pic0; // copy input image pic0 to pic2
pic2.rgb2hsv(); // convert to HSV
for (x=0;x<65536;x++) his[x]=0; // clear histogram
for (y=0;y<pic2.ys;y++) // compute it
for (x=0;x<pic2.xs;x++)
{
c=pic2.p[y][x];
h=c.db[picture::_h];
s=c.db[picture::_s];
w=h+(s<<8); // form 16 bit number from 24bit HSV color
his[w]++; // update color usage count ...
}
for (n=0,x=0;x<65536;x++) if (n<his[x]) n=his[x]; // max probability
// draw the colored HSV base plane and histogram
zed =pic1; zed .clear(999); // zed buffer for 3D
pic1.clear(0); // image of histogram
for (h=0;h<255;h++)
for (s=0;s<255;s++)
{
c.db[picture::_h]=h;
c.db[picture::_s]=s;
c.db[picture::_v]=100; // HSV base darker
c.db[picture::_a]=0;
x=pic1.xs>>1; // HSV base disc position centers on the bottom
y=pic1.ys-100;
a=2.0*M_PI*double(h)/256.0; // disc -> x,y
r=double(s)/256.0;
x+=120.0*r*cos(a); // elipse for 3D ilusion
y+= 50.0*r*sin(a);
z=-y;
if (zed.p[y][x].dd>=z){ pic1.p[y][x]=c; zed.p[y][x].dd=z; } x++;
if (zed.p[y][x].dd>=z){ pic1.p[y][x]=c; zed.p[y][x].dd=z; } y++;
if (zed.p[y][x].dd>=z){ pic1.p[y][x]=c; zed.p[y][x].dd=z; } x--;
if (zed.p[y][x].dd>=z){ pic1.p[y][x]=c; zed.p[y][x].dd=z; } y--;
w=h+(s<<8); // get histogram index for this color
i=((pic1.ys-150)*his[w])/n;
c.db[picture::_v]=255; // histogram brighter
for (;(i>0)&&(y>0);i--,y--)
{
if (zed.p[y][x].dd>=z){ pic1.p[y][x]=c; zed.p[y][x].dd=z; } x++;
if (zed.p[y][x].dd>=z){ pic1.p[y][x]=c; zed.p[y][x].dd=z; } y++;
if (zed.p[y][x].dd>=z){ pic1.p[y][x]=c; zed.p[y][x].dd=z; } x--;
if (zed.p[y][x].dd>=z){ pic1.p[y][x]=c; zed.p[y][x].dd=z; } y--;
}
}
pic1.hsv2rgb(); // convert to RGB to see correct colors
input image is pic0 (rose), output image is pic1 (histogram graph)
pic2 is the pic0 converted to HSV for histogram computation
zed 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 pixels
p[y][x].dd is pixel at (x,y) position as 32 bit integer type
clear(color) - clears entire image
resize(xs,ys) - resizes image to new resolution
rgb2hsv() and hsv2rgb() ... guess what it does :)
[edit1] your 2D histogram
It looks like you have color coded into 2D array. One axis is H and second is S. So you need to calculate H,S value from array address. If it is linear then for HSV[i][j]:
H=h0+(h1-h0)*i/maxi
S=s0+(s1-s0)*j/maxj
or i,j reversed
h0,h1,s0,s1 are the color ranges
maxi,maxj are the array size
As you can see you also discard V like me so now you have H,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 other S and Value is fixed (I choose 200). If your axises are swapped then just mirror it by y=x I think ...
The color sorting is really just an order in which you pick all the colors from array. for example:
v=200; x=0;
for (h=0;h<256;h++)
for (s=0;s<256;s++,x++)
{
y=HSV[h][s];
// here draw line (x,0)->(x,y) by color hsv2rgb(h,s,v);
}
This is the incrementing way. You can compute x from H,S instead to achieve different sorting or swap the fors (x++ must be in the inner loop)
If you want RGB histogram plot instead see:
how to plot rgb color histogram of image with objective c
I'm a newbie in OpenCV. I'm learning the Segmentation by Watershed algorithm and I have a problem.
I have to convert the image color to grayscale for using Watershed.
When I use the BGR color space, no problem but with HSV, I'm not sure that the code below is correct.
Mat im = imread("./Image/118035.jpg", CV_LOAD_IMAGE_COLOR);
Mat imHSV;
cvtColor(im, imHSV, CV_BGR2HSV);
imshow("HSV", imHSV);
cvtColor(imHSV, imHSV, CV_BGR2GRAY);
imshow("HSV to gray", imHSV);
imshow("BGR", im);
cvtColor(im, im, CV_BGR2GRAY);
imshow("BGR to gray", im);
I think, after converting from BGR to HSV, Hue = Blue, Saturation = Green, Value = Red and I can use the operator BGR2GRAY for convert from HSV to grayscale.
The 2 output images are different. Can I convert HSV to grayscale like that?
//Is it similaire with color space LAB?
The conversion from HSV to gray is not necessary: you already have it. You can just select the V channel as your grayscale image by splitting the HSV image in 3 and taking the 3rd channel:
Mat im = imread("C:/local/opencv248/sources/samples/c/lena.jpg", CV_LOAD_IMAGE_COLOR);
Mat imHSV;
cvtColor(im, imHSV, CV_BGR2HSV);
imshow("HSV", imHSV);
//cvtColor(imHSV, imHSV, CV_BGR2GRAY);
Mat hsv_channels[3];
cv::split( imHSV, hsv_channels );
imshow("HSV to gray", hsv_channels[2]);
imshow("BGR", im);
cvtColor(im, im, CV_BGR2GRAY);
imshow("BGR to gray", im);
waitKey();
hsv1 = cv2.cvtColor(frame1, cv2.COLOR_BGR2HSV)
h, s, v1 = cv2.split(hsv1)
cv2.imshow("gray-image",v1)
in HSV color-space, V channel is defined as max(R, G, B) but in gray-scale, value is defined by mean(R, G, B).
in RGB2HSV conversion, we use these formulas for S and V channel:
V = max(R, G, B)
S = (max(R, G, B) - min(R, G, B)) / max(R, G, B)
so if S is zero, max(R, G, B) equals to min(R, G, B) and they are equal to mean(R, G, B). so if this criteria holds, V channel is equal to gray-scale value. other wise, they are different.
one way is to convert image to RGB and then convert it to GRAY. but if you look for a more straight way, you can use picture below:
HSV2RGB converion
and hence gray value is mean(R, G, B) you can calculate it as:
gray = m + (c + x) / 3
where you can compute m,c and x from formula in image.
Unfortunately I am unable to comment because of insufficient earned reputation.
Taking the 3rd channel alone will may give grey values you do not expect, as increasingly fully saturated colors,as an extreme example RGB 0,0,255 will appear as pure white once converted to grey scale by taking the value hsv channel.
This could certainly affect watershed (dependent on image content) as saturated red, greens and blues would not be differentiated in the V channel.
The obvious, converting to BGR then Grayscale could be a better option.
There is a very simple way to construct a Mat from a vector...just by doing:
vector<int> myVector;
Mat myMatFromVector(myVector,true); //the boolean is to define if you want to copy the data
The problem with this contructor is that each vector's element will be placed in each row of the Matrix. What I want is each element of my vector to be placed in each column of the matrix.
As is:
vector<int> = [1,2,3,4]
Matrix = [1;2;3;4]
I want:
vector<int> = [1,2,3,4]
Matrix = [1,2,3,4]
Either specify the shape and type of the Matrix and pass the vector data
// constructor for matrix headers pointing to user-allocated data
Mat(int _rows, int _cols, int _type, void* _data, size_t _step=AUTO_STEP);
Mat(Size _size, int _type, void* _data, size_t _step=AUTO_STEP);
Or call reshape on the Mat to swap the number of row sand columns ( doesn't change any data)
// creates alternative matrix header for the same data, with different
// number of channels and/or different number of rows. see cvReshape.
Mat reshape(int _cn, int _rows=0) const;
The matrix formed by reflecting a matrix through its main diagonal (ie interchanging the rows and columns) is called the transpose. Using OpenCV, you can easily obtain the transpose of a matrix A as:
Mat A;
Mat A_transpose = A.t();
If A is [1; 2; 3; 4], A_transpose will be [1, 2, 3, 4] as required.
So, you could either create a transposed copy of your matrix after converting it from the vector, or you could create it easily when subsequently required in your calculations.
Mat A, B;
Mat answer = A.t() * B;
In order to add constant value to each pixel's saturation value, I do this in double loops. I wonder if there is any simpler and faster command achieving this.
Mat img(200, 300, CV_8UC1);
Mat saturated;
double saturation = 10;
double scale = 1;
// what it does here is dst = (uchar) ((double)src*scale+saturation);
img.convertTo(saturated, CV_8UC1, scale, saturation);
EDIT
If by saturation, you mean the S channel in a HSV image, you need to separe your image in three channels with split(), apply the saturation correction to the S channel, and then put them together with merge().
For the experiments I attempted, the alternative method of splitting hsv values, adjusting the individual channels and then doing a merge gave a better performance. Below is what worked for me many times faster as compared to looping through pixels:
(h, s, v) = cv2.split(imghsv)
s = s*satadj
s = np.clip(s,0,255)
imghsv = cv2.merge([h,s,v])
Note that I had converted the values to float32 during BGR2HSV transformation to avoid negative values during saturation transformation to due uint8 (default) overflow:
imghsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV).astype("float32")
And converted it back to default uint8 after my saturation adjustment:
imgrgb = cv2.cvtColor(imghsv.astype("uint8"), cv2.COLOR_HSV2BGR)