Reputation: 81
What I am trying to do seems simple in concept. But, I am having a hard time with it (error message at the bottom). I figured out most of it using EMGU documentation and prior questions here.
I have a windows form with an Imagebox control and a button. On button click, I want to load a image (from file) and pass it to a C++ dll that can take the imagefile and pass the same file back to the C# program which can be displayed on the Imagebox control
C#
private void rbtnLoadImage_Click(object sender, EventArgs e)
{
Image<Bgr, Int32> img1 = new Image<Bgr, Int32>("abc_color.jpg");
IntPtr pnt = CvInvoke.cvCreateImageHeader(new Size(img1.Width, img1.Height), IPL_DEPTH.IPL_DEPTH_32S , img1.NumberOfChannels);
Marshal.StructureToPtr(img1.MIplImage, pnt, false);
MIplImage mptr = (MIplImage)Marshal.PtrToStructure(testIplImagePass(pnt), typeof(MIplImage));
Image<Bgr, Int32> img2 = Image<Bgr, Int32>.FromIplImagePtr(mptr.imageData);
imgbox.Image = img2;
}
[DllImport("xyz.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr testIplImagePass(IntPtr imagevar);
C++
extern "C" { __declspec(dllexport) IplImage* testIplImagePass(IplImage* imagevar); }
IplImage* testIplImagePass(IplImage* imagevar)
{
return imagevar;
}
The error message I got is "An unhandled exception of type 'System.NotImplementedException' occurred in Emgu.CV.dll. Additional information: Loading of 239, 249 channel image is not implemented."
It occurred on the line
Image<Bgr, Int32> img2 = Image<Bgr, Int32>.FromIplImagePtr(mptr.imageData);
What am I doing wrong here? Any advice is appreciated.
Upvotes: 0
Views: 1995
Reputation: 11
This method is light, minimally requiring only one copy of the image data (in C# from managed to unmanaged memory).
Additionaly it is safe code (no C# pointers or fixing).
The code is tailored for the <Bgr, Byte>
. For a general version you also need to pass the element size and number
of channels between C# and C++.
C# Part:
Image<Bgr, Byte> img = ImageSource();
int colWidth = img.Cols * 3;
int img_size = img.Rows * colWidth;
IntPtr unmanaged = Marshal.AllocHGlobal(img_size);
//Copy img data into unmanaged memory
for (int i = 0; i < img.Rows; i++)
Marshal.Copy(img.Bytes, i * img.MIplImage.WidthStep, img_unmanaged + i * colWidth,
colWidth);
//Call c++ function with
//void FooCpp(IntPtr unmanaged, int rows, int cols);
FooCpp(unmanaged, rows, cols);
//If the values for cols and rows have changed you need to pass them back to c#
int newColWidth = newCols*3;
//Get back the data: outImg constructor DOES NOT COPY the data
//You can directly use outImg as long as unmanaged lives.
Image<Bgr, Byte> outImg = new Image<Bgr, Byte>(newCols, newRows, newColWidth, unmanaged);
img = outImg.Clone();
Marshal.FreeHGlobal(unmanaged);
unmanaged = IntPtr.Zero;
//continue to use img here
C++ Part:
void FooCpp(unsigned char* imgPtr, int rows, int cols){
//Optionally: create a cv::Mat image or just use the image data
cv::Mat img(rows, cols, CV_8UC3, imgPtr);
//use and modify img here.
//Can also use imgPtr directly as long as you know what you're doing
//Passback: you can relax row and cols limits as long as the size is smaller
int newRows = std::min(rows, img.rows);
int newCols = std::min(cols, img.cols);
if (img.elemSize() == 3)
for (int i = 0; i < newRows; i++)
memcpy(imgPtr + i*newCols*img.elemSize(), img.data + i*img.step,
newCols*img.elemSize());
}
Upvotes: 1