バージョン
Microsoft Visual Studio Community 2022 (64 ビット) - Current
Version 17.2.2
.NETframeworkにて作成
内容
OpenCVにて二値化処理を1000回行い、その時間を計測するプログラムを作成しました。
しかし、プログラムを実行したところメモリリークの影響でエラーが発生してしまいます。
使用した画像のサイズ:258 KB
該当のソースコード
C#
1//インストール済みライブラリ 2//OpenCVSharp4 3//OpenCVShaerp4.Extensions 4//OpenCVSharp4.runtime.win 5 6using System; 7using System.Collections.Generic; 8using System.ComponentModel; 9using System.Data; 10using System.Drawing; 11using System.Linq; 12using System.Text; 13using System.Threading.Tasks; 14using System.Windows.Forms; 15using System.Diagnostics; //ストップウォッチクラス使用の為記載 16//以下、OpenCvsharpの使用とMatの変換用として追加 17using OpenCvSharp; 18using OpenCvSharp.Extensions; 19 20 21namespace OpenCV_メモリリークデバッグ用 22{ 23 public partial class Form1 : Form 24 { 25 Mat image; 26 Stopwatch sw = new Stopwatch(); // ストップウォッチオブジェクト生成 27 28 public Form1() 29 { 30 InitializeComponent(); 31 } 32 33 private void Form1_Load(object sender, EventArgs e) 34 { 35 pictureBox1.SizeMode = PictureBoxSizeMode.Zoom; 36 } 37 38 private void BtnLoad_Click(object sender, EventArgs e) 39 { 40 var ofd = new OpenFileDialog(); 41 //ファイルフィルタ 42 ofd.Filter = "Image File(*.bmp,*.jpg,*.png,*.tif)|*.bmp;*.jpg;*.png;*.tif|Bitmap(*.bmp)|*.bmp|Jpeg(*.jpg)|*.jpg|PNG(*.png)|*.png"; 43 //ダイアログの表示 44 if (ofd.ShowDialog() == DialogResult.OK) 45 { 46 string image_path = ofd.FileName; //選択した画像のパスを取得 47 image = Cv2.ImRead(image_path); //ImRead関数で画像を読み込む 48 pictureBox1.Image = BitmapConverter.ToBitmap(image); //MatをBitmapへ変換し pictureBox1で表示させる 49 } 50 } 51 52 private void BtnStart_Click(object sender, EventArgs e) 53 { 54 Bitmap pic = null; 55 56 sw.Start(); //時間計測スタート 57 58 for (int i=0; i<100; i++) 59 { 60 //グレー化 61 var grayImage = image.CvtColor(ColorConversionCodes.BGR2GRAY); 62 //二値化 63 var thresholdImege = grayImage.Threshold(128, 255, ThresholdTypes.Binary); 64 pic = BitmapConverter.ToBitmap(thresholdImege); //MatをBitmapへ変換 65 } 66 pictureBox1.Image = pic; 67 68 sw.Stop();//時間計測ストップ 69 Console.WriteLine(sw.Elapsed);//計測時間表示 70 } 71 } 72}
試したこと
①ピクチャーボックスのメモリ開放なども試しました。が変わらず。
以下の内容を55行に追加し試しました。
if (pictureBox1.Image != null)
{
pictureBox1.Image.Dispose();
}
最後に
諸事情により、1000回処理した時間を計測したかったのですが、そもそもPCの性能的に無理なのでしょうか。
ご教示お願いいたします。
追記 SurferOnWwwさんのサンプルプログラム反映後
//インストール済みライブラリ //OpenCVSharp4 //OpenCVShaerp4.Extensions //OpenCVSharp4.runtime.win using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using System.Diagnostics; //ストップウォッチクラス使用の為記載 //以下、OpenCvsharpの使用とMatの変換用として追加 using OpenCvSharp; using OpenCvSharp.Extensions; namespace OpenCV_メモリリークデバッグ用 { public partial class Form1 : Form { Mat image; Stopwatch sw; public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { pictureBox1.SizeMode = PictureBoxSizeMode.Zoom; } private void BtnLoad_Click(object sender, EventArgs e) { var ofd = new OpenFileDialog(); //ファイルフィルタ ofd.Filter = "Image File(*.bmp,*.jpg,*.png,*.tif)|*.bmp;*.jpg;*.png;*.tif|Bitmap(*.bmp)|*.bmp|Jpeg(*.jpg)|*.jpg|PNG(*.png)|*.png"; //ダイアログの表示 if (ofd.ShowDialog() == DialogResult.OK) { string image_path = ofd.FileName; //選択した画像のパスを取得 image = Cv2.ImRead(image_path); //ImRead関数で画像を読み込む pictureBox1.Image = BitmapConverter.ToBitmap(image); //MatをBitmapへ変換し pictureBox1で表示させる } } private void BtnStart_Click(object sender, EventArgs e) { sw = new Stopwatch(); // ストップウォッチオブジェクト生成 Bitmap pic = null; sw.Start(); //時間計測スタート for (int i=0; i<1000; i++) { //グレー化 using (var grayImage = image.CvtColor(ColorConversionCodes.BGR2GRAY)) //二値化 using (var thresholdImege = grayImage.Threshold(128, 255, ThresholdTypes.Binary)) { pic = BitmapConverter.ToBitmap(thresholdImege); pictureBox1.Image = pic; } } sw.Stop();//時間計測ストップ Console.WriteLine(sw.Elapsed);//計測時間表示 } } }
VisualStudioが出すエラーメッセージを(抜粋せず、内容は出来るだけそのまま)質問文に追記することは出来ますか?
かしこまりました!ありがとうございます!
追記ありがとうございます。
追記してもらった画像中に青色文字で「詳細の表示」の部分がクリックできると思います、
その情報も掲載可能でしょうか?
https://teratail.com/questions/49741
ループ内でのpicへの代入ですが、古いpicが解放されていないからなのでは?
odataikiさん
コメントありがとうございます!詳細こちらです。
OpenCvSharp.OpenCVException
HResult=0x80131500
Message=Failed to allocate 1637624 bytes
Source=<例外のソースを評価できません>
スタック トレース:
<例外のスタック トレースを評価できません>
この例外は、最初にこの呼び出し履歴
[外部コード]
OpenCV_メモリリークデバッグ用.Form1.BtnStart_Click(object, System.EventArgs) (Form1.cs 内)
[外部コード]
OpenCV_メモリリークデバッグ用.Program.Main() (Program.cs 内) でスローされました
リソースリークというやつですね。
https://en.wikipedia.org/wiki/Resource_leak
手元の環境でちょっと試したところ32bitだと特にすぐになくなります。64bitだとなかなかなくなりませんが、なくなるときはほとんどフリーズした感じになる場合が多いです…
画像は以下のオリジナルサイズを使ったところ32bitでは20回くらいでパンクしてました。
https://unsplash.com/ja/%E5%86%99%E7%9C%9F/tfjKJ_gN39E
解決策はYAmaGNZさんのおっしゃるように開放=BitmapもMatもDisposeしてあげるか、usingで宣言しないと駄目です。
原因はどちらもunmanagedなリソースを持っているので、能動的にDisposeしないとそれらがgcから破棄される前にリソースが足りなくなるからです。やり方はちょっと違いますが、OpenCVSharpのサンプルコードでもusingを使っていますよ。
https://github.com/shimat/opencvsharp_samples/blob/master/SamplesCore/Samples/BinarizerSample.cs
回答欄に追加質問を書いたようですが、そういうことをされると質問と回答がゴッチャになって訳がわからなくなるので避けてください。
質問者さんが書いた回答(追加質問)の文章部分は私の回答の下のコメント欄に書いて、コードの部分は、コメント欄ではインデントが効かないので、質問欄に追記する形で書いてください。
質問者さんが書いた回答は削除願います。
話が通じてないです。もう一回書きますのでよく読んでください。
質問者さんが書いた回答(追加質問)の文章部分は私の回答の下のコメント欄に書いて、コードの部分は、コメント欄ではインデントが効かないので、質問欄に追記する形で書いてください。
質問者さんが書いた回答は削除願います。
大変失礼しました。誤ったコメントについては削除依頼中になります。
追記にありように、
頂いた参考例をもとに反映しました。こちら試したところ、スタートボタンを押した一回目だけメモリリークをおこし、(1000回処理だとぎり耐えます)2回目以降はリークを起こさない現象が起きました。理由がわからず困っています。教えていただけたら幸いです。よろしくお願いいたします。
以上よろしくお願いいたします。
上の私のコメントで「質問者さんが書いた回答(追加質問)の文章部分は私の回答の下のコメント欄に書いて」とお願いしましたが、それがイヤなら上の質問者さんのコメントの文章の、
頂いた参考例をもとに反映しました。こちら試したところ、スタートボタンを押した一回目だけメモリリークをおこし、(1000回処理だとぎり耐えます)2回目以降はリークを起こさない現象が起きました。理由がわからず困っています。
・・・を上の質問欄の「追記 SurferOnWwwさんのサンプルプログラム反映後」の行の直下に追記してください。
ここ質問のコメント欄は、もともと質問内容への情報の追加等を依頼する場所で、情報の追加を行う場所ではありません。初期画面では閉じているので読まない人も多々いますし。
> 頂いた参考例をもとに反映しました。こちら試したところ、スタートボタンを押した一回目だけメモリリークをおこし、(1000回処理だとぎり耐えます)2回目以降はリークを起こさない現象が起きました。理由がわからず困っています。
それに対するレスは私の回答欄に追記しておきます。
質問者さん、その後無言ですが、追加質問に対しても回答したのでそれに対するフィードバックを返してください。役に立った/立たなかったぐらいはすぐに返せるのでは? 役に立たなかったならどこがダメかを書くとより期待に近い回答が出てくるかも。解決したなら解決に役立った回答にベストアンサーをつけてクローズしてください。とにかく無言は NG です。
お返事遅れて大変申し訳ございません。
usingステートメントを使用してアンマネージドリソースを開放する必要があるという件に関しては非常にためになりました。しかし、pictureBoxに二値化後の画像を表示したいのですが、usingステートメントで
開放してしまうためかエラーが発生するため解決はしていない段階になります。
回答3件
あなたの回答
tips
プレビュー