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
Related
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.
I've c++ and OpenCV 3.1 and i separated the RGB three channels with these code :
Mat src = imread("original_image.jpg",CV_LOAD_IMAGE_COLOR);
Mat bgr[3] , bluemat ;
split(src,bgr);
bluemat = bgr[0];
std::cout << "bluemat.at<int>(0,1); : " << bluemat.at<int>(0,1) << '\n';
The strange thing is it print out a big number : 1415208581 , why is that ?
Isn't it suppose to be in 0-255 range ? why it is not ?
(expanding comment for search)
A feature of openCV is that the cv::Mat image type is a very simple object based on the original C version of the library.
The cv::Mat contains a field giving the pixel type but the data is stored simply as a block of memory bytes. You can use this block of memory to store and access pixels however you want. It makes it very flexible and easy to link with other libraries, but means you need to manually keep track of what the data is.
So data.at<int>(0,0) will extract the first pixel as if the data was ints (whatever int is on your platform), data.at<uchar> (0,0) will get the first pixel as a byte (which is generally what you want).
The main difference here is casting the memory byte to uchar vs int. What form the data you have depends on how you read it in. CV_LOAD_IMAGE_COLOR read the image as 16-bit/32-bit values. I cannot compile the code you gave me becuase of the memory issues that are created by converting that data to an int.
Now, if you use uchar, that will be solved. The problem however with the cout printing a character, has to do with the overload functions of <<
Mat src = imread("1.jpg", CV_LOAD_IMAGE_COLOR);
Mat bgr[3], bluemat;
split(src, bgr);
bluemat = bgr[0];
std::cout << "bluemat.at<uchar>(0,1); : " << (int)bluemat.at<uchar>(0, 1) << '\n';
What I changed was just to insert the typecast int. This issue is expanded here, but tells the output stream that this is a number, not a character.
I just installed opencv 2.4.10. I use the free version of visual studio.
It compiles opencv commands and I was able to present my computer camera input on a window.
However, it seems that any attempt to access the values in the Mat object fails. If I start with (for example):
Mat M;
M.create(4, 4, CV_8UC(2));
I can see that M is not empty but:
double b = M.at<double>(0, 0);
results in:
Unhandled exception at 0x75C92F71 in myOpenCVStudy.exe: Microsoft C++ exception: std::bad_alloc at memory location 0x006AECD4.
int a = countNonZero(M);
results in:
Unhandled exception at 0x75C92F71 in myOpenCVStudy.exe: Microsoft C++ exception: cv::Exception at memory location 0x00ADF868.
and:
cout << "M = " << endl << " " << M << endl << endl;
results in:
Unhandled exception at 0x734ADE19 (msvcp100.dll) in myOpenCVStudy.exe: 0xC0000005: Access violation reading location 0x00000000.
For the second case I get a "No Symbols Loaded". Something about C:\Windows\SysWoW64\KernelBase.dll. Saying I need to change PDB and binary search paths (Microsoft Symbol Servers), how it is done.
Would appreciate your help,
Thanks,
Yair
When you created your Mat, you specify the type as two channels of 8-bit unsigned data by passing CV_8UC(2) to the create function:
M.create(4, 4, CV_8UC(2));
Attempting to access 8 bytes by casting to double will cause problems:
double b = M.at<double>(0, 0);
So, you need to access using an appropriate structure like Vec2b, which contains two 8-bit bytes:
Vec2b b = M.at<Vec2b>(0, 0);
Now you can access the values from each of the two channels:
unsigned char uCH1 = b[0];
unsigned char uCH2 = b[1];
The reason that countNonZero crashes is probably because according to the documentation, it is expecting a single-channel input, not a two-channel input. Running in Debug mode should have resulted in an appropriate assertion.
If you really do want a single-channel image of doubles, just change the type to CV_64F when you create it:
Mat M;
M.create(4, 4, CV_64F);
double b = M.at<double>(0, 0);
int a = countNonZero(M);
Your image has two channels, use cv::Vec2b instead of double
cv::Mat M;
M.create(4, 4, CV_8UC(2));
...
cv::Vec2b b = M.at<cv::Vec2b>(0, 0);
Try to create the mat this way:
cv::Mat M = cv::Mat(2,2, CV_8UC3, Scalar(0,0,255));
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 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();