PythonとOpenCVを使えば図形描画も簡単です。ただ絵を描くだけだとつまらないので、ここでは線や円、四角形の描画の練習も兼ねて、GIF動画を作る所までを紹介します。
こんにちは。wat(@watlablog)です。プログラム的にちょっとした図形描画を覚えておくと結構便利!ここではOpenCVの図形描画を練習します!
この記事の目標
作成する動画
この記事ではOpenCVの図形描画コードを練習するために、以下のGIF動画を作成します。
この絵に特に意味はありませんが、一つの画像の中で線、四角、円といった各図形の引数を関連付けてコマを作らないと動画は完成しませんので、これくらいの事ができるようになれば、ある程度自由に描画できるようになると思います。
この動画は何に使う?
一体なぜこんな動画を作り始めたか、疑問に思う方もいらっしゃると思いますが、特に深い意味はありません。
この後画像のトラッキングコードを試したかったのですが、その目的で利用可能な動きのあるフリー素材がなかなか見つからず、無いなら作ってしまえというノリだけで作りました。
そのためこの記事はその前段階という事です。
動作環境
PC
本記事のコードは以下のWindows機とMac機の両方で確認しています。
Windows | OS | Windows10 64bit |
---|---|---|
CPU | 2.4[GHz] | |
メモリ | 4[GB] |
Mac | OS | macOS Catalina 10.15.7 |
---|---|---|
CPU | 1.4[GHz] | |
メモリ | 8[GB] |
Python
Python関連のバージョンは次の通りです。OpenCVを使って図形描画を行いますが、計算やGIF作成にはNumpyやPillowも使っています。
Python | Python 3.7.7 |
---|---|
PyCharm (IDE) | PyCharm CE 2020.1 |
Numpy | 1.19.0 |
opencv-python | 4.2.0.34 |
pillow | 7.1.2 |
OpenCVによる図形描画の基礎コード
ここではいきなり動画まで作らず、基礎的な描画コードを試していきます。
とは言え、こちらのOpenCVチュートリアルの方をまずはご覧頂き、ここではさらっと済ませます。
線を描画する
線の描画は、
cv2.line(描画する画像, 始点, 終点, 色)で行います。オプションでthickness(線の太さ)等を設定する事が可能です。
以下のコードは背景画像を適当な色で生成し、背景画像に対して線描画を行なっているPythonコード例です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import numpy as np import cv2 # 指定サイズと色で背景画像を生成 img_w = 600 img_h = 400 img = np.full((img_h, img_w, 3), 100, dtype=np.uint8) # 始点、終点、色(BGR) start = (100,100) end = (400, 300) color = (255, 0, 0) # 線を描画 cv2.line(img, start, end, color, thickness=5) # 画像表示 cv2.imshow('img', img) cv2.waitKey(0) cv2.destroyAllWindows() |
こんな感じで描画可能です。
四角形を描画する
四角形は線と同様の引数で、
cv2.rectangle(描画する画像, 始点, 終点, 色)で描画します。
thickness=-1と負値にする事で、内部を塗り潰す事も可能です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import numpy as np import cv2 # 指定サイズと色で背景画像を生成 img_w = 600 img_h = 400 img = np.full((img_h, img_w, 3), 100, dtype=np.uint8) # 始点、終点、色(BGR) start = (100,100) end = (400, 300) color = (255, 0, 0) # 四角形を描画 cv2.rectangle(img, start, end, color, thickness=-1) # 画像表示 cv2.imshow('img', img) cv2.waitKey(0) cv2.destroyAllWindows() |
下図は始点と終点で作成した四角形の中身を塗り潰してみた結果です。
円を描画する
円は中心座標と半径を使って、
cv2.circle(描画する画像, 中心点, 半径, 色)で描きます。thicknessの考え方は四角形と同様です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import numpy as np import cv2 # 指定サイズと色で背景画像を生成 img_w = 600 img_h = 400 img = np.full((img_h, img_w, 3), 100, dtype=np.uint8) # 始点、終点、色(BGR) center = (300,200) radius = 100 color = (255, 0, 0) # 円を描画 cv2.circle(img, center, radius, (255, 0, 0), thickness=5) # 画像表示 cv2.imshow('img', img) cv2.waitKey(0) cv2.destroyAllWindows() |
下図が結果です。今回はここまでの内容で動画を作成していきます。
OpenCVで図形描画した画像を動画にするコード
以下のコードが冒頭で紹介した動画を生成するコードです。
構成は「Pythonで複数画像からGIFを作る時に便利な処理まとめ」で紹介した内容とほぼ同じですが、inputというフォルダに図形描画済み画像を保存していき、別関数でanimation.gifというGIF動画を作っています。
何かと便利なので僕は毎回この方法で動画を作っています。
肝心の図形ですが、四角形の座標を移動させるのと連動して円と線も移動するように関連付けています。線は円の中心で回転させるように式で書いているのがみそです(車輪をイメージしていますが特に意味はない)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
import numpy as np import cv2 from PIL import Image import os import glob # GIFアニメーションを作成 def create_gif(in_dir, out_filename): path_list = sorted(glob.glob(os.path.join(*[in_dir, '*']))) # ファイルパスをソートしてリストする imgs = [] # 画像をappendするための空配列を定義 # ファイルのフルパスからファイル名と拡張子を抽出 for i in range(len(path_list)): img = Image.open(path_list[i]) # 画像ファイルを1つずつ開く imgs.append(img) # 画像をappendで配列に格納していく # appendした画像配列をGIFにする。durationで持続時間、loopでループ数を指定可能。 imgs[0].save(out_filename, save_all=True, append_images=imgs[1:], optimize=False, duration=100, loop=0) # 画像を複数保存する関数 def plot_img_generator(dir, frame): # 初期値 rec_x = 100 # 四角形のx座標中心 rec_y = 150 # 四角形のy座標中心 rec_wx = 25 # 四角形の幅 rec_wy = 100 # 四角形の高さ cir_r = 80 # 円の半径 angle = 0 # 線の位相角 dx = 20 # 1コマ当たりのx(変位)増分 da = 20 # 1コマ当たりのa(角度)増分 # 各フレームで速度を足しながら画像を作っていくループ for i in range(frame): print(i) # 現在のフレーム数をコンソールに表示 # 毎ループで背景画像をリセット img_w = 1000 img_h = 400 img = np.full((img_h, img_w, 3), 100, dtype=np.uint8) # 値の更新(画像の端に到達したら折り返す仕様) if rec_x + dx >= img_w: dx = -dx da = -da elif rec_x + dx <= 0: dx = -dx da = -da rec_x = rec_x + dx cir_cx = rec_x cir_cy = rec_y + rec_wy angle = angle + da cir_dx = int(cir_r * np.cos(np.deg2rad(angle))) cir_dy = int(cir_r * np.sin(np.deg2rad(angle))) # 描画(各図形が関連) cv2.rectangle(img, (rec_x - rec_wx, rec_y - rec_wy), (rec_x + rec_wx, rec_y + rec_wy), (0, 255, 0), thickness=-1) cv2.circle(img, (cir_cx, cir_cy), cir_r, (255, 0, 0), thickness=5) cv2.line(img, (cir_cx, cir_cy), (cir_cx + cir_dx, cir_cy + cir_dy), (0, 0, 255), thickness=5) # dirフォルダが無い時に新規作成 if os.path.exists(dir): pass else: os.mkdir(dir) # 画像保存パスを準備 path = os.path.join(*[dir, str("{:05}".format(i)) + '.png']) # 画像を保存 cv2.imwrite(path, img) # GIFアニメーションを作成する関数を実行する create_gif(in_dir='input', out_filename='animation.gif') # 画像を生成して動画にする関数を実行(frame回数分の動画ができる) plot_img_generator(dir='input', frame=100) |
上記コードを実行する事で冒頭の動画が保存されます。多分実行時間は一瞬と思います。
まとめ
またつまらぬものを作ってしまった…。
今回はチュートリアルにもある描画機能を使ってみたという事と、各引数に関連を付けて一つのオブジェクトとして動作するように動画を作成するという内容でした。
次回この動画を使ってトラッキングをする予定です。
図形の描画は画像内にちょっとした指示を入れる時にも重宝します!是非使ってみて下さい!
Twitterでも関連情報をつぶやいているので、wat(@watlablog)のフォローお待ちしています!