title: Reverse (remove) anti-aliasing filter
body: <p>I have a set of anti-aliased greyscale PNG images. I need to know how to programatically revert the anti-aliasing effect and get sharp edges again.</p>
  <p>I'm using GDI+ but I am less interested in code. I need an algorithm.</p>
  <p>The greyscale images (should) contain only 6 colors (or different shades of grey). This is so that later on I can re-color them using a Color-Lookup filter. However, when the images where saved, Photoshop automatically applied anti-aliasing so the edges were blurred (because the Bicubic Interpolation mode was enabled). I need to revert that effect.</p>
  <p>Here is an example:</p>
  <p><img src="https://i.stack.imgur.com/QcAyt.png" alt="enter image description here" /></p>
  <p>This is a screenshot from Photoshop</p>
  <p>Someone suggested that I should apply a Sharpen filter, so I tried it on photoshop. Here is how it looks:</p>
  <p><img src="https://i.stack.imgur.com/J4VwJ.png" alt="enter image description here" /></p>
  <p>Even though the outer edges are fine, the edges where 2 different colors meet show artifacts.</p>
  <p>EDIT:</p>
  <p>This is how I ended up doing it. It is very much improvised and can probably be done better and faster, but I couldn't find any better solution.</p>
  <p>The idea is to iterate over each pixel, get its direct neighbors and compare its color to theirs. If it's backed by at least 2 pixels of same color, it checks if the neighbor pixel is backed as well. If not, it replaces the neighbor pixel with its own.</p>
  <p>Code:</p>
  <pre><code>    private static void Resample(Bitmap bmp)
      {
          // First we look for the most prominent colors
          // i.e. They make up at least 1% of the image
          Hashtable stats = new Hashtable();
  
          for (int x = 0; x &lt; bmp.Width; x++)
          {
              for (int y = 0; y &lt; bmp.Height; y++)
              {
                  Color px = bmp.GetPixel(x, y);
                  if (px.A == 0)
                      continue;
  
                  Color pxS = Color.FromArgb(255, px);
                  if (stats.ContainsKey(pxS.ToArgb()))
                      stats[pxS.ToArgb()] = (int)stats[pxS.ToArgb()] + 1;
                  else
                      stats.Add(pxS.ToArgb(), 1);
              }
          }
  
          float totalSize = bmp.Width*bmp.Height;
          float minAccepted = 0.01f;
          List&lt;int&gt; selectedColors = new List&lt;int&gt;();
  
          // Make up a list with the selected colors
          foreach (int key in stats.Keys)
          {
              int total = (int)stats[key];
              if (((float)total / totalSize) &gt; minAccepted)
                  selectedColors.Add(key);
          }
  
          // Keep growing the zones with the selected colors to cover the invalid colors created by the anti-aliasing
          while (GrowSelected(bmp, selectedColors));
      }
  
      private static bool GrowSelected(Bitmap bmp, List&lt;int&gt; selectedColors)
      {
          bool flag = false;
  
          for (int x = 0; x &lt; bmp.Width; x++)
          {
              for (int y = 0; y &lt; bmp.Height; y++)
              {
                  Color px = bmp.GetPixel(x, y);
                  if (px.A == 0)
                      continue;
  
                  Color pxS = Color.FromArgb(255, px);
  
                  if (selectedColors.Contains(pxS.ToArgb()))
                  {
                      if (!isBackedByNeighbors(bmp, x, y))
                          continue;
  
                      List&lt;Point&gt; neighbors = GetNeighbors(bmp, x, y);
                      foreach(Point p in neighbors)
                      {
                          Color n = bmp.GetPixel(p.X, p.Y);
                          if (!isBackedByNeighbors(bmp, p.X, p.Y))
                              bmp.SetPixel(p.X, p.Y, Color.FromArgb(n.A, pxS));
                      }
                  }
                  else
                  {
                      flag = true;
                  }
              }
          }
  
          return flag;
      }
  
      private static List&lt;Point&gt; GetNeighbors(Bitmap bmp, int x, int y)
      {
          List&lt;Point&gt; neighbors = new List&lt;Point&gt;();
  
          for (int i = x - 1; i &gt; 0 &amp;&amp; i &lt;= x + 1 &amp;&amp; i &lt; bmp.Width; i++)
              for (int j = y - 1; j &gt; 0 &amp;&amp; j &lt;= y + 1 &amp;&amp; j &lt; bmp.Height; j++)
                  neighbors.Add(new Point(i, j));
          return neighbors;
      }
  
      private static bool isBackedByNeighbors(Bitmap bmp, int x, int y)
      {
          List&lt;Point&gt; neighbors = GetNeighbors(bmp, x, y);
          Color px = bmp.GetPixel(x, y);
          int similar = 0;
          foreach (Point p in neighbors)
          {
              Color n = bmp.GetPixel(p.X, p.Y);
              if (Color.FromArgb(255, px).ToArgb() == Color.FromArgb(255, n).ToArgb())
                  similar++;
          }
  
          return (similar &gt; 2);
      }
  </code></pre>
  <p>Result:
  Original Image:
  <a href="https://i.imgur.com/8foQwFe.png" rel="nofollow noreferrer">http://i.imgur.com/8foQwFe.png</a></p>
  <p>De-anti-aliased Result:
  <a href="https://i.imgur.com/w6gELWJ.png" rel="nofollow noreferrer">http://i.imgur.com/w6gELWJ.png</a></p>