量化(减少图像色彩) [英] quantization (Reduction of colors of image)

查看:106
本文介绍了量化(减少图像色彩)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用C#将图像量化为10种颜色,并且在绘制量化图像时遇到问题,我制作了映射表,并且它是正确的,我已经制作了原始图像的副本,并且正在更改基于映射表的像素颜色,我正在使用以下代码:

I am trying to quantize an image into 10 colors in C# and I have a problem in draw the quantized image, I have made the mapping table and it is correct, I have made a copy of the original image and I am changing the color of pixels based on the mapping table , I am using the below code:

bm = new Bitmap(pictureBox1.Image);
        Dictionary<Color, int> histo = new Dictionary<Color, int>();
        for (int x = 0; x < bm.Size.Width; x++)
            for (int y = 0; y < bm.Size.Height; y++)
            {
                Color c = bm.GetPixel(x, y);
                if (histo.ContainsKey(c))
                    histo[c] = histo[c] + 1;
                else
                    histo.Add(c, 1);
            }
        var result1 = histo.OrderByDescending(a => a.Value);
                  int ind = 0;
        List<Color> mostusedcolor = new List<Color>();
        foreach (var entry in result1)
        {
            if (ind < 10)
            {
                mostusedcolor.Add(entry.Key);
                ind++;
            }
            else
                break;
        }
        Double temp_red,temp_green,temp_blue,temp;
        Dictionary<Color, Double> dist = new Dictionary<Color, double>();
        Dictionary<Color, Color> mapping = new Dictionary<Color, Color>();
        foreach (var p in result1)
        {
            dist.Clear();
            foreach (Color pp in mostusedcolor)
            {
                temp_red = Math.Pow((Convert.ToDouble(p.Key.R) - Convert.ToDouble(pp.R)), 2.0);
                temp_green = Math.Pow((Convert.ToDouble(p.Key.G) - Convert.ToDouble(pp.G)), 2.0);
                temp_blue = Math.Pow((Convert.ToDouble(p.Key.B) - Convert.ToDouble(pp.B)), 2.0);
                temp = Math.Sqrt((temp_red + temp_green + temp_blue));
                dist.Add(pp, temp);
            }
            var min = dist.OrderBy(k=>k.Value).FirstOrDefault();
            mapping.Add(p.Key, min.Key);
        }
  Bitmap copy = new Bitmap(bm);

        for (int x = 0; x < copy.Size.Width; x++)
            for (int y = 0; y < copy.Size.Height; y++)
            {
                Color c = copy.GetPixel(x, y);
                Boolean flag = false;
                foreach (var entry3 in mapping)
                {
                    if (c.R == entry3.Key.R && c.G == entry3.Key.G && c.B == entry3.Key.B)
                    {
                        copy.SetPixel(x, y, entry3.Value);
                        flag = true;
                    }
                    if (flag == true)
                        break;

                }
            }
pictureBox2.Image=copy;

推荐答案

您的代码有两个问题:

  • 这太慢了
  • 量化不是我期望的.

这是原始图像,代码结果以及当要求减少为10种颜色时Photoshop的操作:

Here is an original image, the result of your code and what Photoshop does when asked to reduce to 10 colors:

  • 加速代码可以通过两个步骤完成:

  • Speeding up the code can be done in two steps:

  • 摆脱最讨厌的时间浪费者
  • GetPixelSetPixel循环转换为Lockbits循环.
  • Get rid of the most obnoxious time wasters
  • Turn the GetPixel and the SetPixel loops into Lockbits loops.

这是第一步的解决方案,可将代码加速至少100倍:

Here is a solution for step one, that speeds up the code by at least 100x:

Bitmap bm = (Bitmap)Bitmap.FromFile("d:\\ImgA_VGA.png");
pictureBox1.Image = bm;

Dictionary<Color, int> histo = new Dictionary<Color, int>();
for (int x = 0; x < bm.Size.Width; x++)
    for (int y = 0; y < bm.Size.Height; y++)
    {
        Color c = bm.GetPixel(x, y);   // **1**
        if (histo.ContainsKey(c))  histo[c] = histo[c] + 1;
        else histo.Add(c, 1);
    }
