1

My code calculates average color of each pixel in an image and returns a new image.

Image calculateAverage(Bitmap image1, Bitmap image2)
{
    // Locking the image into memory.
    BitmapData sourceData = image1.LockBits(new Rectangle(0, 0, image1.Width, image1.Height),
        ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);

    // All of the pixels will be stored in this array(each 4 numbers represent a pixel).
    byte[] sourceBuffer = new byte[sourceData.Stride * sourceData.Height];

    // Copying the image pixels into sourceBuffer.
    Marshal.Copy(sourceData.Scan0, sourceBuffer, 0, sourceBuffer.Length);

    // Don't need the image, unlock memory.
    image1.UnlockBits(sourceData);

    // --- Same thing for the second image ---
    BitmapData blendData = image2.LockBits(new Rectangle(0, 0, image2.Width, image2.Height),
        ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
    byte[] blendBuffer = new byte[blendData.Stride * blendData.Height];
    Marshal.Copy(blendData.Scan0, blendBuffer, 0, blendBuffer.Length);
    image2.UnlockBits(blendData);
    //---

    // Calculating average of each color in each pixel.
    for (int k = 0; (k + 4 < sourceBuffer.Length) && (k + 4 < blendBuffer.Length); k += 4)
    {
        sourceBuffer[k] = (byte)calcualteAverage(sourceBuffer[k], blendBuffer[k]);
        sourceBuffer[k + 1] = (byte)calcualteAverage(sourceBuffer[k + 1], blendBuffer[k + 1]);
        sourceBuffer[k + 2] = (byte)calcualteAverage(sourceBuffer[k + 2], blendBuffer[k + 2]);
        // Average of Alpha
        sourceBuffer[k + 3] = (byte)calcualteAverage(sourceBuffer[k + 3], sourceBuffer[k + 3]);
    }
    
    // Saving the result.
    Bitmap resultBitmap = new Bitmap(image1.Width, image1.Height);
    BitmapData resultData = resultBitmap.LockBits(new Rectangle(0, 0,
        resultBitmap.Width, resultBitmap.Height),
        ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);

    Marshal.Copy(sourceBuffer, 0, resultData.Scan0, sourceBuffer.Length);
    resultBitmap.UnlockBits(resultData);

    return resultBitmap;
}

For performance reasons I need to rewrite this method using unsafe code though I'm a noob with pointers; don't know how it can improve performance or what will change in the algorithm?

Hossein Ebrahimi
  • 632
  • 10
  • 20
  • Why don't you write in c++ and call the c++ dll from c#? How long does code take to execute? How much of a performance increase are you looking for? – jdweng Aug 11 '20 at 11:36
  • Would be Just a small improvement since it is a small portion of code and I'm not gonna do that much changes in a small application – Hossein Ebrahimi Aug 11 '20 at 11:42

1 Answers1

2

The function can be replaced by this:

public static Bitmap CalculateAverage(
    Bitmap sourceBitmap, Bitmap blendBitmap)
{
    // Locking the images into the memory.
    BitmapData sourceData = sourceBitmap.LockBits(
        new Rectangle(0, 0, sourceBitmap.Width, sourceBitmap.Height),
        ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);

    BitmapData blendData = blendBitmap.LockBits(
        new Rectangle(0, 0, blendBitmap.Width, blendBitmap.Height),
        ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);

    // Get required variables. This wont work for images with different sizes.
    int resultWidth = sourceBitmap.Width;
    int resultHeight = sourceBitmap.Height;
    int stride = sourceData.Stride;

    // result will be stored here.
    var resultBitmap = new Bitmap(resultWidth, resultHeight);
    BitmapData resultData = resultBitmap.LockBits(
        new Rectangle(0, 0, resultWidth, resultHeight),
        ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);

    unsafe
    {
        // Getting address of locked images.
        byte* sourceAddress = (byte*)sourceData.Scan0.ToPointer();
        byte* blendAddress = (byte*)blendData.Scan0.ToPointer();
        byte* resultAddress = (byte*)resultData.Scan0.ToPointer();

        // Iterating throgh pixels and storing averages inside the result variable.
        for (int y = 0; y < resultHeight; y++)
        {
            for (int x = 0; x < resultWidth; x++)
            {
                for (int color = 0; color < 4; color++)
                {
                    int currentByte = (y * stride) + x * 4 + color;

                    resultAddress[currentByte] = (byte)average(
                        sourceAddress[currentByte],
                        blendAddress[currentByte]);
                }
            }
        }
    }
    
    // Unlocking the Bits; returning the result bitmap.
    sourceBitmap.UnlockBits(sourceData);
    blendBitmap.UnlockBits(blendData);
    resultBitmap.UnlockBits(resultData);
    return resultBitmap;
}

I cannot test it at the moment but this might give a hint in the right direction. If there are any questions please don't hesitate to ask. It's based on this code: How to calculate the average rgb color values of a bitmap.

Hossein Ebrahimi
  • 632
  • 10
  • 20
Simon Tulling
  • 176
  • 1
  • 7