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.
Related
I simply can't understand the opencv Mat types:
CV_16UC3 is well known as unsigned 16 bit 3 channel integer. However when I access each channels, I get negative values. According to How should I name my RGB channels, using cv::Mat_ Vec3s is the keyword to access. Here is what I did:
Mat mat_l(img_height,img_width,CV_16UC3);
mat_l = imread("/home/zhao/workspace/rectified_images/l_rectified_fountain.ppm");
cout << vec_mats_l[1].at<Vec3s>(44,500)[0] << " "
<< vec_mats_l[1].at<Vec3s>(44,500)[1] << " "
<< vec_mats_l[1].at<Vec3s>(44,500)[2] << endl;
Output is : 27522 -32382 -32407
why negative values despite type beeing defined unsigned???
try imread(path, -1);
without the flag, the image will be forced into CV_8UC3
(you can check the outcome with cout << mat_l.type();)
also, preallocating mat_l has no effect at all when you use imread, it will be overwritten anyway, so better leave it empty.
"why negative values" - Vec3s is signed. the unsigned version is Vec3w
I'm new in openCV and I have many problems using it.
I have to work with a pointer on a cv::Mat and I can't even initialize it:
cv::Mat* _image_3D = new cv::Mat();
*_image_3D = cv::Mat::zeros(height,width,CV_32FC3);
Point3f vt = (Point3f)_image_3D->at<uchar>(0,0);
std::cout << vt.x << " " << vt.y << " " << vt.z<< " ";
I thought that zeros would initialize my Matrix, so normally it's possible to access the first point of the matrix, but when I execute the code I get this error:
Unhandled exception at 0x761cb727 in glwidget.exe: Microsoft C++ exception: cv::Exception at memory location 0x002eeda0.
glwidget is my application
I tried to do this to a Matrix that I loaded and it worked:
Mat mat = imread( "..XXX.jpg",0);
Point3f vt = (Point3f)mat.at<uchar>(0,0);
std::cout << vt.x << " " << vt.y << " " << vt.z << " ";
I need actually to access/edit the pixels of my matric !
Thanks!
You can create a 3 channel float Mat initialized to Zero using
cv::Mat image_3D(height, width, CV_32FC3, cv::Scalar(0.0f, 0.0f, 0.0f));
or
cv::Mat image_3D = cv::Mat::zeros(height, width, CV_32FC3);
Furthermore your access with at<uchar>() is wrong. It should be (e.g.)
Point3f vt(cv::Vec3f>(0,0));
(although there are many possible permutations, such as Point3f vt = mat.at<Vec3f>(0,0);)
In your code, Point3f vt = (Point3f)mat.at<uchar>(0,0); may work for you because
at<uchar>() returns a reference to a single byte, and when you cast it to a Point3f the correct data is adjacent to that byte. Without a deeper knowledge of C++ I wouldn't expect this to work on every compiler.
However: for me with VS2012 in release mode both of your access methods yield the same incorrect data, and in debug mode both fail the assertion in at<>() because the size of a uchar is not the same as elemSize1() for the Mat.
Perhaps you used debug mode for your first case, and release mode for the second?
If you are accessing your pixels sequntially, at<>() isa n inefficient way to do it. See http://docs.opencv.org/doc/tutorials/core/how_to_scan_images/how_to_scan_images.html#the-efficient-way
I want to convert an SVG graphic to an OpenCV Mat object. Therefore the SVG graphic is loaded into a QSvgRenderer object and afterwards converted into a QImage object from which I use its raw data to create my final Mat object:
void scaleSvg(const cv::Mat &in, QSvgRenderer &svg, cv::Mat &out)
{
if (!svg.isValid())
{
return;
}
QImage image(in.cols, in.rows, QImage::Format_ARGB32);
// Get QPainter that paints to the image
QPainter painter(&image);
svg.render(&painter);
std::cout << "Image byte count: " << image.byteCount() << std::endl;
std::cout << "Image bits: " << (int*)image.constBits() << std::endl;
std::cout << "Image depth: " << image.depth() << std::endl;
uchar *data = new uchar[image.byteCount()];
memcpy(data, image.constBits(), image.byteCount());
out = cv::Mat(image.height(), image.width(), CV_8UC4, data, CV_AUTOSTEP);
std::cout << "New byte count: " << out.size() << std::endl;
std::cout << "New depth: " << out.depth() << std::endl;
std::cout << "First bit: " << out.data[0] << std::endl;
}
Unfortunately, I get a "memory access violation" error when writing my resulting object into a file:
std::cout << (int*)out.data << std::endl; // pointer can still be accessed without errors
cv::imwrite("scaled.png", out); // memory access error
The file which is being written gets to to size of 33 Bytes not more (header data only??).
On the Internet there is some explanation of pointer ownership in cv::Mat and I thought it would be released after the last reference to it is release which should not be the case since "out" is a reference. Btw. another way to convert an SVG into a cv::Mat is always welcome. As OpenCV seem to do not support SVGs this looked like a simple way to get it done.
As constBits does indeed not work and it is not always safe to assume that the number of bytes per line will be the same (it causes a segfault for me). I found the following suggestion in StereoMatching's anwser:
cv::Mat(img.height(), img.width(), CV_8UC4, img.bits(), img.bytesPerLine()).clone()
Baradé's concern about using bits is valid, but because you clone the result the Mat will copy the data from bits, so it will not be a issue.
usually, cv::Mat is refcounted, but this case is special. if you use an external / borrowed data pointer, you'll have to clone() the mat, to make sure it owns its own copy of the pixels, else, as soon as you leave the scope of 'scaleSvg()', the Mat 'out' holds a 'dangling pointer'.
you tried to 'new' the data to be copied, unfortunately, that does not solve it (you only added another problem there).
you also would have to delete[] the uchar *data pixels on your own, too, and you don't, so your code currently combines the worst of all worlds.
instead, try :
out = cv::Mat(image.height(), image.width(), CV_8UC4, image.constBits(), CV_AUTOSTEP).clone();
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!
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