var result1 = histo.OrderByDescending(a => a.Value);
int number = 10;
var mostusedcolor = result1.Select(x => x.Key).Take(number).ToList();

Double temp;
Dictionary<Color, Double> dist = new Dictionary<Color, double>();
Dictionary<Color, Color> mapping = new Dictionary<Color, Color>();
foreach (var p in result1)
{
    dist.Clear();
    foreach (Color pp in mostusedcolor)
    {
        temp = Math.Abs(p.Key.R - pp.R) + 
               Math.Abs(p.Key.R - pp.R) + 
               Math.Abs(p.Key.R - pp.R);
        dist.Add(pp, temp);
    }
    var min = dist.OrderBy(k => k.Value).FirstOrDefault();
    mapping.Add(p.Key, min.Key);
}
Bitmap copy = new Bitmap(bm);

for (int x = 0; x < copy.Size.Width; x++)
    for (int y = 0; y < copy.Size.Height; y++)
    {
        Color c = copy.GetPixel(x, y);   // **2**
        copy.SetPixel(x, y, mapping[c]);
    }
pictureBox2.Image = copy;

请注意,如果我们只想订购颜色,则无需用毕达哥拉斯的全部力来计算距离. 曼哈顿距离就可以了.

Note that there is no need to calculate the distances with the full force of Pythagoras if all we want is to order the colors. The Manhattan distance will do just fine.

还要注意,我们已经有了查找字典mapping,该字典包含图像中的每种颜色作为其键,因此我们可以直接访问这些值. (这是迄今为止最浪费的时间.)

Also note that we already have the lookup dictionary mapping, which contains every color in the image as its key, so we can access the values directly. (This was by far the worst waste of time..)

测试图像的处理时间为〜1s ,因此我什至不去进行LockBits修改.

The test image is processed in ~1s, so I don't even go for the LockBits modifications..

  • 但是校正量化并非如此简单,恐怕imo超出了一个很好的SO问题的范围.

  • But correcting the quantization is not so simple, I'm afraid and imo goes beyond the scope of a good SO question.

  • 但是让我们看一下出了什么问题:看看结果,我们乍看之下几乎可以看到它:天空很多,所有许多蓝色像素的色调都超过10,所以所有前10名列表中的颜色是蓝色.

  • But let's look at what goes wrong: Looking at the result we can see it pretty much at the first glance: There is a lot of sky and all those many many blues pixels have more than 10 hues and so all colors on your top-10 list are blue.

因此整个图像没有没有其他色调了!

要解决此问题,您最好研究常见量化算法..

To work around that you best study the common quantization algorithms..

一种修复代码的简单方法是丢弃/映射最常用列表中的所有颜色,这些颜色与您已经拥有的任何一种颜色都太近了.但是,要找到最佳的最小距离,则需要进行体数据分析.

One simplistic approach at repairing the code would be to discard/map together all colors from the most-used-list that are too close to any one of those you already have. But finding the best minimum distance would require soma data analysis..

更新另一种非常简单的代码改进方法是,通过将真实颜色的一些低位掩盖起来,以将相似的颜色映射在一起.仅选择10种颜色仍然太少了,但是即使对于此测试图像,改进也是显而易见的:

Update Another very simple way to improve on the code is to mask the real colors by a few of its lower bits to map similar colors together. Picking only 10 colors will still be too few, but the improvement is quite visible, even for this test image:

Color cutOff(Color c, byte mask)
{  return Color.FromArgb(255, c.R & mask, c.G & mask, c.B & mask );   }

在此处( 1 )插入此内容:

Insert this here (1) :

byte mask = (byte)255 << 5 & 0xff;  // values of 3-5 worked best
Color c = cutOff(bm.GetPixel(x, y), mask);

和此处( 2 ):

Color c = cutOff(copy.GetPixel(x, y), mask);

然后我们得到:

仍然缺少所有的黄色,橙色或棕色色调,但是只有一个额外的行,这是一个不错的改进..

Still all yellow, orange or brown hues are missing, but a nice improvement with only one extra line..

这篇关于量化(减少图像色彩)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