I'm trying to teach myself opencv and c++ and this example program for face and eye detection includes the line:
for(size_t i = 0; i < faces.size(); i++)
I don't understand what faces.size() means, and following from that at what point i can be greater than faces.size().
How does it acquire a numerical value?
I see plenty of instances of faces throughout the rest of the program, but the only time I see size is as a parameter for face_cascade.detectMultiScale. It is capitalized though, which makes me think that it has nothing to do with faces.size().
faces.size()
Returns the size of 'faces', i.e. how many faces there are in 'faces'.
In general a basic for loop is structured like so:
for ( init; condition; increment )
{
//your code...
}
It will run as long as the condition is true, i.e. as long as 'i' is less than faces.size() (which might be '10' or some other integer value).
'i' will get bigger as for each loop iteration 1 is added to it. This is managed by the i++ instruction.
I'd suggest if you're struggling with loop syntax that openCV might not be the best place to start learning C++ as a lot of the examples expect a level of competence higher than 'beginner' (intentionally and unintentionally via simple bad coding/lack of commenting etc.)
faces is being populated here :
//-- Detect faces
face_cascade.detectMultiScale( frame_gray, faces, 1.1, 2, 0|CASCADE_SCALE_IMAGE, Size(30, 30) );
According to OpenCV documentation :
void cv::CascadeClassifier::detectMultiScale ( InputArray image,
std::vector< Rect > & objects,
double scaleFactor = 1.1,
int minNeighbors = 3,
int flags = 0,
Size minSize = Size(),
Size maxSize = Size()
)
where std::vector< Rect > & objects (faces in your case) is a
Vector of rectangles where each rectangle contains the detected
object, the rectangles may be partially outside the original image.
As you can see, objects is passed by reference to allow its modification inside the function.
Also std::vector<Type>::size() will give you the size of your vector, so, i<faces.size() is necessary to get the index i inside the bounds of the vector.
Related
I have got a vector containing contours of an image. The code looks like this:
cv::Mat img = cv::imread ("whatever");
cv::Mat edges;
double lowThresh = 100, highThresh = 2*lowThresh;
Canny(img, edges, lowThresh, highThresh);
std::vector<std::vector<cv::Point>> contourVec;
std::vector<cv::Vec4i> hierarchy;
int mode = CV_RETR_LIST;
int method = CV_CHAIN_APPROX_TC89_KCOS;
findContours(edges, contourVec, hierarchy, mode, method);
What I now would like to do is to transform these points. I therefore created another vector, which shall have the same structure as the other one, just with Point3delements instead of Point. At the moment, I do it like this:
std::vector<std::vector<cv::Point3d>> contour3DVec(contourVec.size());
for (int i = 0; i < contourVec.size(); i++)
contour3DVec[i].resize(contourVec[i].size());
But I'm not sure whether that is really the best way to do it, as I don't know how resize()is actually working (e.g. in the field of memory location).
Does anybody have an idea whether there is a faster and/or "smarter" way to solve this? Thanks in advance.
Given that you surely want to do something with the contours afterwards, the resizing probably won't be a performance hotspot in your Program.
Vectors in c++ are usually created with a bit of slack to grow. They may take up to double their current size in memory.
If you resize a vector it will first check if the resizing will fit in the reserved memory.
If that's the case the resizing is free.
Otherwise new memory (up to double the new size) will be reserved and the current vector content moved there.
As your vectors are empty in the beginning, they will have to reserve new memory anyway, so (given a sane compiler and standard library) it would be hard to beat your Implementation speed wise.
if you want 3d points, you'll have to create them manually, one by one:
std::vector<std::vector<cv::Point3d>> contour3DVec(contourVec.size());
for (size_t i = 0; i < contourVec.size(); i++)
{
for (size_t j = 0; j < contourVec[i].size(); j++)
{
Point p = contourVec[i][j];
contour3DVec[i].push_back( Point3d(p.x, p.y, 1) );
}
}
Suppose I have row of points
Mat_<Point> src(1,4);
src << Point(border,border), Point(border,h-border), Point(w-border,h-border), Point(w-border,h-border);
now I want to pass this row to polylines() function which accepts InputArrayOfArrays. In my case it should be array of ONE array.
How to convert row of points to this type?
You can use another form of this function with C arrays. The first array should be size of 1 and inner should be size of 4 and contain your points.
Also you can try the same and pass a vector<vector<Point> > instead of C arrays.
In OpenCV ArrayOfArrays usually means vector of something (Mat or another vector).'
Update: Also InputArrayOfArrays is just a typedef for InputArray. So you can try to pass a vector<Point>. It will not work for every function which require InputArrayOfArrays, but it should work for polylines(). I have not tested it, so please provide your results.
Maybe someone will find it useful as a starting point. I spent almost a whole day looking for a solution. In the end I can draw with Polyline and fillPoly. I have to admit it was not a little annoying.
t2 contains 3 points .
So this add 3 Points to pt.
vector<Point> pt;
for(int ao=0; ao<t2.size(); ao++){
pt.push_back( t2.at(ao) );
}
polylines(image,pt,false,Scalar(255,255,255),2,150,0);
This to use fillPoly
just to fill a triangle
Point pt[1][3];
//set one point into pt matrix
pt[0][0].x = yourvalue_x;
pt[0][0].y = yourvalue_y;
//set onother point into pt matrix
pt[0][1].x = yourvalue_x;
pt[0][1].y = yourvalue_y;
//set onother point into pt matrix
pt[0][2].x = yourvalue.x;
pt[0][2].y = yourvalue.y;
//adding 2 times (i have one triangle ) but
ppt can contains many more polygons
//const Point* ppt[2] = {pt[0], pt[1], ... };
//i used only one triangle as a test
const Point* ppt[2] = {pt[0], pt[0]};
int npt[] = {3, 3};
fillPoly(atom_image, ppt, npt, 1, (255,0,255));
I have a fairly simple question: how to take one row of cv::Mat and get all the data in std::vector? The cv::Mat contains doubles (it can be any simple datatype for the purpose of the question).
Going through OpenCV documentation is just very confusing, unless I bookmark the page I can not find a documentation page twice by Googling, there's just to much of it and not easy to navigate.
I have found the cv::Mat::at(..) to access the Matrix element, but I remember from C OpenCV that there were at least 3 different ways to access elements, all of them used for different purposes... Can't remember what was used for which :/
So, while copying the Matrix element-by-element will surely work, I am looking for a way that is more efficient and, if possible, a bit more elegant than a for loop for each row.
It should be as simple as:
m.row(row_idx).copyTo(v);
Where m is cv::Mat having CV_64F depth and v is std::vector<double>
Data in OpenCV matrices is laid out in row-major order, so that each row is guaranteed to be contiguous. That means that you can interpret the data in a row as a plain C array. The following example comes directly from the documentation:
// compute sum of positive matrix elements
// (assuming that M is double-precision matrix)
double sum=0;
for(int i = 0; i < M.rows; i++)
{
const double* Mi = M.ptr<double>(i);
for(int j = 0; j < M.cols; j++)
sum += std::max(Mi[j], 0.);
}
Therefore the most efficient way is to pass the plain pointer to std::vector:
// Pointer to the i-th row
const double* p = mat.ptr<double>(i);
// Copy data to a vector. Note that (p + mat.cols) points to the
// end of the row.
std::vector<double> vec(p, p + mat.cols);
This is certainly faster than using the iterators returned by begin() and end(), since those involve extra computation to support gaps between rows.
From the documentation at here, you can get a specific row through cv::Mat::row, which will return a new cv::Mat, over which you can iterator with cv::Mat::begin and cv::Mat::end. As such, the following should work:
cv::Mat m/*= initialize */;
// ... do whatever...
cv::Mat first_row(m.row(0));
std::vector<double> v(first_row.begin<double>(), first_row.end<double>());
Note that I don't know any OpenCV, but googling "OpenCV mat" led directly to the basic types documentation and according to that, this should work fine.
The matrix iterators are random-access iterators, so they can be passed to any STL algorithm, including std::sort() .
This is also from the documentiation, so you could actually do this without a copy:
cv::Mat m/*= initialize */;
// ... do whatever...
// first row begin end
std::vector<double> v(m.begin<double>(), m.begin<double>() + m.size().width);
To access more than the first row, I'd recommend the first snippet, since it will be a lot cleaner that way and there doesn't seem to be any heavy copying since the data types seem to be reference-counted.
You can also use cv::Rect
m(cv::Rect(0, 0, 1, m.cols))
will give you first row.
matrix(cv::Rect(x0, y0, len_x, len_y);
means that you will get sub_matrix from matrix whose upper left corner is (x0,y0) and size is (len_x, len_y). (row,col)
I think this works,
an example :
Mat Input(480, 720, CV_64F, Scalar(100));
cropping the 1st row of the matrix:
Rect roi(Point(0, 0), Size(720, 1));
then:
std::vector<std::vector<double> > vector_of_rows;
vector_of_rows.push_back(Input(roi));
Question Answered, see solution at end of question. More comments / answers still welcome.
So I'm having some trouble getting all my elements into one memory store, the best I have managed to do is overwrite the store each time and only get out the final elements that overwrite it which sadly isn't enough.
I'm primarily a Java programmer so I'll probably use some Java terms in place of the C equivalent and possiblly mention the wrong types when refering to C objects so I appoligise in advance for this (and don't get me started on pointers).
Ultimately what I am trying to do is go grab all of the Convexity Defects in an image and store them into one continuous space which I will then pass back into Java via JNI. The type of the store isn't important, at this stage I just need it working and I can work on optomization if we get the go ahead but I'm thinking a Stack, Queue, List, Vector or similar would do the job. Currently I've been trying to loop round adding groups of CvSeq objects to one large CvSeq, I'll post up my code and discuss it after the post:
CvSeq *allDefects;
allDefects = cvCreateSeq(0, sizeof (CvSeq), sizeof (CvConvexityDefect), mem_storage4);
CvContourScanner scanner3 = cvStartFindContours(img_bin, mem_storage3);
while ((c2 = cvFindNextContour(scanner3)) != NULL) {
if (threshold != 0 && cvContourPerimeter(c2) < threshold) {
cvSubstituteContour(scanner3, NULL);
} else { // otherwise create the hull
CvSeq* c_new;
c_new = cvConvexHull2(c2, mem_storage5, CV_CLOCKWISE, 0);
CvMemStorage* storage;
storage = cvCreateMemStorage(0);
defects = cvConvexityDefects(c2, c_new, storage);
allDefects = defects;
// for (int i = 0; i < defects->total; i++){
// cvSeqPush(allDefects, CV_GET_SEQ_ELEM(CvSeq, defects, i));
// }
}
}
So we create a scanner and whilst we can find contours we do so and check it is bigger than a given threshold. Assuming it is we create a hull round it and then find the defects between it and the hull. This means defects can have multi objects in it which I want to add to allDefects each time we loop round but the only way I can get it going is by making allDefects equal to defects meaning it is overwritten each loop. You can see a little bit of commented code where I attempt to push it like a stack but this crashes with the error:
Assertion failed: sizeof(((defects))->first[0]) == sizeof(CvSeqBlock) && ((defects))->elem_size == sizeof(CvSeq), file vtoolsModified.cpp, line 1407
Going by the documention "The cvConvexityDefects() routine returns a sequence of CvConvexityDefect structures" so that is a CvSeq full of CvConvexityDefect(s). The signiture of cvConvexityDefects in case that is of any help is:
CVAPI(CvSeq*) cvConvexityDefects( const CvArr* contour, const CvArr*
convexhull, CvMemStorage* storage CV_DEFAULT(NULL));
So to summarise what I am looking to do is find a contour, find its hull, find the defects between them, add all these defects to one large store, repeat till there is no contours left, return the large store with all defects to Java. It is the bit in bold I am looking for help with.
Anyone able to help or point me in the direction of a source that can? (I've been working on this specific problem for about 2 weeks now so have hit up a lot of resources, and yes I feel really daft for not managing to figure what should effectively be a simple loop on my own.)
Thanks
EDIT - More detail added as a result of comments.
EDIT 2 - Question Answered, the resultant, fixed code is below
Looks like my lack of understanding of C was the underlying problem. I was assuming I needed to use CvSeq in a place where I should have been using CvConvexityDefect. The corrected code is as follows:
CvSeq *defects;
CvSeq *allDefects;
allDefects = cvCreateSeq(0, sizeof(CvSeq), sizeof(CvConvexityDefect), mem_storage4);
CvContourScanner scanner3 = cvStartFindContours(img_bin, mem_storage3);
while ((c2 = cvFindNextContour(scanner3)) != NULL) {
if (threshold != 0 && cvContourPerimeter(c2) < threshold) {
cvSubstituteContour(scanner3, NULL);
} else { // otherwise create the hull
CvSeq* c_new;
c_new = cvConvexHull2(c2, mem_storage5, CV_CLOCKWISE, 0);
CvMemStorage* storage;
storage = cvCreateMemStorage(0);
defects = cvConvexityDefects(c2, c_new, storage);
// allDefects = defects;
if (defects->total < 100) {
for (int i = 0; i < defects->total; i++) {
CvConvexityDefect* element = CV_GET_SEQ_ELEM(CvConvexityDefect, defects, i);
if (element != 0){
cvSeqPush(allDefects, element); }
}
}
}
}
You can see I am also checking to make sure less than 100 defects are returned (sometimes it is millions which breaks the system) and also making sure CV_GET_SEQ_ELEM doesn't return null (I'm not sure if it can return null but I'll check just in case.
My guess is that your loop has a mistake in this line:
CV_GET_SEQ_ELEM(CvSeq, defects, i)
If you look at the definition of it :
#define CV_GET_SEQ_ELEM( elem_type, seq, index ) CV_SEQ_ELEM( (seq), elem_type, (index) )
you can see that the first parameter has to be the element type(cvFindContours usually makes sequences of CvPoint) and not the type of the sequence (CvSeq)
Other ways that you can store your sequences would be to use
void* cvCvtSeqToArray(const CvSeq* seq,
void* elements,
CvSlice slice = CV_WHOLE_SEQ
);
and store the arrays in a list, in so keeping the defects separated for each object.
There are even more options, depending on what you need.
EDIT2:
Ok, so i just reviewed cvConvexityDefects and saw that it returns a sequence of structures, not CvPoint, then your allDefects should be a custom made CvSeq and you will modify you code like this:
cvSeqPush(allDefects, CV_GET_SEQ_ELEM(CvConvexityDefect, defects, i));
I am trying to write a bag of features system image recognition system. One step in the algorithm is to take a larger number of small image patches (say 7x7 or 11x11 pixels) and try to cluster them into groups that look similar. I get my patches from an image, turn them into gray-scale floating point image patches, and then try to get cvKMeans2 to cluster them for me. I think I am having problems formatting the input data such that KMeans2 returns coherent results. I have used KMeans for 2D and 3D clustering before but 49D clustering seems to be a different beast.
I keep getting garbage values for the returned clusters vector, so obviously this is a garbage in / garbage out type problem. Additionally the algorithm runs way faster than I think it should for such a huge data set.
In the code below the straight memcpy is only my latest attempt at getting the input data in the correct format, I spent a while using the built in OpenCV functions, but this is difficult when your base type is CV_32FC(49).
Can OpenCV 1.1's KMeans algorithm support this sort of high dimensional analysis?
Does someone know the correct method of copying from images to the K-Means input matrix?
Can someone point me to a free, Non-GPL KMeans algorithm I can use instead?
This isn't the best code as I am just trying to get things to work right now:
std::vector<int> DoKMeans(std::vector<IplImage *>& chunks){
// the size of one image patch, CELL_SIZE = 7
int chunk_size = CELL_SIZE*CELL_SIZE*sizeof(float);
// create the input data, CV_32FC(49) is 7x7 float object (I think)
CvMat* data = cvCreateMat(chunks.size(),1,CV_32FC(49) );
// Create a temporary vector to hold our data
// we'll copy into the matrix for KMeans
int rdsize = chunks.size()*CELL_SIZE*CELL_SIZE;
float * rawdata = new float[rdsize];
// Go through each image chunk and copy the
// pixel values into the raw data array.
vector<IplImage*>::iterator iter;
int k = 0;
for( iter = chunks.begin(); iter != chunks.end(); ++iter )
{
for( int i =0; i < CELL_SIZE; i++)
{
for( int j=0; j < CELL_SIZE; j++)
{
CvScalar val;
val = cvGet2D(*iter,i,j);
rawdata[k] = (float)val.val[0];
k++;
}
}
}
// Copy the data into the CvMat for KMeans
// I have tried various methods, but this is just the latest.
memcpy( data->data.ptr,rawdata,rdsize*sizeof(float));
// Create the output array
CvMat* results = cvCreateMat(chunks.size(),1,CV_32SC1);
// Do KMeans
int r = cvKMeans2(data, 128,results, cvTermCriteria(CV_TERMCRIT_EPS+CV_TERMCRIT_ITER, 1000, 0.1));
// Copy the grouping information to our output vector
vector<int> retVal;
for( int y = 0; y < chunks.size(); y++ )
{
CvScalar cvs = cvGet1D(results, y);
int g = (int)cvs.val[0];
retVal.push_back(g);
}
return retVal;}
Thanks in advance!
Though I'm not familiar with "bag of features", have you considered using feature points like corner detectors and SIFT?
You might like to check out http://bonsai.ims.u-tokyo.ac.jp/~mdehoon/software/cluster/ for another open source clustering package.
Using memcpy like this seems suspect, because when you do:
int rdsize = chunks.size()*CELL_SIZE*CELL_SIZE;
If CELL_SIZE and chunks.size() are very large you are creating something large in rdsize. If this is bigger than the largest storable integer you may have a problem.
Are you wanting to change "chunks" in this function?
I'm guessing that you don't as this is a K-means problem.
So try passing by reference to const here. (And generally speaking this is what you will want to be doing)
so instead of:
std::vector<int> DoKMeans(std::vector<IplImage *>& chunks)
it would be:
std::vector<int> DoKMeans(const std::vector<IplImage *>& chunks)
Also in this case it is better to use static_cast than the old c style casts. (for example static_cast(variable) as opposed to (float)variable ).
Also you may want to delete "rawdata":
float * rawdata = new float[rdsize];
can be deleted with:
delete[] rawdata;
otherwise you may be leaking memory here.