I have a method that takes a System.Drawing.Bitmap, splits up the channels into RGBA and converts to double.
private BitmapToImage32(Bitmap bmap)
{
var r = new double[bmap.Width, bmap.Height];
var g = new double[bmap.Width, bmap.Height];
var b = new double[bmap.Width, bmap.Height];
var a = new double[bmap.Width, bmap.Height];
var watch = System.Diagnostics.Stopwatch.StartNew();
for (int x = 0; x < bmap.Width; x++)
{
for (int y = 0; y < bmap.Height; y++)
{
r[x, y] = (double)bmap.GetPixel(x, y).R / 255;
g[x, y] = (double)bmap.GetPixel(x, y).G / 255;
b[x, y] = (double)bmap.GetPixel(x, y).B / 255;
a[x, y] = (double)bmap.GetPixel(x, y).A / 255;
}
}
watch.Stop();
Console.WriteLine("Milliseconds: {0}: ", watch.ElapsedMilliseconds);
}
For a JPEG with dimensions 1500x1000 this takes about 4.5 seconds.
For a PNG of the same size it takes about 3.5 seconds.
Question: How can I speed this up? Is there a way to vectorize the whole operation?
Also: Why is a PNG faster than a JPEG? The image has been converted to Bitmap, so shouldn't the speed be the same?
EDIT: I found a solution. Don't have enough reputation to post as an answer so I hope here is OK.
I found the examples in C# - Faster Alternatives to SetPixel and GetPixel for Bitmaps for Windows Forms App rather convoluted. What finally worked for me is the information here: http://csharpexamples.com/fast-image-processing-c/
Here is what I ended up with:
var r = new double[bmap.Width, bmap.Height];
var g = new double[bmap.Width, bmap.Height];
var b = new double[bmap.Width, bmap.Height];
var a = new double[bmap.Width, bmap.Height];
var watch = System.Diagnostics.Stopwatch.StartNew();
unsafe
{
BitmapData bitmapData = bmap.LockBits(
new Rectangle(0, 0, bmap.Width, bmap.Height),
ImageLockMode.ReadWrite, bmap.PixelFormat);
int bytesPerPixel = Bitmap.GetPixelFormatSize(bmap.PixelFormat) / 8;
int heightInPixels = bitmapData.Height;
int widthInBytes = bitmapData.Width * bytesPerPixel;
byte* PtrFirstPixel = (byte*)bitmapData.Scan0;
if (bytesPerPixel == 3)
{
for (int y = 0; y < heightInPixels; y++)
{
byte* currentLine = PtrFirstPixel + (y * bitmapData.Stride);
for (int x = 1; x <= widthInBytes; x = x + bytesPerPixel)
{
r[(x - 1) / bytesPerPixel, y] = (double)currentLine[x + 1] / 255;
g[(x - 1) / bytesPerPixel, y] = (double)currentLine[x] / 255;
b[(x - 1) / bytesPerPixel, y] = (double)currentLine[x - 1] / 255;
a[(x - 1) / bytesPerPixel, y] = 0.0d;
}
}
}
else
{
for (int y = 0; y < heightInPixels; y++)
{
byte* currentLine = PtrFirstPixel + (y * bitmapData.Stride);
for (int x = 1; x <= widthInBytes; x = x + bytesPerPixel)
{
r[(x - 1) / bytesPerPixel, y] = (double)currentLine[x + 1] / 255;
g[(x - 1) / bytesPerPixel, y] = (double)currentLine[x] / 255;
b[(x - 1) / bytesPerPixel, y] = (double)currentLine[x - 1] / 255;
a[(x - 1) / bytesPerPixel, y] = (double)currentLine[x + 2] / 255;
}
}
}
bmap.UnlockBits(bitmapData);
}
watch.Stop();
Console.WriteLine("Milliseconds: {0}: ", watch.ElapsedMilliseconds);
For a JPEG at 1500x1000 this takes 57 milliseconds.
And for a PNG of the same size about 70 milliseconds.
The difference in speed here is due to the PNG having an alpha channel.