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.
I'm using GDI+ but I am less interested in code. I need an algorithm.
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.
Here is an example:
This is a screenshot from Photoshop
Someone suggested that I should apply a Sharpen filter, so I tried it on photoshop. Here is how it looks:
Even though the outer edges are fine, the edges where 2 different colors meet show artifacts.
EDIT:
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.
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.
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 < bmp.Width; x++)
{
for (int y = 0; y < 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<int> selectedColors = new List<int>();
// Make up a list with the selected colors
foreach (int key in stats.Keys)
{
int total = (int)stats[key];
if (((float)total / totalSize) > 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<int> selectedColors)
{
bool flag = false;
for (int x = 0; x < bmp.Width; x++)
{
for (int y = 0; y < 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<Point> 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<Point> GetNeighbors(Bitmap bmp, int x, int y)
{
List<Point> neighbors = new List<Point>();
for (int i = x - 1; i > 0 && i <= x + 1 && i < bmp.Width; i++)
for (int j = y - 1; j > 0 && j <= y + 1 && j < bmp.Height; j++)
neighbors.Add(new Point(i, j));
return neighbors;
}
private static bool isBackedByNeighbors(Bitmap bmp, int x, int y)
{
List<Point> 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 > 2);
}
Result: Original Image: https://i.sstatic.net/dcPTV.png
De-anti-aliased Result: https://i.sstatic.net/R4VL1.png
The reversing procedure of a filter is called Deconvolution (Which is a specific case of the General Inverse Problem).
There are two types of Deconvolution:
Those are usually (Any of them) complex algorithms which take time (Unless using the naive "Wiener Filter" approach).
Assuming the filter is some kind of LPF poor's man solution would be some kind High Pass Filter (HPF). Any of those would give a look of "Sharper Image" and "Enhanced Edges". Known filter of this type is the Unsharp Mask:
alpha
is a predefined scaling factor of the "Sharpening" level.Enjoy...