Title: Warholize an image in C#
When you "Warholize" an image, you reduce the number of colors it contains. The term comes from the name of Andy Warhol, the American painter, print maker, and film maker.
One particularly striking kind of image he produced featured a famous person such as John Lennon or Marilyn Monroe painted in very few colors. Sometimes several images of the same person painted with different color schemes were displayed together. The picture on the right shows a result that uses Warholized images of Marilyn Monroe.
This program achieves a vaguely similar result by mapping each pixel in an image to the closest target pixel and then changing its value to a corresponding result color. For example, in the picture shown at the top of this post the pixels that are closer to red than to the other target colors are mapped to light green in the result.
The program uses the following code to Warholize an image.
// Warholize.
private void btnGo_Click(object sender, EventArgs e)
{
picOutput.Image = null;
picOutput.Refresh();
// Get the input and output color data.
PictureBox[] in_boxes = { picFromColor0, picFromColor1,
picFromColor2, picFromColor3, picFromColor4 };
PictureBox[] out_boxes = { picToColor0, picToColor1,
picToColor2, picToColor3, picToColor4 };
byte[] in_r = new byte[in_boxes.Length];
byte[] in_g = new byte[in_boxes.Length];
byte[] in_b = new byte[in_boxes.Length];
byte[] out_r = new byte[in_boxes.Length];
byte[] out_g = new byte[in_boxes.Length];
byte[] out_b = new byte[in_boxes.Length];
for (int i = 0; i < in_boxes.Length; i++)
{
in_r[i] = in_boxes[i].BackColor.R;
in_g[i] = in_boxes[i].BackColor.G;
in_b[i] = in_boxes[i].BackColor.B;
out_r[i] = out_boxes[i].BackColor.R;
out_g[i] = out_boxes[i].BackColor.G;
out_b[i] = out_boxes[i].BackColor.B;
}
// Get and lock the Bitmap32.
Bitmap original_bm = picInput.Image as Bitmap;
Bitmap bm = new Bitmap(original_bm);
Bitmap32 bm32 = new Bitmap32(bm);
bm32.LockBitmap();
// Process the pixels.
for (int y = 0; y < bm.Height; y++)
{
for (int x = 0; x < bm.Width; x++)
{
// Process pixel (row, col).
byte r, g, b, a;
bm32.GetPixel(x, y, out r, out g, out b, out a);
int best_i = 0;
int best_dist = int.MaxValue;
for (int i = 0; i < in_boxes.Length; i++)
{
// Compute the distance from this pixel
// to input pixel i.
int dr = r - in_r[i];
int dg = g - in_g[i];
int db = b - in_b[i];
int dist = dr * dr + dg * dg + db * db;
// See if this is an improvement.
if (dist < best_dist)
{
best_dist = dist;
best_i = i;
}
}
// Update the pixel.
bm32.SetPixel(x, y,
out_r[best_i], out_g[best_i], out_b[best_i], 255);
}
}
// Unlock the Bitmap32.
bm32.UnlockBitmap();
// Display the result.
picOutput.Image = bm;
}
The code uses the Bitmap32 class to quickly manipulate the red, green, and blue color components of the image's pixels. For information about that class, see Use the Bitmap32 class to manipulate image pixels very quickly in C#.
The code loads the image into a Bitmap32 object and locks it. It then loops over each of the image's pixels.
For each pixel, the program finds the target color closest to the pixel's color. It then changes the pixel's value to the corresponding output color.
Load your own images and experiment with the program. Click a target or output color to change the color values and see what happens.
This program can save the result images, but it uses 24-bit color depth even though the result image only uses 5 colors.
This example can process a 1000x1000 pixel image in about half a second so it's fast enough that you can use it to make other programs that manipulate an image's pixels in a similar way.
Download the example to experiment with it and to see additional details.
|