おもちゃラボ

Unityで遊びを作ってます

OpenCV超入門 サンプルプログラム集

OpenCVはバージョンが1.0のころに使っていましたが、久しぶりに使おうとするとバージョンが3.0になって、ガラッと様子が変わってしまっていることに気がついた。

  • IplImageはどこへいった?
  • cvのプリヘッダがなくなった!?
  • Matってメモリの割り当てはどうするんだ?
  • ピクセルへのアクセスってどうやるの?

などなど、疑問が山のようにでてきました。そんな疑問に答えるべく、OpenCV3の使い方とサンプルをまとめてみたので、どうぞ活用してやって下さい。ちなみに、サンプル写真にはレナ嬢ではなく京都の風景を使用しています(笑)

画像を表示する

f:id:nn_hokuson:20170529235347j:plain
OpenCV3を使って、画像を表示するだけのプログラムです。

void main()
{
    Mat img = imread("img.png");
    imshow("image",img);
    waitKey(0);
}

OpenCV3からは画像を格納する型としてIplImageではなくMatを使います。画像の読み込みにはimread関数を使います。引数は次のようになります。

■imread(filename, flag)

引数 意味
filename 読み込みファイル名
flag >0: 3チャンネルカラー
=0: グレースケール
<0 読み込み画像に従う


画像の表示はimshow関数で、第1引数にウインドウ名、第2引数に表示したいMatを指定します。

■imshow(name, img)

引数 意味
name ウインドウの名前
img 表示する画像

グレースケール変換

f:id:nn_hokuson:20170531195046j:plain:w300 f:id:nn_hokuson:20170531195217j:plain:w300
画像をグレースケールに変換するプログラムです。

void main()
{
    Mat img = imread("img.png");
    Mat dst;
    cvtColor(img, dst, COLOR_RGB2GRAY);
    imshow("image",dst);
    waitKey(0);
}

グレースケール変換にはcvtColor関数を使います。第3引数に指定するコードで変換前と変換後の画像フォーマットを指定します。ここではRGBからグレースケールに変換するため、COLOR_RGB2GRAYを指定しています。第3引数を変更することでHSVなどのフォーマットに変換することも出来ます。

■cvtColor(src, dat, code)

引数 意味
src 入力画像
dst 出力画像
code RGB→GRAY COLOR_RGB2GRAY
RGB→HSV COLOR_RGB2HSV
・・・など

ここで、出力画像のメモリ割り当てを行っていないことに注目して下さい。IplImageとは異なり、Matでは自動的にメモリの割り当て&開放を行ってくれるようになっています。

2値化

f:id:nn_hokuson:20170530065445j:plain f:id:nn_hokuson:20170530065451j:plain
画像を白黒の2値に変換するプログラムです。

void main()
{
    Mat img = imread("img.png");
    Mat gray, dst;
    cvtColor(img, gray, COLOR_RGB2GRAY);
    threshold(gray, dst, 80, 200, 0);
    imshow("image",dst);
    waitKey(0);
}

画像を2値化するには、まずグレースケールに変換し、その画像に対してしきい値以上なら該当するピクセルを白、しきい値以下なら黒にする処理を行います。この処理はthreshold関数で行っています。

■threshold(src, dat, thresh, max, type)

引数 意味
src 入力画像
dst 出力画像
thresh 閾値
max 明度の最大値
type THRESH_BINARY
THRESH_BINARY_INV
・・・など

リサイズ

f:id:nn_hokuson:20170531195512j:plain:w300  f:id:nn_hokuson:20170531195512j:plain:w150
画像のサイズを変更するプログラムです。

void main()
{
    Mat img = imread("img.png");
    Mat dst;
    resize(img, dst, Size(150, 100));
    imshow("image",dst);
    waitKey(0);
}

画像のサイズを変更するにはresize関数の第3引数に、出力画像のサイズを指定します。あとはこの関数がメモリ割り当てなども自動的に行なってくれます。

■resize(src, dat, size)

引数 意味
src 入力画像
dst 出力画像
size 出力画像のサイズ

トリミング

f:id:nn_hokuson:20170531201225j:plain:w300   f:id:nn_hokuson:20170531201234j:plain
画像の一部をトリミングするためのプログラムです。

void main()
{
    Mat img = imread("img.jpg");
    Mat dst(img, Rect(50, 100, 200, 200));;
    imshow("result", dst);
    waitKey(0);
}

Matクラスのコンストラクタに元画像の変数と、トリミングする長方形をRect型で指定します。

図形を描画する

