#はじめに
opencvのマウスイベントを使いこなしていなかったので、カメラのライブ映像にマウスで描画する実験をしたメモ。
記述言語はpython。
#pythonプラグラム
##参考文献
以下の情報を参考にして、プログラムを作成した。
オリジナルなのは、以下。
- マウスでクリックした座標を辞書型変数に格納して、履歴を残せるようにした
- 右クリックで直前の座標を削除できるようにした
- 画像と座標を保存できるようにした
##ライブラリ
opencvがインストールされている前提。anaconda環境なので、condaでインストールする。バージョンは3.4.2だった。
conda install opencv
##プログラム
opencvとjsonをインポートして、変数を初期化する。座標はptに格納する。
import cv2
import json
#データの初期化
pt = {}
m = 0
n = 0
p = 0
マウスイベントに対するコールバック関数を記述する。今回はシンプルに、左クリックでマーカーと直線を描画していく。
#マウスの操作があるとき呼ばれる関数
def callback(event, x, y, flags, param):
global pt, m, n, p
#マウスの左ボタンがクリックされたとき
if event == cv2.EVENT_LBUTTONDOWN:
m = n
pt[n] = (x, y)
#print(n)
#print(pt)
n = n + 1
p = p + 1
#マウスの右ボタンがクリックされたとき
if event == cv2.EVENT_RBUTTONDOWN and n > 0:
#print(m, n)
#print(pt[m])
pt.pop(m)
m = m - 1
n = n - 1
p = p + 1
カメラウィンドウやマウスのコールバック関数をセットする。
#カメラを設定
cap = cv2.VideoCapture(0)
#ウィンドウの名前を設定
cv2.namedWindow("img", cv2.WINDOW_NORMAL)
#コールバック関数の設定
cv2.setMouseCallback("img", callback)
#処理開始のプロンプト
print(">>> Start !!")
カメラのライブ画像を読み込んで描画する。画像ファイルの場合は ret, frame = cap.read() はコメントアウトして、 frame = cv2.imread("lena.jpg") を有効化する。
操作とキーコマンドは以下。
- 左クリックするごとに、描画が進行
- 右クリックすると、直前の描画を削除
- sキーで画像と座標をファイルに保存(画像は連番ファイル)
- cキーで座標履歴クリア(描画は辞書型変数ptに基づくので、描画もクリアされる)
- ESCキーで終了
#処理ループ
while True:
#カメラ画像を読み込む
ret, frame = cap.read()
#画像ファイルを読み込む
#frame = cv2.imread("lena.jpg")
if n >= 1:
cv2.drawMarker(frame, pt[m], (0, 255, 255), markerType=cv2.MARKER_TILTED_CROSS, markerSize=15)
if n >= 2:
for i in range(1, n):
cv2.line(frame, pt[i - 1], pt[i], (0, 255, 255), 1)
cv2.imshow("img", frame)
k = cv2.waitKey(1)
#Escキーを押すと終了
if k == 27:
print(">>> Exit")
break
#cを押すと描画クリア(ptを初期化)
elif k == ord("c"):
print(">>> Clear Coordinates.")
pt.clear()
m = 0
n = 0
#sを押すと画像を保存
elif k == ord("s"):
print(">>> Save Image and Coordinates.")
str = "painted{no:03}.png".format(no = p)
cv2.imwrite(str, frame)
with open("painted.json","w") as f:
json.dump(pt, f, indent = 4)
cap.release()
cv2.destroyAllWindows()
##プログラム全体
もう少しスマートにかけそうなところもあるが、やりたいことはできたのでこれで良しとする。
import cv2
import json
#データの初期化
pt = {}
m = 0
n = 0
p = 0
#マウスの操作があるとき呼ばれる関数
def callback(event, x, y, flags, param):
global pt, m, n, p
#マウスの左ボタンがクリックされたとき
if event == cv2.EVENT_LBUTTONDOWN:
m = n
pt[n] = (x, y)
#print(n)
#print(pt)
n = n + 1
p = p + 1
#マウスの右ボタンがクリックされたとき
if event == cv2.EVENT_RBUTTONDOWN and n > 0:
#print(m, n)
#print(pt[m])
pt.pop(m)
m = m - 1
n = n - 1
p = p + 1
#カメラを設定
cap = cv2.VideoCapture(0)
#ウィンドウの名前を設定
cv2.namedWindow("img", cv2.WINDOW_NORMAL)
#コールバック関数の設定
cv2.setMouseCallback("img", callback)
#処理開始のプロンプト
print(">>> Start !!")
#処理ループ
while True:
#カメラ画像を読み込む
ret, frame = cap.read()
#画像ファイルを読み込む
#frame = cv2.imread("lena.jpg")
if n >= 1:
cv2.drawMarker(frame, pt[m], (0, 255, 255), markerType=cv2.MARKER_TILTED_CROSS, markerSize=15)
if n >= 2:
for i in range(1, n):
cv2.line(frame, pt[i - 1], pt[i], (0, 255, 255), 1)
cv2.imshow("img", frame)
k = cv2.waitKey(1)
#Escキーを押すと終了
if k == 27:
print(">>> Exit")
break
#cを押すと描画クリア(ptを初期化)
elif k == ord("c"):
print(">>> Clear Coordinates.")
pt.clear()
m = 0
n = 0
#sを押すと画像を保存
elif k == ord("s"):
print(">>> Save Image and Coordinates.")
str = "painted{no:03}.png".format(no = p)
cv2.imwrite(str, frame)
with open("painted.json","w") as f:
json.dump(pt, f, indent = 4)
cap.release()
cv2.destroyAllWindows()
##実行結果
思ったようにできた。これは画像を使った場合で、ffmpegで連番画像ファイルをgifアニメーションにした。
##おまけ
gifアニメーションの生成コマンド
ffmpeg -i painted%03d.png -vf palettegen palette.png
ffmpeg -f image2 -r 1 -i painted%03d.png -i palette.png -filter_complex paletteuse anim.gif