C# Convert Bitmap to indexed colour format - c#

How can I convert a 24-bit colour System.Drawing.Bitmap to an indexed (256-colour) format? I'm having trouble working out how to calculate the palette. I can iterate over the pixels and use an int[] to contain the various colours but the problem comes when there are more than 256 colours. Is there a way to convert to an indexed format and extract a 256-colour palette from an Bitmap ?

Using the Bitmap Clone Method you can directly convert the Source Image to a 256 color Palette Indexed image like this:
Bitmap Result = Source.Clone(new Rectangle(0, 0, Source.Width, Source.Height), PixelFormat.Format8bppIndexed);
Then if you want access the Palette Colors, just use the Result.Palette.Entries property.

I had the same challenge earlier. It's possible to solve using GDI+ in .Net.
This article helped me a lot (including samples): http://msdn.microsoft.com/en-us/library/Aa479306
For best quality use "Octree-based Quantization".

WPF has access to the Windows Imaging Component, from there you can use a FormatConvertedBitmap to convert the image to a new pixel format. WIC is much much faster than the System.Drawing methods on Vista and 7 and will allow you a lot more options.

This is not built-in but you can either use external .NET libraries for this or shell out to the console to invoke ImageMagic.

Some reading material to get you started.
Graphic Gems I pp. 287-293, "A Simple Method for Color Quantization: Octree Quantization"
B. Kurz. Optimal Color Quantization for Color Displays. Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition, 1983, pp. 217-224.
Graphic Gems II pp. 116-125, "Efficient Inverse Color Map Computation"
This paper describes an efficient technique to map actual colors to a reduced color map, selected by some other technique described in the other papers.
Graphic Gems II pp. 126-133, "Efficient Statistical Computations for Optimal Color Quantization"
Xiaolin Wu. Color Quantization by Dynamic Programming and Principal Analysis. ACM Transactions on Graphics, Vol. 11, No. 4, October 1992, pp 348-372.

Related

Byte array or bitmap to svg in c#

How can we convert byte array or bitmap to svg in .net and save the svg file.
Is there any library provided by .net or any third party library which can handle this.
The problem is that Bimap, JPG, PNG, etc. files are raster graphics: they store a fixed array of pixels in various shades of red, green and blue (or hue, lightness and shade - whatever) while SVG files are vector graphics - which means they store images as "draw line from (x1, y1) to (x2, y2)" and "draw an arc here" commands.
And the two are not compatible.
It is possible to do - the process is called "vectorisation" - but the results are unlikely to be perfect, and if you are talking about a natural world object (like a photo of a face) it's very, very unlikely to work without some considerable effort on your part.
read the original article at codeproject.com

C# Converting 32bpp image to 8bpp

I'm trying to convert a 32bpp screenshot image to an 8bpp (or 4bpp, or 1bpp) format using C#. I've already looked at several stackoverflow answers on similar subjects and most suggest variations using the following code:
public static Bitmap Convert(Bitmap oldbmp)
{
Bitmap newbmp = new Bitmap(oldbmp.Width, oldbmp.Height, PixelFormat.Format8bppIndexed);
Graphics gr = Graphics.FromImage(newbmp);
gr.PageUnit = GraphicsUnit.Pixel;
gr.DrawImageUnscaled(oldbmp, 0, 0);
return newbmp;
}
However, when this executes, I get a the exception: A graphics object cannot be created from an image that has an indexed pixel format. I understand that 8, 4 and 1bpp images have colour table mappings rather than the actual colour pixels themselves (as in 32 or 16bpp images) so I assume I'm missing some conversion step somewhere, but I'm fairly new to C# (coming from a C++ background) and would prefer to be able do this using native C# calls rather than resorting to PInvoking BitBlt and GetDIBits etc. Anybody able to help me solve this? Thanks.
EDIT: I should point out that I need this to be backwardly compatible to .NET framework 2.0
GDI+ in general has very poor support for indexed pixel formats. There is no simple way to convert an image with 65536 or 16 million colors into one that only has 2, 16 or 256. Colors have to be removed from the source image and that is a lossy conversion that can have very poor results. There are multiple algorithms available to accomplish this, none of them are perfect for every kind of image. This is a job for a graphics editor.
There is one trick I found. GDI+ has an image encoder for GIF files. That's a graphics format that has only 256 colors, the encoder must limit the number of colors. It uses a dithering algorithm that's suitable for photos. It does have a knack for generating a grid pattern, you'll be less than thrilled when it does. Use it like this:
public static Image Convert(Bitmap oldbmp) {
using (var ms = new MemoryStream()) {
oldbmp.Save(ms, ImageFormat.Gif);
ms.Position = 0;
return Image.FromStream(ms);
}
}
The returned image has a 8bpp pixel format with the Palette entries calculated by the encoder. You can cast it to Bitmap if necessary. By far the best thing to do is to simply not bother with indexed formats. They date from the stone age of computing back when memory was severely constrained. Or use a professional graphics editor.
AForge library is doing it perfectly using Grayscale.
var bmp8bpp = Grayscale.CommonAlgorithms.BT709.Apply(bmp);
This class is the base class for image grayscaling [...]
The filter accepts 24, 32, 48 and 64 bpp color images and produces 8
(if source is 24 or 32 bpp image) or 16 (if source is 48 or 64 bpp
image) bpp grayscale image.
Negative stride signifies the image is bottom-up (inverted). Just use the absolute of the stride if you dont care. I know that works for 24bpp images, unaware if it works for others.
You can use System.Windows.Media.Imaging in PresentationCore Assembly take a look at here for more information

