I am trying to use NETLink to control a camera exposure in our experiment. I have successfully connected to the camera and taken images. However, downloading it off the camera is trickier. The code that works in C# looks like this.
//Expose for image and wait for triggering
ApogeeCamera.Expose(0.1, true);
do { } while (ApogeeCamera.ImagingStatus != Apn_Status.Apn_Status_ImageReady);
//Download image from camera
ushort[] pixels = new ushort[width * height];
unsafe
{
fixed (ushort* ptr = pixels)
{
int ptrValue = (int)ptr;
ApogeeCamera.GetImage(ptrValue);
}
}
The signature for the GetImage process is
4.7 GetImage
4.7.1 Format:
GetImage(long pImageBuffer)
4.7.2 Parameters:
pImageBuffer: Returns a pointer to 16 bit, unsigned short data located in memory. The image
data region should be allocated by the application prior to calling this method.
4.7.3 Description:
The GetImage method returns a pointer to a previously-allocated region of memory (allocated by
the calling application) that will be filled with image data.
The application must take care to assure that it properly allocates the image memory region
before calling this method.
My understanding is that I have to pass the pointer that points to a preallocated array of the right size to GetImage. My attempt in Mathematica looks like this
camera@Expose[0.1, True];
While[camera@ImagingStatus@ToString[] != "Apn_Status_ImageReady",
Null];
buf = Table[0, {imgHeight*imgWidth}];
camera@GetImage[buf]
NET::methodargs: Improper arguments supplied for method named GetImage.
buf = 2;
camera@GetImage[buf]
NET::netexcptn: A .NET exception occurred: System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
at NETApogee.Camera2Class.GetImage(Int32 pImageBuffer).
Neither passing in an integer nor an array works. I feel like I need to use a pointer. I can't use DefineDLLFunction either, as the method belongs to a class that is in an assembly. I don't know how to use DefineDLLFunction in this situation anyway.
My sleep deprived self greatly appreciate any help.
Update - working version
@WReach, thank you for your help. Here's the working version of Mathematica/NETLink code, based off of WReach's
camera@Expose[0.1, True];
While[camera@ImagingStatus@ToString[] != "Apn_Status_ImageReady",
Null];
LoadNETType["System.Runtime.InteropServices.GCHandle"];
LoadNETType["System.Runtime.InteropServices.GCHandleType"];
buffer = NETNew["System.UInt16[]", imgHeight*imgWidth];
hBuffer = GCHandle`Alloc[buffer, GCHandleType`Pinned];
pBuffer = hBuffer@AddrOfPinnedObject[]@ToInt32[];
camera@GetImage[pBuffer];
hBuffer@Free[];
pixels = buffer // NETObjectToExpression;
Dimensions[pixels]
(* {1048576} *)
shapedPixels = ArrayReshape[pixels, {imgHeight, imgWidth}];
Image[shapedPixels, "Bit16"]
And you are rewarded with a grainy picture of my laser probe beam.
Thank you WReach. I would have never figured this one out on my own.
Update 10/5/15
@WReach, your answer makes a lot of sense. Your example works perfectly fine on my copy of Mathematica. However, I can't seem to get it to work with GetImage Method. Using NETTypeInfo, the signature of GetImage is this
virtual "void" GetImage "(int pImageBuffer)"
It seems that Mathematica/NETLink convert from the parameter type from long to int. I have tried the both of the following codes.
buf = NETNew["System.Int32[]", imgHeight*imgWidth];
camera@GetImage[buf@Address[buf]]
buf = NETNew["System.UInt16[]", imgHeight*imgWidth];
camera@GetImage[buf@Address[buf]]
both gave the same error of "Improper arguments supplied for method named GetImage."
I suspect this is because of some funny conversion that NETLink does to import the class into Mathematica. I can't seem to figure it out though. I have noticed that System.Int32 has this method
"int*" Address "(int noname)"
Could we maybe try to get this to work somehow? I have tried without success.