Number of channels in System.Drawing.Bitmap C# - c#

I'm coding in C#, and loading images this way :
// loading image
string imageFileName = "myImage.jpg";
Bitmap bmp = new Bitmap(imageFileName, false);
// trying to display color so that I know how many channels I have
// except it always displays 4 values, whether I have 1, 3 or 4 channels
Color color = bmp.GetPixel(0, 0);
Console.WriteLine(color.ToString());
// locking the bitmap's bits
System.Drawing.Imaging.BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat);
// doing stuff that requires me to know the number of channels of the image, amongst other things
// unlocking the bitmap's bits
bmp.UnlockBits(bmpData);
I need to know the number of channels in my image (usually they are grayscale (1), RGB (3) or RGBA (4)), and I don't know how is that information storred.
EDIT :
I'm not looking to force a pixel format. I'm trying to load an image, and figure out procedurally what is the number of channel in the image I loaded.

There is a PixelFormat property, you can read about in on MSDN.
You can combine with: GetPixelFormatSize it to get the bytes per pixel if you want.

Creation:
You need to use an overload of the Bitmap contructor that takes a PixelFormat parameter.
Usage:
The the bitmap.PixelFormat property will tell you what you have.
Here is more info about the PixelFormats

Related

What Does the Byte[] Representation of an Image Actually Mean?

I loaded a 1 pixel image into a bitmap and then converted it to a byte[]
_Image = "test.jpg";
Bitmap testImage = new Bitmap(_Image);
ImageConverter converter = new ImageConverter();
byte[] byteTestImage = (byte[])converter.ConvertTo(testImage,typeof(byte[]));
The single pixel has RGB values (255, 116, 25). Each of these can be represented by a byte,
so I assumed that byteTestImage would correspond to this. But, byteTestImage is 635 elements in total.
What is the relationship between those bytes and the 1 pixel image?
The file you loaded is a JPG. It has certain additional information (width, height, EXIF data) not just colors. Look at https://en.wikipedia.org/wiki/JPEG
Try opening it in a hex editor. You might even be able to read info about the camera used to take it.
There is no always RGB format for single pixel in Bitmap. It all depends on format. You can have a alfa component, you can have a palette to which martix of pixels refers to and more...
Check out: Bitmap format

Loading raw data into a Bitmap - what have I done wrong?

I'm using c sharp, .net 4 (client profile, if that's important) and I have a byte array that contains the raw data of an image. Specifically, this image:
It is the output from the SANE test backend and the format is fully described on the SANE web site here. Incidentally, I have passed in the parameters:
depth: 8
mode: Color
and it has returned:
format: RGB
depth: 8
lines: 196
pixels per line: 157
bytes per line: 471
a byte stream that is 92316 bytes long
So, the numbers seem reasonable (196 * (157 * 471) = 92316) - three bytes (24bits) per pixel.
And from reading the SANE documentation the data is sequenced three bytes per pixel from the top left corner going left to right, top to bottom - like this (they have a better picture sorry for this ASCIItastic approach):
red,green,blue red,green,blue
-------------- --------------
byte 1 byte 2 ...
Since I know so much about the image I figured that it would be super simps to load it up into a Bitmap and I knocked up this:
var bmp = new Bitmap(157, 196, PixelFormat.Format24bppRgb);
BitmapData bmpData = bmp
.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height),
ImageLockMode.ReadWrite,
bmp.PixelFormat);
Marshal.Copy(data, 0, bmpData.Scan0, data.Length);
bmp.UnlockBits(bmpData);
but it produced this:
close, but no cigar, so to speak.
So, what have I done wrong?
The problem is that in a bitmap there is a gap between the bytes of a line and the bytes of the next line (more here).If speed is not your primary concern, you can do this much more easily with SetPixel and a loop.
EDIT:
This would give a significant speed up compared to SetPixel, but I'm afraid but you'd still have to use a loop ;)
for(int i = 0; i < bmp.Height; i++) {
Marshal.Copy(data,
i * bmp.Height,
bmpData.Scan0 + i * bmpData.Stride,
bmp.Width * 3);
}
Note that I haven't tested the code, but it should be enough to give you the idea.
Bitmap requires lines to be padded to some particular size (4 bytes, searc hor format details i.e. here ).
As result 157 pixels per line don't map exactly to binary format for bits of the bitmap and you see each next line shifted a bit. I think you need to allocate 158*3 bytes per line and copy each line from source (not exactly bitmap due to lack of padding) format to destination, filling last 3 bytes of each line with 0.

