Pynote

Python、機械学習、画像処理について

OpenCV - 長方形、円、テキストなどを画像に描画する方法

概要

OpenCV で長方形、円、テキストなどを画像に描画する方法を紹介する。

描画系の関数の共通の仕様

図形を描画する関数は thickness 引数を負の値にすると、塗りつぶしになる。
描画は inplace で行われる。つまり、第1引数に渡した画像に直接描画される。
shift でオフセットを指定できる。例えば、line() 関数の場合、始点を pt1、終点を pt2 で指定するが、オフセットを加算して shift + pt1, shift + p2 として描画される。これは ROI の座標で指定して、元の画像に描画する際に使用できる。

矢印を描画する。 (arrowedLine())

img = cv2.arrowedLine(
    img, pt1, pt2, color[, thickness[, line_type[, shift[, tipLength]]]])

引数

  • img: 描画対象の画像 (inplace で描画される)
  • pt1: 始点
  • pt2: 終点
  • color: 色
  • thickness: 太さ
  • line_type: 線の種類
  • shift: オフセット
  • tipLength: 矢印の長さに対する末端の長さ

返り値

  • img: 描画結果
img = np.zeros((300, 300, 3), dtype=np.uint8)
cv2.arrowedLine(img, (50, 50), (250, 250), color=(255, 0, 0), thickness=2)
imshow(img)

出力画像

円を描画する。 (circle())

img = cv2.circle(img, center, radius, color[, thickness[, lineType[, shift]]])

引数

  • img: 描画対象の画像 (inplace で描画される)
  • center: 中心
  • radius: 半径
  • color: 色
  • thickness: 太さ。負の値で塗りつぶし。
  • lineType: 線の種類
  • shift: オフセット

返り値

  • img: 描画結果

塗りつぶさないで円を描画する。

img = np.zeros((300, 300, 3), dtype=np.uint8)
cv2.circle(img, (150, 150), 100, color=(255, 0, 0), thickness=2)
imshow(img)

出力画像

塗りつぶして円を描画する。

img = np.zeros((300, 300, 3), dtype=np.uint8)
cv2.circle(img, (150, 150), 100, color=(255, 0, 0), thickness=-1)
imshow(img)


輪郭を描画する。 (drawContours())

image = cv2.drawContours(image, contours, contourIdx,
                        color[, thickness[, lineType[, hierarchy[, maxLevel[, offset]]]]])

引数

  • image: 描画対象の画像 (inplace で描画される)
  • contours: 輪郭一覧
  • contourIdx: 輪郭一覧のうち、描画する輪郭のインデックス。すべての輪郭を描画する場合は負の値を指定する。
  • color: 色
  • thickness: 線の太さ。負の値で塗りつぶし。
  • lineType: 線の種類
  • hierarchy: 輪郭の階層構造。maxLevel を指定する場合に渡す。
  • maxLevel: 階層の深さいくつまで描画するか。
    • 0の場合は contourIdx で指定した輪郭のみ描画する。
    • 1以上の場合は、指定した深さの輪郭まで描画する。
    • offset: オフセット

返り値

  • image: 描画した画像

入力画像 sample.png

塗りつぶさないですべての輪郭を描画する。

contourIdx=-1 とすると、すべての輪郭を描画する。

# 画像を読み込む。
img = cv2.imread("sample.png")

