我有一个采用System.Drawing.Bitmap的方法,将通道拆分为RGBA并转换为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);
}
对于尺寸为1500x1000的JPEG,大约需要4.5秒。
对于相同大小的PNG,大约需要3.5秒。
问题:如何加快速度?有没有办法对整个操作进行矢量化?
另外:为什么PNG比JPEG更快?图像已经转换为位图,所以速度不应该相同吗?
编辑:我找到了解决方案。没有足够的声誉发布作为答案,所以我希望这里没问题。
我发现C# - Faster Alternatives to SetPixel and GetPixel for Bitmaps for Windows Forms App中的示例相当令人费解。最终对我有用的是这里的信息:http://csharpexamples.com/fast-image-processing-c/
以下是我最终的结果:
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);
对于1500x1000的JPEG,这需要57毫秒。
对于相同大小的PNG大约70毫秒。
这里的速度差异是由于PNG具有alpha通道。