f:id:nn_hokuson:20170530234921p:plain:w300
円や長方形、直線などを描画するプログラムです。

void main()
{
    Mat img(512,512, CV_8UC3, Scalar(100, 100, 100));
    circle(img, Point(256, 256), 100, Scalar(200, 100, 100), 5);
    line(img, Point(0, 0), Point(512, 512), Scalar(0, 200, 0), 3, 4);
    rectangle(img, Point(100, 100), Point(300, 200), Scalar(0, 0, 200), 2, 4);
    imshow("image",img);
    waitKey(0);
}

図形を描画するためのキャンバスを最初に作ります。ここでは大きさを指定してMat型の変数を作成しています。
円を描くにはcircle関数、長方形はrectangle関数、線はline関数を使います。それぞれの関数の引数は次のようになります。図形を塗りつぶすにはticknessの値をマイナスにします。

■circle(img, center, radius, coor, tickness)

引数 意味
img 描画する画像
center 円の中心座標
radius 円の半径
color 円の色
tickness >=0: 線の太さ
<0: 塗りつぶし

■rectangle(img, pt1, pt2, coor, tickness)

引数 意味
img 描画する画像
pt1 左上座標
pt2 右下座標
color 円の色
tickness >=0: 線の太さ
<0: 塗りつぶし

■line(img, pt1, pt2, coor, tickness)

引数 意味
img 描画する画像
pt1 開始座標
pt2 終点座標
color 円の色
tickness >=0: 線の太さ
<0: 塗りつぶし

画像の保存

f:id:nn_hokuson:20170531201717j:plain:w300
画像を保存するプログラムです。

void main()
{
  Mat img = imread("img.png");
  imwrite("img.jpg", img);
}

画像を保存するにはimwriteを使用します。第1引数には保存ファイル名を指定します。拡張子によって自動的に保存形式が選択されます。

■imwrite(filename, img)

引数 意味
filename 保存するファイル名
img 保存する画像

画像の上下左右反転

f:id:nn_hokuson:20170529232958j:plain f:id:nn_hokuson:20170529233006j:plain
画像を上下反転・左右反転(フリップ)するプログラムです。

void main()
{
   Mat img = imread("img.png");
    Mat dst;
    flip(img, dst,0);
    imshow("image",dst);
    waitKey(0);
}

画像を反転するにはflip関数を使います。filp関数の第3引数に反転方向を指定します。反転方向の指定は次のようになります。

■flip(src, dat, code)

引数 意味
src 入力画像
dst 出力画像
code 反転方向の指定
> 0: 左右の反転
= 0 上下の反転
< 0 左右上下の反転

ぼかしフィルタ

f:id:nn_hokuson:20170529233430j:plain f:id:nn_hokuson:20170529233437j:plain
ボックスフィルタを使って画像をぼかすプログラムです。

void main()
{
    Mat img = imread("img.png");
    Mat dst;
    blur(img, dst, Size(7,7));
    imshow("image",dst);
    waitKey(0);
}

ボックスフィルタを使って画像をぼかすにはblur関数を使います。ボックスフィルタは周辺のピクセルの平均値を現在のピクセルの値にします。どの範囲の平均値をとるかは、第3引数にぼかしフィルタのカーネルサイズで指定します。

■blur(src, dat, ksize)

引数 意味
src 入力画像
dst 出力画像
ksize ボックスフィルタのカーネルサイズを指定します

ガウシアンフィルタ

f:id:nn_hokuson:20170530065030j:plain f:id:nn_hokuson:20170530065053j:plain
ガウシアンフィルタを使って画像をぼかすプログラムです。

void main()
{
     Mat img = imread("img.png");
     Mat dst;
     GaussianBlur(img, dst, Size(7,7), 10, 10);
     imshow("image",dst);
     waitKey(0);
}

ガウシアンフィルタを使って画像をぼかすにはgaussianBlur関数を使います。ボックスフィルタに比べて綺麗にぼけ、またぼけの量を微調整できるのが特徴です。調整項目は次のようになります。

■gaussianBlur(src, dat, ksize, sigma)

引数 意味
src 入力画像
dst 出力画像
ksize ガウシアンフィルタのカーネルサイズを指定します
sigma ガウシアンフィルタのシグマ値を指定します

Sobelフィルタ

f:id:nn_hokuson:20170531202927j:plain:w300 f:id:nn_hokuson:20170531202936j:plain:w300
Sobelフィルタを使って画像から輪郭線を抽出するプログラムです。

