並列処理に挑戦したく思い、1枚の画像を複数スレッドで加工するテストコードを書きました。 { // 画像をメモリに読み込む FileStream fs = File.OpenRead(filename); Image img = Image.FromStream(fs, false, false); Bitmap bitmap = new Bitmap(img); //読み込んだ画像を表示する PictureBox.Image = bitmap; PictureBox.Refresh(); // 縦横サイズを取得 int w = img.Width; int h = img.Height; Parallel.Invoke( () => TransformPixel(bitmap, 0, 1000, w), () => TransformPixel(bitmap, 1000, h, w) ); //作成した画像を表示する PictureBox.Image = bitmap; } private void TransformPixel(Bitmap bitmap, int start, int end, int w) { Color pixel; for (int y = start; y < end; y++) { for (int x = 0; x < w; x++) { // ピクセル加工 pixel = PixelTrans(bitmap.GetPixel(x, y)); bitmap.SetPixel(x, y, pixel); } } } ファイルから読み込んだ画像の頭1000ラインまでをスレッド1、残りをスレッド2で加工するような感じです。 実行してみたところ、 Parallel.Invoke(の行で、 System.InvalidOperationException HResult=0x80131509 Message=Object is currently in use elsewhere. Source=System.Drawing.Common スタック トレース: at System.Drawing.Bitmap.GetPixel(Int32 x, Int32 y) のようなエラーとなってしまいます。 同じbitmapに複数スレッドからの操作は出来ないんじゃないか?という気はするのですが、何をどうすればいいのかがわかりません。 ご教授をお願いいたします。
2022/07/31(Sun) 20:55:54 編集(投稿者) ■No35119に返信(Azuleanさんの記事) > ■No35118に返信(Transさんの記事) >>同じbitmapに複数スレッドからの操作は出来ないんじゃないか?という気はするのですが、何をどうすればいいのかがわかりません。 > > そうですね、1 つの Bitmap を複数のスレッドから触ることはできません。 > > LockBits を使ってメモリに展開したものを複数のスレッドで同時に加工することはできるかもしれません。 > https://dobon.net/vb/dotnet/graphics/drawnegativeimage.html#lockbits なるほど。一旦メモリ展開ですか。 案内の内容を参考に以下のように直してみたら動いたようです。 ありがとうございました。 { // 画像をメモリに読み込む FileStream fs = File.OpenRead(filename); Image img = Image.FromStream(fs, false, false); Bitmap bitmap = new Bitmap(img); //読み込んだ画像を表示する PictureBox.Image = bitmap; PictureBox.Refresh(); // 縦横サイズを取得 int w = img.Width; int h = img.Height; // Bitmapをロックする Rectangle rect = new Rectangle(0, 0, w, h); BitmapData bmpData = bitmap.LockBits(rect, ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppArgb); // ピクセルデータをバイト型配列で取得する IntPtr ptr = bmpData.Scan0; string[] array = new string[3]; byte[] pixels = new byte[bmpData.Stride * img.Height - 1]; System.Runtime.InteropServices.Marshal.Copy(ptr, pixels, 0, pixels.Length); Parallel.Invoke( () => TransformPixel(pixels, 0, 99999), () => TransformPixel(pixels, 100000, pixels.Length) ); // ピクセルデータを元に戻す System.Runtime.InteropServices.Marshal.Copy(pixels, 0, ptr, pixels.Length); // ロックを解除する bitmap.UnlockBits(bmpData); //作成した画像を表示する PictureBox.Image = bitmap; } private void TransformPixel(byte[] pixels, int start, int end) { for (int i = start; i < end; i++) { pixels[i] = (byte)~pixels[i]; } }