# Padding an image for convolution (image processing)

I have written the following routines.

Do you know of any better algorithm?

Is there any faster way to pad an image for image processing?

How can I optimize them for better performance?

public class ImagePadder
{
public static Bitmap Pad(Bitmap image, int newWidth, int newHeight)
{
int width = image.Width;
int height = image.Height;
/*
It is always guaranteed that,

width < newWidth

and

height < newHeight
*/
if ((width < newWidth && height < newHeight)
|| (width<newWidth && height == newHeight)
|| (width==newWidth && height < newHeight))
{

BitmapLocker inputImageLocker = new BitmapLocker(image);

inputImageLocker.Lock();

int startPointX = (int)Math.Ceiling((double)(newWidth - width) / (double)2) - 1;
int startPointY = (int)Math.Ceiling((double)(newHeight - height) / (double)2) - 1;

for (int y = startPointY; y < (startPointY + height) ; y++)
{
for (int x = startPointX; x < (startPointX + width) ; x++)
{
int xxx = x - startPointX;
int yyy = y - startPointY;

string str = string.Empty;
}
}

inputImageLocker.Unlock();

}
else if (width == newWidth && height == newHeight)
{
return image;
}
else
{
throw new Exception("Pad() -- threw an exception");
}
}

public static double[,] Pad(double[,] image, int newWidth, int newHeight)
{
int width = image.GetLength(0);
int height = image.GetLength(1);
/*
It is always guaranteed that,

width < newWidth

and

height < newHeight
*/
if ((width < newWidth && height < newHeight)
|| (width < newWidth && height == newHeight)
|| (width == newWidth && height < newHeight))
{
double[,] resizedImage = new double[newWidth, newHeight];

double color = 0.0;

Grayscale.Fill(resizedImage, color);

int startPointX = ((newWidth - width) / 2)-1;
int startPointY = ((newHeight - height) / 2)-1;

for (int y = startPointY; y < startPointY + height; y++)
{
for (int x = startPointX; x < startPointX + width; x++)
{
int xxx = x - startPointX;
int yyy = y - startPointY;
resizedImage[x, y] = image[xxx, yyy];
}
}

return resizedImage;
}
else if (width == newWidth && height == newHeight)
{
return image;
}
else
{
throw new Exception("Pad() -- threw an exception");
}
}

//public static int[,] Pad(int[,] image, int newWidth, int newHeight)
//{
//    int width = image.GetLength(0);
//    int height = image.GetLength(1);

//    if ((width == height) && (width < newWidth && height < newHeight))
//    {
//        int[,] resizedImage = new int[width, height];

//        for (int j = 0; j < height; j++)
//        {
//            for (int i = 0; i < width; i++)
//            {
//            }
//        }

//        if (newWidth != width || newHeight != height)
//        {
//            int startPointX = (newWidth - width) / 2;
//            int startPointY = (newHeight - height) / 2;

//            for (int y = startPointY; y < startPointY + height; y++)
//            {
//                for (int x = startPointX; x < startPointX + width; x++)
//                {
//                    int temp = image[y - startPointY, x - startPointX];
//                    resizedImage[y, x] = temp;
//                }
//            }

//            string str = string.Empty;
//        }
//        else
//        {
//            for (int j = 0; j < height; j++)
//            {
//                for (int i = 0; i < width; i++)
//                {
//                    resizedImage[j,i] = image[j,i];
//                }
//            }
//        }

//        return resizedImage;
//    }
//    else
//    {
//        throw new Exception("Pad() - threw an exception!");
//    }
//}

/// <summary>
/// Pad an image to make it bigger in dimensions.
/// </summary>
/// <param name="image">image to be padded</param>
/// <param name="newMaskWidth">width to be attained</param>
/// <param name="newMaskHeight">height to be attained</param>
/// <param name="value">the value to be used as a pad</param>
/// <returns></returns>
//{
//    int width = image.GetLength(0);
//    int height = image.GetLength(1);

//    if ((width == height) && (width < newMaskWidth && height < newMaskHeight))
//    {

//        for (int y = 0; y < newMaskHeight; y++)
//        {
//            for (int x = 0; x < newMaskWidth; x++)
//            {
//                newMask[y,x] = new Complex(value, value);
//            }
//        }

//        {
//            int startPointX = (newMaskWidth  - width)/ 2;
//            int startPointY = (newMaskHeight - height)/ 2;

//            for (int y = startPointY; y < startPointY + height; y++)
//            {
//                for (int x = startPointX; x < startPointX + width; x++)
//                {
//                    newMask[y,x] = new Complex(image[y - startPointY, x - startPointX].Real, image[y - startPointY, x - startPointX].Imaginary);
//                }

//                Console.WriteLine();
//            }

//            string str = string.Empty;
//        }
//        else
//        {
//            for (int y = 0; y < newMaskHeight; y++)
//            {
//                for (int x = 0; x < newMaskWidth; x++)
//                {
//                    newMask[y, x] = new Complex(image[y, x].Real, image[y, x].Imaginary);
//                }
//            }
//        }

//    }
//    else
//    {
//        throw new Exception("Pad() - threw an exception!");
//    }
//}
#endregion
}


