I have been trying do a comparison of data contents in my IplImage object.
I have the following:
IplImage img1 = IplImage(cv::imread("C:\\TestIm\\barrier_snapshot1.png"),
CV_LOAD_IMAGE_GRAYSCALE);
for (int i=0; i < img1.widthStep * img1.height; i++) {
cout << img1.imageData[i] << endl;
}
But when I try to print it, it is causing an exception and I cannot even catch it to print the message and see what I am doing wrong. My image is Grayscale and I believe if I don't use cvCreateImage() it is okay? I know it will be something stupid or related to array access that I cannot seem to be getting easily from the IplImage documenation.
*WHY AM I BOTHERING TO MIX C AND C++ CODE IN MY DESIGN? *
Unfortunately, I have no choice! I am working on a project that is with improved Motion Detection Applications. My legacy application source code uses heavy BOOST and OpenCV stuff. Especially, it uses IplImage* (I hate it, makes life difficult and causes memory leak) to store stuff like image masks. I understand that If I save IplImage* in long run I will have illegal reference and access violation. so I save the copy of the actual content pointed by the IplImage*. To exemplify:
// getLongHistory() returns IplImage*
IplImage history_long = *(motionHistory.getLongHistory());
There are total 6 mask images that are made using IplImage*. At this moment, I am condemning the programmer who decided to do it in IplImage*. The problem arises when I am trying to load those mask images and this is how I do it:
// Passing pointer to the address of the mask stored (alive in the memory)
motionHistory.setLongHistory(&(matcher.getCurrentSceneObject().getLongHistory()));
I believe I am having a problem with deep copy and shallow copy of IplImage objects. I believe doing saving it as cv::Mat from IplImage* and loading it as IplImage* from cv::Mat will probably reduce the burden as I suspect it probably does SOMETHING underneath the high-level functions so that the copying for data and ROI is done accordingly. But, as a newbee I can assume anything. Please help!
UPDATE
In my code I was doing this in the past:
/* I store all my mask images in a vector of pairs made of <int, IplImage>
* __MASK_LONG__ etc. are predefined intergers
* getMaskLong() etc. methods return IplImage* to the respective mask images.
*/
myImages.clear(); // To make sure that I have no extra stuff
myImages.push_back(std::make_pair<int, IplImage>(_MASK_LONG_, maskHistory.getMaskLong()));
myImages.push_back(std::make_pair<int, IplImage>(_MASK_SHORT_, maskHistory.getMaskShort()));
However, after getting suggestions and doing some basic R&A, I am now doing this to prevent shallow copying:
myImages.clear();
myImages.push_back(std::make_pair<int, IplImage>(_MASK_LONG_, *cvCloneImage(maskHistory.getMaskLong())));
myImages.push_back(std::make_pair<int, IplImage>(_MASK_SHORT_, *cvCloneImage(maskHistory.getMaskShort())));
I can confirm that this works as I can see the latest mask images getting loaded on the OpenCV window! And I am pretty certain how IMPORTANT it is to do Deep Copy at least 2/3 times in any programming task. So thanks for putting me to the right track. But now I have the problem which I was having in mind whilst implementing those changes - memory allocation failure. And the message was encountered:
OpenCV Error: Insufficient memory (Failed to allocate 3686404 bytes) in OutOfMemoryError,
file /home/naresh/OpenCV-2.4.0/modules/core/src/alloc.cpp, line 52
If I am deep enough to know about C/C++, firstly I am commiting a crime of mixing them together (I HAVE NO CHOICE!! IT IS A LEGACY APPLICATION!). Secondly, there is a mismatch i.e. incorrect set of calls to malloc/free in alloc.cpp file (where the problem is being produced). Or it may just be that the heap is corrupted or full. Am I being stupid?
Don't mix the C interface of OpenCV with the C++ interface.
Ideally, you would solve the problem by using exclusively the C++ interface, like the following:
cv::Mat gray = cv::imread("C:\\TestIm\\barrier_snapshot1.png", CV_LOAD_IMAGE_GRAYSCALE);
cv::Mat_<uchar>::iterator it = gray.begin<uchar>();
cv::Mat_<uchar>::iterator end = gray.end<uchar>();
for (; it != end; ++it)
{
cout << *it << endl;
}
Or:
cv::Mat gray = cv::imread("C:\\TestIm\\barrier_snapshot1.png", CV_LOAD_IMAGE_GRAYSCALE);
for (int i = 0; i < gray.cols; i++)
{
for (int j = 0; j < gray.rows; j++)
{
cout << gray[gray.cols * j + i] << endl;
}
}
Yes, cv::imread() can also load an input image as grayscale. But if you really need to stick with the C interface, then drop cv::imread() and use cvLoadImage() instead. There are several posts explaining how to do this, use the search box.
If you decide to continue to mix the interfaces (please don't), check this thread since it explains how to convert IplImage* to cv::Mat.
Related
Please let me know if this question is too broad, but I am trying to learn some c++ so I thought it would be a good idea to try and recreate some opencv functions.
I am still grabbing the frames or reading the image with opencv's API, but I then want to feed the cv::Mat into my custom function(s), where I modify its data and return it to display it. (For example a function to blur the image, where I pass the original Mat to a padding function, then the output of that to a fn that convolves the padded image with the blurring kernel, and returns the Mat to cv for displaying)
I am a little confused as to what the best (or right) way to do this is. OpenCV functions use a function argument as the return matrix ( cv_foo(cv::Mat src_frame, cv::Mat dst_frame) ) but I am not entirely clear how this works, so I have tried a more familiar approach, something like
cv::Mat my_foo(cv::Mat src_frame) {
// do processing on src_frame data
return dst_frame;
}
where to access the data from src_frame I use uchar* framePtr = frame.data; and to create the dst_frame I followed this suggestion
cv::Mat dst_frame = cv::Mat(n_rows, n_cols, CV_8UC3);
memcpy(dst_frame.data, &new_data_array, sizeof(new_data_array));
I have however encountered various segmentation faults that I find hard to debug, as it seems they occur almost at random (could this be due to the way I am handling the memory management with frame.data or something like that?).
So to come back to my original question, what is the best way to access, modify and pass the data from a cv::Mat in the most consistent way?
I think what would make the most intuitive sense to me (coming from numpy) would be to extract the data array from the original Mat, use that throughout my processing and then repackage it into a Mat before displaying, which would also allow me to feed any custom array into the processing without having to turn it into a Mat, but I am not sure how to best do that (or if it is the right approach).
Thank you!
EDIT:
I will try to highlight the main bug in my code.
One of the functions I am trying to replicate is a conversion from bgr to greyscale, my code looks like this
cv::Mat bgr_to_greyscale(cv::Mat& frame){
int n_rows = frame.rows;
int n_cols = frame.cols;
uchar* framePtr = frame.data;
int channels = frame.channels();
uchar grey_array[n_rows*n_cols];
for(int i=0; i<n_rows; i++){
for(int j=0; j<n_cols; j++){
uchar pixel_b = framePtr[i*n_cols*channels + j*channels];
uchar pixel_g = framePtr[i*n_cols*channels + j*channels + 1];
uchar pixel_r = framePtr[i*n_cols*channels + j*channels + 2];
uchar pixel_grey = 0.299*pixel_r + 0.587*pixel_g + 0.144*pixel_b;
grey_array[i*n_cols + j] = pixel_grey;
}
}
cv::Mat dst_frame = cv::Mat(n_rows, n_cols, CV_8UC1, &grey_array);
return dst_frame;
}
however when I display the result of this function on a sample image I get this result: the bottom part of the image looks like random noise, how can I fix this? what exactly is going wrong in my code?
Thank you!
This question is too broad to answer in any detail, but generally a cv::Mat is a wrapper around the image data akin to the way an std::vector<int> is a wrapper around a dynamically allocated array of int values or an std::string is a wrapper around a dynamically allocated array of characters with one exception: a cv::Mat will not perform a deep copy of the image data on assignment or usage of the copy constructor.
std::vector<int> b = { 1, 2, 3, 4};
std::vector<int> a = b;
// a now contains a copy of b and a[0] = 42 will not effect b.
cv::Mat b = cv::imread( ... );
cv::Mat a = b;
// a and b now wrap the same data.
But that said, you should not be using memcpy et. al. to copy a cv::Mat ... You can make copies with clone or copyTo. From the cv documentation:
Mat F = A.clone();
Mat G;
A.copyTo(G);
My old project was wrote in C, which includes lots of "IplImage".
Here is one example,
IplImage** img_array = new IplImage* [img_number];
for(int i = 0; i < img_number; i++)
{
img_array[i] = cvCreateImage(cvSize(320, 240), IPl_DEPTH_8U, 3);
cvSet(img_array[i], cvScalar(0, 0, 0));
}
.
.
.
.
.
for(int i = 0; i < img_number; i++)
{
cvReleaseImage(&img_array[i]);
}
delete[] img_array;
Now I have to rewrite my project into C++ form, which I should use cv::Mat.
However, I am not sure the equal way to write my code above into cv::Mat?
I tried write in this way:
int dims[] = {img_number, 320, 240};
cv::Mat img_array(3, dims, CV_8UC3);
everything seems good though, but when comes to a line:
for(int i = 0; i < img_number; i++)
{
img_array[i] = random_img.clone();
}
an error showup:
C2676:binary operator'[':'cv::Mat'does not define this operator or a conversion to a type acceptable to the predefined operator
After that I found another way that might possibly do, but requires using vector:
vector<Mat> img_array;
for(int i = 0 ; i < img_number; i++)
{
img_array.push_back(random_img.clone());
}
I haven't implement yet, I am not sure whether this is the solution I want?
Any advice is welcome.
You should view cv::Mat class as a replacement for old IplImage*. The constructor you were using is supposed to create a multidimensional cv::Mat instance, which is probably not what you were looking for. According to your IplImage* based code you were probably trying to create a set of Mat images from a set of IplImage pointers . For that this ctor will serve better:
Mat::Mat(const IplImage* img, bool copyData=false)
If you set copyData argument to true, the constructor will copy the contents of IplImage* and control the new data buffer as usual (reference counting, proper deallocation etc.) If you set it to false, the original IplImage* data will be used and you must take care not to free it before you are done with your cv::Mat instance.
The original docs are here
And, yes, generally it is better to place your cv::Mat instances into a vector. In this case the memory used by Mats will be automatically freed when the vector goes out of scope (unless you have created some other cv::Mats which reference the original data, but that's another story)
Edit
It appears that the Mat constructor above is not recommended to use. You should use cvarrToMat() function instead, as suggested in answers to this question
As you may know, many things changed in OpenCV 3. In previous verion of OpenCV I used to do it that way:
Mat trainData(classes * samples, ImageSize, CV_32FC1);
Mat trainClasses(classes * samples, 1, CV_32FC1);
KNNLearning(&trainData, &trainClasses); //learning function
KNearest knearest(trainData, trainClasses); //creating
//loading input image
Mat input = imread("input.jpg");
//digital recognition
learningTest(input, knearest);//test
Also I found an example how to figured it out, but I still have errors in create function:
Ptr<KNearest> knearestKdt = KNearest::create(ml::KNearest::Params(10, true, INT_MAX, ml::KNearest::KDTREE));
knearestKdt->train(trainData, ml::ROW_SAMPLE, trainLabels);
knearestKdt->findNearest(testData, 4, bestLabels);
Can you please provide me with information, how to rewrite the actual code of KNearest to openCV 3 properly?
The API has changed once again since #aperture-laboratories answer. I hope they keep up with the documentation when they release new features or changes in the future.
A working example is as follows
using namespace cv::ml;
//Be sure to change number_of_... to fit your data!
Mat matTrainFeatures(0,number_of_train_elements,CV_32F);
Mat matSample(0,number_of_sample_elements,CV_32F);
Mat matTrainLabels(0,number_of_train_elements,CV_32F);
Mat matSampleLabels(0,number_of_sample_elements,CV_32F);
Mat matResults(0,0,CV_32F);
//etcetera code for loading data into Mat variables suppressed
Ptr<TrainData> trainingData;
Ptr<KNearest> kclassifier=KNearest::create();
trainingData=TrainData::create(matTrainFeatures,
SampleTypes::ROW_SAMPLE,matTrainLabels);
kclassifier->setIsClassifier(true);
kclassifier->setAlgorithmType(KNearest::Types::BRUTE_FORCE);
kclassifier->setDefaultK(1);
kclassifier->train(trainingData);
kclassifier->findNearest(matSample,kclassifier->getDefaultK(),matResults);
//Just checking the settings
cout<<"Training data: "<<endl
<<"getNSamples\t"<<trainingData->getNSamples()<<endl
<<"getSamples\n"<<trainingData->getSamples()<<endl
<<endl;
cout<<"Classifier :"<<endl
<<"kclassifier->getDefaultK(): "<<kclassifier->getDefaultK()<<endl
<<"kclassifier->getIsClassifier() : "<<kclassifier->getIsClassifier()<<endl
<<"kclassifier->getAlgorithmType(): "<<kclassifier->getAlgorithmType()<<endl
<<endl;
//confirming sample order
cout<<"matSample: "<<endl
<<matSample<<endl
<<endl;
//displaying the results
cout<<"matResults: "<<endl
<<matResults<<endl
<<endl;
//etcetera ending for main function
KNearest::Params params;
params.defaultK=5;
params.isclassifier=true;
//////// Train and find with knearest
Ptr<TrainData> knn;
knn= TrainData::create(AmatOfFeatures,ROW_SAMPLE,AmatOfLabels);
Ptr<KNearest> knn1;
knn1=StatModel::train<KNearest>(knn,params);
knn1->findNearest(AmatOfFeaturesToTest,4,ResultMatOfNearestNeighbours);
/////////////////
The names of these functions will help you find them in the documentation.
However, the documentation might be a little confusing until it is fully updated, so the best way to do exactly what you want is to make a small toy example and use the trial-and-error way of things.
This is a working example, pasted right out of my own code, which is proven to be working. Hope that helps.
after reading an image of unknown depth and channel number i want to access its pixels one by one.
on opencv 1.x the code goes:
IplImage * I = cvLoadImage( "myimage.tif" );
CvScalar pixel = cvGet2D( I, y, x );
but on opencv 2.x the cv::Mat.at() method demands that i know the image's type:
cv::Mat I = cv::imread( "myimage.tif" );
if( I.depth() == CV_8U && I.channels() == 3 )
cv::Vec3b pixel = I.at<cv::Vec3b>( x, y );
else if( I.depth() == CV_32F && I.channels() == 1 )
float pixel = I.at<cv::float>( x, y );
is there a function resembling cvGet2D that can receive cv::Mat and return cv::Scalar without knowing the image's type in compile time?
For someone who is really a beginner in C++ ...
... and/or a hacker who just need to save mere seconds of code typing to finish off the last project
cv::Mat mat = ...; // something
cv::Scalar value = cv::mean(mat(cv::Rect(x, y, 1, 1)));
(Disclaimer: This code is only slightly less wasteful than a young man dying for a revolutionary cause.)
The short answer is no. There's no such function in the C++ API.
The rationale behind this is performance. cv::Scalar (and CvScalar) is the same thing as cv::Vec<double,4>. So, for any Mat type other than CV_64FC4, you'll need a conversion to obtain cv::Scalar. Moreover, this method would be a giant switch, like in your example (you have only 2 branches).
But I suppose quite often this function would be convenient, so why not to have it? My guess is that people would tend to overuse it, resulting in really bad performance of their algorithms. So, OpenCV makes it just a tiny bit less convenient to access individual pixels, in order to force client code to use statically typed methods. This isn't such a big deal convenient-wise, since more often than not, you actually know the type statically and it's a really big deal performance-wise. So, I consider it a good trade-off.
I had the same issue, I just wanted to test something quickly and performance was not an issue. But all parts of the code uses cv::Mat(). What I did was the following
Mat img; // My input mat, initialized elsewhere
// Pretty fast operation, Will only create an iplHeader pointing to the data in the mat
// No data is copied and no memory is mallocated.
// The Header resides on the stack (note its type is "IplImage" not "IplImage*")
IplImage iplImg = (IplImage)img;
// Then you may use the old (slow converting) legacy-functions if you like
CvScalar s = cvGet2D( &iplImg, y, x );
Just a warning: you are using cvLoadImage and imread with default flags. This means that any image you read will be a 8-bit 3-channel image. Use appropriate flags (IMREAD_ANYDEPTH / IMREAD_ANYCOLOR) if you want to read image as is (which seems to be your intention).
I have a question about this peace of code.
...............
cv::Mat image;
image = cv::imread(filename.c_str(), CV_LOAD_IMAGE_COLOR);
if (image.empty()) {
std::cerr << "Couldn't open file: " << filename << std::endl;
exit(1);
}
cv::cvtColor(image, imageRGBA, CV_BGR2RGBA);
imageGrey.create(image.rows, image.cols, CV_8UC1);
*inputImage = (uchar4 *)imageRGBA.ptr<unsigned char>(0);
*greyImage = imageGrey.ptr<unsigned char>(0);
As I understand we create a openCV mat object. Read the image into it. But why we use filename.c_str()? instead of just filename? And why we convert from BGR to RGBA?
cv::cvtColor(image, imageRGBA, CV_BGR2RGBA); I read in the documentation that imread reads the image as RGB not BGR.
The most confusing for we is this part:
*inputImage = (uchar4 *)imageRGBA.ptr<unsigned char>(0);
*greyImage = imageGrey.ptr<unsigned char>(0);
What's happening here? why we need all this casts?
I know this is a lot of question, but I really want to know whats happening here.)
imread takes a const char* as first argument and you cannot pass a std::string directly to it
OpenCV stores matrices as BGR. So also imread adheres to this channel order (documentation might be misleading, don't confuse image format read (RGB) versus internal representation (BGR)). Based on your cuda tag I guess somebody wants to pass the image data to the GPU. GPUs typically work with RGBA format. It is not only about BGR<->RGB but also about having four channels in the interleaved format.
The Mat::ptr() is templated (it is not casting!) because Mat hides the datatype from you. The code is risky, as it just assumes imread would create a Mat_<uchar> and so this is the right type to access. It would be better to start with a cv::Mat_<uchar> in the first place, then use Mat_<T>::operator[] to get a pointer to the first row etc.
I don't know what comes next in your code but there might be a bug if the stride (step) is not considered.