0

私は最近、画像処理のトピックを調べ始めました。私が最初にすべきことの 1 つは、画像がどのように機能するかを学ぶことだと考えました。私の最新のプロジェクトには、画像の新しいコピーの作成が含まれます。できるだけ早くやりたかったので、できるだけ多くのアプローチを考え出そうとしました。各アプローチのメソッドを作成し、そのメソッドを 100 回呼び出すのにかかる時間を計りました。これらは私の結果です:

Marshal:    0.45584
Instance:   1.69299
Clone:      0.30687
GetSet:   341.74056
Pointer:    2.54130
Graphics:   1.07960

各メソッドには、ソース イメージと宛先イメージが渡されます。最終的な目標は、すべてのピクセルを最初の画像から 2 番目の画像にコピーすることです。

private void MarshalCopyMethod(Bitmap sourceImage, Bitmap destinationImage)
{
    // Lock the bitmap's bits.
    Rectangle rect = new Rectangle(0, 0, sourceImage.Width, sourceImage.Height);
    BitmapData readData = sourceImage.LockBits(rect, ImageLockMode.ReadOnly, sourceImage.PixelFormat);
    BitmapData writeData = destinationImage.LockBits(rect, ImageLockMode.WriteOnly, sourceImage.PixelFormat);

    // Get the address of the first line.
    IntPtr sourcePtr = readData.Scan0;
    IntPtr destinationPtr = writeData.Scan0;
    byte[] rgbValues = new byte[readData.Stride * readData.Height];

    Marshal.Copy(sourcePtr, rgbValues, 0, rgbValues.Length);
    Marshal.Copy(rgbValues, 0, destinationPtr, rgbValues.Length);

    sourceImage.UnlockBits(readData);
    destinationImage.UnlockBits(writeData);
}
private void PointerCopyMethod(Bitmap sourceImage, Bitmap destinationImage)
{
    // Lock the bitmap's bits.
    Rectangle rect = new Rectangle(0, 0, sourceImage.Width, sourceImage.Height);
    BitmapData readData = sourceImage.LockBits(rect, ImageLockMode.ReadOnly, sourceImage.PixelFormat);
    BitmapData writeData = destinationImage.LockBits(rect, ImageLockMode.WriteOnly, sourceImage.PixelFormat);
    unsafe
    {
        // Get the address of the first line.
        byte* readPointer = (byte*)readData.Scan0.ToPointer();
        byte* writePointer = (byte*)writeData.Scan0.ToPointer();

        int lengthOfData = readData.Stride * readData.Height;
        for (int i = 0; i < lengthOfData; i++)
        {
            *writePointer++ = *readPointer++;
        }
    }
    sourceImage.UnlockBits(readData);
    destinationImage.UnlockBits(writeData);
}
private void InstanceCopyMethod(Bitmap sourceImage, Bitmap destinationImage)
{
    destinationImage = new Bitmap(sourceImage);
}
private void CloneRegionMethod(Bitmap sourceImage, Bitmap destinationImage)
{
    destinationImage = sourceImage.Clone(new Rectangle(860, 440, 200, 200), sourceImage.PixelFormat);
}
private void CloneCopyMethod(Bitmap sourceImage, Bitmap destinationImage)
{
    destinationImage = (Bitmap)sourceImage.Clone();
}
private void GetSetPixelCopyMethod(Bitmap sourceImage, Bitmap destinationImage)
{
    for (int y = 0; y < sourceImage.Height; y++)
    {
        for (int x = 0; x < sourceImage.Width; x++)
        {
            destinationImage.SetPixel(x, y, destinationImage.GetPixel(x, y));
        }
    }
}
private void GraphicsCopyMethod(Bitmap sourceImage, Bitmap destinationImage)
{
    using(Graphics g = Graphics.FromImage(destinationImage))
    {
        g.DrawImage(sourceImage, new Point(0, 0));
    }
}

次の 2 行もすべてのメソッドの最後に追加されます。

destinationImage.SetPixel(955, 535, Color.Red);
destinationImage.SetPixel(965, 545, Color.Green);

Image.Clone() について読んだことがあるため、これを行いました。クローンの一部を変更するまで、実際にはコピーが作成されないという趣旨でした。これらのピクセルを設定しないと、Clone() アプローチは 1000 倍速く終了するようです。そこで何が起こっているのか正確にはわかりません。

結果は、私がオンラインで読んでいたものから私が期待するものに近いようです. ただし、ポインター アプローチは、Get/Set Pixel メソッド以外で実装した中で最も低速です。私の個人的な研究から、ポインターは最速ではないにしても、最速の 1 つであると予想していました。

私のプロジェクトに関連する質問がいくつかあります。この状況でポインターを最適に使用していますか? クローン イメージのピクセルを変更すると、クローン アプローチが影響を受けるのはなぜですか? より短い時間で画像をコピーできる別のアプローチはありますか? その他のアドバイス/ヒントはありますか?ありがとう。

4

1 に答える 1

1

数字は合理的に見えます。概要:

  • GetPixel/SetPixel遅い
  • 特別に書かれたコードはより高速です
  • の高速バージョンを作成することmemcpyは非常に困難であり、どの言語でも一般にライブラリ バージョンを打ち負かすことはほとんど不可能です (特定のサイズ/ターゲット CPU などの特殊なケースでは、より優れたパフォーマンスが期待できます)。

ポインターでもっと遊びたい場合 - 試して測定してください: - 通常の C# (インデックス) で同じコードを試してください -intコピーのために切り替えてみてください - 各行が DWORD で整列されていることに注意してください - テールの特別なケースは必要ありません。- マーシャリング サンプルからのブロック コピーの再実装

于 2013-06-01T23:28:49.293 に答える