2014.12. 2.
2014.12. 9. image1.at<Vec3b>(x, y)などをimage1.at<Vec3b>(y, x)などに訂正
石立 喬

OpenCVとVisual C++による画像処理と認識(2)

――― 原画像をRGB成分に分解してピクセル単位の画像処理をする -----

 OpenCVを使った画像処理の手始めとして、従来の.NET Frameworkの場合にならって、画像をRGBに分解して処理し、再び画像に戻す方法を紹介する。具体的に取り上げるのは、原画像の反転(ネガ化)、コントラスト強調、グレイスケール化である。
 しかし、OpenCVには、多数の優れた画像処理専用関数(メソッド)があるので、ここでは、両者を比較し、OpenCVへの理解の一助とする。
 新しいバージョンのOpenCV 2.4.10では、Matクラスで画像を表現する。このMatと言うデータコンテナの概念と、VecクラスやScalarクラスなどのコンテナを紹介する。

Matの概念
 Matは、画像の格納を目的としたデータコンテナで、一見、単なる二次元配列のようであるが、個々の要素には奥行bit depthがあり、さらに分割されたchannelsがある。。
 図1の左に示すように、Matは cols x rows 個の入れ物から成る。個々の入れ物は、図1の右に示すように、さらに最大4個までのchannelsから構成され、r, g, b, a(透過度)の各データを格納できる。グレイ画像の場合には1チャンネル(Vec1bで指定)で十分で、透過度指定を伴わない一般のカラー画像は3チャンネル(Vec3bで指定)を使用する。
 図にPで示したピクセルを呼び出すには、imageをMatクラスのインスタンスとして、
  image.at<Vec3b>(y, x)
などを使う。各チャンネルの値を得るには、
  r = image.at<Vec3b>(y, x)[2]
などを使う。RGBの順序が逆であることに注意されたい。


図1 Matは多次元のデータを格納するコンテナ


Matのコンストラクタ
 数多くのコンストラクタがあるが、良く使うものを以下に述べる。
1)サイズとタイプのみを指定する場合
 Mat(Size size, int type) を使用する。sizeは、Size(width, height)のようにして指定し、typeは、CV_8UC3などで、データの深さdepth(精度、桁数)とチャンネル数channels(3色など)を指定する。
 たとえば、
  Mat image = Mat( Size(320, 240), CV_8UC3);
は、幅320ピクセル、高さ240ピクセルの、各8ビット、3色の画像である。

比較的良く使うtypeは下記の通り。
  CV_8UC1 ------ bit depth = 8(unsigned char),  channels = 1  グレイスケール画像用
  CV_8UC3 ------ bit depth = 8(unsigned char),  channels = 3  一般のカラー画像
  CV_32F ------- bit depth = 32(float),        channels = 1   kernelや積和演算の中間データ用
  
  CV_8UC(n)により、nでchannelsを指定することもある。
2)サイズとタイプの他に、全画面同一のカラーで塗りつぶしたい場合
  Mat(Size size, int type, const Scalar& s) を使用する。  sは、Scalar(255, 0, 0)などと指定する。Scalarについては後述するが、たとえば、
  Mat image = Mat( Size(320, 240), CV_8UC3, Scalar(255, 0,0));
は、前記画像の全ピクセルを、R = 0, G = 0, B = 255の色(青)に設定する場合である。

その他のデータコンテナ
 データコンテナを構成する各種テンプレートクラスと、そこから、特定の条件で定義されたクラスを表1に示す。
 Matxは、比較的小型の二次元マトリックス(行列)を用意する場合に使用される。Vecは、Matxを継承して作られた一次元の配列で、特に、RGB値などを包括してカラーとして扱う場合に使用される。
 Scalar_テンプレートクラスはVec<typename, 4>の継承で、double型のものをScalarクラスと呼ぶ。RGB値から成るカラーを扱うが、double型で4チャンネルである。 Size_とPoint_は、表1のように特定クラス化して用いられる。
 Scalarは、実質的にVec<double, 4>と同一であり、Vec4dで代用できると思ったが、それではビルドできない。しかし、
  Scalar scalar1 = Vec4d(255, 0, 0);
  Mat image2 = Mat(Size(320, 240), CV_8UC3, scalar1);
はビルドでき、正しく実行できた。
 タイプがdoubleでなく、ucharのVec3b(255, 0, 0) でも使えたのは興味がある。

表1 いろいろなデータの格納に適したテンプレートクラスと特定クラス


原画像からピクセル単位にRGB情報を取り出し、処理をして目的画像を生成する
 画像処理は、一言でいえば、画像のRGB情報の操作である。OpenCVの仕組みを理解しやすいように、従来説明してきた.NET Frameworkによる画像処理のステップと、OpenCVの場合とを表2に比較する。ただし、OpenCVには、優れた便利な関数(メソッド)が多数あり、表2のステップを使用する必要はあまりない。

