3

Y軸を横切って画像を反転する最適な方法を決定しようとしています。ピクセルごとに4バイトがあり、4バイトの各セットは順番に一緒にとどまる必要がありますが、シフトされます。これが私がこれまでに思いついた最高のものです。

これは1280x960の画像で.1-.2sしかかかりませんが、ビデオではそのようなパフォーマンスは損なわれます。助言がありますか?

初期実装

        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() メソッドを使用しましたが、驚くべきことに、他の方法よりも高速に思えます (少なくとも、私のマシンでは)。私が使用したソース画像は、1 ピクセルあたり 3 バイトの 720 x 576 ピクセルでした。この方法では、両方のリビジョンで約 0.06 秒かかるのに対し、.001 ~ 0.01 秒かかりました。

    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

Try calling Buffer.BlockCopy on each range of 4 bytes; that should be faster.

于 2012-04-26T00:41:38.977 に答える
2

任意の手法を使用して CPU で実行を並列化することも、ピクセル シェーダーを使用して GPU で実行することもできます。反転したビデオを表示するためだけにそれを行う場合は、DirectX を使用して、GPU で単純に変換を行うのが最適です。

于 2012-04-26T03:01:31.147 に答える
1

試して測定するランダムなものをさらに組み合わせます。

  • 行のコピー先となるインデックスの配列を事前に作成します (いくつかの代わりに [12,13,14,15, 8,9,10,11, 4,5,6,7, 0,1,2,3] のように)各行で実行される複雑なif。
  • インプレースではなく、新しい宛先にコピーしてみてください。
于 2012-04-26T00:57:24.463 に答える
1

.NET ライブラリが提供する多くの変換の 1 つを使用します。

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 に答える
0

もう 1 つのオプションは、XNA フレームワークを使用して画像を操作することです。XNA で Texture2D のサイズを変更して保存する方法の小さな例がありますか? . どれくらい速いかはわかりませんが、fpsの高いゲームで使用することを想定した機能を考えると、かなり高速であることはわかりました。

于 2012-04-26T04:34:27.227 に答える