今回はOpenCVを使って画像に図形を描画する。
はい?そんなことして何の意味が!?
相変わらず想像力が足らんの。
うぐっ。
図形を描画する用途として、画像内に情報を追加することができる。
例えば、今後やっていく物体検知をしたとき、検知した物体を四角で囲むとわかりやすいだろう?
確かに、どの部分を検知したかわかりやすいですね。
このように、画像に図形を足すことで必要な情報を伝えることができる。
では早速いろいろと図形を描いてみるとしよう。
事前準備
図形を描く前に、画像の読込等の処理を掲載しておく。
import cv2
path = "input/fox.jpg"
img = cv2.imread(path)
height, width = img.shape[:2]
#画像のリサイズ(1/4に)
img = cv2.resize(img,(round(width/4), round(height/4)))
いつものやつですね。
共通の引数
OpenCVの図形描画では各関数共通の引数がいくつかある。毎回説明すると面倒だから、最初に解説しておこうと思う。
画像(img)
imgは図形を描画する対象の画像だな。
imageの略でimgですね。
座標(pt1, pt2, center)
線を描く時の両端や円の中心など座標を指定する必要がある。
それらの座標は(x, y)の形式で指定する。
座標って、具体的に画像内の座標はどうやって判断すればいいです?
座標の単位はピクセルで、画像の左上が原点になる。
それをヒントに、具体的な座標の指定は勘だな。
勘、、ですか。
うむ。機械的に座標を算出できる場合はよいが、目分量で図形を描きたいときは勘だ。

色(color)
カラー画像を扱う場合はBGR(Blue, Green, Red)の形式で指定する。
なお、他の色空間に変換している場合はその順番に従うことになる。
黒だったら(0,0,0,)、白だったら(255,255,255)のあれですね
線の太さ、塗りつぶし(thickness)
thicknessは線の太さを指定するパラメータである。単位はピクセル。
「吾輩は猫である」みたいな口調。
からかっているのか?
なお、長方形や円だと、thicknessを負の値(-1など)にすることで図形の塗りつぶしができる。
いや、からかっているわけでは、、
線の種類(lineType)
lineTypeでは線を描画するアルゴリズムを以下から指定できる。
アルゴリズム | 説明 |
---|---|
cv2.LINE_4 | 4連結 |
cv2.LINE_8 | 8連結(デフォルト) |
cv2.LINE_AA | アンチエイリアス |
違いがよくわからないですね、、
cv2.LINE_4 < cv2.LINE_8 < cv2.LINE_AAの順に滑らかな線が描画されるイメージだな。
じゃあ、cv2.LINE_AAにしておけばOKですね!?
その考えもあるが、当然描画処理はcv2.LINE_4が速くなる。必要に応じて使い分けることになるだろう。
座標の小数部分のビット数(shift)
shiftは座標の小数部分のビット数を整数で指定するものだ。
デフォルトは0だな。
つまり、どういうことです?
つまり、通常は座標を整数でしか扱わないということだ。ところが、もっと正確な描画が必要だったり、整数では数値指定が大雑把になってしまうケースがある。
そのときにshiftを使用する。
もう少し細かい話をすると、shiftを指定すると(x*2^(-shift), y*2^(-shift))として計算される。
も、もうやめときましょう?
。。。
わかった。ひとまず、ピクセルより細かい精度で座標指定をしたい場合に使うものと覚えておいてくれ。
あと一つ、shiftを使用する際はアンチエイリアスが前提となる。そのため、lineTypeは必ずcv2.LINE_AAとしておく必要がある。
線を描く(cv2.line())
まずは直線を描いてみる。直線の描画はcv2.line()を使う。
cv.line(img, pt1, pt2, color[, thickness[, lineType[, shift]]])
imageLine = img.copy()
#始点と終点
pt1 = (200,80)
pt2 = (450,80)
cv2.line(imageLine, pt1, pt2, (255, 255, 0), thickness=3)
cv2.imshow('Image Line', imageLine)
cv2.waitKey(0)

この例では、ある座標pt1(x1, y1)からpt2(x2, y2)までの線を描画している。色はBGRで指定し、線の太さは3としている。
色はRGBじゃなくてBGRなんですね。
うむ。順番を間違えないように注意してほしい。
円を描く(cv2.circle())
続いては円を描く。
お願いします。
円の描画には。cv2.circle()を用いる。
cv.circle(img, center, radius, color[, thickness[, lineType[, shift]]])
radiusは円の半径を指定する。
imageCircle = img.copy()
#円の中心座標
circle_center = (350,280)
#半径
radius =100
cv2.circle(imageCircle, circle_center, radius, (0, 0, 255), thickness=3, lineType=cv2.LINE_AA)
cv2.imshow("Image Circle",imageCircle)
cv2.waitKey(0)

