1
\$\begingroup\$

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))
 {
 Bitmap paddedImage = Grayscale.CreateGrayscaleImage(newWidth, newHeight);
 BitmapLocker inputImageLocker = new BitmapLocker(image);
 BitmapLocker paddedImageLocker = new BitmapLocker(paddedImage);
 inputImageLocker.Lock();
 paddedImageLocker.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;
 paddedImageLocker.SetPixel(x, y, inputImageLocker.GetPixel(xxx, yyy));
 string str = string.Empty;
 }
 }
 paddedImageLocker.Unlock();
 inputImageLocker.Unlock();
 return paddedImage;
 }
 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];
 // int padValue = Color.Black.ToArgb();
 // for (int j = 0; j < height; j++)
 // {
 // for (int i = 0; i < width; i++)
 // {
 // resizedImage[j,i] = padValue;
 // }
 // }
 // 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!");
 // }
 //}
 #region public static Complex[,] Pad(Complex[,] image, int newMaskWidth, int newMaskHeight, int value)
 /// <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>
 //public static Complex[,] Pad(Complex[,] image, int newMaskWidth, int newMaskHeight, int value)
 //{
 // int width = image.GetLength(0);
 // int height = image.GetLength(1);
 // if ((width == height) && (width < newMaskWidth && height < newMaskHeight))
 // {
 // Complex[,] newMask = new Complex[newMaskWidth, newMaskHeight];
 // //Creating the padding mask
 // for (int y = 0; y < newMaskHeight; y++)
 // {
 // for (int x = 0; x < newMaskWidth; x++)
 // {
 // newMask[y,x] = new Complex(value, value);
 // }
 // }
 // if (newMaskWidth > width && newMaskHeight > height)
 // {
 // 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);
 // }
 // }
 // }
 // return newMask;
 // }
 // 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),
 ImageLockMode.ReadWrite,
 _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("PaddingOffset = " + PaddingOffset + " memorySize");
 // 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.

asked Aug 28, 2016 at 12:39
\$\endgroup\$
2
  • \$\begingroup\$ 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. \$\endgroup\$ Commented Aug 28, 2016 at 16:29
  • \$\begingroup\$ 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; \$\endgroup\$ Commented Aug 28, 2016 at 16:33

1 Answer 1

1
\$\begingroup\$

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;

answered Aug 28, 2016 at 16:05
\$\endgroup\$
5
  • \$\begingroup\$ What do you think about Grayscale.Fill(resizedImage, color);? \$\endgroup\$ Commented Aug 28, 2016 at 16:13
  • \$\begingroup\$ @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. \$\endgroup\$ Commented Aug 28, 2016 at 16:16
  • \$\begingroup\$ What do I think about Grayscale.Fill? - as long as you are using the Get/SetPixel approach, the image operations will be slow. \$\endgroup\$ Commented Aug 28, 2016 at 16:19
  • \$\begingroup\$ @anonymous oh, haha ;-) you should have posted it right away :-] no one could have known that. \$\endgroup\$ Commented Aug 28, 2016 at 16:21
  • 1
    \$\begingroup\$ @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. \$\endgroup\$ Commented Aug 28, 2016 at 16:23

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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