I have a C++ function that is to be called from someone else's C# application. As input my function is given an array of signed short integers, the dimensions of the image it represents, and memory allocated for the returning data, namely another array of signed short integers. This would represent my function's header:
my_function (short* input, int height, int width, short* output)
Inside my function I create a cv::Mat from input, like this:
cv::Mat mat_in = cv::Mat (height, width, CV_16S, input);
This mat_in is then converted to CV_32F and processed by OpenCV's cv::bilateralFilter. After it returns cv::Mat mat_out, I convert the data back to CV_16S (bilateralFilter only accepts CV_8U and CV_32F). Now I need to convert this cv::Mat mat_out back to an array of short integers so that it may be returned to the calling function. This is my code:
my_function (short* input, int height, int width, short* output)
{
Mat mat_in_16S = Mat (height, width, CV_16S, input);
Mat mat_in_32F = Mat (height, width, CV_32F);
Mat mat_out_CV_32F = Mat (height, width, CV_32F);
mat_in_16S.convertTo (mat_in_32F, CV_32F);
bilateralFilter (mat_in_32F, mat_out_32F, 5, 160, 2);
Mat mat_out_16S = Mat (mat_in_16S.size(), mat_in_16S.type());
mat_out_32F.convertTo (mat_out_16S, CV_16S);
return 0;
}
Obviously, somewhere there at the end I need to get the data that is in mat_out_16S into output. My first try was to return a reference:
output = &mat_out_16S.at<short>(0,0);
but of course I realised that this was a silly idea, as mat_out_16S goes out of scope as soon as the function returns, leaving output pointing at emptiness. Currently my best attempt is as follows (from this question):
memcpy ((short*)output, (short*)mat_out_16S.data, height*width*sizeof(short));
Now I would like to know, is there a better way? It feels kind of inefficient to copy all this data, but I don't see what else I can do. I can't return a cv::Mat unfortunately. If there is no better way, is my current memcpy method safe at least? My data are all 2-byte signed short integers, so I don't think there should be issues with padding, but I don't want to run into any unpleasant surprises.
You can use this constructor for your mat_out_16S:
Mat::Mat(Size size, int type, void* data, size_t step=AUTO_STEP)
So your function will be:
my_function (short* input, int height, int width, short* output)
{
Mat mat_in_16S = Mat (height, width, CV_16S, input);
Mat mat_in_32F = Mat (height, width, CV_32F);
Mat mat_out_CV_32F = Mat (height, width, CV_32F);
mat_in_16S.convertTo (mat_in_32F, CV_32F);
bilateralFilter (mat_in_32F, mat_out_32F, 5, 160, 2);
Mat mat_out_16S = Mat (mat_in_16S.size(), mat_in_16S.type(), output);
mat_out_32F.convertTo (mat_out_16S, CV_16S);
return 0;
}
Related
I am trying to convert this c api to c++
IplImage* hbm0 = cvCreateImage(cvSize(hbmp->width,hbmp->height),hbmp->depth, hbmp->nChannels );
I tried doing like this,
cv::Mat hbm0 = cv::Mat(hbmp.cols,hbmp.rows,hbmp.depth(), hbmp.channels() )
since mat constuctor doesnot seem to have parameters for depth and channels
Mat (int rows, int cols, int type, void *data, size_t step=AUTO_STEP)
what could be the best way to convert,as i am not directly using channels they are in a Mat object itself.
Thanks
I have an image which I am putting into a vector of uchar which I am sending from processor to processor using open mpi, however I need to put the vector of uchar into a cv::Mat.
Is there an easy way to do this?
cv::Mat image_on_proc = newpopulation(cv::Rect(0, start, population.cols, rows_in_section));
std::vector<byte> img = matToBytes(image_on_proc);
std::vector<uchar> test;
for(int i=0; i<image_on_proc.rows; i++)
{
for(int j=0; j<image_on_proc.cols; j++)
{
test.push_back(image_on_proc.at<uchar>(i,j));
}
}
MPI_Barrier(MPI_COMM_WORLD);
MPI_Send(&test[0],test.size()*sizeof(uchar), MPI_BYTE, 0, 99, MPI_COMM_WORLD);
The above shows how I put the image into the vector and send it, there is a corresponding recv and it works, however I do not know how to turn it back into an image.
You can store a Mat into a vector<uchar> using:
Mat img = ...; // img must be CV_8UC1 in this example
vector<uchar> v(img.begin<uchar>(), img.end<uchar>());
and back into a Mat using:
Mat img2(img.rows, img.cols, img.type(), v.data());
Note that here you're not copying values, but just creating a Mat header for the data in v, so img2 will reflect any change done on v. If v goes out of scope, then img2 becomes invalid. You can simply use clone() to copy data, like:
Mat img3 = Mat(img.rows, img.cols, img.type(), v.data()).clone();
I'm working with a Ximea Camera, programming in c++ and using Ubuntu 14.04. I have a XI_IMG image and with the next conversion I'm creating an OpenCV image, copying data from xiAPI buffer to OpenCV buffer.
stat = xiGetImage(xiH, 5000, &image);
HandleResult(stat,"xiGetImage");
XI_IMG* imagen = ℑ
IplImage * Ima = NULL;
char fname_jpg[MAX_PATH] = "";
Ima = cvCreateImage(cvSize(imagen->width, imagen->height), IPL_DEPTH_8U, 1);
memcpy(Ima->imageData, imagen->bp, imagen->width * imagen->height);
imwrite("image1", Ima);
After doing that I should be able to save or show the image, but the next error is shown:
program.cpp:76:24:error:invalid initialization of reference of type 'cv::InputArray {aka const cv::_InputArray&}' from expression of type 'IplImage* {aka IplImage*}'
Is there any other way to obtain or save the image? What else can I do to save a jpg image?
You are mixing old (and obsolete) C syntax like IplImage*, cv<SomeFunction>(), etc... with current C++ syntax.
To make it work be consistent and use only one style.
Using IplImage
int main()
{
IplImage* img = NULL;
img = cvCreateImage(...);
// Save
cvSaveImage("myimage.png", img);
// Show
cvShowImage("Image", img);
cvWaitKey();
return 0;
}
Or using new syntax (much better):
int main()
{
Mat img(...);
// Save
imwrite("myimage.png", img);
// Show
imshow("Image", img);
waitKey();
return 0;
}
Note that you don't need to memcpy the data after you initialize your Mat, but you can call one of these constructors:
C++: Mat::Mat(int rows, int cols, int type, void* data, size_t step=AUTO_STEP)
C++: Mat::Mat(Size size, int type, void* data, size_t step=AUTO_STEP)
C++: Mat::Mat(int ndims, const int* sizes, int type, void* data, const size_t* steps=0)
Last trick, you can wrap your IplImage in a Mat and then use imwrite:
Mat mat(Ima);
imwrite("name.ext", mat);
I'm having problems with putting text into an image.
cvPutText (img, text, cvPoint(pos.x,pos.y), &font, cvScalar(255,255,0));
I'm not sure why, but img in the above code is underlined and it says no suitable conversion cv::Mat to cv::Arr. Here's a condensed version of my program. Thanks for any suggestions. I put notes nest to code.
//function
double getOrientation(vector<Point> &pts, Mat &img)
{
//this works, no problem with img matrix
circle(img, pos, 3, CV_RGB(255, 0, 255), 2);
//i copied next 9 lines from another working program
char text[255];
sprintf(text, "%d", (int)angle);
CvFont font;
int fontface;
double hScale=1.0;
double vScale=1.0;
int lineWidth=1;
cvInitFont(&font,CV_FONT_HERSHEY_PLAIN, hScale,vScale,0,lineWidth);
//here's my problem. if I erase this line, no bugs. Again, img is underlined
//I've tried using &img, and I get a "bad argument (unknown array type) in unknown function. c:\slave\builds\winstallermegapack\src\opencv.... \matrix.cpp
cvPutText (img, text, cvPoint(pos.x,pos.y), &font, cvScalar(255,255,0)); }
int main()
{
Mat img = imread("c:\\picture.jpg"); // here i read jpg and store into img
getOrientation(contours[i], img); // function
}
imshow("Image", img);
char key;
while (true)
It looks like you are mixing legacy cv functions and clases.
IF you are using OpenCV 2.x you should call putText:
putText(Mat& img, const string& text, Point org, int fontFace, double fontScale, Scalar color, int thickness=1, int lineType=8, bool bottomLeftOrigin=false )
For example, a random copy paste from my code:
putText(Image,
text,
Point(a,b),
FONT_HERSHEY_COMPLEX_SMALL,
1,
cvScalar(0,0,0),
1,
CV_AA);
Attempting to do histogram back-projection on a three-channel image results in the following error:
OpenCV Error: Assertion failed (j < nimages) in histPrepareImages, file ../modules/imgproc/src/histogram.cpp, line 148
The code which fails:
cv::Mat _refImage; //contains reference image of type CV_8UC3
cv::Mat output; //contains image data of type CV_8UC3
int histSize[] = {16, 16, 16};
int channels[] = {0, 1, 2};
const float hRange[] = {0.f, 256.f};
const float* ranges[] = {hRange, hRange, hRange};
int nChannels = 3;
cv::Mat hist;
cv::calcHist(&_refImage, 1, channels, cv::noArray(), hist, nChannels, histSize, ranges);
cv::calcBackProject(&output, 1, channels, hist, output, ranges); //This line causes assertion failure
Running nearly identical code on a single-channeled image works. According to the documentation, multi-channel images are also supported. Why won't this code work?
The short answer is that cv::calcBackProject() does not support in-place operation, although this is not mentioned in the documentation.
Explanation
Digging into the OpenCV source yields the following snippet:
void calcBackProject( const Mat* images, int nimages, const int* channels,
InputArray _hist, OutputArray _backProject,
const float** ranges, double scale, bool uniform )
{
//Some code...
_backProject.create( images[0].size(), images[0].depth() );
Mat backProject = _backProject.getMat();
assert(backProject.type() == CV_8UC1);
histPrepareImages( images, nimages, channels, backProject, dims, hist.size, ranges,
uniform, ptrs, deltas, imsize, uniranges );
//More code...
}
The line which causes the problem is:
_backProject.create( images[0].size(), images[0].depth() );
which, if the source and destination are the same, reallocates the input image data. images[0].depth() evaluates to CV_8U, which is numerically equivalent to the type specifier CV_8UC1. Thus, the data is created as a single-channel image.
This is a problem because histPrepareImages still expects the input image to have 3 channels, and the assertion is thrown.
Solution
Fortunately, the workaround is simple. The output parameter must be different from the input, like so:
cv::Mat result;
cv::calcBackProject(&output, 1, channels, hist, result, ranges);