75

私の知る限り、ビットマップをコピーする方法は2つあります。

Bitmap.Clone()

Bitmap A = new Bitmap("somefile.png");
Bitmap B = (Bitmap)A.Clone();

新しいBitmap()

Bitmap A = new Bitmap("somefile.png");
Bitmap B = new Bitmap(A);

これらのアプローチはどのように異なりますか?特にメモリとスレッドの違いに興味があります。

4

2 に答える 2

112

以前の回答を読んで、ピクセルデータがビットマップの複製されたインスタンス間で共有されるのではないかと心配しました。そこで、との違いを見つけるためにいくつかのテストを実行しましBitmap.Clone()new Bitmap()

Bitmap.Clone()元のファイルをロックしたままにします。

  Bitmap original = new Bitmap("Test.jpg");
  Bitmap clone = (Bitmap) original.Clone();
  original.Dispose();
  File.Delete("Test.jpg"); // Will throw System.IO.IOException

new Bitmap(original)代わりに使用すると、の後original.Dispose()にファイルのロックが解除され、例外はスローされません。Graphicsクラスを使用して(で作成された.Clone())クローンを変更しても、元のクローンは変更されません。

  Bitmap original = new Bitmap("Test.jpg");
  Bitmap clone = (Bitmap) original.Clone();
  Graphics gfx = Graphics.FromImage(clone);
  gfx.Clear(Brushes.Magenta);
  Color c = original.GetPixel(0, 0); // Will not equal Magenta unless present in the original

同様に、このLockBitsメソッドを使用すると、元のクローンとクローンに対して異なるメモリブロックが生成されます。

  Bitmap original = new Bitmap("Test.jpg");
  Bitmap clone = (Bitmap) original.Clone();
  BitmapData odata = original.LockBits(new Rectangle(0, 0, original.Width, original.Height), ImageLockMode.ReadWrite, original.PixelFormat);
  BitmapData cdata = clone.LockBits(new Rectangle(0, 0, clone.Width, clone.Height), ImageLockMode.ReadWrite, clone.PixelFormat);
  Assert.AreNotEqual(odata.Scan0, cdata.Scan0);

結果はとの両方object ICloneable.Clone()で同じBitmap Bitmap.Clone(Rectangle, PixelFormat)です。

次に、次のコードを使用していくつかの簡単なベンチマークを試しました。

リストに50コピーを保存するには、6.2秒かかり、1.7 GBのメモリ使用量になりました(元のイメージは24bppおよび3456x2400ピクセル=25 MBです)。

  Bitmap original = new Bitmap("Test.jpg");
  long mem1 = Process.GetCurrentProcess().PrivateMemorySize64;
  Stopwatch timer = Stopwatch.StartNew();

  List<Bitmap> list = new List<Bitmap>();
  Random rnd = new Random();
  for(int i = 0; i < 50; i++)
  {
    list.Add(new Bitmap(original));
  }

  long mem2 = Process.GetCurrentProcess().PrivateMemorySize64;
  Debug.WriteLine("ElapsedMilliseconds: " + timer.ElapsedMilliseconds);
  Debug.WriteLine("PrivateMemorySize64: " + (mem2 - mem1));

代わりに使用Clone()すると、0.7秒間に1 000 000のコピーをリストに保存でき、0.9GBを使用できます。予想通り、Clone()と比較して非常に軽量ですnew Bitmap()

  for(int i = 0; i < 1000000; i++)
  {
    list.Add((Bitmap) original.Clone());
  }

このメソッドを使用するクローンは、Clone()コピーオンライトです。ここでは、クローンの1つのランダムなピクセルをランダムな色に変更します。この操作により、元のピクセルデータからすべてのピクセルデータのコピーがトリガーされるようです。これは、7.8秒と1.6GBに戻ったためです。

  Random rnd = new Random();
  for(int i = 0; i < 50; i++)
  {
    Bitmap clone = (Bitmap) original.Clone();
    clone.SetPixel(rnd.Next(clone.Width), rnd.Next(clone.Height), Color.FromArgb(rnd.Next(0x1000000)));
    list.Add(clone);
  }

画像からオブジェクトを作成するだけGraphicsでは、コピーはトリガーされません。

  for(int i = 0; i < 50; i++)
  {
    Bitmap clone = (Bitmap) original.Clone();
    Graphics.FromImage(clone).Dispose();
    list.Add(clone);
  }

Graphicsコピーをトリガーするには、オブジェクトを使用して何かを描画する必要があります。最後に、LockBits一方、を使用すると、ImageLockMode.ReadOnly指定されている場合でもデータがコピーされます。

  for(int i = 0; i < 50; i++)
  {
    Bitmap clone = (Bitmap) original.Clone();
    BitmapData data = clone.LockBits(new Rectangle(0, 0, clone.Width, clone.Height), ImageLockMode.ReadOnly, clone.PixelFormat);
    clone.UnlockBits(data);
    list.Add(clone);
  }
于 2012-12-18T15:15:29.037 に答える
77

これは、「深い」コピーと「浅い」コピーの一般的な違いであり、ほとんど廃止されたIClonableインターフェイスの問題でもあります。Clone()メソッドは新しいビットマップオブジェクトを作成しますが、ピクセルデータは元のビットマップオブジェクトと共有されます。Bitmap(Image)コンストラクターも新しいビットマップオブジェクトを作成しますが、ピクセルデータの独自のコピーを持っています。

SOでのClone()に関する多くの質問。プログラマーは、ビットマップの一般的な問題、つまり、ロード元のファイルのロックを回避することを望んでいます。そうではありません。おそらく実用的な使用法は、渡されたビットマップでDispose()を不適切に呼び出すライブラリメソッドの問題を回避することです。

オーバーロードは、ピクセル形式の変換またはトリミングオプションを利用して役立つ場合があります。

于 2012-10-03T13:34:54.150 に答える