Segmentation faults when modifying cv::Mat data in c++ - c++

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);

Related

copy an opencv::Mat to a Mat*

Apologies if this is a stupid question, I am not the most experienced with using pointers in c++.
I am using openCv, and have a cv::Mat initialized as:
cv::Mat* frame;
I also have another Mat, filled with data initialized as:
cv::Mat frameRaw;
Obviously one is a pointer, and the other is not.
I need to copy the data from frameRaw into frame.
I have tried *frame = frameRaw; but it gives me an exception error.
How should I do this?
Thanks.
Thank you for pointing out my mistake. I had not initialized the Mat properly. Adding
frame = new cv::Mat();
fixes the issue.
See the code snippet, hope this will answer you:
Mat * f = new Mat(2,2,CV_32FC1);
f->at<float>(0,0) = 1;
f->at<float>(0,1) = 2;
f->at<float>(1,0) = 3;
f->at<float>(1,1) = 4;
Mat ff;
f->copyTo(ff);
cout<<ff;

OpenCV - How to write IplImage array in Mat form?

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

Cannot access IplImage data

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.

Save cv::Mat data for later usage using NO C++ constructs

I'm using OpenCV within a DLL that provides plain C interfaces, no C++objects are allowed to be handed over to the calling application.
One part of this DLL performs fiducial learning for later pattern recognition which results in a list of keypoints and a Mat object. These data have to be stored by the calling application.
Handing over the keypoints via DLL interface is no problem by using a plain C struct, the members of such a keypoint can be converted easily. But I don't see which parts of cv::Mat are really needed. Or to be more exact: my Mat-object makes use of the member "data" which points to a memory area but I have no idea how much data are contained.
So my question: how can I convert a cv::Mat object into a plain C-style structure, how can I estimate the exact length of the data field?
Thanks!
The easy way is to convert cv::Mat to the classical OpenCV C structure: IplImage.
cv::Mat mat = imread(...);
IplImage img(mat); // hope it's the correct syntax...
A more detailed explanation of the Mat parameters:
data: pointer to data
rows, columns: ...
type() - data type:
channels() - number of channels
step() - stride between two consecutive rows in the image, in bytes. "Includes the gaps, if any"
size_t elemSize() similar to CV_ELEM_SIZE(cvmat->type)
size_t elemSize1() returns the size of element channel in bytes.
And here's how you calculate data field length:
Mat::rows * Mat::step()
If you need to pass a raw pointer to image data, then in the worst case you'll have to do some copying with pointer magic, because image data may not be continous. It is well described in this tutorial.
int channels = I.channels();
int nRows = I.rows * channels;
int nCols = I.cols;
if (I.isContinuous())
{
nCols *= nRows;
nRows = 1;
}
int i,j;
uchar* p;
for( i = 0; i < nRows; ++i)
{
p = I.ptr<uchar>(i);
// And here "p" points to "nCols" components
// row size = nCols * channels * component size (1 byte usually)
}

OpenCV image array, 4D matrix

I am trying to store a IPL_DEPTH_8U, 3 channel image into an array so that I can store 100 images in memory.
To initialise my 4D array I used the following code (rows,cols,channel,stored):
int size[] = { 324, 576, 3, 100 };
CvMatND* cvImageBucket; = cvCreateMatND(3, size, CV_8U);
I then created a matrix and converted the image into the matrix
CvMat *matImage = cvCreateMat(Image->height,Image->width,CV_8UC3 );
cvConvert(Image, matImage );
How would I / access the CvMatND to copy the CvMat into it at the position of stored?
e.g. cvImageBucket(:,:,:,0) = matImage; // copied first image into array
You've tagged this as both C and C++. If you want to work in C++, you could use the (in my opinion) simpler cv::Mat structure to store each of the images, and then use these to populate a vector with all the images.
For example:
std::vector<cv::Mat> imageVector;
cv::Mat newImage;
newImage = getImage(); // where getImage() returns the next image,
// or an empty cv::Mat() if there are no more images
while (!newImage.empty())
{
// Add image to vector
imageVector.push_back(image);
// get next image
newImage = getImage();
}
I'm guessing something similar to:
for ith matImage
memcpy((char*)cvImageBucket->data+i*size[0]*size[1]*size[2],(char*)matImage->data,size[0]*size[1]*size[2]);
Although I agree with #Chris that it is best to use vector<Mat> rather than a 4D matrix, this answer is just to be a reference for those who really need to use 4D matrices in OpenCV (even though it is a very unsupported, undocumented and unexplored thing with so little available online and claimed to be working just fine!).
So, suppose you filled a vector<Mat> vec with 2D or 3D data which can be CV_8U, CV_32F etc.
One way to create a 4D matrix is
vector<int> dims = {(int)vec.size(), vec[0].rows, vec[0].cols};
Mat m(dims, vec[0].type(), &vec[0]);
However, this method fails when the vector is not continuous which is typically the case for big matrices. If you do this for a discontinuous matrix, you will get a segmentation fault or bad access error when you would like to use the matrix (i.e. copying, cloning, etc). To overcome this issue, you can copy matrices of the vector one by one into the 4D matrix as follows:
Mat m2(dims, vec[0].type());
for (auto i = 0; i < vec.size(); i++){
vec[i].copyTo(temp.at<Mat>(i));
}
Notice that both methods require the matrices to be the same resolution. Otherwise, you may get undesired results or errors.
Also, notice that you can always use for loops but it is generally not a good idea to use them when you can vectorize.