LoginSignup
12
9

More than 5 years have passed since last update.

OpenCVの汎用入出力InputArray, OutputArrayの使い方(応用編)

Last updated at Posted at 2015-12-03

はじめに

これは,OpenCV Advent Calendar 2015の記事です.関連記事は,リンク先に目次としてまとめられています.

本記事は,前回のOpenCVの汎用入出力InputArray, OutputArrayの使い方(入門編) の続きです.

従来のMatで受けるつけることが出来たMat以外の形式

これまでのMatで受けることが出来たのは,下記のようなMat_<T>だけでした.
Mat_<T>:Mat_<uchar>, Mat_<short>, Mat_<ushort>, Mat_<float>, Mat_<double>, Mat_<Vec3b>...

InputArrayで受けることが出来る形式

Matで受ける形式

前回説明したように,基本的にはOpenCVで使うような型は何でも受け付けることが出来ます.例えば,下記のようなものが受け付けられます.
Mat, Mat_<T>, Matx, vector<uchar>, vector<Point>, vector<Vec3b>, vector<vector<Mat>>, vector<vector<Point2f>>...

このような入力が入ったあと,すべての型はMatに変換されます.
(後述しますが,vector<vector<T>>の型の場合は,vectorに変換されます.)

void funcmat(InputArray in)
{
    Mat mat = in.getMat();
}

MatはLチャネルを持つMxNの値の配列を表現することが出来るため,1次元配列やMatの亜種はすべてMatの形に変換できます.例えば下記のように変換されます.

  • Mat_<T>やMatxは,そのままMatに変換されます.
  • vector<uchar>やvector<float>はvectorのサイズがNなら[Nx1]のサイズのMatで8UC1や32FC1のtypeに変換されます.
  • vector<T>がVec3b,Vec3fなどのベクトルの値をとるなら,[Nx1]の3チャネルとなる8UC3や32FC3などのtypeに変換されます.
  • vector<T>がPoint,Point3dを取る場合も同様に32SC2や64FC3などのtypeに変換されます.

vector<Mat>で受ける形式

vector<vector<T>>の形で受けるには,getMatVectorメソッドを呼ぶ必要があります.サンプルは以下のようになります.

void funcvec(InputArray in)
{
    vector<Mat> src;
    in.getMatVector(src);
}

戻り値ではなく,引数に入れることに注意しましょう.ポインタではなくて内部でvectorをresizeしているため,この形になっています.

まず,入れ子になっているvector<vector<T>>の中の任意のvector<T>は全てMatに変換可能です(上記のMatで受ける場合と同様です.).つまり,そのvectorをさらにvectorで囲っているわけですから,置換すればvector<Mat>の形式で全て受けることが出来ることは自明です.入力される配列がどのような種類なのかによって,明示的にMatとvectorは受けるときに呼ぶメソッドを変える必要があります.今回は説明しませんが,GPUなどを使うUMatやGpuMatを使う場合も明示的に指示する必要があります.次は,これらを切り替えるために必要な情報を取得するためのkindメソッドを説明します.なお,3次元vectorや4次元vectorなどより高次元の配列は,今のところ受け付けることが出来ません.

さらにOutputArray(もしくはOutputArrayOfArrays)がvectorを取ると想定され,かつ,関数内でOutputArrayOfArraysのメモリを確保しなければならない場合は下記で書くことができます.
(おそらくほとんど情報がありません.)

具体的には,OutputArrayOfArraysについてまずvectorのサイズを確保し,各要素には,getMatRef(i)メソッドで各vectorの要素にMatを参照させます.

void hoge(cv::InputArray src, cv::OutputArrayOfArrays dest)
{
    Mat s = src.getMat();

    vector<Mat> dst;
    if (dest.empty())
    {
        dest.create(3, 1, src.type());

        dest.getMatVector(dst);
        dst[0].create(src.size(), src.depth());
        dst[1].create(src.size(), src.depth());
        dst[2].create(src.size(), src.depth());

        dest.getMatRef(0) = dst[0];
        dest.getMatRef(1) = dst[1];
        dest.getMatRef(2) = dst[2];
    }
    else
    {
        dest.getMatVector(dst);
    }
}