.

Here is BitmapLocker class,

public class BitmapLocker
{
//private properties
Bitmap _bitmap = null;
bool _isLocked = false;
BitmapData _bitmapData = null;
private byte[] _imageData = null;

//public properties
public IntPtr IntegerPointer { get; private set; }
public int Width { get { return _bitmap.Width; } }
public int Height { get { return _bitmap.Height; } }
public int Stride { get { return _bitmapData.Stride; } }
public int ColorDepth { get { return Bitmap.GetPixelFormatSize(_bitmap.PixelFormat); } }
public int Channels { get { return ColorDepth / 8; } }
public int PaddingOffset { get { return _bitmapData.Stride - (_bitmap.Width * Channels); } }
public PixelFormat ImagePixelFormat { get { return _bitmap.PixelFormat; } }
public bool IsGrayscale { get { return Grayscale.IsGrayscale(_bitmap); } }

//Constructor
public BitmapLocker(Bitmap source)
{
IntegerPointer = IntPtr.Zero;
this._bitmap = source;
}

/// Lock bitmap
public void Lock()
{
if (_isLocked == false)
{
try
{
// Lock bitmap (so that no movement of data by .NET framework) and return bitmap data
_bitmapData = _bitmap.LockBits(
new Rectangle(0, 0, _bitmap.Width, _bitmap.Height),
_bitmap.PixelFormat);

// Create byte array to copy pixel values
int noOfBitsNeededForStorage = _bitmapData.Stride * _bitmapData.Height;

int noOfBytesNeededForStorage = noOfBitsNeededForStorage / 8;

_imageData = new byte[noOfBytesNeededForStorage * ColorDepth];//# of bytes needed for storage

IntegerPointer = _bitmapData.Scan0;

// Copy data from __IntegerPointer to PixelArray
Marshal.Copy(IntegerPointer, _imageData, 0, _imageData.Length);

_isLocked = true;
}
catch (Exception)
{
throw;
}
}
else
{
throw new Exception("Bitmap is already locked.");
}
}

/// Unlock bitmap
public void Unlock()
{
if (_isLocked == true)
{
try
{
// Copy data from PixelArray to __IntegerPointer
Marshal.Copy(_imageData, 0, IntegerPointer, _imageData.Length);

// Unlock bitmap data
_bitmap.UnlockBits(_bitmapData);

_isLocked = false;
}
catch (Exception)
{
throw;
}
}
else
{
throw new Exception("Bitmap is not locked.");
}
}

//public void Show()
//{
//    if (_isLocked == true)
//    {
//        Console.WriteLine("ImagePixelFormat = " + ImagePixelFormat.ToString());
//        Console.WriteLine("Width = " + Width + " pixels");
//        Console.WriteLine("Height = " + Height + " pixels");
//        Console.WriteLine("_imageData.Length = " + _imageData.Length + " memorySize");
//        Console.WriteLine("Stride = " + Stride + " memorySize");
//        Console.WriteLine("Color Depth = " + ColorDepth + " bits");
//        Console.WriteLine();
//    }
//    else
//    {
//        throw new Exception("Bitmap is not locked.");
//    }
//}

/// <summary>
/// Get the color of the specified pixel
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns></returns>
public Color GetPixel(int x, int y)
{
Color clr = Color.Empty;

// Get color components count
int cCount = ColorDepth / 8;

// Get start index of the specified pixel
int i = (Height - y - 1) * Stride + x * cCount;

if (i > _imageData.Length - cCount)
{
throw new IndexOutOfRangeException();
}

if (ColorDepth == 32) // For 32 bpp get Red, Green, Blue and Alpha
{
byte b = _imageData[i];
byte g = _imageData[i + 1];
byte r = _imageData[i + 2];
byte a = _imageData[i + 3]; // a
clr = Color.FromArgb(a, r, g, b);
}
if (ColorDepth == 24) // For 24 bpp get Red, Green and Blue
{
byte b = _imageData[i];
byte g = _imageData[i + 1];
byte r = _imageData[i + 2];
clr = Color.FromArgb(r, g, b);
}
if (ColorDepth == 8)
// For 8 bpp get color value (Red, Green and Blue values are the same)
{
byte c = _imageData[i];
clr = Color.FromArgb(c, c, c);
}
return clr;
}

public void SetPixel(int x, int y, Color color)
{
// Get color components count
int cCount = ColorDepth / 8;

// Get start index of the specified pixel
int i = (Height - y) * Stride + x * cCount;

if (ColorDepth == 32) // For 32 bpp set Red, Green, Blue and Alpha
{
_imageData[i] = color.B;
_imageData[i + 1] = color.G;
_imageData[i + 2] = color.R;
_imageData[i + 3] = color.A;
}
if (ColorDepth == 24) // For 24 bpp set Red, Green and Blue
{
_imageData[i] = color.B;
_imageData[i + 1] = color.G;
_imageData[i + 2] = color.R;
}
if (ColorDepth == 8)
// For 8 bpp set color value (Red, Green and Blue values are the same)
{
_imageData[i] = color.B;
}
}
}


