如何从图像生成突出色彩的调色板?
c#
image-processing
6
0

我试图找出如何采样的所有像素的图像,并从它产生的颜色的调色板,像这样这样 。我什至不知道从哪里开始。谁能指出我正确的方向?

__编辑:__

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

我使用此Pixelate函数来获取建议的joe_coolish之类的大块部分。它工作得很好,并为我提供了很好的色彩示例(这是从Windows示例水母图片中获得的):

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

参考资料:
Stack Overflow
收藏
评论
共 6 个回答
高赞 时间 活跃

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

收藏
评论

我从这里开始:

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

首先,获取图片中的像素:(假设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
收藏
评论

在任何一张丰富的图像中,大多数颜色可能都会以某种方式是唯一的。随之而来的是,获取不同的颜色可能不会帮助您实现目标。

我建议检查图像中每个像素的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]);
    }
}

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

收藏
评论

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

首先,您要在图片中绘制颜色及其频率的直方图。

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

这样,您可以将调色板逐渐减小到所需的保真度,同时保留高对比度但细腻的细节,并且仅在渐变更加微妙的情况下才失去保真度。

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

收藏
评论

涉及代码的答案将向您展示如何获取完整的调色板。如果您想获得所发布网站中的平均颜色,这就是我的方法。

源图像:

资源

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

在此处输入图片说明

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

在此处输入图片说明

从那里,针对每个块并遍历每个像素,并获得该块的平均像素并将其添加到调色板索引中。结果是这样的:

在此处输入图片说明

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

收藏
评论
新手导航
  • 社区规范
  • 提出问题
  • 进行投票
  • 个人资料
  • 优化问题
  • 回答问题

关于我们

常见问题

内容许可

联系我们

@2020 AskGo
京ICP备20001863号