Holiday Status Update

Even with the busy holiday season I still managed to make a lot of progress. The new version of RasterWave is getting nearer to release. Completed full unicode support and owner-drawn controls. No more system controls for RasterWave! ClockworkPDF is just about out of beta, finishing up a few minor fixes. I’ve also been working on a new palette management library for C# developers which should be available this week.

Oil Painting Filter in C#

Converting an image to make it look like an oil painting is not only a nice effect, but is also an easy to implement algorithm. This article demonstrates how to create an oil painting effect from an image. The oil painting filter consists of two main components: color gradients and pixel color intensities. Result images express a lesser degree of detail and tend to appear to have smaller color ranges.

Implementation

The following steps are required for the algorithm:

  1. Iterate each pixel – Every pixel forming part of the source image should be iterated. When iterating a pixel determine the neighboring pixel values based on the specified filter size (or radius).
  2. Calculate color intensityDetermine the Color Intensity of each pixel being iterated and that of the neighboring pixels. The neighboring pixels included should extend to a range determined by the Filter Size specified. The calculated value should be reduced in order to match a value ranging from zero to the number of Intensity Levels specified.
  3. Determine maximum neighborhood color intensity – When calculating the color intensities of a pixel neighborhood determine the maximum intensity value. In addition, record the occurrence of each intensity level and sum each of the Red, Green and Blue pixel color component values equating to the same intensity level.
  4. Assign the result pixel – The value assigned to the corresponding pixel in the resulting image equates to the pixel color sum total, where those pixels expressed the same intensity level. The sum total should be averaged by dividing the color sum total by the intensity level occurrence

When calculating color intensity reduced to fit the number of levels specified the formula can be expressed as follows:

colorintensityalgorithm.png

The variables in the above formula can be described as follows:

  • i – Intensity: the calculated intensity value.
  • R – Red: The value of the pixel’s red color component.
  • G – Green:
  • B – Blue:
  • l – Number of intensity levels: the maximum number of intensity levels specified.

Result Image: Filter 15, Levels 30

Oil Painting - Filter 15, Levels 30

The sample source code defines OilPaintFilter method, an extension method targeting the Bitmap class. This method determines the maximum color intensity from a pixel’s neighbors.

public static Bitmap OilPaintFilter(this Bitmap sourceBitmap, int levels, int filterSize)
        {
            Bitmap image;
            ArgbColor[] originalData;
            ArgbColor[] resultData;
            Size size;

            image = sourceBitmap;
            size = image.Size;

            originalData = image.GetPixelsFrom32BitArgbImage();
            resultData = new ArgbColor[size.Width * size.Height];

            int[] intensityBin = new int[levels];
            int[] blueBin = new int[levels];
            int[] greenBin = new int[levels];
            int[] redBin = new int[levels];

            levels = levels - 1;

            int filterOffset = (filterSize - 1) / 2;
            int byteOffset = 0;
            int calcOffset = 0;
            int currentIntensity = 0;
            int maxIntensity = 0;
            int maxIndex = 0;

            double blue = 0;
            double green = 0;
            double red = 0;

            for (int offsetY = filterOffset; offsetY < size.Height - filterOffset; offsetY++)
            {
                for (int offsetX = filterOffset; offsetX < size.Width - filterOffset; offsetX++)
                {
                    ArgbColor result;

                    blue = green = red = 0;

                    currentIntensity = maxIntensity = maxIndex = 0;

                    intensityBin = new int[levels + 1];
                    blueBin = new int[levels + 1];
                    greenBin = new int[levels + 1];
                    redBin = new int[levels + 1];

                    byteOffset = offsetY * size.Width + offsetX;

                    for (int filterY = -filterOffset; filterY <= filterOffset; filterY++)
                    {
                        for (int filterX = -filterOffset; filterX <= filterOffset; filterX++)
                        {
                            calcOffset = byteOffset + (filterX) + (filterY * size.Width);

                            currentIntensity = (int)Math.Round(((double)(originalData[calcOffset].R +
                                                originalData[calcOffset].G + originalData[calcOffset].B) / 3.0 * (levels)) / 255.0);

                            intensityBin[currentIntensity] += 1;
                            blueBin[currentIntensity] += originalData[calcOffset].B;
                            greenBin[currentIntensity] += originalData[calcOffset].G;
                            redBin[currentIntensity] += originalData[calcOffset].R;

                            if (intensityBin[currentIntensity] > maxIntensity)
                            {
                                maxIntensity = intensityBin[currentIntensity];
                                maxIndex = currentIntensity;
                            }
                        }
                    }

                    blue = blueBin[maxIndex] / maxIntensity;
                    green = greenBin[maxIndex] / maxIntensity;
                    red = redBin[maxIndex] / maxIntensity;

                    result.A = 255;
                    result.R = ClipByte(red);
                    result.G = ClipByte(green);
                    result.B = ClipByte(blue);

                    resultData[byteOffset] = result;
                }
            }

            return resultData.ToBitmap(size); ;
        }

Downloads

OilPaintingFilter.zip 13 Nov 2014 137 KB