C# - Copy an Image into an 8-bit Indexed Image

I want to create an 8-bit indexed image from a regular 32-bit Image object.
Bitmap img = new Bitmap(imgPath); // 32-bit
Bitmap img8bit = new Bitmap(imgW, imgH, Format8bppIndexed); // 8-bit
// copy img to img8bit -- HOW?
img8bit.Save(imgNewPath, ImageFormat.Png);
I cannot use SetPixel to copy it over pixel-by-pixel since Graphics doesn't work with indexed images.
How else?
I found a C# library that converts a bitmap into a palettized (8-bit) image. The technique is fast because it calls GDI32 (the windows graphics system) directly.
To convert to an 8bpp (palettized) image with a greyscale palette, do
System.Drawing.Bitmap b0 = CopyToBpp(b,8);
If you want to convert to an image with a different palette, look at the comments in the source code of CopyToBpp for suggestions. Note that, when you convert to a 1bpp or 8bpp palettized copy, Windows will look at each pixel one by one, and will chose the palette entry that's closest to that pixel. Depending on your source image and choice of palette, you may very well end up with a resulting image that uses only half of the colours available in the palette.
Converting an arbitrary RGBA image to an 8-bit indexed bitmap is a non-trivial operation; you have to do some math to determine the top 256 colors and round the rest (or do dithering, etc).
http://support.microsoft.com/kb/319061 has the details of everything except for a good algorithm, and it should give you an idea of how to get started.

Convert BitmapImage to grayscale, and keep alpha channel

I'm having an issue with converting a BitmapImage (WPF) to grayscale, whilst keeping the alpha channel. The source image is a PNG.
The MSDN article here works fine, but it removes the alpha channel.
Is there any quick and effective way of converting a BitmapImage to a grayscale?
You should have a look at image transformation using matrices.
In particular, this article describes how to convert a bitmap to grayscale using a ColorMatrix. (It is written in VB.NET, but it should be easy enough to translate to C#).
I haven't tested if it works with the alpha channel, but I'd say it's worth a try, and it definitely is a quick and effective way of modifying bitmaps.
It really depends upon what your source PixelFormat is. Assuming your source is PixelFormats.Bgra32 and that you want to go to grayscale, you might consider using a target pixel format of PixelFormats.Gray16. However, Gray16 doesn't support alpha. It just has 65,535 graduations between black and white, inclusive.
You have a few options. One is to stay with Bgra32 and just set the blue, green and red channels to the same value. That way you can keep the alpha channel. This may be wasteful if you don't require an 8-bit alpha channel (for differing levels of alpha per pixel).
Another option is to use an indexed pixel format such as PixelFormats.Indexed8 and create a palette that contains the gray colours you need and alpha values. If you don't need to blend alpha, you could make the palette colour at position zero be completely transparent (an alpha of zero) and then progress solid black in index 1 through to white in 255.
if relying on API calls fails. You can always try the 'do it yourself' approach: Just get access to the RGBA bytes of the picture, and for every RGBA replace it with MMMA, where M = (R+G+B)/3;
If you want it more perfect, you should add weights to the contribution of the RGB components. I believe your eye is more receptive for green, and as such that value should weigh more.
While not exactly quick and easy, a ShaderEffect would do the job and perform quite well. I've done it myself, and it works great. This article references how to do it and has source associated. I've not used his source, so I can't vouch for it. If you run into problems, ask, and I may be able to post some of my code.
Not every day you get to use HLSL in your LOB app. :)

image conversion

I need to convert a RGB (jpg) grayscale CMYK using only to black channel (K).
I'm trying to do this with imageglue, but the result is not what i'm looking for since it converts the grays using the C,M and Y channel and leaves the black channel to 0%.
What I need is if anyone has experience in using any other library/api in .net that could work?
I would start by looking at the ColorConvertedBitmap class in WPF. Here is a link to the docs and a basic example:
http://msdn.microsoft.com/en-us/library/system.windows.media.imaging.colorconvertedbitmap(VS.85).aspx
Have you triedAForge.Net?
There is also ImageMagick, a c++ framework for image processing, with a .net wrapper (google for MagickNet)
Her is RGB to/from CMYK question which is related this one:
How is 1-bit bitmap data converted to 8bit (24bpp)?
I found The bitmap transform classes useful when trying to do some image format conversions but ... CYMK is one of the most complicated conversions you can tackle because there is more than one way to represent some colours. In particular equal CYM percentages give you shades of grey which are equivalent to the same percentage of K. Printers often use undercolour removal/transformation which normalises CYMK so that the a large common percentage is taken from CYM and transfered to the K. This is suppose to give purer blacks and grey tones. So even if you have a greyscale image represented using nothing but CYM with a zero black channel it could still print using nothing but K when you get it to a printer using undercolour removal.

Categories