Create a bitmap from byte array [duplicate] - c#

This question already has answers here:
convert array of bytes to bitmapimage
(2 answers)
Closed 8 years ago.
I have a scanner that delivers Grayscale 8 bit images. My goal is to convert this image to Monochrome, which I already have implemented. To convert i need to operate on bitmap objects, rather than just the byte array. My code to get the Bitmap from the byte array looks like this:
public static Bitmap ByteArrayToBitmap(byte[] data, int width, int height)
{
Bitmap bmp = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, bmp.PixelFormat);
Marshal.Copy(data, 0, bmpData.Scan0, data.Length);
bmp.UnlockBits(bmpData);
bmp.Save(#"C:\Type7Test\TestImage.bmp", ImageFormat.Bmp);
return bmp;
}
My problem is that from this point the bitmap is getting formatted the wrong way. Notice the line where I save the bitmap to disk(just for testing), this is the result:
As you can see this is not the expected image. I expect this as the result:
I suspect that there is something wrong with the PixelFormats and the creation of the bitmap. So can anyone pin-point me in the right direction to create a proper Bitmap from an array of raw image data?

After thinking about it, are you sure that PixelFormat.Format8bppIndexed is a correct format? The Bitmap is created and it - more or less - looks like the desired image. That shows that you give too much or too little data for a single picture - which means that your PixelFormat may be wrong.
You can try to experiment with different types and check which one works. They wary, dependent on how many color does your image has (the more colors there are, the bigger bites-per-pixel ratio you need).

Related

A Graphics object cannot be created from an image that has an indexed pixel format [duplicate]

I am getting error:
"A Graphics object cannot be created from an image that has an indexed
pixel format."
in function:
public static void AdjustImage(ImageAttributes imageAttributes, Image image)
{
Rectangle rect = new Rectangle(0, 0, image.Width, image.Height);
Graphics g = Graphics.FromImage(image);
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImage(image, rect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, imageAttributes);
g.Dispose();
}
I would like to ask you, how can I fix it?
Refering to this, it can be solved by creating a blank bitmap with the same dimensions and the correct PixelFormat and the draw on that bitmap.
// The original bitmap with the wrong pixel format.
// You can check the pixel format with originalBmp.PixelFormat
Bitmap originalBmp = (Bitmap)Image.FromFile("YourFileName.gif");
// Create a blank bitmap with the same dimensions
Bitmap tempBitmap = new Bitmap(originalBmp.Width, originalBmp.Height);
// From this bitmap, the graphics can be obtained, because it has the right PixelFormat
using(Graphics g = Graphics.FromImage(tempBitmap))
{
// Draw the original bitmap onto the graphics of the new bitmap
g.DrawImage(originalBmp, 0, 0);
// Use g to do whatever you like
g.DrawLine(...);
}
// Use tempBitmap as you would have used originalBmp embedded in it
return tempBitmap;
The simplest way is to create a new image like this:
Bitmap EditableImg = new Bitmap(IndexedImg);
It creates a new image exactly like the original was with all its contents.
Overall, if you want to work with indexed images and actually preserve their colour depth and palette, this will always mean writing explicit checks and special code for them. Graphics simply can't work with them, because it manipulates colours, and the actual pixels of indexed images contain no colours, just indices.
For anyone still seeing this all these years later... the valid way to paint an image onto an existing (8-bit) indexed image is this:
Go over all the pixels of the image you want to paste and, for each colour, find the closest match on the target image's colour palette, and save its index into a byte array.
Open the backing bytes array of the indexed image using LockBits, and paste your matched bytes onto it, at the desired location, by looping over the relevant indices using the height and image stride.
It's not an easy task, but it's certainly possible. If the pasted image is also indexed, and contains more than 256 pixels, you can speed up the process by doing the colour matching on the palette instead of on the actual image data, then getting the backing bytes from the other indexed image, and remapping them using the created mapping.
Note that all of this only applies to eight bit. If your image is four-bit or one-bit, the simplest way to handle it is to convert it to 8-bit first so you can handle it as one byte per pixel, and convert it back afterwards.
For more information on that, see How can I work with 1-bit and 4-bit images?
Though the accepted answer works, it creates a new 32bpp ARGB image from the indexed bitmap.
To manipulate indexed bitmaps directly you can use this library (alert: shameless self promotion). Its GetReadWriteBitmapData extension allows creating a writable managed accessor even for indexed pixel formats.
And then you can use one of the DrawInto methods that can be used similarly to Graphics.DrawImage. Of course, as the target bitmap is indexed, the drawing operation must quantize the pixels using the target palette colors but there are a sort of overloads that can use dithering to preserve more image details.
Usage example (see more examples in the links above):
using (IReadWriteBitmapData indexedTarget = myIndexedBitmap.GetReadWriteBitmapData())
using (IReadableBitmapData source = someTrueColorBitmap.GetReadableBitmapData())
{
// or DrawIntoAsync if you want to use async-await
source.DrawInto(indexedTarget, targetRect, OrderedDitherer.Bayer8x8);
}
Image examples:
All images below had been created with PixelFormat.Format8bppIndexed format with the default palette, and a 256x256 icon and an alpha gradient rainbow were drawn on top of each other. Note that blending is used as much as possible with the available palette.
Image
Description
No dithering
Ordered Bayer8x8 dithering
Floyd-Steinberg error diffusion dithering
Disclaimer: Of course, the library has also some limitations compared to Graphics, for example there are no shape-drawing methods. But in worst case you still can use the accepted answer, and then call the ConvertPixelFormat method in the end if you need to produce an indexed result.

