note.nkmk.me

Python, OpenCVで図形描画(線、長方形、円、矢印、文字など)

Date: 2018-01-31 / tags: Python, OpenCV, 画像処理

OpenCVには線分や四角(長方形、正方形)、円、楕円、円弧、矢印、マーカー、文字など、様々な図形を描画する関数が用意されている。物体を検出した位置を図示したりする際などに便利。

次の画像のように図形を描画できる。サンプルコードは最後に示す。

OpenCV draw function sample

公式のドキュメント(英語)は以下。

今回の例で用いているOpenCVのバージョンは3.3.02.x系だと異なる部分があるかもしれない。

なお、Pillowでも図形を描画することができる。

スポンサーリンク

OpenCVでの図形描画の共通の引数

OpenCVの様々な図形描画関数にはいくつかの共通の引数がある。

コードの例とその出力結果は最後に示す。

画像: img

図形を描画する画像。NumPyの配列ndarray。このオブジェクト自体が変更される。

座標: pt1, pt2 / centerなど

線の両端の座標や円の中心などを(x, y)で指定する。単位はピクセル、原点は左上。

色: color

カラー画像でBGRの場合は(Blue, Green, Red)で指定する。OpenCVのimread()で画像ファイルを読み込んだ場合はBGRの並びになっている。

線の太さ、内部の塗りつぶし: thickness

線の太さは引数thicknessで指定する。単位はピクセル。

長方形や円の場合、-1などの負の値を指定すると内部が塗りつぶされる。

線の色と塗りつぶし色を変えたい場合は、塗りつぶし図形を描画して同じ位置にさらに枠線を描画する。一度に指定することは出来ない。

線の種類: lineType

線を描画するアルゴリズムの種類を以下の定数から指定する。

  • 4連結: cv2.LINE_4
  • 8連結: cv2.LINE_8(デフォルト)
  • アンチエイリアス: cv2.LINE_AA

なお、太い線の端や太い線の長方形の角は、丸く描画される。

座標の小数部分のビット数: shift

座標の小数部分のビット数を整数で指定する。デフォルトは0。

shiftを指定すると、座標が(x*2^(-shift), y*2^(-shift))として計算される。

例えば、座標を(100, 101)shift=1とすると、実際に描画に用いられる座標は(50, 50.5)となり、サブピクセル精度で座標を指定することができる。

アンチエイリアス前提なので、lineType=cv2.LINE_AAとする必要がある。

np.full()を使って全面が128のグレー画像を生成し、その上に図形を描画する。

もちろん、cv2.imread()で既存の画像ファイルを読み込めば、画像の上に図形を描画できる。

例として、長方形cv2.rectangle()cv2.line()を使って、様々なthicknesslineTypeshiftの値で描画する。

shiftの違いは拡大しないと分からないかもしれない。

import cv2
import numpy as np

print(cv2.__version__)
# 3.3.0

img = np.full((210, 425, 3), 128, dtype=np.uint8)

cv2.rectangle(img, (50, 10), (125, 60), (255, 0, 0))
cv2.rectangle(img, (50, 80), (125, 130), (0, 255, 0), thickness=-1)
cv2.rectangle(img, (50, 150), (125, 200), (0, 0, 255), thickness=-1)
cv2.rectangle(img, (50, 150), (125, 200), (255, 255, 0))

cv2.rectangle(img, (175, 10), (250, 60), (255, 255, 255), thickness=8, lineType=cv2.LINE_4)
cv2.line(img, (175, 10), (250, 60), (0, 0, 0), thickness=1, lineType=cv2.LINE_4)
cv2.rectangle(img, (175, 80), (250, 130), (255, 255, 255), thickness=8, lineType=cv2.LINE_8)
cv2.line(img, (175, 80), (250, 130), (0, 0, 0), thickness=1, lineType=cv2.LINE_8)
cv2.rectangle(img, (175, 150), (250, 200), (255, 255, 255), thickness=8, lineType=cv2.LINE_AA)
cv2.line(img, (175, 150), (250, 200), (0, 0, 0), thickness=1, lineType=cv2.LINE_AA)

cv2.rectangle(img, (600, 20), (750, 120), (0, 0, 0), lineType=cv2.LINE_AA, shift=1)
cv2.rectangle(img, (601, 160), (751, 260), (0, 0, 0), lineType=cv2.LINE_AA, shift=1)
cv2.rectangle(img, (602, 300), (752, 400), (0, 0, 0), lineType=cv2.LINE_AA, shift=1)

cv2.imwrite('data/dst/opencv_draw_argument.png', img)

OpenCV draw function argument

