ITや趣味など気軽に投稿しています。

【Python】OpenCVで画像に図形を描画する

今回は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)の形式で指定する。

座標って、具体的に画像内の座標はどうやって判断すればいいです?

座標の単位はピクセルで、画像の左上が原点になる。
それをヒントに、具体的な座標の指定は勘だな。

勘、、ですか。

うむ。機械的に座標を算出できる場合はよいが、目分量で図形を描きたいときは勘だ。

OpenCV の座標体系
OpenCV における画像の原点

色(color)

カラー画像を扱う場合はBGR(Blue, Green, Red)の形式で指定する。
なお、他の色空間に変換している場合はその順番に従うことになる。

黒だったら(0,0,0,)、白だったら(255,255,255)のあれですね

線の太さ、塗りつぶし(thickness)

thicknessは線の太さを指定するパラメータである。単位はピクセル。

「吾輩は猫である」みたいな口調。

からかっているのか?
なお、長方形や円だと、thicknessを負の値(-1など)にすることで図形の塗りつぶしができる。

いや、からかっているわけでは、、

線の種類(lineType)

lineTypeでは線を描画するアルゴリズムを以下から指定できる。

アルゴリズム説明
cv2.LINE_44連結
cv2.LINE_88連結(デフォルト)
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)
cv2.line(imageLine, start, end, (255, 255, 0), thickness=3)

この例では、ある座標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)
cv2.circle(imageCircle, circle_center, radius, (0, 0, 255), thickness=3, lineType=cv2.LINE_AA)

塗りつぶしの円

thicknessを負の値にすると、図形が塗りつぶされるんだったな。

cv2.circle(imageCircle, circle_center, radius, (0, 0, 255), thickness=-1, lineType=cv2.LINE_AA)
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)
cv2.rectangle(imageRectangle, start_point, end_point, (0, 0, 255), thickness= 3, lineType=cv2.LINE_8)

pt1では長方形の(top, left)の位置を指定し、pt2では(bottom, right)を指定する。

(top, left)、、よくわかんないです。

長方形は、対角の端2点が定まれば形が決まる。その2点を指定するんだよ。

↑top
↓bottom
←left
→right

なるほど、対角の2点が決まれば上下左右の座標がわかりますからね。

ちなみに、長方形もthickness=-1にすると塗りつぶされるのだ。

cv2.rectangle(imageRectangle, start_point, end_point, (0, 0, 255), thickness= -1, lineType=cv2.LINE_8)
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)
cv2.ellipse(imageEllipse, ellipse_center, axis1, 0, 0, 360, (255, 0, 0), thickness=3)

引数

引数説明
imgインプット画像
center楕円の中心座標
axes(横方向半径、縦方向半径)
angle楕円の回転角度
startAngle楕円の描画開始角度
endAngle楕円の描画終了角度
color楕円の色
thickness楕円の太さ
lineType線描画の種類
shift座標の小数部分のビット数

axes

axesは各軸方向への長さだが、(x軸方向,y軸方向)の順で長さを指定する。

例だと(100,50)でx軸方向のほうがy軸方向より長いので、横長の楕円になってるんですね。

その通り、逆にy軸方向を長くすると縦長になるし、両方同じ長さにすると円になる。

はえー、なるほどです。

angle

angleは楕円のを回転させるときの角度である。例えば例にある横長の楕円も、角度を90°とすれば縦長の楕円になる。

ちょっと、axesangleが混同します、、

確かにややこしいが、楕円のサイズを決めるのがaxes、楕円を回転させたときの位置を決めるのがangleと考えるといい。

startAngle, endangle

続いてstartAngleendangleだが、これは楕円を描画を開始する角度と終了する角度を示す。
線を描画する角度を0~360°(または0~-360°)までで指定する。

例えば、半分の楕円を描くとか?

その通り。開始を0°、終了を360°とした場合は全円を描画できる。終了を180°とした場合は半円を描画できるといった具合だ。

まとめ

今回は図形をいろいろと描画してみたが、それぞれメソッドが異なっていたり、パラメータが多かったりと少々大変だったと思う。

少々どころか、大変でしたよ。

今日の内容をすべて暗記しておく必要はない。そういうことができるということを知っていれば、都度調べればよい。

そういわれると、気が楽です。

今日はここまでにしよう。次回は画像に文字を書く方法を紹介する。

ありがとうございました~~♪