DOSEIの日記

技術メモ+日常ログ

Mat_ の (本当の) 罠

id:DOSEI:20130306:p1 の訂正記事。のつもりだったけど、まだ理解が不十分だったので以下の文章は治す予定。うのむな

まとめ

cv::Mat_ を関数の引数の型にしてはいけない。

理由

cv::Mat_ は、 cv::Mat の型を固定した薄いラッパーです。 Mat は任意の型を持てるのだが、メンバ関数 at() などには(通常は正しい)型を指定しなければならない。しかし、あらかじめ型が既知なら、 Mat_ を使うと、その指定が省略できたり、コンストラクタが簡単になったりする。

関数の引数で Mat を受け取ったら、まずは型などを調べて、 Mat_ でラップすればいい。

void f(Mat const& _M)
{
  assert(M.type() == CV_8UC1);
  Mat_<unsigned char> M = _M;
  // M を安全に使う
}

以下のコードだと、型に合わせて変換される。変換の要不要で、もとのインスタンスのクローンになるかが異なるため、わかりづらい。
Mat_::type() は、ラップしてる型を常に返すので、 Mat_ でラップするともともと何の型だったかを調べることが不可能になる。

void f(Mat_<unsigned char> const& M) // !! 変換が不要ならインスタンスそのものがそのまま、必要なら新しいインスタンスが生成・変換されて渡される。
{
  assert(M.type() == CV_8UC1); // !! 常に真
  // M は、呼び出し元で見ているデータを書き換えているかがわかりづらい。
}