戻り値

cv2.rectangle()cv2.line()のような図形描画の関数は、戻り値としてNumPyの配列ndarrayを返す。

入力画像img自体に図形が描画され変更されるが、それと同じオブジェクトが返される。

Jupyter Notebookのセルの末尾で図形描画関数を実行するとndarrayの中身が表示されるので邪魔くさい。なぜこのような実装になっているかはよく分からない。

img_rect = cv2.rectangle(img, (10, 10), (110, 60), (255, 0, 0))
print(img is img_rect)
# True

線分を描画: cv2.line()

線分を描画する関数はcv2.line()

cv2.line(img, pt1, pt2, color, thickness=1, lineType=cv2.LINE_8, shift=0)
cv2.line(img, (50, 10), (125, 60), (255, 0, 0))
cv2.line(img, (50, 60), (125, 10), (0, 255, 255), thickness=4, lineType=cv2.LINE_AA)

矢印を描画: cv2.arrowedLine()

矢印を描画する関数はcv2.arrowedLine()

cv2.arrowedLine(img, pt1, pt2, color, thickness=1, lineType=cv2.LINE_8, shift=0, tipLength=0.1)

引数tipLengthは矢の先の部分の長さ。全体の長さに対する比で表し、デフォルトは0.1

cv2.arrowedLine(img, (50, 80), (125, 130), (0, 255, 0), thickness=4)
cv2.arrowedLine(img, (50, 130), (125, 80), (255, 0, 255), tipLength=0.3)

長方形を描画: cv2.rectangle()

長方形を描画する関数はcv2.rectangle()

cv2.rectangle(img, pt1, pt2, color, thickness=1, lineType=cv2.LINE_8, shift=0)
cv2.rectangle(img, (50, 150), (125, 200), (255, 255, 0))

円を描画: cv2.circle()

円を描画する関数はcv2.circle()

cv2.circle(img, center, radius, color, thickness=1, lineType=cv2.LINE_8, shift=0)

中心の座標centerと半径radiusを指定する。

cv2.circle(img, (190, 35), 15, (255, 255, 255), thickness=-1)
cv2.circle(img, (240, 35), 20, (0, 0, 0), thickness=3, lineType=cv2.LINE_AA)

楕円を描画: cv2.ellipse()

楕円を描画する関数はcv2.ellipse()

cv2.ellipse(img, box, color, thickness=1, lineType=cv2.LINE_8)

引数box(center, axes, angle)で表し、center(x, y)axes(横方向直径, 縦方向直径)、回転角度angleはx軸方向を0度として時計回りに度で指定する。

cv2.ellipse(img, ((190, 105), (20, 50), 0), (255, 255, 255))
cv2.ellipse(img, ((240, 105), (20, 50), 30), (0, 0, 0), thickness=-1)

円弧を描画: cv2.ellipse()

同じくcv2.ellipse()で円弧を描画することもできる。

cv2.ellipse(img, center, axes, angle, startAngle, endAngle, color, thickness=1, lineType=cv2.LINE_8, shift=0)

引数が異なり、axes(横方向半径, 縦方向半径)

startAngleは円弧の開始角度、endAngleは終了角度で、どちらも回転角度angleが基準。

cv2.ellipse(img, (190, 175), (10, 25), 0, 0, 270, (255, 255, 255))
cv2.ellipse(img, (240, 175), (10, 25), 30, 0, 270, (0, 0, 0), thickness=-1)

マーカーを描画: cv2.drawMarker()

マーカーを描画する関数はcv2.drawMarker()

ポイントを図示したい場合は、cv2.rectangle()で小さい四角を描くよりもマーカーのほうが便利。

cv2.drawMarker(img, position, color, markerType=cv2.MARKER_CROSS, markerSize=20, thickness=1, line_type=cv2.LINE_8)

引数markerTypeで指定するマーカーの種類は以下のドキュメントを参照。十字や四角、三角など。

cv2.drawMarker(img, (300, 20), (255, 0, 0))
cv2.drawMarker(img, (337, 20), (0, 255, 0), markerType=cv2.MARKER_TILTED_CROSS, markerSize=15)
cv2.drawMarker(img, (375, 20), (0, 0, 255), markerType=cv2.MARKER_STAR, markerSize=10)

cv2.drawMarker(img, (300, 50), (0, 255, 255), markerType=cv2.MARKER_DIAMOND)
cv2.drawMarker(img, (337, 50), (255, 0, 255), markerType=cv2.MARKER_SQUARE, markerSize=15)
cv2.drawMarker(img, (375, 50), (255, 255, 0), markerType=cv2.MARKER_TRIANGLE_UP, markerSize=10)

