OpenCVで学ぶ画像認識

第2回OpenCVを使ってみよう

第1回では、画像認識の概要や基本原理、実例などを紹介しました。第2回の今回は、これから皆さんが画像認識のプログラムを組んでいく上で必要なOpenCVというツールについて紹介します。

OpenCVとは?

OpenCVは正式名称を"Intel Open Source Computer Vision Library"と言い、その名のとおりインテル社が開発したオープンソースのC/C++ライブラリ集で、コンピュータ・ビジョンに必要な各種機能がパッケージされています。

具体的には、だいたい以下の処理を行う関数群が用意されています。

  • 線形代数や統計処理など、コンピュータビジョンに必要な各種数学関数
  • 直線や曲線、テキストなど画像への描画関数
  • OpenCVで使用したデータを読み込み/保存するための関数
  • エッジ等の特徴抽出や画像の幾何変換、カラー処理等々の画像処理関数
  • 物体追跡や動き推定などの動画像処理用関数
  • 物体検出などのパターン認識関数
  • 三次元復元のためのカメラ位置や姿勢の検出などのカメラキャリブレーション関数
  • コンピュータにパターンを学習させるための機械学習関数
  • 画像の読み込みや保存、表示、ビデオ入出力などインターフェース用関数

このように、OpenCVはどちらかというとビジョンの研究者や開発者の仕事を楽にするためのツールです。 OpenCVがなかった頃は、ビジョン関係の開発者は、画像フォーマットを解析して画像を読みこむプログラムやGUI上に表示するプログラム、画像をぼかしたり特徴のあるピクセルを強調するような「フィルタリング」と呼ばれる処理を、全て一から書かなくてはいけませんでした。しかし、OpenCVのお陰でそれらの処理を一から書かなくても済むようになり、開発が大変楽になりました。

もちろん、コンピュータ・ビジョンの専門知識がない人でも、例えばエッジを抽出したり、ぼかしたりといった画像処理プログラムを、このライブラリを使うことで簡単に組むことができます。

ただし、これらのライブラリ群を使ったとしても、例えば画像認識や画像理解(三次元復元)のようなプログラムを一から書こうとすると、専門知識なしに開発するのは難しいかもしれません。というのは、これらのライブラリの関数群はあくまで画像認識や画像理解などで共通して使用する処理をまとめたものであって、これらをどう組み合わせて、例えば顔を検出するプログラムを作るかは、結局開発者に委ねられているからです。

ただし、これには例外があります。実はOpenCVには、画像の中から物体を検出してくれる画像認識のプログラムが既に用意されているのです。この連載では、皆さんにこのプログラムを使いこなすことで、自分で物体検出アプリケーションを開発できるようになってもらうことを目指します。

開発環境の構築

この連載では、以下のような開発環境をベースに解説を行っていきます。

  • バージョン:OpenCV 1.0
  • OS:Winodows XP
  • IDE:Microsoft Visual C++ 2005

ただしOpenCVはLinuxやMac OS Xもサポートされてますし、もちろん他のIDE(例えばEclipse CDT)などでもコーディング可能です。以降の説明は、皆さんの開発環境にあわせて内容を解釈しながら読み進めてください。

ダウンロードとインストール

OpenCVはsourceforgeからダウンロード可能です。

入手先
URLhttp://www.sourceforge.net/projects/opencvlibrary

2008年5月現在の最新版はバージョン1.0です。こちらのサイトからWindows用のOpenCV_1.0.exeというファイルをダウンロードします。chopencv-2.5.0-win-binary.zipというファイルは今回使用しません。こちらはChと呼ばれるインタープリタ型C/C++言語プラットフォーム用のものです。

図1 ダウンロード
図1 ダウンロード

ダウンロードしたら、OpenCV_1.0.exeを実行してインストールを開始してください。指示に従えば簡単にインストールできると思います。

環境設定

では、次に開発環境を構築します。

開発環境は、インストールしたOpenCVのインクルードファイルのディレクトリへパスを張り、リンク先にOpenCVの各ライブラリを加えるだけで簡単にできます。

まずはVisual C++で新規プロジェクトを作成します。メニューバーの「ファイル」から"新規作成->プロジェクト"と選択し、⁠Win32 コンソール アプリケーション」のプロジェクトを作成して、適当なプロジェクト名をつけて下さい。

なお、プロジェクト作成の際、⁠追加のオプション」から「プリコンパイル済みヘッダー」のチェックボックスを外しておいてください。

プロジェクトが作成できたら、メニューバーの「プロジェクト」からプロパティを選択してください。

「構成」「すべての構成」を選択し、左側のツリーから「C/C++」タブを選択し、⁠追加のインクルードディレクトリ」にインストールしたOpenCVのホームディレクトリの下の以下のディレクトリを追加します。