GDI+ generic error saving bitmap created from memory using LockBits

The GDI+ generic error when saving a bitmap is obviously a common problem according to my research here on SO and the web. Given following simplified snippet:
byte[] bytes = new byte[2048 * 2048 * 2];
for (int i = 0; i < bytes.Length; i++)
{
// set random or constant pixel data, whatever you want
}
Bitmap bmp = new Bitmap(2048, 2048, PixelFormat.Format16bppGrayScale);
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, 2048, 2048), ImageLockMode.ReadWrite, bmp.PixelFormat);
System.Runtime.InteropServices.Marshal.Copy(bytes, 0, bmpData.Scan0, 8388608);
bmp.UnlockBits(bmpData);
bmp.Save(#"name.bmp");
This results in the 0x80004005 generic error. The usual reason for this are said to be locks on components, but I do not see anything here I. Am I just blind? The path I am saving to exists, of course, only a empty bmp file is created (0B).
Background: I am getting pixel data from a camera driver that I transfer to .NET using a C++/CLI wrapper, so the Bitmap object above is returned by a function call. But since this small example already fails, I guess that there is nothing wrong with the adapter.
Any suggestions are highly appreciated!
Bitmap bmp = new Bitmap(2048, 2048, PixelFormat.Format16bppGrayScale);
GDI+ exceptions are rather poor, you'll have little hope to diagnose the two mistakes. The lesser one is your Save() call, it doesn't specify the ImageFormat you want to save. The default is PNG, not BMP as you hoped.
But the core one is PixelFormat.Format16bppGrayScale. When GDI+ was designed, long before .NET came around, everybody was still using CRTs instead of LCD monitors. CRTs were quite good at displaying a gamut of colors. Although good, there were no mainstream CRTs yet that were capable of display 65536 distinct gray colors. Most of all restricted by the DAC in the video adapter, the chip that converts the digital pixel value to an analog signal for the CRT. A DAC that can convert with 16-bit accuracy at 100 MHz or more wasn't technologically feasible yet. Microsoft gambled on display technology improving to make that possible someday so specified Format16bppGrayScale as a pixel format that might someday be available.
That did not happen. Rather the opposite, LCDs are significantly worse at color resolution. Typical LCD panels can only resolve 6 bits of a color rather than the 8 bits available from the pixel format. Getting to 16-bit color resolution is going to require a significant technological break-through.
So they guessed wrong and, since the pixel format isn't useful, GDI+ doesn't actually have an image encoder that can write a 16bpp grayscale image format. Kaboom when you try to save it to disk, regardless of the ImageFormat you pick.
16bpp grayscale is actually used, radiological imaging uses that pixel format. With very expensive displays to make it actually useful. Such equipment however invariable uses a custom image format to go with that, DICOM is the usual choice. GDI+ doesn't have a codec for it.
You'll need to go shopping for a library that supports the image format that your customer wants. Lead Tools is the thousand pound gorilla in that product segment.
PixelFormat.Format32bppArgb seems to work for me on Ubuntu 20 using GDI.
var bitmapdata = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly,
PixelFormat.Format32bppArgb);
The error I was getting was
System.ArgumentException: 'Parameter is not valid.'
at System.Drawing.SafeNativeMethods.Gdip.CheckStatus(Int32 status)
at System.Drawing.Bitmap.LockBits(Rectangle rect, ImageLockMode flags, PixelFormat format, BitmapData bitmapData)
at System.Drawing.Bitmap.LockBits(Rectangle rect, ImageLockMode flags, PixelFormat format)