.

Here is, Grayscale.Fill()

    public static Bitmap Fill(Bitmap image, Color fill)
{
BitmapLocker locker = new BitmapLocker(image);
locker.Lock();
for (int i = 0; i < image.Width; i++)
{
for (int j = 0; j < image.Height; j++)
{
locker.SetPixel(i, j, fill);
}
}
locker.Unlock();

return image;
}


.

N.B. The commented out code need extensive debugging and intended for future inclusion. So, those are optional for your consideration at this moment.

• You are calling the public int ColorDepth { get { return Bitmap.GetPixelFormatSize(_bitmap.PixelFormat); } } for each SetPixel to calculate the color components. Make it a one time initialization property and set its value in the constructor instead of evaluating it each time. Aug 28, 2016 at 16:29
• I checked its source code and I think this probably would be micro-optimization becasue all it does is return(unchecked((int)pixfmt) >> 8) & 0xFF; Aug 28, 2016 at 16:33

As far as performance is concerned and if your code allows it you may consider using a jagged array instead of a multidimensional one. They are said to have a better performance.

And as usual the bottle-neck of your code are the GetPixel/SetPixel that are really slow. You should be accessing the bitmap directly

Edit:

You can improve BitmapLocker - make it IDisposable and use it with a using to automatically release the bitmap - you won't however gain any performance but you'll be sure that it's released without having to thing about releasing it manually.

Edit 2:

You are calling the public int ColorDepth { get { return Bitmap.GetPixelFormatSize(_bitmap.PixelFormat); } } for each SetPixel to calculate the color components. Make it a one time initialization property and set its value in the constructor instead of evaluating it each time.

Edit 3:

I checked its source code and I think this probably would be micro-optimization because all it does is return(unchecked((int)pixfmt) >> 8) & 0xFF;

• What do you think about Grayscale.Fill(resizedImage, color);?
– user3804
Aug 28, 2016 at 16:13
• @anonymous it's not the same as LockBits becasue you are still usting GetPixel/SetPixel which are probably extensions calling inputImageLocker.Bitmap.GetPixel - arent't they? - because the locker object does not have such methods. Aug 28, 2016 at 16:16
• What do I think about Grayscale.Fill? - as long as you are using the Get/SetPixel approach, the image operations will be slow. Aug 28, 2016 at 16:19
• @anonymous oh, haha ;-) you should have posted it right away :-] no one could have known that. Aug 28, 2016 at 16:21
• @anonymous but you can improve this one too - make it IDisposable and use it with a using to automatically release the bitmap - you won't however gain any performance but you'll be sure that it's released without having to think about releasing it manually. Aug 28, 2016 at 16:23