如何从图像中生成突出颜色的调色板?

时间:2011-04-28 19:22:39

标签: c# image-processing colors color-palette

我正在尝试弄清楚如何对图像中的所有像素进行采样并从中生成颜色调色板,例如thisthis。我不知道从哪里开始。有人能指出我正确的方向吗?

__编辑:__

这是我到目前为止所得到的:

我使用这个Pixelate函数来获取像joe_coolish建议的大块部分。它工作得很完美,给我一个非常好的颜色样本(这是来自windows样本果冻鱼图片):

现在,如果有人可以帮助我获得5种最鲜明的颜色(最深的蓝色,最浅的蓝色,橙色,灰色和桃色(?)),我会永远爱你。我真的不明白如何平均添加颜色。我也无法弄清楚如何以编程方式判断颜色是否相似,在你的解释中有一些数字和变量我会迷失,试图找出对谁做什么。

6 个答案:

答案 0 :(得分:24)

涉及代码的答案向您展示如何获得完整的调色板。如果你想获得你发布的网站上的平均颜色,我就是这样做的。

来源图片:

Source

首先,我会通过应用低通滤波器(类似于高斯模糊)来平均颜色

enter image description here

这样你就限制了整个调色板。从那里我将屏幕划分为N个块(N是您在调色板中想要的像素总数)

enter image description here

从那里开始,定位每个块并迭代每个像素,并获得该块的平均像素并将其添加到调色板索引中。结果是这样的:

enter image description here

这样你的调色板是有限的,你可以得到不同地区的平均颜色。你可以在代码中完成所有这些,如果你想要一些帮助,请告诉我,我会发布一些。这只是“我会做什么”的高级别。

答案 1 :(得分:6)

首先,拍摄照片中的像素:(假定using System.Drawing.Imaging;using System.Runtime.InteropServices

Bitmap b = new Bitmap(myImage);
BitmapData bd = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadOnly, ImageFormat.Format32Bpp);
int[] arr = new int[bd.Width * bd.Height - 1];
Marshal.Copy(bd.Scan0, arr, 0, arr.Length);
b.UnlockBits(bd);

然后你可以创建你的调色板:

var distinctColors = arr.Distinct();

可选:在您拥有首选调色板大小之前,消除相似的颜色。这是你如何做到这一点(虽然这绝对不是最有效或最准确的方式,只是最简单的方法):

var dc = distinctColors.toArray(); // int dc[] = distinctColors.toArray() is what it used to be
int cmIndex1 = -1;
int cmIndex2 = -1;
int cmDiff = -1;
for (int i = 0; i < dc.length; i++) {
    Color c1 = Color.FromArgb(dc[i]);
    for (int j = i + 1; j < dc.length; j++) {
        Color c2 = Color.FromArgb(dc[j]);
        // Note: you might want to include alpha below
        int diff = Math.Abs(c1.R - c2.R) + Math.Abs(c1.G - c2.G) + Math.Abs(c1.B - c2.B);
        if (cmDiff < 0 || diff < cmDiff) {
            cmIndex1 = i;
            cmIndex2 = j;
            cmDiff = diff;
        }
    }
}
// Remove the colors, replace with average, repeat until you have the desired number of colors

答案 2 :(得分:5)

在任何丰富的图像中,大多数颜色都可能在某种程度上是独一无二的。然后,接下来,获取不同的颜色可能无法帮助您实现目标。

我建议检查图像中每个像素的HSV值。我将为您提供无数的在线示例,将图像检索为HSV值数组。

使用您的HSV值,您可以通过创建256个色调计数的整数数组来计算突出色调的聚类,计算图像数据中色调的直方图。您可以通过查找具有高计数总和的4-6个连续色调的簇来确定突出的色调。

选取几个突出的色调后,将这些色调的像素细分为另一个测量饱和度的直方图,并选出突出的簇,等等。

粗略示例

下面的代码尝试帮助识别突出的色调。最有可能的其他方法可以做到这一点;但是,这可能会提供一些想法。

首先,我将所有图像颜色作为Color个对象的数组,如下所示:

private static Color[] GetImageData(Image image)
{
    using (var b = new Bitmap(image))
    {
        var bd = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
        byte[] arr = new byte[bd.Width * bd.Height * 3];
        Color[] colors = new Color[bd.Width * bd.Height];
        Marshal.Copy(bd.Scan0, arr, 0, arr.Length);
        b.UnlockBits(bd);

        for (int i = 0; i < colors.Length; i++)
        {
            var start = i*3;
            colors[i] = Color.FromArgb(arr[start], arr[start + 1], arr[start + 2]);
        }

        return colors;
    }
}

您可以考虑验证我在Color.FromArgb方法调用中以正确的顺序获得RGB的顺序。

接下来,我将一个用于转换为HSV的实用方法藏匿在一边。在我的例子中,我只使用色调,但这里有一个完整的转换示例:

private static void ColorToHSV(Color color, out int hue, out int saturation, out int value)
{
    int max = Math.Max(color.R, Math.Max(color.G, color.B));
    int min = Math.Min(color.R, Math.Min(color.G, color.B));

    hue = (int)(color.GetHue() * 256f / 360f);
    saturation = (max == 0) ? 0 : (int)(1d - (1d * min / max));
    value = (int)(max / 255d);
}

最后,我构建色调直方图,定义色调的宽度(比方说,9个色调),在其中聚合计数,然后我将计数报告给控制台。

private static void ProcessImage(Color[] imagecolors)
{
    var hues = new int[256];
    var hueclusters = new int[256];
    int hue, saturation, value;

    // build hue histogram.
    foreach (var color in imagecolors) {
        ColorToHSV(color, out hue, out saturation, out value);
        hues[hue]++;
    }

    // calculate counts for clusters of colors.
    for (int i = 0; i < 256; i++) {
        int huecluster = 0;
        for (int count = 0, j = i; count < 9; count++, j++) {
            huecluster += hues[j % 256];
        }

        hueclusters[(i + 5) % 256] = huecluster;
    }

    // Print clusters on the console
    for (int i = 0; i < 256; i++) {
        Console.WriteLine("Hue {0}, Score {1}.", i, hueclusters[i]);
    }
}

我没有尝试过滤到选择哪种色调。您可能需要考虑一些启发式而不是盲目地选择最重要的数量,因为您可能希望选择在色谱上有些分离的色调。我没有时间进一步探讨这个问题,但我希望这可以为您提供一些可以考虑的策略。

答案 3 :(得分:3)

我从这里开始:

System.Drawing.Image img = System.Drawing.Bitmap.FromFile("file");
System.Drawing.Imaging.ColorPalette palette = img.Palette;
foreach (Color color in palette.Entries)
{
  //...
}

答案 4 :(得分:3)

我将在很高的层次上描述最佳方法。

首先,您可以构建图片中颜色的直方图及其频率。

您最终会得到图像中所有颜色的列表,您可以使用数据聚类来查找要合并的候选颜色。根据原始颜色的频率合并为加权平均值的颜色。

通过这种方式,您可以将调色板逐渐减小到所需的保真度,同时保留高对比度但精细的细节,并且只有在渐变更微妙的情况下才会失去保真度。

使用缩小的调色板后,使用调色板中最近的邻居颜色重新着色图片。

答案 5 :(得分:2)

K-Means聚类算法适用于此问题。它在提取图像颜色聚类的质心方面做得很好,但要注意其非确定性行为使得难以确定每个聚类的实际突出性。