Miyabiarts.net

一年に一度の更新頻度

OpenCVのcv::Mat / cv::Mat_の使い方(cv::Mat編)

OpenCVのC++インタフェースで行列、および画像を扱うためのcv::Mat / cv::Mat_ですが、主要なクラスの割にはまとまった情報が少ないので、基本的な使い方を紹介します。
ここでは、通常の行列として用いる方法ではなく、あくまで画像として扱うものとします。

今回は、cv::Matの方の使い方について書きます。
次回は、cv::Mat_について書きますが、内容としては少し扱い方が違うだけでほぼ同じです。

cv::Matとcv:Mat_

まずは、今回の対象であるcv::Matとcv::Mat_について説明します。
いずれも行列、および画像を扱うための二次元配列を表します。
cv::Matは、行列の1つの要素を型なしとして扱い、アクセスする際に型を与えます。
cv::Mat_は、定義する際に型を与え、要素にアクセスする際には型を必要としません。

これらのクラスを利用するためには、以下のようにヘッダファイルをインクルードします。
パスはちゃんと通しておいてください。

#include <opencv2/opencv.hpp>

画像の作成

任意のサイズ・画素の型で初期化する場合は、 以下のように書きます。
さっきcv::Matは、画素を型なしで扱うと書きましたが、あくまでC++の型のことで、1画素をどのように表現するかの型は必要となります。
この型は、OpenCVの定数で与えます。

cv::Mat image( 高さ, 幅, 画素の型 );

// グレースケール画像(8bit)画像
cv::Mat image( height, width, CV_8UC1 );

// RGB画像(24bit)画像
cv::Mat image( height, width, CV_8UC3 );

ここで、widthとheightを与える順番に気をつけてください。

画像の入出力

画像の入力は、読み込んだ画像をどのような型で扱うかで引数が異なります。

// グレースケール画像(8bit)画像
cv::Mat image = cv::imread( "ファイル名", 0 );

// RGB画像(24bit)画像
cv::Mat image = cv::imread( "ファイル名", 1 );

画像の出力は簡単で、以下のようにするだけです。

cv::imread( "ファイル名", image );

任意の画素へのアクセス

任意の位置の画素へのアクセスは以下のようにします。
cv::Matでは、画素の実際の値を読み書きするため、cv::Mat::atに対して型を与える必要があります。
以下の例では、位置(x,y)の画素に0xffを代入して白画素にしています。

// グレースケール画像(8bit)画像
image.at( y, x ) = 0xff;

// RGB画像(24bit)画像
cv::Vec3b &p = image.at( y, x );
p[ 0 ] = 0xff;
p[ 1 ] = 0xff;
p[ 2 ] = 0xff;

画像全体の走査

画像処理では、画像全体を画素毎にアクセスして処理を行うことが多々ありますが、上記の方法だと呼び出しのオーバヘッドが大きいので、ポインタを利用してアクセスする方法が多く取られます。
ただ、それでは範囲外アクセスの危険があるので、なるべくMatIterator_経由でアクセスすることをお勧めします。

やり方は以下のとおりです。
この例では、全画素に0xffを代入して白画素にしています。

加筆・修正(2011/2/24)(アクセス速度などに関する調査不足があったので、修正しました。)

任意の画素にアクセスする仕方は前述の通りですが、画像全体を走査して処理を行う場合は色々なアクセスの仕方があります。
ここでは、3つのアクセス方法を紹介します。
例では、全画素に0xffを代入して白画素にしています。

任意の画素へのアクセスによる走査

前述した任意の画素へのアクセスを行う方法です。

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

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

ポインタアクセスによる走査

スキャンラインごとに先頭画素のポインタを取得し、そのポインタ経由で画素にアクセスします。
次の画素へはポインタをインクリメントします。

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

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

MatIterator_による走査

画像全体をMapIterator_というIteratorを用いて走査することができます。

// グレースケール画像(8bit)画像
cv::MatIterator_ itr = image.begin();
for( ; itr != image.end() ; ++itr )
{
   *itr = 0xff;
}

// RGB画像(24bit)画像
cv::MatIterator_ itr = image.begin();
for( ; itr != image.end() ; ++itr )
{
   ( *itr )[ 0 ] = 0xff;
   ( *itr )[ 1 ] = 0xff;
   ( *itr )[ 2 ] = 0xff;
}

任意の画素を処理する際に隣接画素から値を求めたい場合には、その画素の位置情報が必要となります。
この位置情報については、以下のように取得できます。

for( ; itr != image.end() ; ++itr )
{
   // 画素の位置を取得
   const cv::Point &pos = itr.pos();

   // 隣接画素へアクセス可能
   image.at( pos.y - 1, pos.x - 1 );
}

アクセス速度

3つのアクセス方法を紹介しましたが、それぞれの方法でアクセス速度が異なります。
詳しくは「画像ピクセル値へのアクセスと計算速度」を参照してください。

広告

Comments are closed.

%d人のブロガーが「いいね」をつけました。