2
\$\begingroup\$

I wrote a function that will pixelate an image using the average color of each 4x4 cell.

The function will "split" the image into 4x4 cells, and find the average color of each pixel in the 4x4 cell, and set the 4x4 cell to the average color.

I'd like some help optimizing my function so it can pixelate an image as fast as possible. In its current state the pixelation process is quite slow.

private static Image PixelateImage(Image image)
{
    var bitmap = new Bitmap(image.Width, image.Height);
    using (var graphics = Graphics.FromImage(bitmap))
    {
        graphics.DrawImage(image, new Rectangle(0, 0, image.Width, image.Height),
            new Rectangle(0, 0, image.Width, image.Height), GraphicsUnit.Pixel);
    }

    // Loop through the image in 4x4 cells.
    for (var yy = 0; yy < image.Height && yy < image.Height; yy += 4)
    {
        for (var xx = 0; xx < image.Width && xx < image.Width; xx += 4)
        {
            var cellColors = new List<Color>();

            // Store each color from the 4x4 cell into cellColors.
            for (var y = yy; y < yy + 4 && y < image.Height; y++)
            {
                for (var x = xx; x < xx + 4 && x < image.Width; x++)
                {
                    cellColors.Add(bitmap.GetPixel(x, y));
                }
            }

            // Get the average red, green, and blue values.
            var averageRed = cellColors.Aggregate(0, (current, color) => current + color.R) / cellColors.Count;
            var averageGreen = cellColors.Aggregate(0, (current, color) => current + color.G) / cellColors.Count;
            var averageBlue = cellColors.Aggregate(0, (current, color) => current + color.B) / cellColors.Count;

            var averageColor = Color.FromArgb(averageRed, averageGreen, averageBlue);

            // Go BACK over the 4x4 cell and set each pixel to the average color.
            for (var y = yy; y < yy + 4 && y < image.Height; y++)
            {
                for (var x = xx; x < xx + 4 && x < image.Width; x++)
                {
                    bitmap.SetPixel(x, y, averageColor);
                }
            }
        }
    }
    return bitmap;
}

enter image description here enter image description here

\$\endgroup\$
1
  • 2
    \$\begingroup\$ There are a few ways to improve performance. One is to scale the image down to the smaller size, then scale it back up to the original size. Another is to render the picture using a pixel shader that handles the averaging. Yet another is to load the image as a texture, generate a MIP map from the texture, then render from the appropriate level of MIP map. Any of these will normally use the GPU for most of the "grunt" work, improving performance substantially. \$\endgroup\$ Commented Sep 1, 2016 at 0:59

1 Answer 1

3
\$\begingroup\$

Jerry Coffin suggested that I should simply scale the image down to a smaller size, and then back up to the original size. (Thanks!)

Using Emgu CV, I was able to shorten my function to a single return statement.

private Image<Bgr, byte> PixelateImage(Image<Bgr, byte> image)
{
    return image.Resize((int)(image.Width * .25), (int)(image.Height * .25), Inter.Area)
        .Resize(image.Width, image.Height, Inter.Nearest);
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.