【问题标题】:Find closest match to an RGB color value in a color swatch在色样中查找与 RGB 颜色值最接近的匹配
【发布时间】:2014-09-01 11:21:09
【问题描述】:

在我正在编写的 WPF 用户控件中,我有 3 个滑块代表 RGB,其值从 0 到 255。标准的东西!还显示了一个色样

基类是

    private class SwatchPixels
    {
        public Point P { get; set; }
        public int X { get; set; }
        public int Y { get; set; }

        public int R { get; set; }
        public int G { get; set; }
        public int B { get; set; }
        public int A { get; set; }

    }

private List<SwatchPixels> _points = new List<SwatchPixels>();

现在我想做的是(我有一些工作但不够准确!)

使用色样中像素的所有 RGB 值加载列表,然后找到与所提供的一组 RGB 值最接近的像素颜色。然后我可以将一个椭圆移动到该像素的 x,y 位置。我想要这个的原因当然是色板通常不会包含所有 1600 万种颜色!

如果有人能提出更好的方法,我将不胜感激!

样本位于相同大小的画布对象中。

 <Canvas Name="CanvasImage" Grid.Row="0" Grid.Column="0" Grid.RowSpan="3" Width="150" Height="150"
         HorizontalAlignment="Center" Background="Transparent" VerticalAlignment="Top" Margin="2" 
         MouseMove="CanvasImage_MouseMove" MouseDown="CanvasImage_MouseDown" MouseUp="CanvasImage_MouseUp">

          <Ellipse Name="EllipsePixel" Width="8" Height="8" Stroke="Black" Fill="White" 
                   Canvas.Left="0" Canvas.Top="0"/>
 </Canvas>

加载所有像素值的方法

private List<SwatchPixels> FindAllPixelLocations()
{
    // http://stackoverflow.com/questions/1176910/finding-specific-pixel-colors-of-a-bitmapimage

    var img = new BitmapImage(new Uri(@"Resources/Cws.png", UriKind.RelativeOrAbsolute));
    Image Ti = new Image();
    Ti.Source = img;

    ImageSource ims = Ti.Source;
    BitmapImage bitmapImage = (BitmapImage)ims;

    try
    {
        int stride = bitmapImage.PixelWidth * 4;
        int size = bitmapImage.PixelHeight * stride;  // stride
        pixels = new byte[size];
        bitmapImage.CopyPixels(pixels, stride, 0);

        for (int y = 0; y < bitmapImage.PixelHeight; y++)
        {
            for (int x = 0; x < bitmapImage.PixelWidth; x++)
            {
                int index = y * stride + 4 * x;
                byte red = pixels[index];
                byte green = pixels[index + 1];
                byte blue = pixels[index + 2];
                byte alpha = pixels[index + 3];

                var swatchPixels = new SwatchPixels
                {
                    X = x,
                    Y = y,
                    P = new Point(x, y),
                    R = red,
                    G = green,
                    B = blue,
                    A = alpha
                };

                _points.Add(swatchPixels);

            }
        }
    }
    catch (Exception e)
    {
        MessageBox.Show(e.Message);
    }

    return _points;
}

接下来是找点方法(我不满意的一点!)

private const int initVariance = 40;
private const int dropPercent = 5;

    private SwatchPixels FindXy(string a, string r, string g, string b)
    {
        var iR = 0;
        var iG = 0;
        var iB = 0;

        if (RgbDec.IsChecked == true)
        {
            iR = Convert.ToByte(r);
            iG = Convert.ToByte(g);
            iB = Convert.ToByte(b);
        }
        else
        {
            // Number in hex format so convert to dec first

            iR = Convert.ToByte(int.Parse(r, NumberStyles.HexNumber));
            iG = Convert.ToByte(int.Parse(g, NumberStyles.HexNumber));
            iB = Convert.ToByte(int.Parse(b, NumberStyles.HexNumber));
        }

        List<SwatchPixels> closepoints = new List<SwatchPixels>();
        List<SwatchPixels> prevCp = new List<SwatchPixels>(closepoints);

        var v = Convert.ToInt32(initVariance);

        foreach (var p in _points)
        {
            if ((iR >= p.R - v && iR <= p.R + v) &&
                (iG >= p.G - v && iG <= p.G + v) &&
                (iB >= p.B - v && iB <= p.B + v))
            {
                closepoints.Add(p);
            }
        }

        var c = closepoints.Count;

        while (c > 10)
        {
            v = v - (v * dropPercent / 100);
            closepoints = LoopAgain(closepoints, v, iR, iG, iB);
            c = closepoints.Count;
            //Variance.Text += c.ToString() + Environment.NewLine;
            if (c == 0)
            {
                closepoints = new List<SwatchPixels>(prevCp);
                break;
            }
            else
            {
                prevCp = new List<SwatchPixels>(closepoints);
            }
        }

        if (c > 1)
            return closepoints[0];

        return null;
    }

    private List<SwatchPixels> LoopAgain(IEnumerable<SwatchPixels> cpoints, int v, int iR, int iG, int iB)
    {
        var closepoints = new List<SwatchPixels>();

        foreach (var p in cpoints)
        {
            if ((iR >= p.R - v && iR <= p.R + v) &&
                                    (iG >= p.G - v && iG <= p.G + v) &&
                                    (iB >= p.B - v && iB <= p.B + v))
            {
                closepoints.Add(p);
            }
        }

        return closepoints;
    }