折れ線、多角形を描画: cv2.polylines(), cv2.fillPoly()

折れ線や多角形を描画する関数はcv2.polylines()

cv2.polylines(img, pts, isClosed, color, thickness=1, lineType=cv2.LINE_8, shift=0)

引数isClosedで開始点と終了点を結ぶかどうかを指定する。

引数ptsには各点の座標をndarray形式で指定する。[]で囲んで指定する。

pts = np.array(((300, 80), (300, 130), (335, 130)))
cv2.polylines(img, [pts], True, (255, 255, 255), thickness=2)

cv2.polylines()isCLosed=True, thickness=-1としてもエラーになり、内部を塗りつぶす事はできない。

内部を塗りつぶした多角形を描画したい場合は、cv2.fillPoly()を使う。

cv2.fillPoly(img, pts, color, lineType=cv2.LINE_8, shift=0)
pts = np.array(((335, 80), (375, 80), (375, 130)))
cv2.fillPoly(img, [pts], (0, 0, 0))

文字列を描画: cv2.putText()

文字列を描画する関数はcv2.putText()

cv.putText(img, text, org, fontFace, fontScale, color, thickness=1, lineType=cv2.LINE_8)

引数textは描画する文字列、orgは文字列の左下座標、fontFaceはフォントの種類、fontScaleは縮尺(1.0が標準)。

fontFaceに指定できるフォントは以下のドキュメントを参照。Hershey Fontsというシンプルなフォントが使える。日本語は使えない。

cv2.putText(img, 'nkmk', (300, 170), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (255, 255, 255), thickness=2)
cv2.putText(img, 'nkmk', (300, 195), cv2.FONT_HERSHEY_COMPLEX, 0.8, (0, 0, 0), lineType=cv2.LINE_AA)

サンプルコードと出力結果

img = np.full((210, 425, 3), 128, dtype=np.uint8)

cv2.line(img, (50, 10), (125, 60), (255, 0, 0))
cv2.line(img, (50, 60), (125, 10), (0, 255, 255), thickness=4, lineType=cv2.LINE_AA)

cv2.arrowedLine(img, (50, 80), (125, 130), (0, 255, 0), thickness=4)
cv2.arrowedLine(img, (50, 130), (125, 80), (255, 0, 255), tipLength=0.3)

cv2.rectangle(img, (50, 150), (125, 200), (255, 255, 0))

cv2.circle(img, (190, 35), 15, (255, 255, 255), thickness=-1)
cv2.circle(img, (240, 35), 20, (0, 0, 0), thickness=3, lineType=cv2.LINE_AA)

cv2.ellipse(img, ((190, 105), (20, 50), 0), (255, 255, 255))
cv2.ellipse(img, ((240, 105), (20, 50), 30), (0, 0, 0), thickness=-1)

cv2.ellipse(img, (190, 175), (10, 25), 0, 0, 270, (255, 255, 255))
cv2.ellipse(img, (240, 175), (10, 25), 30, 0, 270, (0, 0, 0), thickness=-1)

cv2.drawMarker(img, (300, 20), (255, 0, 0))
cv2.drawMarker(img, (337, 20), (0, 255, 0), markerType=cv2.MARKER_TILTED_CROSS, markerSize=15)
cv2.drawMarker(img, (375, 20), (0, 0, 255), markerType=cv2.MARKER_STAR, markerSize=10)

cv2.drawMarker(img, (300, 50), (0, 255, 255), markerType=cv2.MARKER_DIAMOND)
cv2.drawMarker(img, (337, 50), (255, 0, 255), markerType=cv2.MARKER_SQUARE, markerSize=15)
cv2.drawMarker(img, (375, 50), (255, 255, 0), markerType=cv2.MARKER_TRIANGLE_UP, markerSize=10)

pts = np.array(((300, 80), (300, 130), (335, 130)))
cv2.polylines(img, [pts], True, (255, 255, 255), thickness=2)

pts = np.array(((335, 80), (375, 80), (375, 130)))
cv2.fillPoly(img, [pts], (0, 0, 0))

cv2.putText(img, 'nkmk', (300, 170), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (255, 255, 255), thickness=2)
cv2.putText(img, 'nkmk', (300, 195), cv2.FONT_HERSHEY_COMPLEX, 0.8, (0, 0, 0), lineType=cv2.LINE_AA)

cv2.imwrite('data/dst/opencv_draw_etc.png', img)

OpenCV draw function sample

スポンサーリンク
シェア
このエントリーをはてなブックマークに追加

関連カテゴリー

関連記事