Creating Bitmap from part of an image with BitmapImage.Create

Background:
I'm working with an external (unmanaged) API which will render an image for me and pass the memory location of the image(as well as stride, etc.) back.
This API is not yet fully developed, so I'm trying to mock the behaviour for the time being.
Task:
I'm trying to create an image from part of the image which is created in the unmanaged code.
For example, I would like to create a BitmapSource which contains only the right half of the image.
To simluate this, I create an image in memory:
Bitmap logo = Properties.Resources.test2;
BitmapData bmpData =
logo.LockBits(new System.Drawing.Rectangle(0, 0, 970, 844), System.Drawing.Imaging.ImageLockMode.ReadWrite, logo.PixelFormat);
Now I can get the starting point of the image in memory with
IntPtr startOfImage = bmpData.Scan0;
To get the right hand side of the image, i'm unsuccessfully trying (assuming the image dimensions are 970*844 and the Pixelformat is 24bit RGB):
IntPtr locationOfImage = bmpData.Scan0 += bmpData.Stride / 2 ;
BitmapSource source = BitmapImage.Create(485, 844, 96, 96, System.Windows.Media.PixelFormats.Rgb24, null, locationOfImage, 844 * bmpData.Stride / 2, bmpData.Stride / 2);
However, this is no way is giving me the right hand side of the image, what am I missing?
Best Whishes
Daniel
EDIT:
It seems clear that this is not returning the right hand side of the image because only for the first line I am skipping the half of the stride.
Is it somehow possible to create an image while always skipping part of the bytes (e.g. have a skip value for on each line skipping a part of the line)?
Obviously, I also could first read in all necessary bytes into an array and pass it instead of the IntPtr, however it would be nice to be able to directly pass the location.

Bitmap.LockBits usage causing AccessViolation?

I have some code that copies a Bitmap into a Direct3D Texture for rendering video. When the system is under heavy load, I get occasional AccessViolationException's on the call to Bitmap.LockBits.
Here is a very simple example of what happens:
// Get a PixelFormat.Format24bppRgb image
Bitmap bitmap24 = getVideoFrame();
// Copy into a 32bpp ARGB texture
BitmapData bitmapData = null;
try
{
// Why am I allowed to do this?
bitmapData = this.bitmap24.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
ImageLockMode.ReadOnly,
PixelFormat.Format32bppArgb);
// Blit from bitmapData to texture32
Texture texture32 = convertBitmapToTexture(bitmapData);
}
finally
{
if (bitmapData != null)
this.bitmap.UnlockBits(bitmapData);
}
Why am I allowed to call Bitmap.LockBits and pass in PixelFormat.Format32bppArgb when I know that the bitmap format is PixelFormat.Format24bppRgb? I assume that LockBits must automagically expand from 3 bytes per pixel to 4.
Could the LockBits expansion be causing these exceptions? Why would it only happen occasionaly?
LockBits will store the bitmap in memory in whatever format you specify.
AccessViolationException occurs when you're accessing memory you aren't supposed to be accessing. I can think of two scenarios where this will happen:
1) You're using ImageLockMode.ReadOnly.. is there anywhere you're actually trying to change a value of a pixel? Perhaps try changing this to ImageLockMode.ReadWrite and see if that helps.
2) You've confused yourself about how big your buffer must be and you have a Marshal.Copy in your convertBitmapToTexture function that is attempting to copy past the end of your buffer.
It might be easier for you to think in terms of int's instead of bytes when you're using 32bppArgb. That way you'll be less likely to run into any confusion.

Simple way to implement an efficient framebuffer in a modern language?

I'm looking for a simple way to implement a framebuffer in C#, D or Java. Something (an API or library) that would allow me to work with a 2d array of colors and update individual pixels or areas. Also, something that doesn't incur large overhead on update. I know that this can be done with OpenGL, but the API seems far too complicated for what I'm doing.
Try using a plain old System.Drawing.Bitmap in .NET?
You could use Bitmap.Lockbits() to get access to the byte array behind the bitmap and update it. This is much more efficient than normal pixel operations on the bitmap.
MSDN has an example here that I've pasted from:
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);
}
Arrays will take lots of time while iterating through such large amount pixel data for a complete screen. It will be better to find something that doesn't needs or need very less amount of iteration. Something more like pointers in C.
If what you need is a 2D array, In C# you can create a multidimensional array that gives you direct access to every member. To make this efficient, try to avoid frequent boxing and unboxing and don't allocate and deallocate large memory chunks frequently, and if you do it right there's no reason why this task would be much less efficient in C# or Java than in other languages.

Categories

Resources