6

カメラからのライブ画像を表​​示する C# アプリケーションに取り組んでいます。次のコード スニペットで直面している問題は、この関数をスレッドで連続して実行すると、Marshal.Copy で AccessViolationException が発生することです。しかし、これは一度実行すると正常に実行されます (単一の静的イメージを取得します)。メモリ破損の問題に関係していると思います。この問題に対処する方法についてのアイデア/提案はありますか?

    private Image ByteArrayToImage(byte[] myByteArray) 
    {
        if (myByteArray != null)
        {
            MemoryStream ms = new MemoryStream(myByteArray);
            int Height = 504;
            int Width = 664;
            Bitmap bmp = new Bitmap(Width, Height, PixelFormat.Format24bppRgb);
            BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, bmp.PixelFormat);
            Marshal.Copy(myByteArray, 0, bmpData.Scan0, myByteArray.Length);
            bmp.UnlockBits(bmpData);

            return bmp;
        }
        return null;
    }
4

8 に答える 8

10

あなたは常にバイト数myByteArray.Lengthをビットマップバッファにコピーしようとしているように見えます。

ビットマップバッファが実際にそれと同じくらい大きいことを確認していないため、おそらくビットマップバッファの最後を書き留めています。

myByteArray.LengthがbmpData.Stride x bmp.Heightよりも大きいかどうかを確認してみてください

この場合、幅、高さ、およびピクセル形式のハードコードされた値で行った仮定を再検討する必要があります。

于 2009-03-24T10:59:02.043 に答える
6

イメージ全体を一度にコピーしないでください。ビットマップ オブジェクトのメモリ割り当ては、予期したものではない可能性があります。たとえば、最初のスキャン ラインが最後にメモリに格納される場合があります。これは、2 番目のスキャン ラインのデータが、ビットマップ オブジェクトに割り当てられたメモリ領域の外にあることを意味します。また、スキャンラインを偶数アドレスに配置するために、スキャンライン間にパディングがある場合もあります。

bmpData.Stride を使用して一度に 1 行ずつコピーし、次のスキャン行を見つけます。

int offset = 0;
long ptr = bmpData.Scan0.ToInt64();
for (int i = 0; i < Height; i++) {
   Marshal.Copy(myByteArray, offset, new IntPtr(ptr), Width * 3);
   offset += Width * 3;
   ptr += bmpData.Stride;
}
于 2009-03-24T11:02:13.720 に答える
0

私は少し検索していましたが、配列が適切なサイズではない可能性をスキップすると、BitmapData.Stride プロパティの備考で終わります:

ストライドは、1 行のピクセル (走査線) の幅で、4 バイト境界に切り上げられます。ストライドが正の場合、ビットマップはトップダウンです。ストライドが負の場合、ビットマップはボトムアップです。

したがって、おそらく次のようにする必要があります。

BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, bmp.PixelFormat);
Marshal.Copy(myByteArray, 0, bmpData.Scan0 + 
( bmpData.Stride >= 0 ? 0 : bmpData.Stride*(bmp.Height-1) ),
myByteArray.Length);

しかし、私は疑問に思いました: 私たちはビットマップを作成しました: new Bitmap(Width, Height, PixelFormat.Format24bppRgb);...では、どうしてマイナスになる可能性があるのでしょうか? ILSpy
の時間:

public Bitmap(int width, int height, PixelFormat format)
{
    IntPtr zero = IntPtr.Zero;
    int num = SafeNativeMethods.Gdip.GdipCreateBitmapFromScan0(width, height, 0, (int)format, NativeMethods.NullHandleRef, out zero);
    if (num != 0)
    {
            throw SafeNativeMethods.Gdip.StatusException(num);
    }
    base.SetNativeImage(zero);
}

// System.Drawing.SafeNativeMethods.Gdip
[DllImport("gdiplus.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
internal static extern int GdipCreateBitmapFromScan0(int width, int height, int stride, int format, HandleRef scan0, out IntPtr bitmap);

そして、それは私をここここに向けました:

Bitmap(
  [in]  INT width,
  [in]  INT height,
  [in]  INT stride,
  [in]  PixelFormat format,
  [in]  BYTE *scan0
);

stride [入力]
型: INT
1 つのスキャン ラインの先頭と次のスキャン ラインの間のバイト オフセットを指定する整数。これは通常 (必ずしもそうとは限りません)、ピクセル形式のバイト数 (たとえば、ピクセルあたり 16 ビットの場合は 2) にビットマップの幅を掛けたものです。このパラメーターに渡される値は、4 の倍数である必要があります。

0を渡すとはどういう意味ですか? わからない、見つけられなかった。誰でもできますか?負のストライドでビットマップを作成できますか? (.NET によるnew Bitmap(Width, Height, PixelFormat.Format24bppRgb))。いずれにせよ、少なくともBitmapData.Strideを確認する必要があります。

于 2014-08-13T12:30:42.253 に答える
0

多分

BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, bmp.PixelFormat);

書き込み専用の無効な引数があります - ReadWrite または ImageLockMode の ReadOnly を試してください。たぶんそれが役立ちます。

于 2011-02-02T07:25:30.237 に答える
0

私への回答: するのを忘れた ->

        // Unlock the bits right after Marshal.Copy
        bmp.UnlockBits(bmpData);

誰かがこれを理解しましたか?これは答えのない4ページ目くらいです。msdn の正確なコードを使用: http://msdn.microsoft.com/en-us/library/system.drawing.imaging.bitmapdata.aspx これは次のとおりです。

                        Bitmap bmp = new Bitmap("c:\\picture.jpg");

                        // Lock the bitmap's bits.  
                        Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
                        System.Drawing.Imaging.BitmapData bmpData =
                            bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite,
                            bmp.PixelFormat);

                        // Get the address of the first line.
                        IntPtr ptr = bmpData.Scan0;

                        // Declare an array to hold the bytes of the bitmap.
                        int bytes = bmpData.Stride * bmp.Height;
                        byte[] rgbValues = new byte[bytes];

                        // Copy the RGB values into the array.
                        //This causes read or write protected memory
                        System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);

これは最適化モードでは機能せず、IDE ではなく exe として実行されます。これを入れようとしたアイデアはすべて新しいプロジェクトであり、ボタンを押したときにプロセスにアタッチし、このボタンを複数回押すとエラーが発生しますが、私のコードでは一度しか呼び出していません。エラー。

于 2010-01-25T22:56:58.523 に答える