#概要
ちょっとした操作をマウスで行いたかった。さらに線や円などのプリミティブな図形も描きたかったので、OpenCVSharpを使ってみることにした。そこで引っ掛かったことを記す。
###不具合例
using OpenCvSharp;
class Program
{
public static Mat img;
static Point mousepos = new Point();
static Scalar color = new Scalar(0, 255, 255);
static void Main(string[] args)
{
img = new Mat(new Size(640, 480), MatType.CV_8UC3);
using (Window win = new Window("OpenCV", img))
{
Cv2.SetMouseCallback("OpenCV", Win_OnMouseCallback);
while(true)
{
Cv2.ImShow("OpenCV", img);
int ret = Cv2.WaitKey(1);
if (ret == 27) { break; }
}
Cv2.DestroyAllWindows();
}
}//End of Main
private static void Win_OnMouseCallback(MouseEventTypes @event, int x, int y, MouseEventFlags flags, IntPtr userdata)
{
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で宣言されているけど、ガベージコレクタが回収してしまうようだ。ちゃんと確保して回収されないようにしてあげよう。差分だけ書かせてもらうと正しくは
public static Mat img;
static Point mousepos = new Point();
static Scalar color = new Scalar(0, 255, 255);
static MouseCallback mcb; //追加
static void Main(string[] args)
{
img = new Mat(new Size(640, 480), MatType.CV_8UC3);
mcb=new MouseCallback(Win_OnMouseCallback); //追加
using (Window win = new Window("OpenCV", img))
{
Cv2.SetMouseCallback("OpenCV", mcb);//変更
while(true)
//以下略
この「ガベージコレクタが回収する」エラーは、IDEでデバッグしているときは出てこず、使い始めてしばらくすると出てくるのと、再現するのに時間がかかるので、だいぶ厄介だった。わかってみれば「型を合わせてメモリを確保する」をちゃんとするだけなのだが。C++のDLLを使うプログラムでは気を付けよう。