开发者

Convert from 32-BPP to 8-BPP Indexed (C#)

I need to take a full color JPG Image and remap it's colors to a Indexed palette. The palette will consist of specific colors populated from a database. I need to map each color of the image to it's "closest" value in the index. I am sure there are different algorithms for comparing and calculating the "closest" value. Looking for C#, .NET managed code libraries only.

(It will be used in a process where we have 120 or so specific colors of buttons, and we want to map any image to those 120 colors to make a c开发者_开发技巧ollage).


Nothing will help you with GDI. It seems indexed images are too backward a technology for Microsoft to care. All you can do is read and write indexed image files.

There are usually two step when quantizing colors in an image:
1) Find the best palette for the image (Color Quantization)
2) Map the source solors to the found palette (Color Mapping)

From what I understand, you already have the palette in the database, that means the hardest part has been done for you. All you need to do is map the 24 bit colors to the provided palette colors. If you don't have the starting palette, then you will have to compute it yourself using a quantisation algorithm: Octrees or Median Cut are the most well known. Median Cut gives better results but is slower and harder to implement and fine tune.

To map the colors, the simplest algorithm in your case is to calculate the distance from your source color to all the palette colors and pick the nearest.

float ColorDistanceSquared(Color c1, Color c2)
{
    float deltaR = c2.R - c1.R;
    float deltaG = c2.G - c1.G;
    float deltaB = c2.B - c1.B;
    return deltaR*deltaR + deltaG*deltaG + deltaB*deltaB;
}

You can also ponderate the channels so that blue has less weight, don't go too overboard with it, else it will give horrible results, specifically 30/59/11 won't work at all:

float ColorDistanceSquared(Color c1, Color c2)
{
    float deltaR = (c2.R - c1.R) * 3;
    float deltaG = (c2.G - c1.G) * 3;
    float deltaB = (c2.B - c1.B) * 2;
    return deltaR*deltaR + deltaG*deltaG + deltaB*deltaB;
}

Call that thing for all source and palette colors and find the Min. If you cache your results as you go in a map, this will be very fast.

Also, the source color will rarely fit a palette color enough to not create banding and plain areas and loss of details in your image. To avoid that, you can use dithering. The simplest algorithm and the one that gives the best results is Error Diffusion Dithering.

Once you mapped your colors, you will have to manually lock a Bitmap and write the indices in there as .Net won't let you write to an indexed image.


This process is called Quantization. Since each color represents 3 packed values, you'll need to use Octrees to solve this problem.

Check out this article with example code.

The article focuses on getting the ultimate palette for the image, but your process it would be reverse for the second part, only reduce the most used colors that are close to the given palette.


I had to do this in a big .NET project. There's nothing in the framework for it, but this article quickly led me to a solution: http://codebetter.com/blogs/brendan.tompkins/archive/2004/01/26/6103.aspx


The JPEG word should ring alarm bells. The images are very likely to already be in a heavily quantised colour space, and further resampling will potentially introduce aliasing. If you can, work from uncompressed images to reduce this effect.

The answer to your question is yes - you can save the images in an alternate format - but I'm not sure if the native functionality is adequate for what sounds like a quite complex requirement. If you are able to define the colour palette from the collection of images, you will likely improve the quality of the output.

The already referenced blog entry entitled Use 'GDI+ to Save Crystal-Clear GIF Images with .NET' contains useful references to code.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