cxcore\include, cv\include, otherlibs\highgui, cvaux\include.
図2 インクルードディレクトリの設定
図2 インクルードディレクトリの設定

今度は、ライブラリへのリンクをはります。⁠リンカ」タブを選択し、⁠追加のライブラリディレクトリ」にOpenCVディレクトリ下の"lib"ディレクトリを追加して下さい(例:C:\Program Files\OpenCV\lib⁠⁠。

続いて、⁠構成」「Debug」に変更し、⁠リンカ」タブを展開した中の「入力」を選択し、⁠追加の依存ファイル」で"cxcore.lib cv.lib highgui.lib cvaux.lib"を追加します。

図3 ライブラリの設定
図3 ライブラリの設定

以上で設定は完了です。詳細なマニュアルなどは、インストールディレクトリのdocsディレクトリ以下にあるので参照して下さい(例:"C:\Program Files\OpenCV\docs"⁠⁠。

また、リファレンスマニュアルの日本語訳が以下のサイトで見ることができます。

参考:
OpenCVリファレンスマニュアル(日本語訳)

画像を表示してみよう

それでは、いよいよコーディングに入ります。なお、以下の説明の中で使用している画像は、OpenCVのインストールディレクトリ以下の"\samples\c\lena.jpg"をコードのあるディレクトリへコピーして使用しています。

まずは、画像をファイルから読み込み、表示するプログラムを書いてみましょう。

画像の読み込み・表示プログラム
#include "cv.h"
#include "highgui.h"

int main(int argc, char* argv[])
{
        IplImage* img;  // 画像ファイルポインタの宣言
        char imgfile[] = "lena.jpg";    // 読み込み画像ファイル名

        // 画像の読み込み
        img = cvLoadImage(imgfile, CV_LOAD_IMAGE_COLOR);

        // 画像の表示
        cvNamedWindow ("lena", CV_WINDOW_AUTOSIZE);
        cvShowImage ("lena", img);
        cvWaitKey (0);
        cvDestroyWindow("lena");

        // 画像の解放
        cvReleaseImage(&img);

        return 0;
}

まず始めの2行で"cv.h"と"highgui.h"をインクルードします。

次に画像領域を宣言します。IplImageというのが、OpenCVで使用される画像を表す構造体になります。この構造体には、画像の横幅や縦幅、画像のデータ型、チャネル数など画像を表すのに必要なデータが全て入っています。

cvLoadImage関数で、IplImage構造体に画像データを読み込みます。

OpenCVで読みこむことのできるファイルフォーマットは以下のとおりです。

  • Windows bitmaps - BMP, DIB
  • JPEG files - JPEG, JPG, JPE
  • Portable Network Graphics - PNG
  • Portable image format - PBM, PGM, PPM
  • Sun rasters - SR, RAS
  • TIFF files - TIFF, TIF
  • OpenEXR HDR images - EXR
  • JPEG 2000 images - jp2

次に読みこんだデータを表示させます。

cvNameWindowでは、表示させるためのウィンドウを作成します。第1引き数はウィンドウの識別に用いられる名前です。ウィンドウのタイトルバーに表示されます。

次に、cvShowImageで、読みこんだ画像を先ほど作成したウィンドウに読みこんで表示させています。cvWaitKeyは、何かキーが押されるまで待つという意味です。

最後にcvDestroyWindowでウィンドウを廃棄し、cvReleaseImageで読みこんだ画像のメモリ領域を解放します。

では、コンパイルして実行してみましょう。ウィンドウが開いて、指定した画像が表示できれば成功です。

画像処理関数を使う

さて、画像を読みこんで表示させることはできました。次はこれに、なんらかの画像変換の処理を加えます。

エッジ抽出処理

では、手始めに画像から階調変化の強い箇所(エッジ)を抽出してみましょう。OpenCVには何種類かのエッジ抽出関数が用意されていますが、ここではCannyのアルゴリズムと呼ばれているものを使うことにします。

エッジ抽出プログラム
#include "cv.h"
#include "highgui.h"

int main(int argc, char* argv[])
{
        IplImage* img;  // 入力画像ポインタ
        IplImage* img2; // 出力画像ポインタ
        char imgfile[] = "lena.jpg";    // 読み込み画像ファイル名

        // 画像の読み込み
        img = cvLoadImage(imgfile, CV_LOAD_IMAGE_GRAYSCALE);    // グレースケールで読み込み
        img2 = cvCreateImage(cvGetSize(img),IPL_DEPTH_8U,1);    // 画像のメモリ領域割り当て

        // エッジ抽出
        cvCanny(img,img2,64,128);

        // 画像の表示
        cvNamedWindow ("lena", CV_WINDOW_AUTOSIZE);
        cvShowImage ("lena", img2);
        cvWaitKey (0);
        cvDestroyWindow("lena");

        // 画像の解放
        cvReleaseImage(&img);
        cvReleaseImage(&img2);

        return 0;
}

前回のプログラムと違うポイントとしては、まずcvLoadImageによって入力画像を呼び出す際、"CV_LOAD_IMAGE_GRAYSCALE"という引数を与えることで、モノクロ画像として抽出しています。

また、今回は出力の画像ポインタimg2を用意し、それに対しcvCreateImageという関数を用いて領域確保を行っています。

1番目の引き数は画像の縦サイズと横サイズを指定しており、ここではcvGetSize関数を使って入力と同じサイズを割り当てています。2番目の引き数は1ピクセルあたりのデータ型指定で、"IPL_DEPTH_8U"は8bitのUnsigned型(つまり0から255までの値を持つ)を表しています。3番目の引き数の1は、1ピクセルあたりのチャネル数で、ここではモノクロ画像なので1を指定しています。もしカラー画像を使用したい場合は、R,G,Bの三種類のチャネルを容易する必要があるため、この引き数は"3"になります。

エッジ抽出は、cvCanny関数で行います。1番目と2番目の引き数にそれぞれ入力と出力の画像を指定し、3番目と4番目にはエッジ抽出のための弱い閾値と強い閾値の2つを指定します。この2つの閾値はここでは、エッジらしさを2段階で評価している、程度に考えていただければ結構です。

さて、このコマンドを実行した結果、下の図のような結果が出れば成功です。

図4 エッジ抽出結果
図4 エッジ抽出結果

画像の幾何学変換

次は画像の幾何学的な変換について、画像を回転させる方法を例に説明します。

画像回転プログラム
#include "cv.h"
#include "highgui.h"

int main(int argc, char* argv[])
{
        IplImage* img;  // 入力画像ポインタ
        IplImage* img2; // 出力画像ポインタ
        char imgfile[] = "lena.jpg";    // 読み込み画像ファイル名

        CvMat* rotationMat;     // 回転行列

        // 画像の読み込み
        img = cvLoadImage(imgfile, CV_LOAD_IMAGE_COLOR);        // カラーで読み込み
        img2 = cvCreateImage(cvGetSize(img),IPL_DEPTH_8U,3);    // 画像のメモリ領域割り当て

        // 回転行列領域の確保
        rotationMat = cvCreateMat(2,3,CV_32FC1);

        // 30度の回転行列を求める
        cv2DRotationMatrix(cvPoint2D32f(img->height/2,img->width/2),30,1,rotationMat);

        // 回転
        cvWarpAffine(img,img2,rotationMat);

        // 画像の表示
        cvNamedWindow ("lena", CV_WINDOW_AUTOSIZE);
        cvShowImage ("lena", img2);
        cvWaitKey (0);
        cvDestroyWindow("lena");

        // 画像の解放
        cvReleaseImage(&img);
        cvReleaseImage(&img2);
        
        cvReleaseMat(&rotationMat);

        return 0;
}

画像の回転は、数学的には行列の計算で求めることが出来ます。つまり画像はピクセル値を数値と見なした行列と考え、それに対して回転行列をかけることで、画像を回転させます。

まず回転行列をCvMat型のrotationMatととして宣言します。ここで、cvCreateMat関数を用いて2×3の行列領域を確保します。CV_32FC1とは、行列の1要素が32ビット浮動小数点型であるという宣言です。

次にcv2DRotationMatrixを用いて、回転行列を求めます。1番目の引き数では回転の中心位置を指定しています。ここでは、画像の中心を回転の中心としたいので、入力画像の縦幅と横幅の半分の値をcvPoint2D32f関数で浮動小数点型の点座標に変換しています。

2番目の引き数が、回転させたい角度です。ここでは30度とします。

3番目の引き数が、拡大/縮小を行う比率です。ここでは拡大/縮小は行わないため1としています。

4番目の引き数が出力先の先ほど領域を確保したrotationMatです。

ここで取得できた回転行列を元に、cvWarpAffine関数で画像変換を行います。cvWarpAffine関数は、画像の幾何学変換を行うための関数です。入力画像、出力画像、回転行列をそれぞれ引き数に入れることで、回転画像を得ることができます。

さて、このコマンドを実行した結果、下の図のような結果が出れば成功です。

図5 画像回転結果
図5 画像回転結果

終わりに

というわけで、今回はOpenCVについて、画像処理のプログラムを動かすまで行いました。OpenCVには他にもボカシや二値化など、様々な画像処理関数が用意されてますので、OpenCV付属のマニュアルを見ながら、ぜひ色々と試して見て下さい。

次回はいよいよ、画像から顔を検出するプログラムの作成と、その仕組みについて解説したいと思います。

おすすめ記事

記事・ニュース一覧