C# 量化(图像颜色的减少)

C# 量化(图像颜色的减少),c#,quantization,C#,Quantization,我试图用C#将一幅图像量化为10种颜色,但我在绘制量化图像时遇到问题,我制作了映射表,它是正确的,我制作了原始图像的副本,我正在根据映射表更改像素的颜色,我使用以下代码: bm = new Bitmap(pictureBox1.Image); Dictionary<Color, int> histo = new Dictionary<Color, int>(); for (int x = 0; x < bm.Size.Width; x

我试图用C#将一幅图像量化为10种颜色,但我在绘制量化图像时遇到问题,我制作了映射表,它是正确的,我制作了原始图像的副本,我正在根据映射表更改像素的颜色,我使用以下代码:

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;
bm=新位图(pictureBox1.Image);
字典历史=新字典();
对于(int x=0;xa.Value);
int ind=0;
List mostusedcolor=新列表();
foreach(result1中的var条目)
{
如果(ind<10)
{
mostusedcolor.Add(entry.Key);
ind++;
}
其他的
打破
}
双温红、双温绿、双温蓝、双温;
Dictionary dist=新字典();
字典映射=新字典();
foreach(结果1中的var p)
{
清除区();
foreach(MOSTUSEDCLOR中的彩色pp)
{
temp_red=数学功率((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=数学功率((Convert.ToDouble(p.Key.B)-Convert.ToDouble(pp.B)),2.0);
温度=数学Sqrt((温度红+温度绿+温度蓝));
区域添加(pp、温度);
}
var min=dist.OrderBy(k=>k.Value).FirstOrDefault();
mapping.Add(p.Key,min.Key);
}
位图复制=新位图(bm);
用于(int x=0;x
您的代码有两个问题:

  • 太慢了
  • 量化不是我所期望的
这是一张原始图像、代码的结果以及Photoshop在被要求减少到10种颜色时所做的操作:

  • 可通过两个步骤加快代码速度:

    • 摆脱最讨厌的时间浪费
    • GetPixel
      SetPixel
      循环转换为
      Lockbits
      循环
下面是第一步的解决方案,它将代码速度提高至少100倍:

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;
在此插入(1):

我们得到:


尽管如此,所有的黄色、橙色或棕色色调都没有了,但只增加了一行,这是一个很好的改进。

我刚刚尝试了这个,效果很好。到底是什么问题?图像是否未被渲染?我注意到在你的帖子中没有任何代码可以实际渲染图像,例如OnPaint事件中的
e.Graphics.DrawImage(复制,新点(0,0))
。问题是当我计算结果图像的颜色时,结果@BrettWolfington应该是10种颜色,但不是,我该如何进行渲染呢?你之前的评论被打断了,但如果问题是颜色比应该的多或少,那么问题可能是量化算法,而不是映射代码。在进入映射算法之前,你能发布该代码和映射字典的内容吗?我已经在@BrettWolfingtonI上面发布了完整的代码,刚刚在100x100图像上测试了这一点。它正确地将颜色的数量从15273减少到了10。你能发布你正在使用的输入图像吗?非常感谢你的帮助我非常感谢你的帮助,但是结果只有十种颜色吗?根据IrfanView,它只有7种不同的颜色。Photoshop直方图显示9种颜色。也不知道为什么他们中的任何一个。。如果你有疑问,你可以在上面运行一段代码。。顺便说一句,这将是一个很好的机会来考虑一个
字典gethistgram(位图)
函数;-)再次感谢您的回答,但是我不明白如何使用您上面提到的功能?我已经标记了两行以替换WITH 1/2。它清除了一些,例如每个颜色通道的5个低位。你说“如果你有疑问,你可以在上面运行一段代码。顺便说一句,这将是一个很好的机会来考虑字典getHistgramm(位图)函数”我不明白你说的gethitgramm(位图)函数是什么意思?
Color cutOff(Color c, byte mask)
{  return Color.FromArgb(255, c.R & mask, c.G & mask, c.B & mask );   }
byte mask = (byte)255 << 5 & 0xff;  // values of 3-5 worked best
Color c = cutOff(bm.GetPixel(x, y), mask);
Color c = cutOff(copy.GetPixel(x, y), mask);