I'm using Bitmap.GetHbitmap for passing image to C++ dll from c# like below.
Bitmap img = Bitmap.FromFile(PATH); // this is 24bppRGB bitmap.
IntPtr hBit = img.GetHbitmap(); // Make hbitmap for c++ dll.
Here's problem:
Bitmap temp = Bitmap.FromHbitmap(hBit); // It changes to 32bppRGB.
I need 24bpp bitmap for c++ dll methods, but GetHbitmap() changes the bitcount.
How can I make a 24bpp HBITMAP?
Short Version
Use Bitmap.LockBits and CreateDIBSection to manually create your desired HBITMAP.
Long Version
Bitmap.GetHBITMAP will always return a 32bpp HBITMAP; no matter what the format of the image you actually loaded is. This is because it is the format the image is internally stored as. You will need to manually create an HBITMAP yourself.
You can use CreateDIBSection to create your own 24bpp bitmap. The function returns a pointer where you can place the raw pixel data.
Fortunately GDI+ lets you obtain pixel data in whatever format you want, by calling LockBits and specifying the pixel format you want (i.e. PixelFormat24bppRGB). Then you can just copy the pixel data. There may be issues with -Stride, and top-down or bottom-up bitmaps.
Define a BITMAPINFO for the bitmap we want to create:
BITMAPINFO bm;
bm.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bm.bmiHeader.biWidth = img.Width;
bm.bmiHeader.biHeight = -img.Height; // oriented top-down
bm.bmiHeader.biPlanes = 1;
bm.bmiHeader.biBitCount = 24; // 24bpp
bm.bmiHeader.biCompression = BI_RGB; // no compression
bm.bmiHeader.biSizeImage = 0; // let Windows determine size
bm.bmiHeader.biXPelsPerMeter = 0; // Not used by CreateDIBSection
bm.bmiHeader.biYPelsPerMeter = 0; // Not used by CreateDIBSection
Create a DIBSection based on the bitmap info, and get the buffer where we place our pixel data
Pointer dibPixels; //will receive a point where we can stuff our pixel data
HBITMAP bmp = CreateDIBSection(0, bmInfo, DIB_RGB_COLORS, out dibPixels, 0, 0);
Use Bitmap.LockBits to obtain a pointer to pixel data in the format you want:
BitmapData bitmapData = img.LockBits(
img.Bounds, //get entire image
ImageLockModeRead,
PixelFormat24bppRGB //we want the pixel data in 24bpp format
);
Copy pixel data from our bitmapData source image into the pixel buffer returned by CreateDIBSectin:
int stride = bitmapData.Stride;
int bufferSize = stride * bmData.Height;
CopyMemory(bitmapData.Scan0, dibPixels, bufferSize);
Unlock the bits:
img.UnlockBits(bitmapData);
Now you have your HBITMAP bitmap ready to pass to your dll seven years later.
Bonus Reading
The Supercomputing Blog: Using LockBits in GDI+ archive
Related
I have a method in c# that the only thing it does its LockBits, and then UnlockBits, and the images(input/output, transformed to byte arrays) are different. The one from output has less 100 and something bytes than the one from the input. This happens only with .jpg files. And checking the files in HxD I came to the understanding that it´s removing a part of the header, the exif signature to be exact. But I don't know how and why.
Does someone know what this is doing?
Here's the code:
public Image Validate (image){
BitmapData original = null;
Bitmap originalBMP = null;
try{
originalBMP = image as Bitmap;
original = originalBMP.LockBits(new Rectangle(0, 0,
originalBMP.Width, originalBMP.Height),
ImageLockMode.ReadWrite,
originalBMP.PixelFormat);
originalBMP.UnlockBits(original);
}catch{}
return image;
}
Calling Bitmap.LockBits() followed by Bitmap.UnlockBits() does nothing.
The behavior you observe is because of loading a JPEG image, and then saving it again. JPEG uses a lossy algorithm. So what happens:
You load the JPEG from disk
The JPEG data gets decoded into individual pixels with color information, i.e. a bitmap
You save the bitmap again in the JPEG format, resulting in a different file than #1
You also potentially lose metadata that was present in the JPEG file in doing so. So yes, the file is different and probably smaller, because every time you do this, you lose some pixel data or metadata.
Lockbits/Unlockbits are used to allow the program to manipulate the image data in memory. Nothing more, nothing less. See also the documentation for those methods.
Use the LockBits method to lock an existing bitmap in system memory so that it can be changed programmatically. You can change the color of an image with the SetPixel method, although the LockBits method offers better performance for large-scale changes.
A Rectangle structure that specifies the portion of the Bitmap to lock.
Example:
private void LockUnlockBitsExample(PaintEventArgs e)
{
// Create a new bitmap.
Bitmap bmp = new Bitmap("c:\\fakePhoto.jpg");
// Lock the bitmap's bits.
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
System.Drawing.Imaging.BitmapData bmpData =
bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite,
bmp.PixelFormat);
// Get the address of the first line.
IntPtr ptr = bmpData.Scan0;
// Declare an array to hold the bytes of the bitmap.
int bytes = Math.Abs(bmpData.Stride) * bmp.Height;
byte[] rgbValues = new byte[bytes];
// Copy the RGB values into the array.
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
// Set every third value to 255. A 24bpp bitmap will look red.
for (int counter = 2; counter < rgbValues.Length; counter += 3)
rgbValues[counter] = 255;
// Copy the RGB values back to the bitmap
System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);
// Unlock the bits.
bmp.UnlockBits(bmpData);
// Draw the modified image.
e.Graphics.DrawImage(bmp, 0, 150);
}
I have two monochromatic images as byte[] taken from cameras. I want to combine these images and write the combined image into a writeable bitmap.
Merging images: OpenCV
Using openCV, I create Mat objects (of type CV_8UC1) from the byte arrays (greenMat for the green and redMat for the red color channel image) and merge them via Cv2.Merge
Mat mergedMat = new Mat(greenMat.Height, greenMat.Width, MatType.CV_8UC3);
Mat emptyMat = Mat.Zeros(greenMat.Height, greenMat.Width, MatType.CV_8UC1);
Cv2.Merge(new[]
{
redMat, greenMat, emptyMat
}, mergedMat);
The mergedMat.ToBytes() now retuns a buffer of size 2175338. Why is it this size? Merging three one-channel matrices (CV_8UC1) if size 2448x2048 into one three-channel matrix(CV_8UC3) should yield to a buffer of size 15040512, or am I missing something here?
Display merged image: WriteableBitmap
I want to display the merged image by writing it into an existing WriteableBitmap, that is initialized via
ImageSource = new WriteableBitmap(
width, //of the green image
height, //of the green image
96, //dpiX: does not affect image as far as I tested
96, //dpiY
PixelFormats.Rgb24, //since the combined image is a three channel color image
null); //no bitmap palette needed for used PixelFormat
Is this initialization correct? I'm unsure about the PixelFormat here. The dpiX and dpiY values also seem to have no effect. What is their use?
The tricky part I can't get to work properly now is to write the image data into the WriteableBitmap.
Using the following code to obtain the byte array of the
merged image and using WriteableBitmaps' WritePixels method, with
stride according to the ImageSources' PixelFormat, yields an
System.ArgumentException: 'Buffer size is not sufficient.'
var mergedBuffer = mergedMat.ToBytes();
var bytesPerPixel = (PixelFormats.Rgb24.BitsPerPixel + 7) / 8;
var stride = bytesPerPixel * width;
((WriteableBitmap)ImageSource).WritePixels(
new Int32Rect(0, 0, width, height),
mergedBuffer,
stride,
0)
Edit: Stride is calculated according to this answer, although it's not neccessary with a fixed PixelFormat like here.
Why is the buffer too small? I did initialize the WriteableBitmap
with the correct width and height and PixelFormats.Rgb24. Am I
calculating my stride correctly?
Solution
Thanks to #Micka s suggestions in the comments I realized the mergedMat.ToBytes method seems not to do what I expceted. Instead, I tried to use mergedMat.Data pointer like so:
var mergedBufferSize = (int)(mergedMat.Total() * mergedMat.Channels());
byte[] mergedBuffer = new byte[mergedBufferSize];
System.Runtime.InteropServices.Marshal.Copy(
mergedMat.Data,
mergedBuffer,
0,
mergedBufferSize);
Everything works fine this way.
I’m trying to crop a 24bpp image using memcpy like I read here: cropping an area from BitmapData with C#. The problem I’m having is that it only works when my sourceImage is 32bpp. It gives me a corrupt image when my sourceImage is 24bpp.
class Program
{
[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
static unsafe extern int memcpy(byte* dest, byte* src, long count);
static void Main(string[] args)
{
var image = new Bitmap(#"C:\Users\Vincent\Desktop\CroppedScaledBitmaps\adsadas.png");
//Creates a 32bpp image - Will work eventhough I treat it as a 24bpp image in the CropBitmap method...
//Bitmap newBitmap = new Bitmap(image);
//Creates a 24bpp image - Will produce a corrupt cropped bitmap
Bitmap newBitmap = (Bitmap)image.Clone();
var croppedBitmap = CropBitmap(newBitmap, new Rectangle(0, 0, 150, 150));
croppedBitmap.Save(#"C:\Users\Vincent\Desktop\CroppedScaledBitmaps\PieceOfShit.png", ImageFormat.Png);
Console.ReadLine();
}
static public Bitmap CropBitmap(Bitmap sourceImage, Rectangle rectangle)
{
Console.WriteLine("Bits per pixel of sourceImage: {0}", Image.GetPixelFormatSize(sourceImage.PixelFormat));
var sourceBitmapdata = sourceImage.LockBits(rectangle, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
var croppedImage = new Bitmap(rectangle.Width, rectangle.Height, PixelFormat.Format24bppRgb);
var croppedBitmapData = croppedImage.LockBits(new Rectangle(0, 0, rectangle.Width, rectangle.Height), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
unsafe
{
byte* sourceImagePointer = (byte*)sourceBitmapdata.Scan0.ToPointer();
byte* croppedImagePointer = (byte*)croppedBitmapData.Scan0.ToPointer();
memcpy(croppedImagePointer, sourceImagePointer, croppedBitmapData.Stride * rectangle.Height);
}
sourceImage.UnlockBits(sourceBitmapdata);
croppedImage.UnlockBits(croppedBitmapData);
return croppedImage;
}
}
I’m very confused, because the only thing I’m changing is the sourceImage PixelFormat, not any of the code in the CropBitmap method. So I always call LockBits using 24bpp Pixelformat, even if the sourceImage is 32bpp.
I’ve tried different methods of calculating the number of bytes I’m copying but everything resulted in more or less the same corrupted image.
Any help is appreciated!
You are trying to copy the data as if it was one continuous block, but it isn't.
The image data is arranged in scan lines, but as you are selecting a part of the image, you don't want all the data from each scan line, you only want the data that represents the pixels that you have selected. A scan line contains the data for the pixels that you specified when you called LockBits, but also data for the pixels outside that area.
The Stride value is the difference in memory address from one scan line to the next. The Stride value may also include padding between the scan lines. Note also that the Stride value can be negative, which happens when the image data is stored upside down in memory.
You want to copy the relevant data from one line of the source image to the line in the destination image. As there can be gaps both in the source data and destination data, you can't copy the data as a single chunk of data.
You would need to loop through the lines and copy each line separately, I haven't tested this code, but something like this:
byte* sourceImagePointer = (byte*)sourceBitmapdata.Scan0.ToPointer();
byte* croppedImagePointer = (byte*)croppedBitmapData.Scan0.ToPointer();
int width = rectange.Width * 3; // for 24 bpp pixel data
for (int y = 0; y < rectangle.Height; y++) {
memcpy(croppedImagePointer, sourceImagePointer, width);
sourceImagePointer += sourceBitmapdata.Stride;
croppedImagePointer += croppedBitmapData.Stride;
}
I have a DrawingContext (part of a Visual or a DrawingGroup), where I draw a bunch of rectangles and/or 1-bit images on top of each other. Think of it as a masking 1-bit image. I would like to convert this into a bitmap image file.
Using RenderTargetBitmap is not an option because it can only render at 32bit pixel format, so if I have to render a 20MB 1-bit image, I will end up with a 640MB (20*32) of memory on my heap. This of course creates magnificent LOH fragmentation, and the application runs out-of-memory on the second shot.
So, I basically need a way to write a 1-bit bitmap file from a drawing context efficiently. Any ideas/suggestions/alternate methods would be appreciated.
A number of ideas, some are a bit convoluted...
Print to XPS then extract Bitmap
You could print the Visual to an XPS Document.
If you're lucky then it will combine the 1bit images that you drew in in the DrawingContext and produce a single bitmap in the XPS file.
For the Rectangles it might keep the Rectangles as vector based information in the XPS (the "Shape" based Rectangle or DrawingContext DrawRectangle might both do this)....if that happens then create a bitmap into which your draw the rectangle part and draw that bitmap into the context.
Once you have the XPS files, you could then parse it and extract the "image" if you are lucky that's what it produced inside. (XPS is just a ZIP file that uses XAML to describe content and uses subdirectories to store bitmap image data files, fonts, etc).
(I think you can use the Package class to access the raw parts of an XPS document, or XpsDocument for a higher level interpretation).
Or just display the XPS in an XPS viewer if your intention is just to provide a way to allow your combined 1bit images to be viewed in a different context.
Convert WPF (XAML) Control to XPS Document
How to create BIG bitmaps with RenderTargetBitmap?
Printing a WPF BitmapImage
Use a Printer Driver that provides Print to Image functionality
This isn't ideal...not least because you have to install a Printer Driver on the machine.....but you might be able to use a "Print to Image" printer driver and then use that to produce your bitmap. Here are some suggestions:
http://sourceforge.net/projects/imageprinter/
http://www.zan1011.com/
Create a GDI+ HBITMAP and do the drawing using GDI+ calls...then wrap it for use by WPF...or save out to disk.
[1] First create a GDI+ Bitmap large enough to hold your composed rendering
(there are a few different ways to do this...one way is to use a WriteableBitmap to provide the backbuffer bits/store...that's if you wanted access to the bits in memory...in your case I don't think you do/need to).
var bmp = new System.Drawing.Bitmap( pixelwidth, pixelheight, System.Drawing.Imaging.Format1bppIndexed );
Or this way if you want WriteableBitmap access.
Getting a DrawingContext for a wpf WriteableBitmap
[2] Convert any WPF BitmapSources to GDI+ Bitmaps so you can draw them onto the GDI+ Graphics context using DrawImage.
You can use CopyPixels to do that on your BitmapSources.
Is there a good way to convert between BitmapSource and Bitmap?
For your rectangles you can just use the GDI+ DrawRectangle rendering command.
[3] Save the GDI+ Bitmap to disk
Use the .Save method on System.Image.Bitmap to save it as a Bitmap.
[4] Use the saved image as the Source to an Image element
(note this will probably use masses of memory when it loads your image even though it's 1bpp...because the WPF rendering path is all 32bpp).
[4] OR wrap the GDI+ Bitmap for use in WPF
You can use InteropBitmap to wrap the GDI+ based bitmap so you can use it as a BitmapSource in WPF. (note it may have to be a 32bpp one....if it has to...then you are back at square 1...but try anyway).
http://arbel.net/2008/10/22/improper-use-of-interopbitmap-can-cause-a-memory-leak/
http://arbel.net/2008/10/22/improper-use-of-interopbitmap-can-cause-a-memory-leak/
Create a Bitmap "Service"
Create another process which acts as a service (doesn't have to be an NT service...could just be a child process you start) to render your combined 1bpp images....there are various ways to communicate with it to give it the rendering commands.
When memory consumption gets too high/the LOH gets fragmented, then you could restart this service.
Other Ideas
You could render using OpenGL (e.g. use OpenTK or SharpGL), or render using DirectX...via the 3D path with D3DImage or Direct2D (whether this behaves the same as RenderTargetBitmap in terms of memory usage...that's to find out).
http://www.codeproject.com/Articles/265903/Using-OpenGL-in-a-WPF-Application
http://www.codeproject.com/Articles/113991/Using-Direct2D-with-WPF
http://www.codeproject.com/Articles/28526/Introduction-to-D3DImage
Try out NET 4.5 as there have been a number of improvements in the LOH Heap:
http://blogs.msdn.com/b/dotnet/archive/2011/10/04/large-object-heap-improvements-in-net-4-5.aspx
http://blogs.msdn.com/b/dotnet/archive/2012/07/20/the-net-framework-4-5-includes-new-garbage-collector-enhancements-for-client-and-server-apps.aspx
I dont think you can do anything better. Since RenderTargetBitmap uses MILcore which you can't access. And I don't think there is any other way to copy Visual. However I think there is one option more. It won't be one-line but I think it will be good enough.
Basically you will render visual block by block(PGBRA32) and convert it into BlackWhite on the fly, and then concat it with Blackwhite buffer. I've started a little example code but in halfway decided that it's not gonna be so easy, but you can finish it.
/// <summary>
/// Renders only part of the visual and returns byte[] array
/// which holds only black/white information.
/// </summary>
/// <param name="oVisual"></param>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="width"></param>
/// <param name="height"></param>
/// <returns>black/white pixel information. one pixel=one bit. 1=white, 0=black</returns>
public static byte[] RenderPartially(Visual oVisual,
int x, int y, double width, double height)
{
int nWidth = (int)Math.Ceiling(width);
int nHeight = (int)Math.Ceiling(height);
RenderTargetBitmap oTargetBitmap = new RenderTargetBitmap(
nWidth,
nHeight,
96,
96,
PixelFormats.Pbgra32
);
DrawingVisual oDrawingVisual = new DrawingVisual();
using (DrawingContext oDrawingContext = oDrawingVisual.RenderOpen())
{
VisualBrush oVisualBrush = new VisualBrush(oVisual);
oDrawingContext.DrawRectangle(
oVisualBrush,
null,
new Rect(
new Point(x, y),
new Size(nWidth, nHeight)
)
);
oDrawingContext.Close();
oTargetBitmap.Render(oDrawingVisual);
}
//Pbgra32 == 32 bits ber pixel?!(4bytes)
// Calculate stride of source and copy it over into new array.
int bytesPerPixel = oTargetBitmap.Format.BitsPerPixel / 8;
int stride = oTargetBitmap.PixelWidth * bytesPerPixel;
byte[] data = new byte[stride * oTargetBitmap.PixelHeight];
oTargetBitmap.CopyPixels(data, stride, 0);
//assume pixels in byte[] are stored as PBGRA32 which means that 4 bytes form single PIXEL.
//so the layout is like:
// R1, G1, B1, A1, R2, G2, B2, A2, R3, G3, B3, A3, and so on.
byte [] bitBufferBlackWhite = new byte[oTargetBitmap.PixelWidth
* oTargetBitmap.PixelHeight / bytesPerPixel];
for(int row = 0; row < oTargetBitmap.PixelHeight; row++)
{
for(int col = 0; col < oTargetBitmap.PixelWidth; col++)
{
//calculate concrete pixel from PBGRA32
int index = stride * row + bytesPerPixel * col;
int r = data[index];
int g = data[index + 1];
int b = data[index + 2];
int transparency = data[index + 3];
//determine whenever pixel is black or white.
//note that I dont know the exact process how one converts
//from PBGRA32 to BlackWhite format. But you should get the idea.
bool isBlack = r + g + b + transparency == 0;
//calculate target index and USE bitwise operators in order to
//set right bits.
}
}
return bitBufferBlackWhite;
}
So essentially, set up new WriteableBitmap with BlackWhite format, and then call this function like this:
WriteableBitmap blackWhiteFullBuffer = new WriteableBItmap(....., PIxelFormats.BlackWhite);
for(int x = 0; x < Visual.Width; x += <some uniform amount that divides correctly>)
{
for(int y = 0; y < VIsual.Height; y+= <some uniform amount that divides correctly>)
{
byte[] partialBuffer = PartialRenderer.RenderPartially(Visual, x, y, <uniform amX>,
<uniform amY>);
//concat that partial blackWhite buffer with other blackWhite buffer.
//you do this as long as needed and memory wont grow too much
//hopefully it will be fast enough too.
PartialRenderer.ConcateBuffers(blackWhiteFullBuffer, partialBuffer);
}
}
//then save blackWhiteFullBuffer to HDD if needed.
What about PixelFormats
Edit: (thanks to Anders Gustafsson)
The lower is PixelFormats.BlackWhite, with 1bit per pixel.
So this way, you can convert any BitmapImage to a FormatConvertedBitmap, where you can modify the format to a lower bpp.
I'm coding a live control/remote desktop solution using DFMirage's free mirror driver. There is a C# sample on how to interface and control the mirror driver here. You would need the mirror driver installed first, of course, here. So, the concept is, the client (helper) requests a screen update, and the server (victim) sends one, using raw pixel encoding. The concept of a mirror driver eliminates the need to expensively poll for screen changes, because a mirror driver is notified of all screen drawing operations in real-time. The mirror driver receives the location and size of the update rectangle, and can simply query memory for the new pixel bytes and send them.
Should be easy, except that I don't know how to do that part where we query memory for the new pixel bytes. The sample shows how to query memory to grab the pixels of the entire screen using something with raw bitmap data and scan lines and stride and all that good stuff:
Bitmap result = new Bitmap(_bitmapWidth, _bitmapHeight, format);
Rectangle rect = new Rectangle(0, 0, _bitmapWidth, _bitmapHeight);
BitmapData bmpData = result.LockBits(rect, ImageLockMode.WriteOnly, format);
// Get the address of the first line.
IntPtr ptr = bmpData.Scan0;
// Declare an array to hold the bytes of the bitmap.
int bytes = bmpData.Stride * _bitmapHeight;
var getChangesBuffer = (GetChangesBuffer)Marshal
.PtrToStructure(_getChangesBuffer, typeof (GetChangesBuffer));
var data = new byte[bytes];
Marshal.Copy(getChangesBuffer.UserBuffer, data, 0, bytes);
// Copy the RGB values into the bitmap.
Marshal.Copy(data, 0, ptr, bytes);
result.UnlockBits(bmpData);
return result;
This is great and works fine. The resulting Bitmap object now has the pixels of the entire screen. But if I wanted to just extract a rectangle of pixel data instead of getting the pixel data from the whole screen, how would I be able to do that? I guess this is more of a rawbitmap-scan-stride question, but I typed all of this so you might know where this is coming from. So any insight on how to get just a portion of pixel data instead of the entire screen's pixel data?
Update: Found something interesting (code portion only).
Here's a function to copy a rectangular area from some source image buffer to a Bitmap:
private static Bitmap ExtractImageRectangle(byte[] sourceBuffer, int sourceStride, PixelFormat sourcePixelFormat, Rectangle rectangle)
{
Bitmap result = new Bitmap(rectangle.Width, rectangle.Height, sourcePixelFormat);
BitmapData resultData = result.LockBits(new Rectangle(0, 0, result.Width, result.Height), ImageLockMode.WriteOnly, result.PixelFormat);
int bytesPerPixel = GetBytesPerPixel(sourcePixelFormat); // Left as an exercise for the reader
try
{
// Bounds checking omitted for brevity
for (int rowIndex = 0; rowIndex < rectangle.Height; ++rowIndex)
{
// The address of the start of this row in the destination image
IntPtr destinationLineStart = resultData.Scan0 + resultData.Stride * rowIndex;
// The index at which the current row of our rectangle starts in the source image
int sourceIndex = sourceStride * (rowIndex + rectangle.Top) + rectangle.Left * bytesPerPixel;
// Copy the row from the source to the destination
Marshal.Copy(sourceBuffer, sourceIndex, destinationLineStart, rectangle.Width * bytesPerPixel);
}
}
finally
{
result.UnlockBits(resultData);
}
return result;
}
You could then use it like this:
Rectangle roi = new Rectangle(100, 150, 200, 250);
Bitmap result = ExtractImageRectangle(getChangesBuffer.UserBuffer, getChangesBuffer.Stride, getChangesBuffer.PixelFormat, roi);
This assumes that GetChangesBuffer has properties for the stride and pixelformat of the source image buffer. It most likely hasn't, but you should have some means to determine the stride and pixel format of your input image. In your example you are assuming the stride of the input image is equal to the stride of your output image, which is a tricky assumption.