title: Find closest match to an RGB color value in a color swatch
body: <p>In a WPF usercontrol I'm writing I have 3 sliders representing RGB with values from 0 - 255.  Standard stuff !  Also displayed is a color swatch </p>
  
  <p><img src="https://i.imgur.com/69kEtdZ.png" alt="Displayed Color Swatch - imgur.com/69kEtdZ"></p>
  
  <p>Base class is </p>
  
  <pre><code>    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&lt;SwatchPixels&gt; _points = new List&lt;SwatchPixels&gt;();
  </code></pre>
  
  <p>Now what I want to do is (what I have sorta works but no accurate enough !)</p>
  
  <p>Load the List with all of the RGB values of the pixels in the color swatch and secondly find the closest pixel color to a supplied set of RGB values.  I can then move an ellipse to the x,y location of that pixel.  Reason I'm wanting this of course is that a color swatch generally will not contain all 16 million plus colors !</p>
  
  <p><strong>So if somebody can suggest a better method I would be most appreciative !</strong></p>
  
  <p>The swatch sits in a canvas object of the same size.
  </p>
  
  <pre><code> &lt;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"&gt;
  
            &lt;Ellipse Name="EllipsePixel" Width="8" Height="8" Stroke="Black" Fill="White" 
                     Canvas.Left="0" Canvas.Top="0"/&gt;
   &lt;/Canvas&gt;
  </code></pre>
  
  <p>Method to load all of the pixel values</p>
  
  <pre><code>private List&lt;SwatchPixels&gt; 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 &lt; bitmapImage.PixelHeight; y++)
          {
              for (int x = 0; x &lt; 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;
  }
  </code></pre>
  
  <p>The find a point method follows (the bit I'm not happy with !)</p>
  
  <pre><code>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&lt;SwatchPixels&gt; closepoints = new List&lt;SwatchPixels&gt;();
          List&lt;SwatchPixels&gt; prevCp = new List&lt;SwatchPixels&gt;(closepoints);
  
          var v = Convert.ToInt32(initVariance);
  
          foreach (var p in _points)
          {
              if ((iR &gt;= p.R - v &amp;&amp; iR &lt;= p.R + v) &amp;&amp;
                  (iG &gt;= p.G - v &amp;&amp; iG &lt;= p.G + v) &amp;&amp;
                  (iB &gt;= p.B - v &amp;&amp; iB &lt;= p.B + v))
              {
                  closepoints.Add(p);
              }
          }
  
          var c = closepoints.Count;
  
          while (c &gt; 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&lt;SwatchPixels&gt;(prevCp);
                  break;
              }
              else
              {
                  prevCp = new List&lt;SwatchPixels&gt;(closepoints);
              }
          }
  
          if (c &gt; 1)
              return closepoints[0];
  
          return null;
      }
  
      private List&lt;SwatchPixels&gt; LoopAgain(IEnumerable&lt;SwatchPixels&gt; cpoints, int v, int iR, int iG, int iB)
      {
          var closepoints = new List&lt;SwatchPixels&gt;();
  
          foreach (var p in cpoints)
          {
              if ((iR &gt;= p.R - v &amp;&amp; iR &lt;= p.R + v) &amp;&amp;
                                      (iG &gt;= p.G - v &amp;&amp; iG &lt;= p.G + v) &amp;&amp;
                                      (iB &gt;= p.B - v &amp;&amp; iB &lt;= p.B + v))
              {
                  closepoints.Add(p);
              }
          }
  
          return closepoints;
      }
  </code></pre>
  
  <p>Here is my modified ColorCanvas.
  Changes include individual color rectangles for RGB, apply variances to a chosen color and use different color models for entry.  Also you can copy the Hex contents to the clipboard with 1 click.</p>
  
  <p><img src="https://imgur.com/Iz93AFV.png" alt="New ColorCanvas"></p>