表2 RGB値レベルで画像処理を行う場合の比較

 mage1は読み込み時にサイズやタイプが自動的に設定されるが、image2は、あらかじめ、サイズやタイプを指定して、インスタンス化しておく必要がある。


原画像の反転(ネガ化)
【RGB値レベルでの処理の場合】
 図2は、RGB値レベルに分解して処理する汎用的手法で画像の反転を行うプログラムで、表1に従っている。
◎画像から特定の位置のピクセル値を求める
 Vec3b color1 = image1.at<Vec3b>(y, x);
を使う。Vec3bは、すでに述べたように、ucharタイプのデータ3個からなるVecクラスである。Mat::atはテンプレートメソッドで、指定した座標にある要素への参照を返す。
◎ピクセル値をRGB値に分解する
 color1はVec3bタイプなので、要素が3個の配列である。したがって、[0]~[2]の添え字で、
 r1 = color1[2];
のようにして、個々の要素を取り出すことができる。RGBの順序が逆であるのに注意されたい。
◎RGB値ごとに反転を行う。
 r2 = 255 - r1;
などを使用する。
◎RGB値からピクセル値を生成する
 Vec3b color2 = Vec3b(b2, g2, r1);
のように、Vec3bの括弧に中に、BGRの順に並べる。
◎画像の特定の位置にピクセル値を設定する
 image2.at<Vec3b>(y, x) = color2;
で代入すればよい。


図2 表2に示す方法で、画像をRGBに分解して画像を反転する


【OpenCVの関数使用の場合】
 OPenCVでは、Matクラスのインスタンスに対して四則演算や論理演算ができる。画像の反転を、白色の画像から減算することと考えると、図3のように、白色画像image0を生成し、image0から原画像image1を減算することにより画像の反転ができる。
 一方、原画像のデータのビットごとの論理否定をとると反転できると考えると、OpenCVに備わっている関数bitwise_not()で反転画像が得られる。


図3 OpenCVの関数で簡単に画像を反転(前述プログラムコードの後半部)


原画像を反転した結果
 図4で、左上は原画像、右上はRGBレベルに分解して得た反転画像、左下は白色画像からの減算結果、右下は原画像の論理否定をとった画像である。これら3種の方法による反転画像は全く同じ結果になっている。


図4 原画像反転で得られた結果


コントラストの強調
 画像のRGB各要素を、中間値128を中心に傾斜を高めるとコントラストを高めることができる。具体的には、原画像の濃度値をd1とし、変換後の濃度値をd2として、
  d2 = (d1 - 128) * alpha + 128
の式を用いる。d1が大きい場合にはd2が255を超える恐れがあり、d1が小さい場合には、d2が負になる場合もある。これらを補正して、unsined charの範囲に収めるには、OpenCVのテンプレート関数
  saturate_cast<uchar>
を用いる。これによれば、256以上は255に、負数は0に飽和(saturate)させてくれる。
 図2のプログラムで、31~34行を図5のように書き換えると、コントラストの強調ができる。


図5 原画像のコントラストを高めるプログラム


 図5の処理は、図6に示すトーンカーブを使用することと同等である。OpenCVには、この種の既製関数はなく、別の項で紹介予定のequalizeHist()が類似の効果をもつ。


図6 図5の処理に相当するトーンカーブ


 得られた結果を図7に示す。


図7 saturation_castを用いてコントラストを高める


原画像のグレイスケール化
【RGB値レベルでの処理の場合】
 図8は、RGB値レベルに分解して画像のグレイ化を行うプログラムである。
◎グレイ画像を格納する画像image2を用意する。
 グレイ画像のチャンネルは1個で良いので、タイプ指定をCV_8UC1にする。
◎NTSCの式によりRGB値からグレイスケールの濃度を計算し、念のためにucharの範囲に収めてdとする。
 uchar d = saturate_cast<uchar>(0.299* r1 + 0.587 * g1 + 0.114 * b1);
を使用する。
◎画像の特定の位置に濃度値を設定する(RGB値をベクトル化したcolor2を用いる必要はない)。
 image2.at<uchar>(y, x) = d;
で代入すればよい。

har

図8 画像をRGBに分解してグレイスケール化する


【OpenCVの関数使用の場合】
 OPenCVでは、カラー変換関数cvtColor()で、CV_BGR2GRAYを指定すると、簡単にグレイスケール画像が得られる。また、原画像の読み取り時に、IMREAD_GRAYSCALEを指定して、グレイ画像として画像を得ることもできる。これらを図9に示す。


図9 OpenCVの関数で簡単に画像をグレイ化(前述プログラムコードの後半部)


原画像をグレイ化した結果
 左上の原画像に対して、種々の方法でグレイスケール化した結果を図10に示す。いずれの方法でも同じ結果が得られている。


図10 原画像のグレイスケール化で得られた結果