OpenCV/C++で画像処理入門 vol.3 〜画像に図形を描画してみよう〜

2019.01.11

 

こんにちは、代表の大野です。

 

OpenCV/C++で画像処理入門シリーズの第3回目です。

 

前回は、画像を白黒にしたり、画像を切り取る操作を
OpenCVを通じて行いました。

 

今日は、画像に図形を描画する内容を書いていきたいと思います。

 

環境があれば、ぜひみなさんも一緒に
プログラムを書いて体験してみてくださいね。

 

バックナンバー

vol1. 画像を表示してみよう
vol2. 画像を加工してみよう

 

今回やること

  • 画像に丸を描画してみよう
  • 画像に長方形を描画してみよう

 

環境

マシン  :mac OS Mojave 10.14.1
言語   :C++
ライブラリ:OpenCV 3.4.3
コンパイラ:Clang / LLVM

 

準備 & 画像の用意

OpenCVのインストールなどは
バックナンバーから、参考にしてください。

今回のサンプルは
ティータイムスワワちゃんを用いることにします。

 

ティータイム スワワちゃん(teatimesuwawachan.jpg)
width : 600px
height : 600px

 

では、さっそくいってみましょう。

 

画像に丸を描画してみよう

スワワちゃんの好物は、アールグレイの紅茶です。

 

休憩時間によく飲んでいるようなのですが、

今回は、せっかくなので
アールグレイの入ったティーポットに焦点を当てていきましょう。

 

こちらの画像は
縦横600pxの正方形の画像です。

 

前回ご紹介したcv::Rectであれば
左上頂点座標を取得すればよかったのですが、

 

丸を描く場合は、
どこの座標を円の中心にして、半径何pxの円を描くか

という指定が必要になります。

 

 

ティーポットのだいたいの中心座標を
調べてみると
(macであればプレビューなどで確認できます)

 

(x, y) = (95, 355)

 

あたりかなとわかります。

 

半径は95pxぐらいかな。

 

では、試しに、
ティーポットを綺麗に囲えているかどうか
実際に丸を描いてみましょう。

 

わかりやすいように青枠の丸にしてみたいと思います。

<ソースプログラム>

circle.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <opencv2/opencv.hpp>

int main() {
 cv::Mat img, dst;

 img = cv::imread("teatimesuwawachan.jpg");
 dst = img.clone();

 cv::circle(dst, cv::Point(95,355), 95, cv::Scalar(255,0,0), 5);

 cv::imshow("input", img);
 cv::imshow("output", dst);
 cv::waitKey(0);

 return 0;
}

 

さて、今回もプログラムについて、
順々に説明していきたいと思います。

 

1
2
3
4
 cv::Mat img, dst;

 img = cv::imread("teatimesuwawachan.jpg");
 dst = img.clone();

 

今回もわかりやすいように、cv::Mat型のオブジェクトを
入力画像用のimgと出力画像用のdstの2つを宣言しておきました。

 

imgには、サンプル画像である画像ファイルを
cv::imread()関数で取り込みます。

 

さて、ここで新しくclone()というものが出てきました。

 

clone()は、cv::Matクラスで定義されている関数で
元のオブジェクトを画素、データ型、サイズなどを
すべてコピーするための関数です。

 

また、clone()はコピー元の画像を変更しないようにもなっているため
入力画像と出力画像の違いを明確に見たい場合には、
ぜひclone()を使ってみてください。

 

ということで、今回のソースコードでは、
imgに取り込まれているサンプル画像データを
dstにそっくりそのままコピーしていることになります。

 

この時点では、
imgもdstも中身は、全く同じ状態です。

 

 

1
 cv::circle(dst, cv::Point(95,355), 95, cv::Scalar(255,0,0), 5);

 

さて、肝心の丸を描く部分です。

 

circle()関数と、わかりやすい名前の関数なので
二度と忘れることもないでしょう。

 

大事なのは引数ですね。

 