void main()
{
    Mat dst, gray;
    Mat img = imread("img.jpg");
    cvtColor(img, gray, COLOR_RGB2GRAY);
    Sobel(gray, dst, -1, 1, 0);
    imshow("image",dst);
    waitKey(0);
}

Sobelフィルタを使うことで画像中からエッジを検出することが出来ます。第4引数と第5引数に横方向と縦方向の微分係数を指定します。上のサンプルでは(1, 0)を指定しているので、縦方向のエッジが検出できます。逆に(0, 1)を指定すると横方向のエッジを検出できます。

全てのエッジを検出するには、縦と横の両方向に処理が必要になります。Sobelフィルタの計算は一次微分なので、ノイズの影響を受けにくく比較的処理速度も速いです。

■sobel(src, dat, depth, dx, dy)

引数 意味
src 入力画像
dst 出力画像
depth 出力画像のビット深度
dx 横方向の微分係数
dy 縦方向の微分係数

Cannyフィルタ

f:id:nn_hokuson:20170531202927j:plain:w300 f:id:nn_hokuson:20170531204417j:plain:w300
Cannyフィルタを使って画像から輪郭線を抽出するプログラムです。

void main()
{
    Mat img = imread("img.jpg");
    Mat dst, gray;
    cvtColor(img, gray, COLOR_RGB2GRAY);
    Canny(gray, dst, 40, 150);
    imshow("image",dst);
    waitKey(0);
}

CannyフィルタはSobelフィルタやLaplacianフィルタと比べて非常に綺麗にエッジを検出することが出来ますが・・・それに比例して処理負荷と処理時間も大きくなります。とにかく綺麗にエッジを抽出したいときはCannyフィルタを使うと良いでしょう。

■ Canny(src, dat, thresold1, thresold2)

引数 意味
src 入力画像
dst 出力画像
thresold1 閾値1
thresold2 閾値2

透視変換

f:id:nn_hokuson:20170531205709j:plain:w300 f:id:nn_hokuson:20170531205722j:plain:w270
画像を透視変換によって変形するプログラムです。

void main()
{
    Mat img = imread("img.png");
    Mat dst;
    Point2f srcPoint[] =
    {
        Point(0,        0),
        Point(0,        img.cols),
        Point(img.rows, img.cols),
        Point(img.rows, 0),
    };
    Point2f dstPoint[] =
    {
        Point(100, 100),
        Point(0,   300),
        Point(400, 450),
        Point(500, 50),
    };

    Mat H = getPerspectiveTransform(srcPoint, dstPoint);
    warpPerspective( img, dst, H, Size(512, 512));
    imshow("image", dst);
    waitKey(0);
}

OpenCVで透視変換を行うためには、まず変換前の4点と変換後の4点の座標を指定します。これらの座標をgetPerspectiveTransform関数に渡すことで、透視変換行列が得られます。この透視変換行列をwarpPerspectiveに渡すことで、画像を変形することができます。

透視変換をよく使う場面としては、QRコードの解析などX軸やY軸に沿って処理を進めたいときに使います。オリジナル画像で処理をすすめると、数学的に大変になるので、一度透視変換で正方形に変形してから、処理をすることでプログラムが簡単になります。

■ getPerspectiveTransform(src, dat)

引数 意味
src 入力座標の配列
dst 出力座標の配列
戻り値 透視変換行列

■ warpPerspective(src, dat, M, size)

引数 意味
src 入力画像
dst 出力画像
M 透視変換行列
size 出力画像のサイズ

ヒストグラムの平滑化

f:id:nn_hokuson:20170530235904j:plain:w300 f:id:nn_hokuson:20170530235917j:plain:w300
グレースケール画像のヒストグラムを均一化するプログラムです。

void main
    Mat img = imread("img.png");
    Mat gray, dst;
    cvtColor(img, gray, COLOR_RGB2GRAY);
    equalizeHist(gray, dst);
    imshow("image",dst);
    waitKey(0);
}

ヒストグラムを均一化することで、とコントラストを向上させたり、画像の全体的な明るさのバランスを改善することが出来ます。変換前のヒストグラム(左)と変換後のヒストグラム(右)は次のようになります。

f:id:nn_hokuson:20170531210247p:plain:w200 f:id:nn_hokuson:20170531210253p:plain:w200

均一化したことで、ヒストグラムが左右に広がって平らになったことがわかると思います。OpneCV3でヒストグラムの均一化を行うためにはequalizeHist関数を使います。

■ equalizeHist(src, dat)

引数 意味
src 入力画像
dst 出力画像

参考図書

今回は次の本を参考にさせていただきました!