概要
ちょっとした操作をマウスで行いたかった。さらに線や円などのプリミティブな図形も描きたかったので、OpenCVSharpを使ってみることにした。そこで引っ掛かったことを記す。
不具合例
Program.cs
usingOpenCvSharp;classProgram{publicstaticMatimg;staticPointmousepos=newPoint();staticScalarcolor=newScalar(0,255,255);staticvoidMain(string[]args){img=newMat(newSize(640,480),MatType.CV_8UC3);using(Windowwin=newWindow("OpenCV",img)){Cv2.SetMouseCallback("OpenCV",Win_OnMouseCallback);while(true){Cv2.ImShow("OpenCV",img);intret=Cv2.WaitKey(1);if(ret==27){break;}}Cv2.DestroyAllWindows();}}//End of MainprivatestaticvoidWin_OnMouseCallback(MouseEventTypes@event,intx,inty,MouseEventFlagsflags,IntPtruserdata){Console.WriteLine($"event: {@event}, (x, y)= ({x}, {y}), flags: {flags}");if(flags==MouseEventFlags.LButton){mousepos.X=x;mousepos.Y=y;img.Circle(mousepos,2,color);}}}
何の変哲もない。
しかしリリースモードでビルドして実行して、暫くすると
これは何のことだろう?デバッグモードでは出てこないけど・・・
ググると、C#のリリースモードはいろいろ最適化されてるから別コードと考えた方がよいとか、Releaseでもデバッグ用にログを取るとかいろいろ出てきた。でもこんな簡単なものなのに・・
しかもアンマネージってOpenCVのところ??
解決策
ようやく探し出せた
https://www.ipentec.com/document/csharp-error-delegate-must-be-kept-alive-by-the-managed-application
よく見るとWin_MouseCallBackはdelegate型になってない。staticで宣言されているけど、ガベージコレクタが回収してしまうようだ。ちゃんと確保して回収されないようにしてあげよう。差分だけ書かせてもらうと正しくは
Program.cs
publicstaticMatimg;staticPointmousepos=newPoint();staticScalarcolor=newScalar(0,255,255);staticMouseCallbackmcb;//追加staticvoidMain(string[]args){img=newMat(newSize(640,480),MatType.CV_8UC3);mcb=newMouseCallback(Win_OnMouseCallback);//追加using(Windowwin=newWindow("OpenCV",img)){Cv2.SetMouseCallback("OpenCV",mcb);//変更 while(true)//以下略
この「ガベージコレクタが回収する」エラーは、IDEでデバッグしているときは出てこず、使い始めてしばらくすると出てくるのと、再現するのに時間がかかるので、だいぶ厄介だった。わかってみれば「型を合わせてメモリを確保する」をちゃんとするだけなのだが。C++のDLLを使うプログラムでは気を付けよう。