OpenCV: memory location issue - c++

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

Related

Why OpenCV return strange value for pixel intensity?

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.

Unable to access pixel values from greyscale image

I am reading an image of size 1600x1200 as greyscale and then trying to access pixel value at location (1201,0). I get segfault in the end as shown in comments:
Mat gray_image = imread(argv[1], CV_LOAD_IMAGE_GRAYSCALE); // Read the file
if(! gray_image.data ) // Check for invalid input
{
cout << "Could not open or find the image" << std::endl ;
return -1;
}
const int width = gray_image.cols;
const int height = gray_image.rows;
cout<<gray_image.cols<<endl; // 1600
cout<<gray_image.rows<<endl; // 1200
cout<<(int)gray_image.at<uchar>(1201,0)<<endl; //SEGFAULT
You already stated that you are reading an image of size 1600x1200, then how can you access 1201 element from a total of 1200 rows only, actually you have misunderstood the convention of mat.at<>(). I would recommend you to use mat.at<>(cv::Point(p_x, p_y)) in this way you would never get confused with rows and cols to be passed in the mat.at<>().
EDIT : However, creating a new cv::Point is not a recommended way of accessing pixels as suggested by #Micka, So consider it as a workaround and don't rely completely on cv::Point() to access a pixel. The best possible way to iterate the pixels is defined in Opencv documentation.
It should be .at(row,col) not (col,row) and you dont have a 1201th row thats why its a seg fault

cv::Exception at memory location

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

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!

Marshal::copy with CvMat

I need to send an image into a byte array using c++/cli. The image is initially in Iplimage format.
int img_sz1 = img1->width * img1->height * img1->nChannels;
array <Byte>^ hh1 = gcnew array<Byte> (img_sz1);
Marshal::Copy( (IntPtr)img->imageData, hh1, 0, img_sz1 );
and it was working fine.
I added the encoding step to send it as jpeg
CvMat* buf1 = cvEncodeImage(".jpeg", img1, jpeg_params);
img_sz1=buf1->width*buf1->height
Marshal::Copy( (IntPtr)buf1, hh1, 0, img_sz1 );
and now it compiles fine but gives me the error at the marshal:copy line
An unhandled exception of type 'System.AccessViolationException' occurred in
mscorlib.dll. Additional information: Attempted to read or write protected memory.
Any help is very appreciated.
The return of cvEncodeImage is a single-row matrix, containing the encoded image data. What you're copying now is the struct itself, e.g., the width field, the height field, etc. I believe you need to copy from buf1->data instead.