塗りつぶしの円
thicknessを負の値にすると、図形が塗りつぶされるんだったな。
cv2.circle(imageCircle, circle_center, radius, (0, 0, 255), thickness=-1, lineType=cv2.LINE_AA)

なるほど、こんな感じに塗りつぶされるんですね。
長方形を描く(cv2.rectangle())
続いては長方形だ。
長方形の描画はcv2.rectangle()を使う。
cv.rectangle(img, pt1, pt2, color[, thickness[, lineType[, shift]]])
imageRectangle = img.copy()
#始点
start_point =(250,185)
#終点
end_point =(430,385)
cv2.rectangle(imageRectangle, start_point, end_point, (0, 0, 255), thickness= 3, lineType=cv2.LINE_8)
cv2.imshow('imageRectangle', imageRectangle)
cv2.waitKey(0)

pt1では長方形の(top, left)の位置を指定し、pt2では(bottom, right)を指定する。
(top, left)、、よくわかんないです。
長方形は、対角の端2点が定まれば形が決まる。その2点を指定するんだよ。

なるほど、対角の2点が決まれば上下左右の座標がわかりますからね。
ちなみに、長方形もthickness=-1にすると塗りつぶされるのだ。
cv2.rectangle(imageRectangle, start_point, end_point, (0, 0, 255), thickness= -1, lineType=cv2.LINE_8)

楕円を描く(cv2.elipse())
最後は楕円の描き方だ。
円とは別物なんです?
うむ。楕円はcv2.elipse()を使う。
cv.ellipse(img, center, axes, angle, startAngle, endAngle, color [, thickness[, lineType[, shift]]])
わ、なんかパラメータが多いですね。
1つずつ解説していくから落ち着きたまえ。まずはコードと結果を紹介する。
imageEllipse = img.copy()
#楕円の中心
ellipse_center = (350,280)
#軸方向の長さ
axis1 = (100,50)
cv2.ellipse(imageEllipse, ellipse_center, axis1, 0, 0, 360, (255, 0, 0), thickness=3)
cv2.imshow('ellipse Image',imageEllipse)
cv2.waitKey(0)

引数
引数 | 説明 |
---|---|
img | インプット画像 |
center | 楕円の中心座標 |
axes | (横方向半径、縦方向半径) |
angle | 楕円の回転角度 |
startAngle | 楕円の描画開始角度 |
endAngle | 楕円の描画終了角度 |
color | 楕円の色 |
thickness | 楕円の太さ |
lineType | 線描画の種類 |
shift | 座標の小数部分のビット数 |
axes
axesは各軸方向への長さだが、(x軸方向,y軸方向)の順で長さを指定する。
例だと(100,50)でx軸方向のほうがy軸方向より長いので、横長の楕円になってるんですね。
その通り、逆にy軸方向を長くすると縦長になるし、両方同じ長さにすると円になる。
はえー、なるほどです。
angle
angleは楕円のを回転させるときの角度である。例えば例にある横長の楕円も、角度を90°とすれば縦長の楕円になる。
ちょっと、axesとangleが混同します、、
確かにややこしいが、楕円のサイズを決めるのがaxes、楕円を回転させたときの位置を決めるのがangleと考えるといい。
startAngle, endangle
続いてstartAngleとendangleだが、これは楕円を描画を開始する角度と終了する角度を示す。
線を描画する角度を0~360°(または0~-360°)までで指定する。
例えば、半分の楕円を描くとか?
その通り。開始を0°、終了を360°とした場合は全円を描画できる。終了を180°とした場合は半円を描画できるといった具合だ。
まとめ
今回は図形をいろいろと描画してみたが、それぞれメソッドが異なっていたり、パラメータが多かったりと少々大変だったと思う。
少々どころか、大変でしたよ。
今日の内容をすべて暗記しておく必要はない。そういうことができるということを知っていれば、都度調べればよい。
そういわれると、気が楽です。
今日はここまでにしよう。次回は画像に文字を書く方法を紹介する。
ありがとうございました~~♪