第一引数 dst … 丸を描く対象となるcv::Mat
第二引数 cv::Point(95,355) … 丸を描く中心座標(x座標が95, y座標が355)
第三引数 95 … 半径サイズ(px)
第四引数 cv::Scalar(255,0,0) … BGRカラー指定(blueが255, greenが0, redが0)
第五引数 5 … 丸枠の線の太さ

 

第一引数は、出力画像用のdstを入れればOK。

 

第二引数は、OpenCVで2次元座標を扱うcv::Pointクラスで、座標を指定しましょう。
cv::Point()クラスは、第一引数がx座標(int型)、第二引数がy座標(int型)になります。

 

第三引数は、描く丸の半径サイズです。直径ではないところに注意。

 

第四引数は、丸枠の色の指定を行います。cv::Scalarは、値を配列のように格納するもので
ここでは、BGR(青緑赤)の順に256階調で、指定しましょう。

 

HTML/CSSに慣れている人は、RGBの順で指定しがちですが、
あくまで、「BGR」の順なので気をつけましょう。

 

今回は青枠にするので、B=255(max)、G=0、R=0になります。

 

最後に第五引数で、丸枠の線の太さを指定します。
今回は少々目立つように5pxにしました。

 

では実際にビルドをして実行してみましょう。

 

<ビルド & 実行>

ターミナル など

1
2
c++ $(pkg-config --cflags --libs opencv) circle.cpp
./a.out


 

dst(output)のほうには、ちゃんとティーポットに
青枠で丸が描かれていますね。

 

 

画像に長方形を描画してみよう

同じ要領で今度は、ティーポットを長方形で囲んでみたいと思います。

 

長方形を描く場合は、
長方形で囲いたい部分の

 

  • 左上の頂点のxy座標
  • 右下の頂点のxy座標

 

の2つが必要になります。

 

 

ティーポットを囲うには、

左上座標が、

 

(x, y) = (0, 295)

 

右下座標が
(x, y) = (180, 430)

 

ぐらいですかね。

 

では、プログラムを見てみましょう。

 

<ソースプログラム>

rectangle.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <opencv2/opencv.hpp>

int main() {

 cv::Mat img, dst;

 img = cv::imread("teatimesuwawachan.jpg");
 dst = img.clone();

 cv::rectangle(dst, cv::Point(0,295), cv::Point(180,430), cv::Scalar(255,0,0), 5);

 cv::imshow("input", img);
 cv::imshow("output", dst);

 cv::waitKey(0);

 return 0;
}

 

先ほどの、「circle」が「rectangle」に変わっただけです。

 

cv::rectangleもその名の通り、
長方形を描画するものです。

 

第一引数 dst … 出力先のcv::Mat
第二引数 cv::Point( 0,295) … 描く長方形の左上の座標
第三引数 cv::Point(180,430) … 描く長方形の右下の座標
第四引数 cv::Scalar(255,0,0) … BGRカラー指定
第五引数 5 … 丸枠の線の太さ

 

第二引数、第三引数には
先ほど説明した、cv::Pointで、長方形の左上と右下の座標の値を決定します。

 

第二引数が、左上の頂点座標、第三引数が右下の頂点座標です。
対角線を引けるように指定するということですね。

 

あとは、丸のときと同じ、第四引数と第五引数を用いて
青枠で太さを5にしてあります。

 

ではビルドをして実行してみましょう。

 

<ビルド & 実行>

ターミナルなど

1
2
c++ $(pkg-config --cflags --libs opencv) rectangle.cpp
./a.out

 


 

今度は、ちゃんとティーポットを囲んだ
長方形が描画されているのがわかりますね?

 

 

まとめ

さて、今回は、丸と長方形を描画してみましたが
OpenCVには他にも

 

線を描画  line()
矢印を描画 arrowedLine()
楕円を描画 ellipse()

 

なども描画できたりするので

 

どんな引数をとるのかを調べながら
実装してみると面白いと思います。

 

というわけで、今日はここまで。


Top