概要
手を使わずにマウスカーソルを操作したいけど、視線や脳波などは難しそう・・・
ARマーカーを使って顔の向きからマウスカーソルを操作できないかな・・・
そう思い、以下のようなプログラムを作成してみました。
- おでこに貼り付けたARマーカーをPC内蔵カメラで読み取る
- ARマーカーの4隅の座標からマウスカーソルの移動先を推定し移動させる
結論としては、「思ったより制御できるてるな」という感触です。
もしご興味ありましたら、ソースコードを掲載していますので是非お試しください。
(※おでこにARマーカーを貼り付けるのは、ちょっと間抜けな感じもありますが笑)
ソースコードと使い方
依存ライブラリ
以下のライブラリを使用しています。
未インストールの場合は下記の通りインストールしてください。
# OpenCVのインストール
pip install opencv-contrib-python
# PyCaretのインストール
pip install pycaret
ソースコード
# -*- coding: utf-8 -*-
import cv2
import sys
import random
import numpy as np
import pyautogui
import pandas as pd
from pycaret.regression import *
import tkinter as tk
TkRoot = tk.Tk()
display_width = TkRoot.winfo_screenwidth()
display_height = TkRoot.winfo_screenheight()
aruco = cv2.aruco
dictionary = aruco.getPredefinedDictionary(aruco.DICT_4X4_50)
df = pd.DataFrame()
COLUMNS = ['MX_1', 'MY_1', 'MX_2', 'MY_2', 'MX_3', 'MY_3', 'MX_4', 'MY_4', 'X', 'Y']
LIST_EXP = ['MX_1', 'MY_1', 'MX_2', 'MY_2', 'MX_3', 'MY_3', 'MX_4', 'MY_4']
N_TRAIN = 50
WIDTH_TRAIN_MARKER = 20
AVG_WINDOW=6
def train():
list_x=[]
list_y=[]
df = pd.DataFrame()
cap = cv2.VideoCapture(0)
cv2.namedWindow('train_mode', cv2.WINDOW_NORMAL)
cv2.setWindowProperty('train_mode', cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)
target_x = WIDTH_TRAIN_MARKER
target_y = WIDTH_TRAIN_MARKER
count = 0
while True:
ret, frame = cap.read()
img = cv2.resize(frame,(display_width,display_height))
cv2.line(img, (target_x-WIDTH_TRAIN_MARKER, target_y), (target_x+WIDTH_TRAIN_MARKER, target_y), (0, 0, 0), thickness=3)
cv2.line(img, (target_x, target_y-WIDTH_TRAIN_MARKER), (target_x, target_y+WIDTH_TRAIN_MARKER), (0, 0, 0), thickness=3)
corners, ids, _ = aruco.detectMarkers(img, dictionary)
aruco.drawDetectedMarkers(img, corners, ids, (0,255,0))
cv2.imshow('train_mode', img)
key = cv2.waitKey(1)
if key & 0xFF == ord(' ') and len(corners)>0:
_df = pd.DataFrame(np.array(corners[0]).flatten().tolist() + [target_x, target_y]).T
_df.columns = COLUMNS
if len(df) == 0:
df = _df
else:
df = pd.concat([df, _df])
target_x = int(random.random()*(display_width-2*WIDTH_TRAIN_MARKER)+WIDTH_TRAIN_MARKER)
target_y = int(random.random()*(display_height-2*WIDTH_TRAIN_MARKER)+WIDTH_TRAIN_MARKER)
count += 1
if count >= N_TRAIN:
break
elif key & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
df = df.reset_index(drop=True)
df.to_csv('df.csv', index=False)
df_train_X = df[LIST_EXP+['X']]
df_train_Y = df[LIST_EXP+['Y']]
print(LIST_EXP+['X'])
print(LIST_EXP+['Y'])
setup(data = df_train_X, target = 'X', silent=True, session_id=42)
print('HOGEHOGE')
m_x = create_model('tr', verbose = False)
save_model(m_x, 'm_x')
setup(data = df_train_Y, target = 'Y', silent=True, session_id=42)
m_y = create_model('tr', verbose = False)
save_model(m_y, 'm_y')
def main():
m_x = load_model('m_x')
m_y = load_model('m_y')
list_x=[]
list_y=[]
cap = cv2.VideoCapture(0)
cv2.namedWindow('main_mode', cv2.WINDOW_NORMAL)
cv2.setWindowProperty('main_mode', cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)
while True:
ret, frame = cap.read()
img = cv2.resize(frame,(display_width,display_height))
corners, ids, _ = aruco.detectMarkers(img, dictionary)
if len(corners) > 0:
d = pd.DataFrame(np.array(corners[0]).flatten().tolist()).T
d.columns = COLUMNS[:-2]
x = predict_model(m_x, data=d)['Label'][0]
y = predict_model(m_y, data=d)['Label'][0]
x = int(x)
y = int(y)
list_x.append(x)
list_y.append(y)
if len(list_x) > AVG_WINDOW:
list_x = list_x[-AVG_WINDOW:]
list_y = list_y[-AVG_WINDOW:]
pyautogui.moveTo(sum(list_x)/len(list_x), sum(list_y)/len(list_y))
aruco.drawDetectedMarkers(img, corners, ids, (0,255,0))
cv2.imshow('main_mode', img)
key = cv2.waitKey(1)
if key & 0xFF == ord("q"):
break
cap.release()
cv2.destroyAllWindows()
if __name__ == '__main__':
args = sys.argv
arg = args[1]
if arg == 'marker':
cv2.imwrite('marker.png', aruco.drawMarker(dictionary, 0, 100))
elif arg == 'train':
train()
elif arg == 'main':
main()
else:
print('invalid argument')
使い方
1.マーカー作成(初回のみ)
%> python ar_pointing_system.py marker
これを実行すると、以下のようなマーカーが作成されます。印刷しておでこに貼ってください。
2.キャリブレーション(初回のみ)
%> python ar_pointing_system.py train
おでこにARマーカーを貼り付けた状態で、画面に表示される「+」の図形が、顔の正面に来るように顔の向きを変えてください。
「+」が顔の正面に向くようにしたら、スペースキーを押してください。「+」の位置が変わります。
これを50回繰り返します。50回繰り返すと自動的に機械学習の処理(PyCaret)が走ります。
3. マウスなしカーソル操作を実行
%> python ar_pointing_system.py main
おでこにARマーカーを貼り付けた状態で、顔を動かすとマウスカーソルを操作できます。