How to clear the cv::Mat contents? - c++

I have a cv::Mat but I have already inserted it with some values, how do I clear the contents in it?

If you want to release the memory of the Mat variable use release().
Mat m;
// initialize m or do some processing
m.release();
For a vector of cv::Mat objects you can release the memory of the whole vector with myvector.clear().
std::vector<cv::Mat> myvector;
// initialize myvector ..
myvector.clear(); // to release the memory of the vector

From the docs:
// sets all or some matrix elements to s
Mat& operator = (const Scalar& s);
then we could do
m = Scalar(0,0,0);
to fill with black pixels. Scalar has 4 components, the last - alpha - is optional.

You should call release() function.
Mat img = Mat(Size(width, height), CV_8UC3, Scalar(0, 0, 0));
img.release();

You can release the current contents or assign a new Mat.
Mat m = Mat::ones(1, 5, CV_8U);
cout << "m: " << m << endl;
m.release(); //this will remove Mat m from memory
//Another way to clear the contents is by assigning an empty Mat:
m = Mat();
//After this the Mat can be re-assigned another value for example:
m = Mat::zeros(2,3, CV_8U);
cout << "m: " << m << endl;

You could always redeclare it if you want to empty the mat but keep using the variable. Idk if that's what you want but since the other answer to "clearing" a mat is .release() I'd just go to mention this.
Edit: My bad. I didn't realise how unclear my answer was. I was just answering the question of "how to clear a Mat variable of it's contents". Another person had answered that one can just do .release() to the variable, like for example, the person has a variable like
cv::Mat testMat; and later on it's declared (as the question implied).
One person said that you could do a simple testMat.release(). And if that's what op wants then there you go. But in the off chance that op just wants to clear the declaration of the variable, i just thought to mention that he/she could just re-declare it, like do a simple testMat = *some new information* later on. Also, i mixed up define and declare. My bad

Related

OpenCV Mat data address shows weird value

I'm suffered by using OpenCV Mat due to unexpected results.
There is an example code:
cv::Mat local_mat = cv::Mat::eye(cv::Size(1000, 1000), CV_8UC1);
qDebug() << "1. local_mat.data: " << local_mat.data;
cv::Mat sobel_img_ = cv::Mat::eye(cv::Size(1000, 1000), CV_8UC1);
qDebug() << "2. sobel_img_.data: " << sobel_img_.data;
sobel_img_ = local_mat; // copy address but no clone()
qDebug() << "3. sobel_img_.data: " << sobel_img_.data;
sobel_img_ = cv::Mat::eye(cv::Size(1000, 1000), CV_8UC1); // renew
qDebug() << "4. sobel_img_.data: " << sobel_img_.data;
local_mat.data: 0x55aa19a53e40
sobel_img_.data: 0x55aa19b480c0
sobel_img_.data: 0x55aa19a53e40
sobel_img_.data: 0x55aa19a53e40
1 and 2 should be different because I create new Mat(), so it is fine.
However, 3 and 4 are same even though I create new Mat() after copying the local_mat into sobel_mat.
I meet many problems like this when I use OpenCV Mat.
Could you explain why it happens and how can I solve this?
Initializing of matrix is a form of matrix expression.
cv::Mat has overloads of operator=. One of them handles MatExpr as its argument:
Assigned matrix expression object. As opposite to the first form of
the assignment operation, the second form can reuse already allocated
matrix if it has the right size and type to fit the matrix expression
result. It is automatically handled by the real function that the
matrix expressions is expanded to. For example, C=A+B is expanded to
add(A, B, C), and add takes care of automatic C reallocation.
by bold font I emphasized what happens in your case. Already allocated memory is used to create identity matrix by cv::Eye.
You can turn MatExpr into cv::Mat just by casting:
sobel_img_ = (cv::Mat)cv::Mat::eye(cv::Size(1000, 1000), CV_8UC1); // renew
then sobel_img will refer to new allocated matrix.

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

Mat copyTo from row to col doesn't work! Why?

This answer states how to copy a row of a matrix to another row: How to copy a row of a Mat to another Mat's column in OpenCv?
However if I try to copy a row of a matrix to a column vector the program is ends abruptly.
Example:
Mat A(640,480,CV_64F);
Mat B(480,1,CV_64F);
A.row(0).copyTo(B.col(0));
A possible workaround is:
Mat A(640,480,CV_64F);
Mat B;
A.row(0).copyTo(B);
B = B.t();
But how can I get type CV_64F in B if A is a different type? Is the data type copied, too? And - most important – why do I need this workaround?
copyTo() function can only copy data between the matrices of same size (width and height) and same type. For this reason you can't use it directly to copy row of matrix to column of matrix. Normally OpenCV will reallocate target matrix so that its size and type will match original image, but call to col() function returns constant temporary object that can't be reallocated. As a result your program crushed.
Actually this problem can be solved. You can copy row to column (if they have same amount of elements) and you can copy data between matrices of different types. Mat provides an iterator to its data, and that means you can use all C++ functions that works with iterators. copy() function for example.
Mat A(640, 480, CV_64F);
Mat B(480, 1, CV_32S); // note that the type don't have to be the same
Mat tmp = A.row(0); // no data is copied here. it is needed only because taking iterator to temporary object is really bad idea
copy(tmp.begin<double>(), tmp.end<double>(), B.begin<int>());
Since code like the following (transposing the row only) is not supported by the API:
A.row(0).t().copyTo( B.col(0) );
The workaround is either create a temporary matrix from the row, transpose it, then copy to your target matrix
Mat temp = A.row(0);
temp = temp.t();
temp.copyTo( B.col(0) );
I would rather do something like this though:
Mat A( 640, 480,CV_64F );
Mat B( 1, 480, CV_64F ); /* rather than 480 x 1, make a 1 x 480 first */
A.row(0).copyTo( B.row(0) );
B = B.t(); /* transpose it afterwards */
I presume this is all just because the API is not supporting it yet.

cv::Mat external data not being modified after using cv::imdecode

edit: In trying to give a straight forward example of the problem it appears I left out what was causing the real issue. I have modified the example to illustrate the problem.
I am trying to use opencv to perform operations on a cv::Mat that is composed of external data.
Consider this example:
unsigned char *extern_data = new unsigned char[1280*720*3];
cv::Mat mat = cv::Mat(1280, 720, CV_8UC3, extern_data); //Create cv::Mat external
//Edit - Added cv::imdecode
mat = cv::imdecode(mat,1);
//In real implementation it would be mat = cv::imdecode(image,'1')
// where image is a cv::Mat of an image stored in a mmap buffer
mat.data[100] = 99;
std::cout << "External array: " << static_cast<int>(extern_data[100]) << std::endl;
std::cout << "cv::Mat array: " << static_cast<int>(mat.data[100]) << std::endl;
The result of this is:
> External array: 0
> cv::Mat array: 100
It is clear this external array is not being modified, therefore new memory is being allocated for the cv::Mat array. From my understanding this was not suppose to happen! This should have caused no copy operation, and mat.data should be a pointer to extern_data[0].
What am I misunderstanding?
So far the way I have got my program to work is to use std::copy. I am still wondering if there is a way to assign the result of cv::imdecode() directly to the external data.
Currently I am using
unsigned char *extern_data = new unsigned char[1280*720*3];
cv::Mat mat = cv::Mat(1280, 720, CV_8UC3, extern_data); //Create cv::Mat external
mat = cv::imdecode(mat,1);
std::copy(mat.data, mat.data + 1280*720*3, extern_data);
I just wish i could figure out how to assign the result of cv::imdecode() directly to extern_data without the additional std::copy line!

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