# グレースケールに変換する。
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 輪郭抽出する。
contours, hierarchy = cv2.findContours(gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# すべての輪郭を描画する。
cv2.drawContours(img, contours, -1, color=(0, 255, 0), thickness=3)
imshow(img)

出力画像

塗りつぶしてすべての輪郭を描画する。

# 画像を読み込む。
img = cv2.imread("sample.png")

# グレースケールに変換する。
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 輪郭抽出する。
contours, hierarchy = cv2.findContours(gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# すべての輪郭を描画する。
cv2.drawContours(img, contours, -1, color=(0, 255, 0), thickness=-1)
imshow(img)

出力画像

指定した輪郭を描画する。

輪郭のリストの contourIdx で指定したインデックスの輪郭のみ描画する。

# 画像を読み込む。
img = cv2.imread("sample.png")

# グレースケールに変換する。
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 輪郭抽出する。
contours, hierarchy = cv2.findContours(gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# すべての輪郭を描画
cv2.drawContours(img, contours, 2, color=(0, 255, 0), thickness=3)
imshow(img)

出力画像

単一の輪郭を描画する。

単一の輪郭を描画するには、リストにする。

# 画像を読み込む。
img = cv2.imread("sample.png")

# グレースケールに変換する。
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 輪郭抽出する。
contours, hierarchy = cv2.findContours(gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# すべての輪郭を描画
cnt = contours[0]
cv2.drawContours(img, [cnt], -1, color=(0, 255, 0), thickness=3)
imshow(img)

出力画像

マーカーを描画する。 (drawMarker())

img = cv2.drawMarker(
    img, position, color[, markerType[, markerSize[, thickness[, line_type]]]])

引数

  • img: 描画対象の画像 (inplace で描画される)
  • position: 位置
  • color: 色
  • markerType: マーカーの種類
  • thickness: 線の太さ。負の値で塗りつぶし。
  • lineType: 線の種類

返り値

  • img: 描画した画像

markerType で指定できる種類

  • cv2.MARKER_CROSS
  • cv2.MARKER_TILTED_CROSS
  • cv2.MARKER_STAR
  • cv2.MARKER_DIAMOND
  • cv2.MARKER_SQUARE
  • cv2.MARKER_TRIANGLE_UP
  • cv2.MARKER_TRIANGLE_DOWN
markers = {
    "cv2.MARKER_CROSS": cv2.MARKER_CROSS,
    "cv2.MARKER_TILTED_CROSS": cv2.MARKER_TILTED_CROSS,
    "cv2.MARKER_STAR": cv2.MARKER_STAR,
    "cv2.MARKER_DIAMOND": cv2.MARKER_DIAMOND,
    "cv2.MARKER_SQUARE": cv2.MARKER_SQUARE,
    "cv2.MARKER_TRIANGLE_UP": cv2.MARKER_TRIANGLE_UP,
    "cv2.MARKER_TRIANGLE_DOWN": cv2.MARKER_TRIANGLE_DOWN,
}

fig = plt.figure(figsize=(10, 10), facecolor="w")

for i, (title, marker) in enumerate(markers.items(), 1):
    img = np.zeros((100, 100, 3), dtype=np.uint8)
    cv2.drawMarker(img, (50, 50), color=(255, 0, 0), markerType=marker, thickness=2)

    ax = fig.add_subplot(3, 3, i)
    ax.set_title(title)
    ax.imshow(img[..., ::-1])
    ax.set_axis_off()

plt.show()

出力画像

楕円を描画する。 (ellipse())

img = cv2.ellipse(img, center, axes, angle, startAngle, endAngle,
                  color[, thickness[, lineType[, shift]])

引数

  • img: 描画対象の画像 (inplace で描画される)
  • center: 中心
  • axes: 長径、短径
  • angle: 回転角度
  • startAngle: 円弧開始角度
  • endAngle: 円弧終了角度
  • color: 色
  • thickness: 太さ。負の値で塗りつぶし。
  • line_type: 線の種類
  • shift: オフセット

返り値

  • img: 描画結果


塗りつぶさないで楕円を描画する。

# 回転角度 45°、円弧の範囲 0° ~ 360°
img = np.zeros((300, 300, 3), dtype=np.uint8)
cv2.ellipse(
    img,
    (150, 150),
    (50, 70),
    angle=45,
    startAngle=0,
    endAngle=360,
    color=(255, 0, 0),
    thickness=2,
)
imshow(img)

出力画像

塗りつぶして楕円を描画する。

# 回転角度 45°、円弧の範囲 0° ~ 360°
img = np.zeros((300, 300, 3), dtype=np.uint8)
cv2.ellipse(
    img,
    (150, 150),
    (50, 70),
    angle=45,
    startAngle=0,
    endAngle=360,
    color=(255, 0, 0),
    thickness=-1,
)
imshow(img)

出力画像

塗りつぶさないで円弧を描画する。

img = np.zeros((300, 300, 3), dtype=np.uint8)
cv2.ellipse(
    img,
    (150, 150),
    (50, 70),
    angle=45,
    startAngle=0,
    endAngle=100,
    color=(255, 0, 0),
    thickness=2,
)
imshow(img)

出力画像

塗りつぶして円弧を描画する。

img = np.zeros((300, 300, 3), dtype=np.uint8)
cv2.ellipse(
    img,
    (150, 150),
    (50, 70),
    angle=45,
    startAngle=0,
    endAngle=100,
    color=(255, 0, 0),
    thickness=-1,
)
imshow(img)

出力画像

凸なポリゴンを描画する。 (fillConvexPoly())

img = cv2.fillConvexPoly(img, points, color[, lineType[, shift]])

引数

  • img: 描画対象の画像 (inplace で描画される)
  • points: 点の一覧。(N, 2) の numpy 配列。
  • color: 色
  • line_type: 線の種類
  • shift: オフセット

返り値

  • img: 描画結果
img = np.zeros((300, 300, 3), dtype=np.uint8)
points = np.array([[100, 100], [120, 180], [190, 250], [270, 120], [220, 50]])

cv2.fillConvexPoly(img, points, color=(0, 255, 0))
imshow(img)

出力画像

ポリゴンを描画する。 (fillPoly())

img = cv2.fillPoly(img, points, color[, lineType[, shift]])

引数

  • img: 描画対象の画像 (inplace で描画される)
  • points: 点の一覧。(1, N, 2) の numpy 配列。
  • color: 色
  • line_type: 線の種類
  • shift: オフセット

返り値

  • img: 描画結果
img = np.zeros((300, 300, 3), dtype=np.uint8)
points = np.array([[100, 50], [120, 180], [50, 250], [270, 120], [220, 50]]).reshape(
    1, -1, 2
)

cv2.fillPoly(img, points, color=(0, 255, 0))
imshow(img)

出力画像

線を描画する。 (line())

img = cv2.line(img, pt1, pt2, color[, thickness[, lineType[, shift]]])

引数

  • img: 描画対象の画像 (inplace で描画される)
  • pt1: 線の始点
  • pt2: 線の終点
  • color: 色
  • thickness: 太さ。負の値で塗りつぶし。
  • line_type: 線の種類
  • shift: オフセット

返り値

  • img: 描画結果
img = np.zeros((300, 300, 3), dtype=np.uint8)
cv2.line(img, (50, 50), (250, 250), color=(255, 0, 0), thickness=2)
imshow(img)

出力画像

ポリゴンの輪郭線を描画する。 (polylines())

img = cv2.polylines(img, pts, isClosed, color[, thickness[, lineType[, shift]]])

引数

  • img: 描画対象の画像 (inplace で描画される)
  • pts: 点の一覧
  • isClosed: ポリゴンが閉じているかどうか
  • color: 色
  • thickness: 太さ。負の値で塗りつぶし。
  • line_type: 線の種類
  • shift: オフセット

返り値

  • img: 描画結果
img = np.zeros((300, 300, 3), dtype=np.uint8)
points = np.array([[100, 50], [120, 180], [50, 250], [270, 120], [220, 50]]).reshape(
    1, -1, 2
)

cv2.polylines(img, points, isClosed=True, color=(0, 255, 0), thickness=2)
imshow(img)

出力画像

テキストを描画する。 (putText())

img = cv2.putText(img, text, org, fontFace, fontScale,
                 color[, thickness[, lineType[, bottomLeftOrigin]]])

引数

  • img: 描画対象の画像 (inplace で描画される)
  • text: テキスト (Ascii 文字のみ対応)
  • org: テキストの描画を開始する左下の点
  • fontFace: フォントの見た目
  • fontScale: フォントの大きさの倍率。1.0 で等倍。
  • color: 色
  • thickness: 線の太さ。
  • line_type: 線の種類
  • bottomLeftOrigin: True の場合、右下が原点。False の場合、左下が原点になる。

返り値

  • img: 描画結果

fontFace で指定できる種類

  • cv2.FONT_HERSHEY_SIMPLEX
  • cv2.FONT_HERSHEY_PLAIN
  • cv2.FONT_HERSHEY_DUPLEX
  • cv2.FONT_HERSHEY_COMPLEX
  • cv2.FONT_HERSHEY_TRIPLEX
  • cv2.FONT_HERSHEY_COMPLEX_SMALL
  • cv2.FONT_HERSHEY_SCRIPT_SIMPLEX
  • cv2.FONT_HERSHEY_SCRIPT_COMPLEX
  • cv2.FONT_ITALIC
font_faces = {
    "cv2.FONT_HERSHEY_SIMPLEX": cv2.FONT_HERSHEY_SIMPLEX,
    "cv2.FONT_HERSHEY_PLAIN": cv2.FONT_HERSHEY_PLAIN,
    "cv2.FONT_HERSHEY_DUPLEX": cv2.FONT_HERSHEY_DUPLEX,
    "cv2.FONT_HERSHEY_COMPLEX": cv2.FONT_HERSHEY_COMPLEX,
    "cv2.FONT_HERSHEY_TRIPLEX": cv2.FONT_HERSHEY_TRIPLEX,
    "cv2.FONT_HERSHEY_COMPLEX_SMALL": cv2.FONT_HERSHEY_COMPLEX_SMALL,
    "cv2.FONT_HERSHEY_SCRIPT_SIMPLEX": cv2.FONT_HERSHEY_SCRIPT_SIMPLEX,
    "cv2.FONT_HERSHEY_SCRIPT_COMPLEX": cv2.FONT_HERSHEY_SCRIPT_COMPLEX,
    "cv2.FONT_ITALIC": cv2.FONT_ITALIC,
}

fig = plt.figure(figsize=(10, 5), facecolor="w")
fig.subplots_adjust(wspace=1.5, hspace=0.6)

for i, (title, font_face) in enumerate(font_faces.items(), 1):
    img = np.full((100, 100, 3), 255, dtype=np.uint8)
    cv2.putText(
        img,
        "TEXT",
        (5, 50),
        fontFace=font_face,
        fontScale=1.0,
        color=(0, 0, 0),
        thickness=2,
        lineType=cv2.LINE_AA,
    )

    ax = fig.add_subplot(3, 3, i)
    ax.set_title(title)
    ax.imshow(img[..., ::-1])
    ax.set_axis_off()

plt.show()

出力画像

注意点として、日本語などの Ascii 文字以外は描画できないため、そのような文字を描画したい場合は Pillow を使う。

www.pynote.info

長方形を描画する。 (rectangle())

img = cv2.rectangle(img, center, radius, color[, thickness[, lineType[, shift]]])

引数

  • img: 描画対象の画像 (inplace で描画される)
  • pt1: 長方形の左上の点
  • pt2: 長方形の右下の点
  • color: 色
  • thickness: 太さ。負の値で塗りつぶし。
  • line_type: 線の種類
  • shift: オフセット

返り値

  • img: 描画結果

塗りつぶさないで長方形を描画する。

img = np.zeros((300, 300, 3), dtype=np.uint8)
cv2.rectangle(img, (50, 50), (250, 250), color=(255, 0, 0), thickness=2)
imshow(img)

出力画像

塗りつぶして長方形を描画する。

img = np.zeros((300, 300, 3), dtype=np.uint8)
cv2.rectangle(img, (50, 50), (250, 250), color=(255, 0, 0), thickness=-1)
imshow(img)

出力画像

線を長方形の範囲に収まるようにクリップする。 (clipLine())

この関数は次の目的で使える。

  • 線が一部でも長方形に含まれるかどうかを調べる。
  • 線のうち、長方形に含まれる範囲を調べる。
retval, pt1, pt2 = cv2.clipLine(imgRect, pt1, pt2)

引数

  • imgRect: 長方形 (TopLeft.x, TopLeft.y, ImageWidth, ImageHeight)
  • pt1: 線の始点
  • pt2: 線の終点

返り値

  • retval: 線が一部でも長方形に含まれるかどうか
  • pt1: 線の始点
  • pt2: 線の終点
img = np.zeros((300, 300, 3), dtype=np.uint8)
h, w = img.shape[:2]

retval, pt1, pt2 = cv2.clipLine((0, 0, w, h), (-100, 100), (100, 100))

print("線が長方形に含まれるかどうか", retval)
# 線が長方形に含まれるかどうか True
print("クリップ後の始点: {}, 終点: {}".format(pt1, pt2))
# クリップ後の始点: (0, 100), 終点: (100, 100)

楕円の円弧上の点を作成する。 (ellipse2Poly())

pts = cv2.ellipse2Poly(center, axes, angle, arcStart, arcEnd, delta)

引数

  • img: 描画対象の画像 (inplace で描画される)
  • center: 中心
  • axes: 長径、短径
  • angle: 回転角度
  • arcStart: 円弧開始角度
  • arcEnd: 円弧終了角度
  • delta: 点同士の角度の間隔

返り値

  • img: 描画結果
pts = cv2.ellipse2Poly((150, 150), (50, 70), 45, 0, 200, 5)
print(pts)
[[185 185]
 [181 190]
 [176 193]
 [171 197]
 [166 200]
 [161 203]
 [156 205]
 [151 207]
 [145 209]
 [140 210]
 [135 211]
 [130 211]
 [125 211]
 [120 210]
 [116 209]
 [111 207]
 [107 205]
 [104 202]
 [101 199]
 [ 98 196]
 [ 95 193]
 [ 93 189]
 [ 91 184]
 [ 90 180]
 [ 89 175]
 [ 89 170]
 [ 89 165]
 [ 90 160]
 [ 91 155]
 [ 93 149]
 [ 95 144]
 [ 97 139]
 [100 134]
 [103 129]
 [107 124]
 [110 119]
 [115 115]
 [119 110]
 [124 107]
 [129 103]
 [134 100]]