6

ここはちょっと不可解です。次のコードは、コードの変更によって回帰が発生していないことを確認するための小さなテスト アプリケーションの一部です。高速化するために、同じサイズの 2 つの画像を比較する最速の方法と思われる方法を使用memcmpました(当然のことですが)。

ただし、かなり驚くべき問題を示すいくつかのテスト画像があります。memcmpビットマップ データでは、それらが等しくないことがわかりますが、ピクセルごとの比較ではまったく違いが見つかりません。LockBitsonを使用するBitmapと、画像の実際の生のバイトが得られるという印象を受けました。24 bpp のビットマップの場合、ピクセルが同じであるが、基になるピクセルデータが異なるという状況を想像するのは少し難しいです。

いくつかの驚くべきこと:

  1. 違いは常に00、一方のイメージと他方のイメージにある 1バイトですFF
  2. PixelFormatforLockBitsFormat32bppRgbまたはに変更するFormat32bppArgbと、比較は成功します。
  3. BitmapData最初の呼び出しで返された値を 4 番目の引数として 2 番目の引数に渡すLockBitsと、比較は成功します。
  4. 上記のように、ピクセルごとの比較も成功します。

率直に言って、なぜこれが起こるのか想像できないので、私はここで少し困惑しています.

(縮小) 以下のコード。csc /unsafe24bpp PNG 画像を最初の引数としてコンパイルして渡すだけです。

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;

class Program
{
    public static void Main(string[] args)
    {
        Bitmap title = new Bitmap(args[0]);
        Console.WriteLine(CompareImageResult(title, new Bitmap(title)));
    }

    private static string CompareImageResult(Bitmap bmp, Bitmap expected)
    {
        string retval = "";

        unsafe
        {
            var rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
            var resultData = bmp.LockBits(rect, ImageLockMode.ReadOnly, bmp.PixelFormat);
            var expectedData = expected.LockBits(rect, ImageLockMode.ReadOnly, expected.PixelFormat);

            try
            {
                if (memcmp(resultData.Scan0, expectedData.Scan0, resultData.Stride * resultData.Height) != 0)
                    retval += "Bitmap data did not match\n";
            }
            finally
            {
                bmp.UnlockBits(resultData);
                expected.UnlockBits(expectedData);
            }
        }

        for (var x = 0; x < bmp.Width; x++)
            for (var y = 0; y < bmp.Height; y++)
                if (bmp.GetPixel(x, y) != expected.GetPixel(x, y))
                {
                    Console.WriteLine("Pixel diff at {0}, {1}: {2} - {3}", x, y, bmp.GetPixel(x, y), expected.GetPixel(x, y));
                    retval += "pixel fail";
                }

        return retval != "" ? retval : "success";
    }

    [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
    static extern int memcmp(IntPtr b1, IntPtr b2, long count);
}
4

2 に答える 2

7

LockBits バッファーを絵で示したこれを見てください。これは、ストライドの行と、パディングがストライドの最後に表示される場所を示しています (必要な場合)。

ストライドはおそらく 32 ビット (つまり、ワード) 境界に揃えられます (効率化のため)...そして、ストライドの終わりにある余分な未使用スペースは、次のストライドを揃えるためのものです。

そのため、比較中にランダムな動作が発生します...パディング領域の誤ったデータです。

Format32bppRgb と Format32bppArgb を使用している場合、それは自然にワード アラインされているため、最後に余分な未使用ビットがないと思います。これが機能する理由です。

于 2012-08-30T20:58:11.303 に答える
4

経験に基づいた推測:

24 ビット (3 バイト) は、32/64 ビット ハードウェアでは少し厄介です。

この形式では、4 バイトの倍数にフラッシュ アウトされるバッファーが存在し、1 つ以上のバイトが 'don't care' として残されます。それらはランダムなデータを含むことができ、ソフトウェアはそれらをゼロにする義務を感じません. これにより、memcmp が失敗します。

于 2012-08-30T20:53:10.380 に答える