Title: Recursively perform red eye reduction on a picture in C#
The example Perform red eye reduction on a picture in C# examines the pixels in a selected rectangle and converts any pixel that is more red than green or blue into grayscale. That removes red eye in the area but it also converts any other mostly red pixels to grayscale. The picture of a face in that example has a lot of pinkish skin. Pink is basically a light shade of red, so the program converts the pink skin to grayscale if it is in the selected rectangle.
This program uses a different approach to selecting the pixels to convert. When you click a pixel, the following code executes.
// Perform red-eye reduction starting with the clicked pixel.
private void picImage_MouseClick(object sender, MouseEventArgs e)
{
Bitmap bm = (Bitmap)picImage.Image;
RemoveRedEyeAtPoint(1, bm, e.X, e.Y);
picImage.Image = bm;
}
This code basically just calls the following recursive RemoveRedEyeAtPoint method, passing it the point that you clicked.
// Remove red-eye in the rectangle.
private void RemoveRedEyeAtPoint(int depth, Bitmap bm, int x, int y)
{
// Don't recurse too deeply.
if (depth > 100) return;
// Do nothing if this pixel isn't mostly red.
Color clr = bm.GetPixel(x, y);
if ((clr.R <= clr.G + 10) || (clr.R <= clr.B + 10)) return;
// Remove red-eye at this point.
byte new_clr = (byte)((clr.R + clr.G + clr.B) / 3);
bm.SetPixel(x, y, Color.FromArgb(new_clr, new_clr, new_clr));
// Check adjacent pixels.
int min_x = Math.Max(x - 1, 0);
int max_x = Math.Min(x + 1, bm.Width - 1);
int min_y = Math.Max(y - 1, 0);
int max_y = Math.Min(y + 1, bm.Height - 1);
for (int new_x = min_x; new_x <= max_x; new_x++)
{
for (int new_y = min_y; new_y <= max_y; new_y++)
{
if ((new_x != 0) || (new_y != 0))
RemoveRedEyeAtPoint(depth + 1, bm, new_x, new_y);
}
}
}
This method call itself recursively so it first checks that its depth of recursion isn't too deep. If it didn't do this and you clicked a large red area on the picture, the method would call itself recursively until it filled the program's stack and crashed. This test just puts an upper bound on how many times the method will recurse. (If you really do need to convert a huge red area to grayscale, just click it a few times.)
Next the code gets the color of its target pixel. If that color isn't mostly red, the method returns. In this version I've added a small bias so the red component must be at least 10 greater than the green add blue components. That prevents the method from converting pixels that are basically gray but that have infinitesimally more red than green or blue.
If the code makes it this far, it converts the target pixel to grayscale.
Next the program loops over the adjacent pixels and recursively calls itself to check them for red pixels.
Unlike the previous version, this program doesn't incorrectly convert skin tones to grayscale (unless you click on the skin).
Download the example to experiment with it and to see additional details.
|