OpenTK Getting pixels byte array from a Bitmap - c#

I need to port some OpenGL code to C# OpenTK.
Here is the chunk where I update a mapped PBO from an array of pixels in C++ :
GLubyte* ptr = (GLubyte*)glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
if(ptr)
{
memcpy(ptr,imageInfo.Data,IMG_DATA_SIZE);
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
}
I need to do the same in OpenTK.My image data comes from an instance of Bitmap.
I tried the following:
IntPtr ptr = GL.MapBuffer(BufferTarget.PixelUnpackBuffer, BufferAccess.WriteOnly);
if(ptr != IntPtr.Zero)
{
BitmapData data = updateColorMap.LockBits(new System.Drawing.Rectangle(0, 0, updateColorMap.Width, updateColorMap.Height),
ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
Marshal.Copy(data.Scan0, 0, ptr, IMG_DATA_SIZE);
}
But Marshal.Copy requires the first param to be of byte[] type.I didn't find how to retrieve it from the BitmapData.It returns only IntPtr (data.Scan0) .
So how can I get the byte array from the Bitmap?
UPDATE:
In the meantime I got help from the OpenTK forum and they proposed to do this instead:
unsafe
{
GL.BufferData(BufferTarget.PixelUnpackBuffer, new IntPtr(IMG_DATA_SIZE), IntPtr.Zero, BufferUsageHint.StreamDraw);
byte* ptr = (byte*)GL.MapBuffer(BufferTarget.PixelUnpackBuffer, BufferAccess.WriteOnly);
if (ptr != null)
{
BitmapData data = updateDepthMap.LockBits(new System.Drawing.Rectangle(0, 0, updateDepthMap.Width, updateDepthMap.Height),
ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
byte* scan0 = (byte*)data.Scan0.ToPointer();
for (int i = 0; i < IMG_DATA_SIZE; ++i)
{
*ptr = *scan0;
++ptr;
++scan0;
}
updateDepthMap.UnlockBits(data);
GL.UnmapBuffer(BufferTarget.PixelUnpackBuffer);
}
}//unsafe
Now,this works,but it is TERRIBLY SLOW! The regular texture update runs 2x faster than this,which is
wrong as async PBO transfer should speed up texture uploads.Indeed in my C++ version PBO upload causes almost 2x performance boost.

Ok so the solution is here: Copy data from from IntPtr to IntPtr
tested on linux.

Related

C# Linux Framebuffer Unsafe byte[] to CairoSharp ImageSurface

I am trying to create an image surface in c# CairoSharp using these two constructors:
public ImageSurface(byte[] data, Format format, int width, int height, int stride); public ImageSurface(IntPtr data, Format format, int width, int height, int stride);
I am trying to get the array of the linux framebuffer from a memorymappedfile:
var file = MemoryMappedFile.CreateFromFile("/dev/fb0", FileMode.Open, null, (3840 * 2160 * (32 / 8)));
I know I have to use an unsafe context to get it but am unsure the proper syntax to get the sequential pointer from the memeoryMapped object.
The constructors for the ImageSurface will not work with the MemoryMappedFile directly. You will have to Read bytes from the MemoryMappedFile and use those bytes to create the ImageSurface.
I never used C# on Linux before so I don't really know if all those objects are available but maybe like this?
private static void test()
{
Bitmap bmp = (Bitmap)Image.FromFile("some image");
BitmapData imgData = null;
try
{
imgData = bmp.LockBits(
new Rectangle(0, 0, bmp.Width, bmp.Height),
ImageLockMode.ReadWrite,
bmp.PixelFormat
);
int finalLength = imgData.Stride * imgData.Height;
byte[] buf = new byte[finalLength];
IntPtr ptr = imgData.Scan0;
System.Runtime.InteropServices.Marshal.Copy(ptr, buf, 0, finalLength);
bmp.UnlockBits(imgData);
// Pointer to first byte lives inside of fixed context.
unsafe
{
fixed (byte* p = buf)
{
// your cairo code...
}
}
// Alternative...
var ptPinned = System.Runtime.InteropServices.GCHandle.Alloc(
buf, System.Runtime.InteropServices.GCHandleType.Pinned);
IntPtr ptCairo = ptPinned.AddrOfPinnedObject();
ptPinned.Free();
}
finally
{
if (imgData != null) {
bmp.UnlockBits(imgData);
}
}
}
In any case I am certain that you have to pass the pointer of an already allocated buffer. In the test above I just loaded the image pixels of a bitmap into a byte array. In order to get the pointer you Marshal it or use fixed. That is all on Windows though.

images difference in byte array c#

i'm trying to do method that should return difference between two images in byte array(rgb values) and send it via UDP. Code: (there's no part with sending this array because it doesn't matter now)
public void getdiff(Bitmap lol,Bitmap lol2)
{
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
Rectangle rect = new Rectangle(0, 0, lol.Width, lol.Height);
BitmapData bmpData = lol.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
IntPtr ptr = bmpData.Scan0;
Rectangle rect2 = new Rectangle(0, 0, lol2.Width, lol2.Height);
BitmapData bmpData2 = lol2.LockBits(rect2, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
IntPtr ptr2 = bmpData2.Scan0;
int numBytes = bmpData.Stride * lol.Height;
byte[] rgbValues = new byte[numBytes];
int numBytes2 = bmpData2.Stride * lol2.Height;
byte[] rgbValues2 = new byte[numBytes2];
byte[] difference = new byte[numBytes];
Marshal.Copy(ptr, rgbValues, 0, numBytes);
Marshal.Copy(ptr2, rgbValues2, 0, numBytes2);
for (int counter = 0; counter < rgbValues.Length; counter++)
{
if (rgbValues[counter] != rgbValues2[counter])
{
difference[counter] = rgbValues[counter];
}
}
Marshal.Copy(rgbValues, 0, ptr, numBytes);
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
textBox1.Text = ts.Milliseconds.ToString();
lol.UnlockBits(bmpData);
lol2.UnlockBits(bmpData2);
}
Right now I have 2 byte arrays from 2 images, and I just compare them, when images are different, I write adequate RGB values from chosen image to difference[]. Problem is, when RGB values are equal in both images, adequate places in this difference array are filled with 0(triple 0 represent White colour). So problem is, if i would "impose" this difference[] on to target image it will be probably mostly white. I really want to use Marshal.Copy and Lockbits because it's really efficient.
Question is: How can I store this image difference to avoid for multiplying "for" loops at reading/imposing it? Maybe I'm missing some methods? I would like to stay with LockBits and Marshal.Copy, but if you have better ideas - please share with me.

Memory Leak when Marshal.Copy

I noticed my program was leaking memory. So I used dotMemory to find the leak, and looks like this is the function causing the leak:
private void LoadBits()
{
// Lock the bitmap's bits.
Rectangle rect = new Rectangle(0, 0, bm.Width, bm.Height);
bmpData = bm.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, bm.PixelFormat);
stride = bmpData.Stride;
// Get the address of the first line.
IntPtr ptr = bmpData.Scan0;
// Declare an array to hold the bytes of the bitmap.
byteCount = Math.Abs(bmpData.Stride) * bm.Height;
bytes = new byte[byteCount];
// Copy the RGB values into the array.
System.Runtime.InteropServices.Marshal.Copy(ptr, bytes, 0, byteCount);
}
And this how I unlock the bits.
private void SaveBits()
{
// Update Stuff
IntPtr ptr = bmpData.Scan0;
System.Runtime.InteropServices.Marshal.Copy(bytes, 0, ptr, byteCount);
bm.UnlockBits(bmpData);
}
I implemented the IDisposable interface for this class. And I call the SaveBits there, so even if I forget to call SaveBits, the GC should do it for me.
And Yes, I do call bm.Dispose() and set everything to null in the Dispose method.
You need to UnlockBits() when you're done.

System::Drawing::Bitmap to unsigned char *

I have a C# .NET library that grabs frames from a camera. I need to send those frames to a native application that takes images from an unsigned char*.
I initially take the frames as System::Drawing::Bitmap.
So far I can retrieve a byte[] from the Bitmap. My test is done with an image of resolution 400*234, I should be getting 400*234*3 bytes to get to the 24bpp a RGB image requires.
However, I'm getting a byte[] of size 11948.
This is how I convert from Bitmap to byte[]:
private static byte[] ImageToByte(Bitmap img)
{
ImageConverter converter = new ImageConverter();
return (byte[])converter.ConvertTo(img, typeof(byte[]));
}
What is the proper way to convert from System::Drawing::Bitmap to RGB unsigned char*?
This has to be done using the lockBits method, here is a code example:
Rectangle rect = new Rectangle(0, 0, m_bitmap.Width, m_bitmap.Height);
BitmapData bmpData = m_bitmap.LockBits(rect, ImageLockMode.ReadOnly,
m_bitmap.PixelFormat);
IntPtr ptr = bmpData.Scan0;
int bytes = Math.Abs(bmpData.Stride) * m_bitmap.Height;
byte[] rgbValues = new byte[bytes];
Marshal.Copy(ptr, rgbValues, 0, bytes);
m_bitmap.UnlockBits(bmpData);
GCHandle handle = GCHandle::Alloc(rgbValues, GCHandleType::Pinned);
unsigned char * data = (unsigned char*) (void*) handle.AddrOfPinnedObject();
//do whatever with data

Copy a channel from one image to another using safe code

I'm trying to refactor this unsafe code to copy a single ARGB channel from one image to another using System.Runtime.InteropServices.Marshal.Copy as per this example on MSDN but I'm totally lost.
Could anyone walk me through how I would go about it?
public enum ChannelARGB
{
Blue = 0,
Green = 1,
Red = 2,
Alpha = 3
}
public static void transferOneARGBChannelFromOneBitmapToAnother(
Bitmap source,
Bitmap dest,
ChannelARGB sourceChannel,
ChannelARGB destChannel )
{
if ( source.Size!=dest.Size )
throw new ArgumentException();
Rectangle r = new Rectangle( Point.Empty, source.Size );
BitmapData bdSrc = source.LockBits( r,
ImageLockMode.ReadOnly,
PixelFormat.Format32bppArgb );
BitmapData bdDst = dest.LockBits( r,
ImageLockMode.ReadWrite,
PixelFormat.Format32bppArgb );
unsafe
{
byte* bpSrc = (byte*)bdSrc.Scan0.ToPointer();
byte* bpDst = (byte*)bdDst.Scan0.ToPointer();
bpSrc += (int)sourceChannel;
bpDst += (int)destChannel;
for ( int i = r.Height * r.Width; i > 0; i-- )
{
*bpDst = *bpSrc;
bpSrc += 4;
bpDst += 4;
}
}
source.UnlockBits( bdSrc );
dest.UnlockBits( bdDst );
}
Edit
In an attempt to work through #Ben Voigt walk though I have come up with this so far. Unfortunately I am now getting the following error:
Attempted to read or write protected memory. This is often an
indication that other memory is corrupt.
private static void TransferOneArgbChannelFromOneBitmapToAnother(
Bitmap source,
Bitmap destination,
ChannelARGB sourceChannel,
ChannelARGB destinationChannel)
{
if (source.Size != destination.Size)
{
throw new ArgumentException();
}
Rectangle rectangle = new Rectangle(Point.Empty, source.Size);
// Lockbits the source.
BitmapData bitmapDataSource = source.LockBits(rectangle,
ImageLockMode.ReadWrite,
PixelFormat.Format32bppArgb);
// Declare an array to hold the bytes of the bitmap.
int bytes = bitmapDataSource.Stride * bitmapDataSource.Height;
// Allocate a buffer for the source image
byte[] sourceRgbValues = new byte[bytes];
// Get the address of the first line.
IntPtr ptrSource = bitmapDataSource.Scan0;
// Copy the RGB values into the array.
System.Runtime.InteropServices.Marshal.Copy(ptrSource,
sourceRgbValues,
0,
bytes);
// Unlockbits the source.
source.UnlockBits(bitmapDataSource);
// Lockbits the destination.
BitmapData bitmapDataDestination = destination.LockBits(rectangle,
ImageLockMode.ReadWrite,
PixelFormat.Format32bppArgb);
// Allocate a buffer for image
byte[] destinationRgbValues = new byte[bytes];
IntPtr ptrDestination = bitmapDataDestination.Scan0;
// Copy the RGB values into the array.
System.Runtime.InteropServices.Marshal.Copy(ptrDestination,
destinationRgbValues,
0,
bytes);
ptrSource += (int)sourceChannel;
ptrDestination += (int)destinationChannel;
for (int i = rectangle.Height * rectangle.Width; i > 0; i--)
{
destinationRgbValues[i] = sourceRgbValues[i];
ptrSource += 4;
ptrDestination += 4;
}
// Copy the RGB values back to the bitmap
// ******This is where I am getting the exception*******.
System.Runtime.InteropServices.Marshal.Copy(destinationRgbValues,
0,
ptrDestination,
bytes);
// Unlock bits the destination.
destination.UnlockBits(bitmapDataDestination);
}
Can anyone see what I have done wrong? This is all a bit over my head to be honest. I think I should buy some books.
LockBits the source.
Marshal.Copy the source BitmapData to a byte[] buffer.
UnlockBits the source.
LockBits the destination.
Marshal.Copy the destination BitmapData to a byte[] buffer.
Loop through and copy that channel from the source byte[] to the destination byte[] (note, use arithmetic on indexes instead of on pointers)
Marshal.Copy the destination byte[] back to the BitmapData.
UnlockBits the destination.
I'm not sure what the point is, though. Code that uses Marshal.Copy is just as dangerous as code that uses the unsafe keyword, and should require similar code security permission.
A potentially more efficient way would be to use ImageAttributes.SetColorMatrix to remove the desired channel from the destination image, remove all other channels from the source image, and then blend. See the example for ColorMatrix
Or use DirectX (or OpenGL) and a shader that just transfers the one channel.
You could use my simple LINQ based image processing framework from Nuget or Codeplex and write a simple query that swaps the channels around.
You could also use a ColorMatrix to perform the channel swap like in this code.
Unfortunately, a ColorMatrix won't work if you want to combine channels from two separate images. You would need an additive (or bitwise or) blending method, and the only blending provided by GDI+ is Over and Copy. It also looks to me like any methods that would allow you to access the bits directly, including LockBits, are locked down.
I think the only option is to use GetPixel and SetPixel on each pixel, something like this:
Color dstColor = bpDst.GetPixel(x, y);
Color srcColor = bpSrc.GetPixel(x, y);
int srcValue = (srcColor.ToArgb() >> (sourceChannel * 8)) & 0xff;
int dstArgb = (dstColor.ToArgb() & ~(0xff << (destChannel * 8))) | (srcValue << (destChannel * 8));
bpDst.SetPixel(x, y, Color.FromArgb(dstArgb));

Categories

Resources