2011年9月22日木曜日

OpenCV2.xのcv::Matから画素を取り出す

OpenCV2.xから実装されたcv::Matクラス

まだ、あまり2.xの参考書がないので画素アクセスの方法がわからなくて当初困りました。

ということでcv::Matの画素アクセスの方法について紹介します。



まず、簡単な方法から
1.「Mat::at」を使う

// グレースケール画像(8bit)画像
for(int y = 0 ; y < image.rows; y++){
    for(int x = 0 ; x < image.cols; x++){
        image.at( y, x ) = 255;
    }
}

// RGB画像(24bit)画像
for(int y = 0 ; y < image.rows; y++){
    for(int x = 0 ; x < image.cols; x++){
        Vec3b &p = image.at<uchar>( y, x );
        p[ 0 ] = 0;
        p[ 1 ] = 255;
        p[ 2 ] = 255;
    }
}


と、簡単ですね。


しかし、Mat::atはどうやらオーバーヘッドが大きく、処理が遅いという欠点があります。

おそらくMat::at内でエラー処理などを行っているからでしょうか。

では次に画素に直接アクセスしてみます。


2.「Mat.data」を使う

Mat.dataには画素が格納されています。
ここに直接アクセスしてみましょう。


// グレースケール画像(8bit)画像
for(int y = 0 ; y < image.rows; y++){
    for(int x = 0 ; x < image.cols; x++){
        image.data[y * image.cols + x] = 255;
    }
}

// RGB画像(24bit)画像
for(int y = 0 ; y < image.rows; y++){
    for(int x = 0 ; x < image.cols; x++){
        image.data[y * image.cols + x * image.step + 0] = 0;
        image.data[y * image.cols + x * image.step + 1] = 255;
        image.data[y * image.cols + x * image.step + 2] = 255;
    }
}


これである程度の速度は確保できたでしょう。

ちなみにここに出てくる変数名を説明しておきます。

image.rows imageの高さ
image.cols imageの幅
image.step imageの幅×チャンネル数






です。



しかし、初めて扱う型の画像だとアクセスする場合どうすればいいのかわからない場合があります。

例)Mat point3D( 480, 640, CV_32FC3);
私はこの時は3次元座標を格納したい時に使いました。
しかし、どうやってアクセスすればいいのかわからなかったのです。
この時とった解決策を紹介します。

3.Mat:atの中身をパクる
Mat:atの中身を見てみましょう。

template<typename _Tp> inline _Tp& Mat::at(int i0, int i1)
{
    CV_DbgAssert( dims <= 2 && data && (unsigned)i0 < (unsigned)size.p[0] &&
        (unsigned)(i1*DataType<_Tp>::channels) < (unsigned)(size.p[1]*channels()) &&
        CV_ELEM_SIZE1(DataType<_Tp>::depth) == elemSize1());
    return ((_Tp*)(data + step.p[0]*i0))[i1];
}


なんかごちゃごちゃ書いてますが必要そうなのはreturnの後の式だけっぽいですね。

point3Dの値をMat::atで取得しようとした場合
for(int y = 0 ; y < point3D.rows; y++){
    for(int x = 0 ; x < point3D.cols; x++){
        Point3f &point = point3D.at<Point3f>(y,x);
        point.x = 0;
        point.y = 0.5;
        point.z = 1000;
    }
}

となる


しかし速度が遅い


ということでMat:atの中身をパクってみよう

for(int y = 0 ; y < point3D.rows; y++){
    for(int x = 0 ; x < point3D.cols; x++){
        Point3f &point = ((Point3f*)(data + step.p[0]*y))[x];
        point.x = 0;
        point.y = 0.5;
        point.z = 1000;
    }
}

すると速度が上がりました


ということでMat::atを使わずMatのアクセス速度を高速化することに成功しました!!





他にも調べるとアクセス高速化の方法はいっぱいあるので、自分のプログラムにあったものを探してください。



参考
Miyabiarts.net

0 件のコメント:

コメントを投稿