Load a bitmap from file in RGB format (without alpha)

i simply want to load a .BMP file and get the Bitmap object in 24bit RGB format (or 32bit in RGB format).
All methods I tried return a Bitmap/Image object with PixelFormat = Format32bppArgb. Even if of course BMPs don't have alpha.
new Bitmap(System.Drawing.Image.FromFile(fileName, true));
new Bitmap(fileName);
I currently solve the problem by copying the first object to another in memory bitmap at 24bit RBG.
Is there a single method to do it?
Thanks
As far as I can tell it is not possible to specify the PixelFormat for loading bitmaps using the classes in System.Drawing. To convert the bitmap check this question: Converting Bitmap PixelFormats in C#
This is currently the top answer there:
Bitmap orig = new Bitmap(#"c:\temp\24bpp.bmp");
Bitmap clone = new Bitmap(orig.Width, orig.Height, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
using (Graphics gr = Graphics.FromImage(clone)) {
gr.DrawImage(orig, new Rectangle(0, 0, clone.Width, clone.Height));
}
// Dispose orig as necessary..
One option would be to put that in a function which takes a filename and PixelFormat. That hides the ugly code, which has its ups and downs since it also hides the fact it is probably not that efficient.
According to the linked SO question using the Clone method not always works.
You can clone it to a RGB format one:
var bitmapInRgbFormat = loadedBitmap.Clone(new Rectangle(0, 0, loadedBitmap.Width, loadedBitmap.Height), PixelFormat.Format32bppRgb)
Exactly what do you mean by copying the first object to another?
To achieve what you want to do, meaning loading an image and converting it to 24 bit, just get the graphics context of a second bitmap that matches the size and that is RGB, and paint the original bitmap onto this one.

8bpp Bitmap format on the Compact Framework

I am messing around with Conway's Game of Life - http://en.wikipedia.org/wiki/Conway's_Game_of_Life
I started out coding algorithmns for winforms and now want to port my work onto windows mobile 6.1 (compact framework). I came across an article by Jon Skeet where he compared several different algorithmns for calculating next generations in the game. He used an array of bytes to store a cells state (alive or dead) and then he would copy this array to an 8bpp bitmap. For each new generation, he works out the state of each byte, then copies the array to a bitmap, then draws that bitmap to a picturebox.
void CreateInitialImage()
{
bitmap = new Bitmap(Width, Height, PixelFormat.Format8bppIndexed);
ColorPalette palette = bitmap.Palette;
palette.Entries[0] = Color.Black;
palette.Entries[1] = Color.White;
bitmap.Palette = palette;
}
public Image Render()
{
Rectangle rect = new Rectangle(0, 0, Width, Height);
BitmapData bmpData = bitmap.LockBits(rect, ImageLockMode.ReadWrite, bitmap.PixelFormat);
Marshal.Copy(Data, 0, bmpData.Scan0, Data.Length);
bitmap.UnlockBits(bmpData);
return bitmap;
}
His code above is beautifully simple and very fast to render. Jon is using Windows Forms but now I want to port my own version of this onto Windows Mobile 6.1 (Compact Framework) but . . . .there is no way to format a bitmap to 8bpp in the cf.
Can anyone suggest a way of rendering an array of bytes to a drawable image in the CF. This array is created in code on the fly (it is NOT loaded from an image file on disk). I basically need to store an array of cells represented by bytes, they are either alive or dead and I then need to draw that array as an image. The game is particularly slow on the CF so I need to implement clever optimised algoritmns but also need to render as fast as possible and the above solution would be pretty dam perfect if only it was available on the compact framework.
Many thanks for any help
Any suggestions?
You could have a look at GDI+ for CF. It's basically a wrapper for most of the GDI implemented in WinCE. Here's a link to the source code and a writeup: http://community.opennetcf.com/articles/cf/archive/2007/10/31/using-gdi-on-windows-mobile.aspx
I think ImagingFactoryClass.CreateBitmapFromBuffer() looks like a good place to start.
Ok, how about this:
use the Bitmap.Save() method to save to a MemoryStream instead of a file;
when you save to the MemoryStream, you get to name the ImageFormat as "GIF" (this is equivalent to 8bpp in .Net, according to this: http://support.microsoft.com/kb/318343)
use MemoryStream.Write() to change whatever data you want in the image, or copy the data using MemoryStream.ToArray() if that jives better.
After you change the MemoryStream, you'll probably have to copy it back into the Bitmap, or make a new Bitmap. If you do make a new Bitmap, be sure to Dispose() the old one, to avoid memory leaks.
Hi Rocjoe and thanks again for the help, I have tried the following
Image bmp = new Bitmap(10, 10);
byte[] array = ImageToByteArray(bmp);
public byte[] ImageToByteArray(Image img)
{
MemoryStream ms = new MemoryStream();
img.Save(ms, System.Drawing.Imaging.ImageFormat.Gif );
return ms.ToArray();
}
The array coming back has over 870 bytes in it, It seems to hold all sorts of header info, padding and what have you. so again it does not work...

Bitmap editing in unsafe context - how to avoid instability?

I'm trying to use a bitmap in an unsafe context, and am seeing instability in that, e.g., the program runs the first time round but fails the second. Here is the code:
private static void RenderBitmap(Graphics g)
{
const int width = 150, height = 150;
using (Bitmap bmp = new Bitmap(width, height,
System.Drawing.Imaging.PixelFormat.Format24bppRgb))
{
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);
NativeMethods.RenderText(Graphics.FromImage(bmp).GetHdc(), bmpData.Scan0,
"This works only first time round", "Segoe", 10,
new RGBA(255, 0, 0, 255), width, height);
bmp.UnlockBits(bmpData);
g.DrawImage(bmp, new Rectangle(width, height, width, -height));
}
}
Seeing how this isn't working, I have a few questions. Is what I'm doing safe and correct, provided the native RenderText method manipulates the bitmap memory directly? Is my way of getting HDC from the bitmap correct, or should I use the parameter g that was passed from a drawing method?
The error I'm getting is this:
System.AccessViolationException was
unhandled Message="Attempted to read
or write protected memory. This is
often an indication that other memory
is corrupt."
The NativeMethods.RenderRext method can't safely work with the bitmap data, as it doesn't know how wide the scan lines of the bitmap is, and if it is stored upside down in memory or not. The symptoms suggests that the method is writing to memory outside the bitmap, overwriting something else that you need in your application.
The BitmapData.Stride property has the information that the method needs to work with the data. It contains the scan line width in bytes, and if it's negative it means that the bitmap is stored upside down in memory. Simply Scan0 is the address of the first scan line, and Scan0 + Stride is the address of the second scan line.
Maybe this is a silly question, but why don't you use the TextRenderer class that comes with .NET instead of using p/invoke?
TextRenderer::DrawText Method (IDeviceContext, String, Font, Point, Color)
http://msdn.microsoft.com/en-us/library/4ftkekek.aspx
-Oisin
Well, after much pain and suffering, I found a solution: instead of passing in a memory buffer to be filled in, I passed a device context (HDC) to be rendered into. Seems to be working so far!
Thanks to all who answered.

Categories