在颜色样本中查找与RGB颜色值最接近的匹配项

时间:2014-07-10 22:40:01

标签: wpf colors

在WPF用户控件中,我写了3个滑块代表RGB,值为0 - 255.标准的东西!还显示了一个色样

Displayed Color Swatch - imgur.com/69kEtdZ

基类是

    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的单个颜色矩形,将差异应用于所选颜色,并使用不同的颜色模型进行输入。您也可以单击一下将十六进制内容复制到剪贴板。

New ColorCanvas

2 个答案:

答案 0 :(得分:1)

请勿重新发明轮子,尝试使用Extended WPF Toolkit™ Community Edition,您有两个控件ColorPickerColorCanvas

答案 1 :(得分:0)

我不完全确定,但是,让我们看看我是否可以帮助你:

你的&#34;色样&#34;看起来很多,但不完全像一个控件来选择HSL颜色选择器的HUE组件,除了奇怪的阴影。这也意味着它中只有一个RGB子集,你已经知道了。

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

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

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

hue-bar

通常,您可以轻松地从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;

    }

让我们来看看另一个颜色选择器,它看起来几乎和你的一样:

hsl

这个选择器有一些优点:

  • 它存储色调和饱和度,允许您将椭圆精确地放置在某个特定位置
  • 它是圆的,这使计算更容易

回答你的实际问题:

当您将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);