3

我正在尝试确定在 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);
    }
4

6 回答 6

4

我遇到了几乎相同的问题,但在我的情况下,我需要翻转图像以将其保存到 .avi 容器中。我改用了 Array.Copy() 方法,令人惊讶的是,它似乎比其他方法更快(至少在我的机器上)。我使用的源图像是 720 x 576 像素,每像素 3 个字节。此方法花费了 0.001 - 0.01 秒,而您的两个修订版都花费了大约 0.06 秒。

    private byte[] ReverseFrameInPlace2(int stride, byte[] framePixels)
    {
        System.Diagnostics.Stopwatch s = System.Diagnostics.Stopwatch.StartNew();
        var reversedFramePixels = new byte[framePixels.Length];
        var lines = framePixels.Length / stride;

        for (var line = 0; line < lines; line++)
        {
            Array.Copy(framePixels, framePixels.Length - ((line + 1) * stride), reversedFramePixels, line * stride, stride);
        }

        s.Stop();
        System.Console.WriteLine("ReverseFrameInPlace2: {0}", s.Elapsed);
        return reversedFramePixels;
    }
于 2012-10-16T16:32:39.583 回答
3

尝试调用Buffer.BlockCopy每个 4 个字节的范围;那应该更快。

于 2012-04-26T00:41:38.977 回答
2

您可以使用任何技术在 CPU 上并行执行,或者使用像素着色器并在 GPU 上执行。如果您这样做只是为了显示翻转的视频 - 您最好使用 DirectX 并简单地在 GPU 上进行转换。

于 2012-04-26T03:01:31.147 回答
1

使用 .NET 库提供的众多转换之一:

http://msdn.microsoft.com/en-us/library/aa970271.aspx

编辑:这是另一个例子:

http://www.switchonthecode.com/tutorials/csharp-tutorial-image-editing-rotate

于 2012-04-26T02:12:27.753 回答
1

尝试和测量更多随机的东西:

  • 预构建索引数组以复制到一行(如 [12,13,14,15, 8,9,10,11, 4,5,6,7, 0,1,2,3] 而不是一些在每一行上执行复杂的 ifs。
  • 尝试复制到新目的地而不是就地。
于 2012-04-26T00:57:24.463 回答
0

另一种选择可能是使用 XNA 框架对图像进行操作。有一个如何在 XNA 中调整和保存 Texture2D 的小例子?. 我不知道它有多快,但考虑到这些功能应该在高 fps 的游戏中使用,我可以看到它应该有多快。

于 2012-04-26T04:34:27.227 回答