这是我修改后的 ColorCanvas。 更改包括用于 RGB 的单个颜色矩形、将差异应用于所选颜色以及使用不同的颜色模型进行输入。您也可以一键将十六进制内容复制到剪贴板。

【问题讨论】:

    标签: wpf colors


    【解决方案1】:

    我不完全确定,但让我们看看能不能帮到你:

    除了奇怪的阴影外,您的“色样”看起来大多,但并不完全像选择 HSL 颜色选择器的 HUE 组件的控件。这也意味着其中只有一个 RGB 子集,您已经知道了。

    现在我看到你的照片中有两个主要缺点:

    • 当给定某种颜色时,无法确定将椭圆放置在靠近中心还是靠近边界,因为颜色在所有半径处都是相同的。

    • 色板以绿色开始,以蓝色结束。 Hue-Selector 通常以红色开始,以红色结束:以下来自 Inkscape:

    通常,您可以轻松地从 RGB 颜色中提取 HSL 组件,例如使用以下函数:

    public static void FromRGB(byte ARed, byte AGreen, byte ABlue, out double AHue, out double ASaturation, out double AValue)
        {
            double h = 0;
            double s = 0;
    
            double r = (double)ARed / 255;
            double g = (double)AGreen / 255;
            double b = (double)ABlue / 255;
    
            double max = Math.Max(r, Math.Max(g, b));
            double min = Math.Min(r, Math.Min(g, b));
    
            if (r == g && g == b) h = 0;
    
            if (r == max) h = 60.0 * ((g - b) / (max - min));
            else if (g == max) h = 60.0 * (2.0 + (b - r) / (max - min));
            else if (b == max) h = 60.0 * (4.0 + (r - g) / (max - min));
    
            if (max != 0) s = (max - min) / max;
    
            AHue = h;
            ASaturation = s;
            AValue = max;
    
        }
    

    让我们看看另一个颜色选择器,它看起来和你的差不多:

    这个选择器有一些优点:

    • 它存储色调和饱和度,让您可以将椭圆精确地放置在某个特定位置
    • 它是圆形的,这使得计算更容易一些

    回答您的实际问题:

    当您将 RGB 转换为 HSV,将饱和度和值分量设置为最大值并将其转换回 RGB 时,您将在选择器中找到最佳匹配像素。

    希望对你有所帮助...

    编辑

    根据要求,我将提供一个有关如何制作自己的 rgb 选择器图像的示例:

    if (w <= 0 || h <= 0) return;
    
            WriteableBitmap wb = new WriteableBitmap(w, h, 96, 96, PixelFormats.Rgb24, null);
    
            byte[] pixels = new byte[wb.PixelWidth * wb.PixelHeight * 3];
    
            Color base_color = GetBaseColorFromHue(Hue * 1.5);
    
            for (int y = 0; y < h; y++)
            {
                double pos_v = (double)y / (double)h;
    
                for (int x = 0; x < w; x++)
                {
                    double pos_s = (double)x / (double)w;
    
                    pixels[(y * wb.PixelWidth + x) * 3] = (byte)((base_color.R * (pos_s) + 255 * (1 - pos_s)) * (1.0 - pos_v));
                    pixels[(y * wb.PixelWidth + x) * 3 + 1] = (byte)((base_color.G * (pos_s) + 255 * (1 - pos_s)) * (1.0 - pos_v));
                    pixels[(y * wb.PixelWidth + x) * 3 + 2] = (byte)((base_color.B * (pos_s) + 255 * (1 - pos_s)) * (1.0 - pos_v));
                }
            }
    
            wb.WritePixels(new Int32Rect(0, 0, wb.PixelWidth, wb.PixelHeight), pixels, wb.PixelWidth * wb.Format.BitsPerPixel / 8, 0);
    

    【讨论】:

    • SnowballTwo,是的,这确实有帮助,对 Pablo 来说,我尝试自己写是有原因的!您从哪里获得样本 SnowballTwo ?它有不同的尺寸吗?
    • 这个来自Paint.Net,但你不应该简单地把它当作一个位图。自己写!这很简单,我将在下面发布一些代码。
    【解决方案2】:

    不要重新发明轮子,试试Extended WPF Toolkit™ Community Edition,你有两个控件ColorPicker和ColorCanvas。

    【讨论】:

    • 回答了 Snowball 之后,我去看看 colorCanvas,我可能会走那条路。我从他们那里知道 Picker,但不知道 Canvas 工具。谢谢。
    • Pablo,标记您的答案,因为我已经仔细查看了 Color Canvas 工具,并且可以很容易地修改它的 UI 以适合我的应用程序。感谢您对它的引用,在这种情况下,它将为我节省很多时间!
    • 完成后会发布我的更改图片:)
    猜你喜欢
    • 2016-11-13
    • 1970-01-01
    • 2017-10-26
    • 2019-08-17
    • 2019-07-02
    • 2011-05-09
    • 2022-10-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多