OpenCV - How to write IplImage array in Mat form? - c++

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

Related

Segmentation faults when modifying cv::Mat data in 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);

OpenCV: How to write vector<point> to image

cv::Mat in = cv::imread("SegmentedImage.png");
// vector with all non-white point positions
std::vector<Point> nonWhiteList;
nonWhiteList.reserve(in.rows*in.cols);
// add all non-white points to the vector
for(int j=0; j< in.rows; ++j)
{
for(int i=0; i<in.cols; ++i)
{
// if not white: add to the list
if(in.at<cv::Vec3b>(j,i) != cv::Vec3b(255,255,255))
{
nonWhiteList.push_back(cv::Point(i,j));
}
}
}
cv::Mat BKGR = imread("photo_booth_Cars.png", CV_LOAD_IMAGE_COLOR); //1529x736
I need to write the vector<Point> nonWhiteList to image BKGR, How to do it?
Basically, need to remove the white background from the image and put non-white points on another background image. Researched very much on grabcut and findcontours.
I am completely new to Opencv. Thanks so much for help.
cv::Mat BKGR = imread("photo_booth_Cars.png", CV_LOAD_IMAGE_COLOR); //1529x736
for(int j=0; j<in.rows; ++j)
for(int i=0; i<in.cols; ++i)
{
if(in.at<cv::Vec3b>(j,i) != cv::Vec3b(255,255,255))
{
BKGR.at<cv::Vec3b>(j,i) = in.at<cv::Vec3b>(j,i);
}
}
cv::imwrite("newFinalImage.png", BKGR);
If the images are of same dimensionality than it makes sense else it is difficult to copy directly unless you know the 2 image camera parameters. However, you may use Interpolation to get the two images to same size.
If the images are same size, than why do you need to create a std::vector than copy it to another cv::Mat. You can achieve this in the same loop without extra computation overhead. Just simple as filling an array. However your question is ambiguous.

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.

How to change cv::Mat image dimensions dynamically?

I would like to declare an cv::Mat object and somewhere else in my code, change its dimension (nrows and ncols). I couldn't find any method in the documentation of OpenCV. They always suggest to include the dimension in the constuctor.
An easy and clean way is to use the create() method. You can call it as many times as you want, and it will reallocate the image buffer when its parameters do not match the existing buffer:
Mat frame;
for(int i=0;i<n;i++)
{
...
// if width[i], height[i] or type[i] are != to those on the i-1
// or the frame is empty(first loop)
// it allocates new memory
frame.create(height[i], width[i], type[i]);
...
// do some processing
}
Docs are available at https://docs.opencv.org/3.4/d3/d63/classcv_1_1Mat.html#a55ced2c8d844d683ea9a725c60037ad0
If you mean to resize the image, check resize()!
Create a new Mat dst with the dimensions and data type you want, then:
cv::resize(src, dst, dst.size(), 0, 0, cv::INTER_CUBIC);
There are other interpolation methods besides cv::INTER_CUBIC, check the docs.
Do you just want to define it with a Size variable you compute like this?
// dynamically compute size...
Size dynSize(0, 0);
dynSize.width = magicWidth();
dynSize.height = magicHeight();
int dynType = CV_8UC1;
// determine the type you want...
Mat dynMat(dynSize, dynType);
If you know the maximum dimensions and only need to use a subrange of rows/cols from the total Mat use the functions cv::Mat::rowRange and/or cv::Mat::colRange
http://docs.opencv.org/modules/core/doc/basic_structures.html#mat-rowrange

Converting cv::Mat to IplImage*

The documentation on this seems incredibly spotty.
I've basically got an empty array of IplImage*s (IplImage** imageArray) and I'm calling a function to import an array of cv::Mats - I want to convert my cv::Mat into an IplImage* so I can copy it into the array.
Currently I'm trying this:
while(loop over cv::Mat array)
{
IplImage* xyz = &(IplImage(array[i]));
cvCopy(iplimagearray[i], xyz);
}
Which generates a segfault.
Also trying:
while(loop over cv::Mat array)
{
IplImage* xyz;
xyz = &array[i];
cvCopy(iplimagearray[i], xyz);
}
Which gives me a compile time error of:
error: cannot convert ‘cv::Mat*’ to ‘IplImage*’ in assignment
Stuck as to how I can go further and would appreciate some advice :)
cv::Mat is the new type introduce in OpenCV2.X while the IplImage* is the "legacy" image structure.
Although, cv::Mat does support the usage of IplImage in the constructor parameters, the default library does not provide function for the other way. You will need to extract the image header information manually. (Do remember that you need to allocate the IplImage structure, which is lack in your example).
Mat image1;
IplImage* image2=cvCloneImage(&(IplImage)image1);
Guess this will do the job.
Edit: If you face compilation errors, try this way:
cv::Mat image1;
IplImage* image2;
image2 = cvCreateImage(cvSize(image1.cols,image1.rows),8,3);
IplImage ipltemp=image1;
cvCopy(&ipltemp,image2);
(you have cv::Mat old)
IplImage copy = old;
IplImage* new_image = ©
you work with new as an originally declared IplImage*.
Here is the recent fix for dlib users link
cv::Mat img = ...
IplImage iplImage = cvIplImage(img);
Personaly I think it's not the problem caused by type casting but a buffer overflow problem; it is this line
cvCopy(iplimagearray[i], xyz);
that I think will cause segment fault, I suggest that you confirm the array iplimagearray[i] have enough size of buffer to receive copyed data
According to OpenCV cheat-sheet this can be done as follows:
IplImage* oldC0 = cvCreateImage(cvSize(320,240),16,1);
Mat newC = cvarrToMat(oldC0);
The cv::cvarrToMat function takes care of the conversion issues.
In case of gray image, I am using this function and it works fine! however you must take care about the function features ;)
CvMat * src= cvCreateMat(300,300,CV_32FC1);
IplImage *dist= cvCreateImage(cvGetSize(dist),IPL_DEPTH_32F,3);
cvConvertScale(src, dist, 1, 0);
One problem might be: when using external ipl and defining HAVE_IPL in your project, the ctor
_IplImage::_IplImage(const cv::Mat& m)
{
CV_Assert( m.dims <= 2 );
cvInitImageHeader(this, m.size(), cvIplDepth(m.flags), m.channels());
cvSetData(this, m.data, (int)m.step[0]);
}
found in ../OpenCV/modules/core/src/matrix.cpp is not used/instanciated and conversion fails.
You may reimplement it in a way similar to :
IplImage& FromMat(IplImage& img, const cv::Mat& m)
{
CV_Assert(m.dims <= 2);
cvInitImageHeader(&img, m.size(), cvIplDepth(m.flags), m.channels());
cvSetData(&img, m.data, (int)m.step[0]);
return img;
}
IplImage img;
FromMat(img,myMat);