我正在尝试确定在 Y 轴上翻转图像的最佳方式。对于每个像素,有 4 个字节,每组 4 个字节需要按顺序保持在一起,但要移位。这是迄今为止我想出的最好的。
对于 1280x960 图像,这只需要 0.1-.2 秒,但对于视频,这样的性能是严重的。有什么建议么?
初步实施
private void ReverseFrameInPlace(int width, int height, int bytesPerPixel, ref byte[] framePixels)
{
System.Diagnostics.Stopwatch s = System.Diagnostics.Stopwatch.StartNew();
int stride = width * bytesPerPixel;
int halfStride = stride / 2;
int byteJump = bytesPerPixel * 2;
int length = stride * height;
byte pix;
for (int i = 0, a = stride, b = stride - bytesPerPixel;
i < length; i++)
{
if (b % bytesPerPixel == 0)
{
b -= byteJump;
}
if (i > 0 && i % halfStride == 0)
{
i = a;
a += stride;
b = a - bytesPerPixel;
if (i >= length)
{
break;
}
}
pix = framePixels[i];
framePixels[i] = framePixels[b];
framePixels[b++] = pix;
}
s.Stop();
System.Console.WriteLine("ReverseFrameInPlace: {0}", s.Elapsed);
}
修订 #1
根据 SLaks 和 Alexei 使用索引和 Buffer.BlockCopy 进行了修订。还添加了 Parallel.For 因为索引允许它。
int[] pixelIndexF = null;
int[] pixelIndexB = null;
private void ReverseFrameInPlace(int width, int height, int bytesPerPixel, byte[] framePixels)
{
System.Diagnostics.Stopwatch s = System.Diagnostics.Stopwatch.StartNew();
if (pixelIndexF == null)// || pixelIndex.Length != (width * height))
{
int stride = width * bytesPerPixel;
int length = stride * height;
pixelIndexF = new int[width * height / 2];
pixelIndexB = new int[width * height / 2];
for (int i = 0, a = stride, b = stride, index = 0;
i < length; i++)
{
b -= bytesPerPixel;
if (i > 0 && i % (width / 2 )== 0)
{
//i = a;
i += width / 2;
a += stride;
b = a - bytesPerPixel;
if (index >= pixelIndexF.Length)
{
break;
}
}
pixelIndexF[index] = i * bytesPerPixel;
pixelIndexB[index++] = b;
}
}
Parallel.For(0, pixelIndexF.Length, new Action<int>(delegate(int i)
{
byte[] buffer = new byte[bytesPerPixel];
Buffer.BlockCopy(framePixels, pixelIndexF[i], buffer, 0, bytesPerPixel);
Buffer.BlockCopy(framePixels, pixelIndexB[i], framePixels, pixelIndexF[i], bytesPerPixel);
Buffer.BlockCopy(buffer, 0, framePixels, pixelIndexB[i], bytesPerPixel);
}));
s.Stop();
System.Console.WriteLine("ReverseFrameInPlace: {0}", s.Elapsed);
}
修订 #2
private void ReverseFrameInPlace(int width, int height, System.Drawing.Imaging.PixelFormat pixelFormat, byte[] framePixels)
{
System.Diagnostics.Stopwatch s = System.Diagnostics.Stopwatch.StartNew();
System.Drawing.Rectangle imageBounds = new System.Drawing.Rectangle(0,0,width, height);
//create destination bitmap, get handle
System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(width, height, pixelFormat);
System.Drawing.Imaging.BitmapData bitmapData = bitmap.LockBits(imageBounds, System.Drawing.Imaging.ImageLockMode.ReadWrite, bitmap.PixelFormat);
IntPtr ptr = bitmapData.Scan0;
//byte[] to bmap
System.Runtime.InteropServices.Marshal.Copy(framePixels, 0, ptr, framePixels.Length);
bitmap.UnlockBits(bitmapData);
//flip
bitmap.RotateFlip(System.Drawing.RotateFlipType.RotateNoneFlipX);
//get handle for bitmap to byte[]
bitmapData = bitmap.LockBits(imageBounds, System.Drawing.Imaging.ImageLockMode.ReadWrite, bitmap.PixelFormat);
ptr = bitmapData.Scan0;
System.Runtime.InteropServices.Marshal.Copy(ptr, framePixels, 0, framePixels.Length);
bitmap.UnlockBits(bitmapData);
s.Stop();
System.Console.WriteLine("ReverseFrameInPlace: {0}", s.Elapsed);
}