22

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

__编辑: __

到目前为止,这是我的最终结果:

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

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

4

6 回答 6

27

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

源图像:

来源

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

在此处输入图像描述

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

在此处输入图像描述

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

在此处输入图像描述

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

于 2011-04-28T19:51:10.980 回答
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
于 2011-04-28T19:29:02.220 回答
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]);
    }
}

我没有尝试过滤到要选择色调。您可能需要考虑一些启发式方法,而不是盲目地选择如此多的计数,因为您可能想要选择在色谱上有些分离的色调。我没有时间进一步探讨这个问题,但我希望这能提供一些关于你可以考虑的策略的见解。

于 2011-04-28T19:39:20.007 回答
4

我从这里开始:

System.Drawing.Image img = System.Drawing.Bitmap.FromFile("file");
System.Drawing.Imaging.ColorPalette palette = img.Palette;
foreach (Color color in palette.Entries)
{
  //...
}
于 2011-04-28T19:47:23.323 回答
3

我将在非常高的层次上描述最好的方法。

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

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

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

一旦你有了缩小的调色板,你就可以使用调色板中最接近的颜色重新给图片上色。

于 2018-02-09T01:17:55.383 回答
2

K-Means聚类算法很好地解决了这个问题。它在提取图像颜色簇的质心方面做得很好,但请注意,它的非确定性行为使得确定每个簇的实际突出度变得困难。

于 2014-07-07T16:34:41.447 回答