kindメソッドによる型の切り替え

InputArrayは入力された型情報を保持しており,それをkind()メソッドで取り出すことが出来ます.InputArrayのヘッダで定義されている取り出し可能な型は以下のようになっています.

enum {
        KIND_SHIFT = 16,
        FIXED_TYPE = 0x8000 << KIND_SHIFT,
        FIXED_SIZE = 0x4000 << KIND_SHIFT,
        KIND_MASK = 31 << KIND_SHIFT,

        NONE              = 0 << KIND_SHIFT,
        MAT               = 1 << KIND_SHIFT,
        MATX              = 2 << KIND_SHIFT,
        STD_VECTOR        = 3 << KIND_SHIFT,
        STD_VECTOR_VECTOR = 4 << KIND_SHIFT,
        STD_VECTOR_MAT    = 5 << KIND_SHIFT,
        EXPR              = 6 << KIND_SHIFT,
        OPENGL_BUFFER     = 7 << KIND_SHIFT,
        CUDA_HOST_MEM     = 8 << KIND_SHIFT,
        CUDA_GPU_MAT      = 9 << KIND_SHIFT,
        UMAT              =10 << KIND_SHIFT,
        STD_VECTOR_UMAT   =11 << KIND_SHIFT,
        STD_BOOL_VECTOR   =12 << KIND_SHIFT
    };

実際に使うときは,以下のように使えば,入力の型が分かります.型に応じて,上記で説明したMat,vector<Mat>や,GPUデバイスなどを用いた高速化関数に使われる型であるUMatやGpuMatなどを切り替える関数を作りましょう.

void func(InputArray src)
{
    int kind = src.kind() >> cv::_InputArray::KIND_SHIFT;
    cout << "kind: ";
    switch (kind)
    {

    case (int)_InputArray::NONE >> _InputArray::KIND_SHIFT:
        default:
        cout << "NONE\n";
        break;
    case (int)_InputArray::MAT >> _InputArray::KIND_SHIFT:
        cout << "Mat\n";
        break;
    case (int)_InputArray::MATX >> _InputArray::KIND_SHIFT:
        cout << "Matx\n";
        break;
    case _InputArray::STD_VECTOR >> _InputArray::KIND_SHIFT:
        cout << "vector\n";
        break;
    case _InputArray::STD_VECTOR_VECTOR >> _InputArray::KIND_SHIFT:
        cout << "vector<vector>\n";
        break;
    case _InputArray::STD_VECTOR_MAT >> _InputArray::KIND_SHIFT:
        cout << "vector<Mat>\n";
        break;
    case _InputArray::EXPR >> _InputArray::KIND_SHIFT:
        cout << "Matexpr\n";
        break;
    case _InputArray::OPENGL_BUFFER >> _InputArray::KIND_SHIFT:
        cout << "OPENGL_BUFFER\n";
        break;
    case _InputArray::CUDA_HOST_MEM >> _InputArray::KIND_SHIFT:
        cout << "cuda mem\n";
        break;
    case _InputArray::CUDA_GPU_MAT >> _InputArray::KIND_SHIFT:
        cout << "GpuMat\n";
        break;
    case _InputArray::UMAT >> _InputArray::KIND_SHIFT:
        cout << "UMat\n";
        break;
    case _InputArray::STD_VECTOR_UMAT >> _InputArray::KIND_SHIFT:
        cout << "vector<UMat>\n";
        break;
    case _InputArray::STD_BOOL_VECTOR >> _InputArray::KIND_SHIFT:
        cout << "vector<bool>\n";
        break;
    }
}
12
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
9