【问题标题】:How to get a majority color in an image?如何在图像中获得多数颜色?
【发布时间】:2011-02-02 11:59:27
【问题描述】:

我想在 .NET 中检索背景图像中的大多数颜色。有可能吗?

【问题讨论】:

  • 你能澄清“多数色”的含义吗?您是指图像中最常用的颜色吗?图像的平均颜色; “最高”值颜色等值 RGB?
  • 您可能需要澄清“多数颜色”的含义。对于大多数图像,任何一种颜色都不太可能构成适当的多数。我假设您的意思是最常见的颜色(逐个像素)或主要成分(R、G 或 B)。
  • 您需要速度还是准确性?您还想要最常用的颜色、平均颜色还是流行颜色?您需要 RGB 混合还是分离?
  • 如果您将图像调整为小图像(例如 10x10 像素左右),然后将颜色空间减少到大约 16 或 256 调色板,然后构建结果图像的直方图并找到其中的最高元素 - 会不会工作?至少应该相当快。

标签: .net wpf windows


【解决方案1】:

您可以遍历图像中的所有像素,并使用 getPixel 方法来确定 RGB 值。然后,您可以拥有一个存储 ARGB 值和计数的字典。然后您可以看到图像中出现最多的 ARGB 值。

var list = new Dictionary<int, int>();
Bitmap myImage = (Bitmap)Bitmap.FromFile("C:/test.jpeg");
for (int x = 0; x < myImage.Width; x++)
{
    for (int y = 0; y < myImage.Height; y++)
    {
        int rgb = myImage.GetPixel(x, y).ToArgb();
        if (!list.ContainsKey(rgb))
            list.Add(rgb, 1);
        else
            list[rgb]++;
    }
}

正如所指出的,这对相似的颜色没有任何同情。如果您想要更“一般”的多数颜色,则可以对相似性设置阈值。例如而不是:

if (!list.ContainsKey(rgb))
        list.Add(rgb, 1);
    else
        list[rgb]++;

你可以这样做:

var added = false;
for (int i = 0; i < 10; i++)
{
    if (list.ContainsKey(rgb+i))
    {
        list[rgb+i]++;
        added = true;
        break;
    }
    if (list.ContainsKey(rgb-i))
    {
        list[rgb-i]++;
        added = true;
        break;
    }
}
if(!added)
    list.Add(rgb, 1);

您可以将 10 的门槛提高到您需要的任何值。

【讨论】:

  • 如果这是一个“真彩色”jpeg...这可能是字典中的大量值,每个键的计数很少。
  • 同意。您需要某种分箱或分组才能体面地工作。有了这个,您可以拥有 2 个完美的红色像素和大量的黑色微妙变体,并以红色作为主要颜色。
  • 从技术上讲,红色将是主要颜色。
  • .. 这取决于什么是多数颜色。如果是一般颜色,那么在他的示例中,黑色应该是大多数。
  • for (int i = 0; i > 10; i++)。你的接线员应该换掉。
【解决方案2】:

您可能还会发现堆栈溢出的算法挑战很有用:

Algorithm challenge: Generate color scheme from an image

还可以考虑创建图像的直方图 - 并将价值最高的颜色作为“多数颜色”:http://www.phpclasses.org/browse/file/15953.html

【讨论】:

    【解决方案3】:

    这将返回图像的平均颜色。

    static Color AverageColor(string fileName)
    {
        using (var bmp = new Bitmap(fileName))
        {
            int width = bmp.Width;
            int height = bmp.Height;
            int red = 0;
            int green = 0;
            int blue = 0;
            int alpha = 0;
            for (int x = 0; x < width; x++)
                for (int y = 0; y < height; y++)
                {
                    var pixel = bmp.GetPixel(x, y);
                    red += pixel.R;
                    green += pixel.G;
                    blue += pixel.B;
                    alpha += pixel.A;
                }
    
            Func<int, int> avg = c => c / (width * height);
    
            red = avg(red);
            green = avg(green);
            blue = avg(blue);
            alpha = avg(alpha);
    
            var color = Color.FromArgb(alpha, red, green, blue);
    
            return color;
        }
    }
    

    【讨论】:

    • 我不认为结果符合“多数颜色”的条件,因为可能甚至没有一个像素是平均颜色。
    • 非常正确...这就是为什么我在问题下的评论中要求提供更多详细信息。没有回应,所以这是我的条目。
    【解决方案4】:

    这将使用不安全的指针访问返回图像的平均颜色。注意:代码仅适用于 24bppRgb,可适用于其他像素格式。

        unsafe static Color GetColor(string filename)
        {
            using (var image = (Bitmap)Bitmap.FromFile(filename))
            {
                if (image.PixelFormat != PixelFormat.Format24bppRgb) throw new NotSupportedException(String.Format("Unsupported pixel format: {0}", image.PixelFormat));
    
                var pixelSize = 3;
                var bounds = new Rectangle(0, 0, image.Width, image.Height);
                var data = image.LockBits(bounds, ImageLockMode.ReadOnly, image.PixelFormat);
    
                long r = 0;
                long g = 0;
                long b = 0;
    
                for (int y = 0; y < data.Height; ++y)
                {
                    byte* row = (byte*)data.Scan0 + (y * data.Stride);
                    for (int x = 0; x < data.Width; ++x)
                    {
                        var pos = x * pixelSize;
                        b += row[pos];
                        g += row[pos + 1];
                        r += row[pos + 2];
                    }
                }
    
                r = r / (data.Width * data.Height);
                g = g / (data.Width * data.Height);
                b = b / (data.Width * data.Height);
                image.UnlockBits(data);
    
                return Color.FromArgb((int)r, (int)g, (int)b);
            }
        }
    

    要点:http://gist.github.com/349210

    【讨论】:

    • 性能+1。在每个像素上调用 GetPixel 非常慢。
    • 极慢有点夸张。我能够使用 GetPixel 方法分析 15fps 800x600 的视频帧。所以如果发帖者只需要分析单张图片GetPixel就绰绰有余了。
    【解决方案5】:

    假设您使用RGB(相对于CMYK)表征每个像素的颜色,您可以构建一个 3d 数组(R、G 和 B 各一维)。然后决定您希望在每个维度中拥有多少个分档 - 分档越多,您在相似色调之间做出的区分就越大。

    完成后,只需遍历图像的位图表示,将落入 3d 数组中每个单元格的 # 个像素求和。总和最高的单元格将成为主要颜色。

    您可能希望为每个维度中的 # 个 bin 轻松配置您的算法,以便您可以调整它区分相似颜色的程度。

    image = new Bitmap("C:\\test.bmp", true);
    int x, y;
    // Loop through the images pixels to product 3d histogram
    for(x=0; x<image.Width; x++)
    {
       for(y=0; y<image.Height; y++)
       {
          Color pixelColor = image.GetPixel(x, y);
    
          // Increment color count in appropriate cell of your 3d histogram here
          ...
       }
    }
    

    【讨论】:

      猜你喜欢
      • 2012-09-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-25
      • 2011-05-24
      • 2011-09-18
      • 2012-11-09
      相关资源
      最近更新 更多