I have a buffer (uint8[] of BGR pixel data) in C holding a video frame. A pointer to this buffer is passed back by the C code to C# code as an IntPtr. I require to add a text overlay to the each frame and then pass on a pointer to the frame for further processing. I believe what I need to do (in C#) is to copy each frame to a bitmap object, get the device context of the bitmap and use then use TextOut (etc) to write text to the bitmap. I would then copy the modified bitmap frame data back to my original array.
My question is twofold:
Is this the best approach?
What is the best (fastest) way to copy the data from my IntPtr to a bitmap object.
Thanks.
The fastest way is by not copying the data. That requires that your data is in a supported pixel format, BGR sounds a bit scary but odds are high it is actually PixelFormat.Format24bppRgb.
Which then allows you to use the Bitmap(int, int, int, PixelFormat, IntPtr constructor).
I can't comment on your approach, but the fastest way to copy data using two pointers would be to do a Platform Invoke call to the memcpy function in msvcrt.dll
Code example below, taken from the WriteableBitmapEx source
internal static class NativeMethods
{
internal static unsafe void CopyUnmanagedMemory(byte* srcPtr, int srcOffset,
byte* dstPtr, int dstOffset, int count)
{
srcPtr += srcOffset;
dstPtr += dstOffset;
memcpy(dstPtr, srcPtr, count);
}
// Win32 memory copy function
[DllImport("msvcrt.dll", EntryPoint = "memcpy",
CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
private static extern unsafe byte* memcpy(byte* dst, byte* src, int count);
}
To convert an IntPtr to byte* simply use
unsafe
{
IntPtr myPtr;
byte* bytePtr = (byte*)myPtr.ToPointer();
}
Related
I am new to this kind of stuff. I'm trying to create a function which is equivalent to the histogram function of an image. I am using windows forms application to show the histogram (and load the image) and CUDA/c++ to make the histogram. I am mentioning from the beginning that I am NOT using openCV, glut, OpenGL or any other third library. Carrying on... I am trying to pass a bitmap to an unmanaged c++ DLL. The problem here is that I don't now how to reference that bitmap in the c++ code. (And even how to get the RGB out of it).
Snippets of code:
c# :
private void calculateHistogram(object sender, EventArgs e)
{
Bitmap bmp = (Bitmap)pictureBox1.Image;
unsafe {
int** matrixAcumulated;
var date = bmp.LockBits(new Rectangle(0, 0, pictureBox1.Width, pictureBox1.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, bmp.PixelFormat);
matrixAcumulated=NativeMethods.GenerateHistogram(date.Scan0, pictureBox1.Width);
bmp.UnlockBits(date);
// Write the string to a file.
System.Console.WriteLine(matrixAcumulated[0][0]);
}
}
Dll import :
using System;
using System.Runtime.InteropServices;
namespace HistogramProcessingCs
{
class NativeMethods
{
[DllImport("HistogramProcessingCpp.dll", CallingConvention = CallingConvention.StdCall)]
public static extern unsafe int** GenerateHistogram(IntPtr bmp, int dimensionImage);
}
}
c++ :
extern "C" __declspec(dllexport) int** __stdcall GenerateHistogram(unsigned char *bmp, int dimensionImage)
{
//How to refere the bitmap from the bmp pointer?
//dimensionImage is Width = Height
}
Okay I worked out something after 2 hours of googling, stackoverflowing and microsoft documentationing!
Because you're using CUDA I think you only want fast and nice solutions, that's why I tried to find a way with which you can modify data without copying it many times only because of C# and C++ connection.
That's what I've done so far.
Some things are important.
I used an integer pointer, that's a bit messy with the current code.
You can of course use instead a char pointer which makes much more sense (haven't tested this).
Another thing is the System.Drawing.Imaging.PixelFormat.
It's really important to know which you have choosen. In my example I've choosen PixelFormat.Format32bppRgb.
The byte order is (so far I learned) just how the name is.
Example:
32bppRgb stands for red, green and blue which consume each 8 bits.
However because it's this format and not 24bppRgb it consumes a whole integer(8 bits aren't used). In this case the first 8 bits arent used (left to right thinking) so to set a pixel to red it works like this. (Sorry formating didn't worked as expected...)
| 8 | 8 | 8 | 8 | consuming bits
|empty | red | green | blue | color
| 00 | FF | 00 | 00 | color code for red
So the code for red is this
=> 0x00 FF 00 00
and as decimal it's 16711680. That's where the number in the C++ comes from.
C++ Code:
Header file "NativeLibrary.h":
namespace NativeLibrary
{
extern "C" __declspec(dllexport) void __stdcall PassBitmap(int* number, int size);
}
Cpp file "NativeLibrary.cpp":
#include <NativeLibrary.h>
void NativeLibrary::PassBitmap(int* number, int size) {
for (int i = 0; i < size; i++) {
number[i] = 16711680;
}
}
C# Code:
using System.Drawing;
using System.Runtime.InteropServices;
[DllImport("NativeLibrary.dll", CallingConvention = CallingConvention.StdCall)]
public static extern void PassBitmap(IntPtr bmp, int size);
public System.Drawing.Bitmap bitmap = null;
public void GenerateAndModifyBitmap()
{
//The pixel format is essential!
System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(100, 100, System.Drawing.Imaging.PixelFormat.Format32bppRgb);
//Just which region we want to lock of the bitmap
System.Drawing.Rectangle rect = new System.Drawing.Rectangle(new System.Drawing.Point(), bmp.Size);
//We want to read and write to the data, pixel format stays the same (anything else wouldn't make much sense)
System.Drawing.Imaging.BitmapData data = bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, bmp.PixelFormat);
//This is a pointer to the data in memory. Can be manipulated directly!
IntPtr ptr = data.Scan0;
// This code is specific to a bitmap with 32 bits per pixels.
// Ignore current calculations. They are still work in progress xD
int size = bmp.Height;
size *= Math.Abs(data.Stride);
size /= 4;
//Call native function with our pointer to the data and of course how many ints we have
PassBitmap(ptr, size);
//Work is finished. Give our data back to the manager
bmp.UnlockBits(data);
bitmap = bmp;
}
This code would generate a bitmap which is completely red.
I'm working on a simple imaging software for my University, and I ran into annoying problem while getting image from camera.
There is an .dll COM library for camera Apogee Alta U57 (Library and documentation is here: http://www.ccd.com/downloads.html) and there are two possible ways of getting image from camera (provided that image is ready):
using "ICamera2 camera.image", which returns
"Returns a 2D SAFEARRAY, of type LONG (4 bytes per
element) or INTEGER (2 bytes per element), which contains
the image data. The type of data (LONG or INTEGER)
returned is controlled by the associated property of
ConvertShortToLong."
using "ICamera2.GetImage(int pImageBuffer)" which is described as:
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.
And I'm pretty well confused while using second method, because int != int* ,
and I really don't know how to pass POINTER TO 16 BIT USHORT.
My simplified method of getting image looks like this:
public unsafe uint[] getImage(int width, int height)
{
// Allocating array of image size (width * height)
// where pixel is size of unsigned int (4 BYTES)
// possible values: 0 to 4,294,967,295
uint[] pixels = new uint[width * height];
// Gets pointer to allocated array and fixes it,
// so that it won't be moved by Garbage Collector
fixed (uint* ptr = pixels)
{
camera.GetImage(ptr);
}
return pixels;
}
Anyone can explain? I'm really tired (been coding for past 10 hours) and maybe I'm missing something :(
Ok, looks like your GetImage function expects you to allocate the memory for image pixels and pass in a pointer to that allocated memory. You're also responsible for freeing up that memory when you're done with it.
I think the only change you need to do is casting your pointer to a long - the GetImage function takes a long, not a pointer (on 32-bit platforms, the pointer would be 32 bits; on 64-bit platforms, it'd be 64 bits).
// Gets pointer to allocated array and fixes it,
// so that it won't be moved by Garbage Collector
fixed (uint* ptr = pixels)
{
long ptrValue = (long) ptr;
camera.GetImage(ptrValue);
}
This cast is not terribly nice but it's safe since the number of bits is adequate for both platforms. (I suspect you already have to target either 32-bit or 64-bit platforms anyway.)
The documentation say it want an Uint16. I though you should be doing something like this
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace ConsoleApplication1
{
class Program
{
[DllImport("Apogee.dll.dll")]
extern static int CreateInstance(out IntPtr ICamera2Ptr);
[DllImport("Apogee.dll.dll")]
extern static void GetImage(ref IntPtr pImageBuffer);
static void Main(string[] args)
{
IntPtr ICamera2Ptr;
int results = CreateInstance(out ICamera2Ptr);
}
static UInt16[] getImage(int width, int height)
{
// Allocating array of image size (width * height)
// where pixel is size of unsigned int (4 BYTES)
// possible values: 0 to 4,294,967,295
// Allocate memory and calculate a byte count
//unsigned short *pBuffer = new unsigned short[ ImgXSize * ImgYSize ];
//unsigned long ImgSizeBytes = ImgXSize * ImgYSize * 2;
UInt16[] pixels = new UInt16[width * height];
IntPtr unmanaged_pPixels = Marshal.AllocHGlobal(Marshal.SizeOf(pixels));
//// External operations ApogeeCamera->ExternalShutter = true;
//ApogeeCamera->ExternalIoReadout = false;
//ApogeeCamera->IoPortAssignment = 0x08;
// Even though the exposure time will not be used, still call Expose
//ApogeeCamera->Expose( 0.001, true );
// Check camera status to make sure image data is ready
//while (ApogeeCamera->ImagingStatus != Apn_Status_ImageReady );
// Get the image data from the camera ApogeeCamera->GetImage( (long)pBuffer );
GetImage(ref unmanaged_pPixels);
pixels = (UInt16[])Marshal.PtrToStructure(unmanaged_pPixels, typeof(UInt16[]));
return pixels;
}
}
}
I have a C# DLL, whose code derives from a base class, which is written in managed C++. (I don't have any control over the base class code)
This base class (which is in managed C++) has a member
int *buffer
is expected to be memset (filled with Zeros) by the derived class (which is in C#). The derived class knows the size of the buffer.
I am using unsafe context, to access the member "int *buffer" of the base class, in the derived class. Please let me know is there any way special way to memset the buffer in "unsafe" context in c#.
I already looked into this What is the equivalent of memset in C#? for details, but I would like to know is there something specifically for "unsafe" context.
Background : This is a conversion project, where the derived class itself was in managed c++ before. Now I am converting the derived class DLL alone to C#. Also I have no control over the base class code! The current code flow is as follows: Only the derived class knows the size of the buffer. The base class creates a memory for that particular size, by getting the size of the buffer from derived, but it doesn't zero fill. The derived class Zero fills it first and then need to appropriately fill the buffer with its contents. Though strange, that is how it is.
Thanks!
Well, there is... memset. Why settle for a replacement when you can p/invoke the real thing?
[DllImport("msvcrt.dll", EntryPoint = "memset", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
public static extern IntPtr MemSet(IntPtr dest, int c, IntPtr count);
Taken from pinvoke.net
edit
As #Hans rightfully mentions in the OP comments, this is useless if you don't already know the size of *buffer.
You can code it on your own:
void memset( byte* buffer, int value, int size )
{
for( int i = 0; i < count; i++)
{
*( buffer + i ) = value;
}
}
Or you can use an API for this. Actually RtlZeroMemory sets values to zero. It's not actually memset.
[DllImport("kernel32.dll")]
static extern void RtlZeroMemory(IntPtr dst, int length);
RtlZeroMemory(buffer, bufferLength);
RtlZeroMemory is not actually an entry point in kernel32. If yo want something like that, use this in C#
public static unsafe void ZeroMemory(IntPtr Safebuffer, int count)
{
if (count == 0) return;
byte* buffer = (byte*)Safebuffer.ToPointer();
memset(buffer, count);
}
public static unsafe void ZeroMemory(byte* buffer, int count)
{
if (count == 0) return;
while (count-- > 0)
{
buffer[count] = 0;
}
}
Asked a few questions about a project I was working on, got some good feedback and made some progress. The idea is to create an application that generates images of fractals, accelerated by CUDA. I am creating the ui in C# and having a DLL do the heavy lifting.
Basically, I am allocating a byte array in C#, passing that to the dll to fill with pixel data, and then using that to create a Bitmap and display that with a Windows Forms PictureBox in the ui. Previous questions have helped - was using dll to allocate memory before, now using consistent calling convention between dll and c#, but the code still gives an System.ArgumentException at "img = new Bitmap(...)
Relevant Code:
C++
extern "C" __declspec(dllexport) void __cdecl generateBitmap(void *bitmap)
{
int width = 1920;
int height = 1080;
int *dev_bmp;
gpuErrchk(cudaMalloc((void**)&dev_bmp, (3*width*height*sizeof(int))));
kernel<<<BLOCKS_PER_GRID, THREADS_PER_BLOCK>>>(dev_bmp, width, height);
gpuErrchk(cudaPeekAtLastError());
gpuErrchk(cudaDeviceSynchronize());
gpuErrchk(cudaMemcpy(bitmap, dev_bmp, (width*height*3), cudaMemcpyDeviceToHost));
cudaFree(dev_bmp);
}
c#
public unsafe class NativeMethods
{
[DllImport(#"C:\Users\Bill\Documents\Visual Studio 2012\Projects\FractalMaxUnmanaged\Debug\FractalMaxUnmanaged.dll", CallingConvention=CallingConvention.Cdecl)]
public static extern void generateBitmap(void *bitmap);
public static Bitmap create()
{
byte[] buf = new byte[1920 * 1080 * 3];
fixed (void* pBuffer = buf)
{
generateBitmap(pBuffer);
}
IntPtr unmanagedPtr = Marshal.AllocHGlobal(buf.Length);
Marshal.Copy(buf, 0, unmanagedPtr, buf.Length);
Bitmap img = new Bitmap(1920, 1080, 3, PixelFormat.Format24bppRgb, unmanagedPtr);
Marshal.FreeHGlobal(unmanagedPtr);
return img;
}
}
//...
private unsafe void mandlebrotButton_Click(object sender, EventArgs e)
{
FractalBox1.Image = (Image)NativeMethods.create();
}
What am I still doing wrong? As far as I can tell, all the parameters are invalid, but I get an invalid parameter exception in System.Drawing when I try to create the bitmap.
I am not sure what happens exactly in your case cause you didn't specify which parameter is invalid in the exception. I see that your stride must not be correct.
stride Type: System.Int32
Integer that specifies the byte offset between the beginning of one
scan line and the next. This is usually (but not necessarily) the
number of bytes in the pixel format (for example, 2 for 16 bits per
pixel) multiplied by the width of the bitmap. The value passed to this
parameter must be a multiple of four..
So your constructor should be like this:
Bitmap img = new Bitmap(1920, 1080, 1920 * 3, PixelFormat.Format24bppRgb, unmanagedPtr);
I'm writing a C# application, and I need part of it to be in C++ for speed efficiency.
The function in C++ is exported as follow:
extern "C" __declspec(dllexport) int fastSegment(char*, int, int);
I import this function in C# as follow:
[DllImport(_dllname, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
static private extern bool fastSegment(byte[] img, int width, int height);
The image I want to process is called as follow:
fastSegment(image, 640, 480);
image is of the right size. Since I don't want to waste memory and allocate a new array, I modified the array directly in the C++ function.
What happens? Blue screen of death. I never saw it in Windows 7 before.
My function only wrote in image[0], image[1] and image[2] for testing purposes, and when I remove this everything is fine.
My guess is that the virtual machine protected the memory, but I find it strange I just can't write in it, or that the virtual machine didn't simply throw an exception. Is there a way to unprotect that buffer, or do I have to allocate a new one?
Edit:
It appiers the program runs now when I write in data. What could have been the cause of this sudden crash?
The garbage collector is allowed to move managed data. The native code is not able to detect this, so it is possible that it writes to the wrong memory address. But you can tell the .NET runtime not to move your array. E.g. with the GCHandle class:
GCHandle handle = GCHandle.Alloc(image, GCHandleType.Pinned);
try
{
fastSegment(handle.AddrOfPinnedObject(), 640, 480);
}
finally
{
// make sure to free the handle to avoid leaks!
handle.Free();
}
Edit: This is only one way, but I think it illustrates the problem. Please read about Marshaling between Managed and Unmanaged Code
You can't do that. Your array will be readonly in this case.
If you want to change your C# array in native you need to marshal it as a pointer.
Have a look at the example from Microsoft.
There is also an explanation why this is needed.
It is possible that your data is getting moved around by the GC - but if you get the crash every time you run your application it's highly unlikely thats the case. Try one of the following:
[DllImport(_dllname, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private static extern bool fastSegment(IntPtr img, int width, int height);
[ReliabilityContract(Consistency.MayCorruptProcess, Cer.None)]
static void FastSegment(byte[] data, int width, int height)
{
var length = width * height;
var ptr = Marshal.AllocHGlobal(width * height);
try
{
Marshal.Copy(data, 0, ptr, length);
fastSegment(ptr, width, height);
Marshal.Copy(ptr, data, 0, length);
}
finally
{
Marshal.FreeHGlobal(ptr);
}
}
// ---- OR ----
[DllImport(_dllname, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private static extern bool fastSegment(IntPtr img, int width, int height);
[ReliabilityContract(Consistency.MayCorruptProcess, Cer.None)]
static void FastSegment(byte[] data, int width, int height)
{
var handle = GCHandle.Alloc(data, GCHandleType.Pinned);
try
{
fastSegment(handle.AddrOfPinnedObject(), width, height);
}
finally
{
handle.Free();
}
}
// ---- OR ----
[DllImport(_dllname, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private static unsafe extern bool fastSegment(byte* img, int width, int height);
[ReliabilityContract(Consistency.MayCorruptProcess, Cer.None)]
static void FastSegment(byte[] data, int width, int height)
{
unsafe
{
fixed (byte* dataPinned = data)
{
fastSegment(dataPinned, width, height);
}
}
}