AIを用いたエイムアシストツールの作り方(画面キャプチャのみ)
1. はじめに
本プロジェクトでは、以下のライブラリなどを利用します。
OpenCV
画像処理および描画用ライブラリmss
画面キャプチャを高速に行うためのライブラリONNX Runtime
ONNX形式の深層学習モデルによる推論を実施NumPy
数値計算ライブラリ
また、設定ファイル(config.ini)を用いてモデルパスやキャプチャサイズなどのパラメータを管理します。
2. 必要なライブラリと環境設定
まずは、以下のパッケージをインストールしてください。
pip install opencv-python mss onnxruntime numpy
次に、プロジェクトルートに config.ini を配置します。初回起動時に自動で作成する仕組みも用意していますが、内容は以下のようになります:
[Settings]
model_path = yolov5nu.onnx
capture_size = 320
3. コードの概要
以下のコードは、画面の中央部分をキャプチャし、ONNXモデルによる物体検出を行い、検出結果のバウンディングボックスと信頼度を描画するシンプルな実装例です。
※エイムアシスト用のマウス移動や発射処理は削除しています。
主な構成要素
plot_one_box
バウンディングボックスとラベル(信頼度)を画像上に描画する関数detect_targets
キャプチャしたフレームに対してONNXモデルの推論を実行し、検出候補(バウンディングボックスと信頼度)を取得する関数load_config
設定ファイルからパラメータを読み込む関数main
画面キャプチャ、推論、描画をループ処理で実行するメイン関数
4. コード詳細と解説
以下に、画面検出のみを行うコード例と、それぞれの処理について解説します。
import os
import cv2
import time
import math
import numpy as np
import mss
import onnxruntime as ort
import configparser
# --- バウンディングボックス描画用関数 ---
def plot_one_box(x, img, color=(128, 0, 0), label=None, line_thickness=None):
# x: [x1, y1, w, h]
tl = line_thickness or round(0.002 * (img.shape[0] + img.shape[1]) / 2) + 1
c1 = (int(x[0]), int(x[1]))
c2 = (int(x[0] + x[2]), int(x[1] + x[3]))
cv2.rectangle(img, c1, c2, color, thickness=tl, lineType=cv2.LINE_AA)
if label:
tf = max(tl - 1, 1)
t_size = cv2.getTextSize(label, 0, fontScale=tl/3, thickness=tf)[0]
c2_label = (c1[0] + t_size[0], c1[1] - t_size[1] - 3)
cv2.rectangle(img, c1, c2_label, color, -1, cv2.LINE_AA)
cv2.putText(img, label, (c1[0], c1[1] - 2), 0, tl/3, (225, 255, 255), thickness=tf, lineType=cv2.LINE_AA)
# --- ONNX推論用、物体検出処理 ---
def detect_targets(session, frame, capture_size):
input_shape = session.get_inputs()[0].shape
input_width = input_shape[3] if input_shape[3] is not None else capture_size
input_height = input_shape[2] if input_shape[2] is not None else capture_size
frame_resized = cv2.resize(frame, (input_width, input_height))
frame_rgb = cv2.cvtColor(frame_resized, cv2.COLOR_BGR2RGB)
frame_tensor = frame_rgb.astype(np.float32) / 255.0
frame_tensor = np.transpose(frame_tensor, (2, 0, 1))
frame_tensor = np.expand_dims(frame_tensor, axis=0)
input_dtype = session.get_inputs()[0].type
if input_dtype == 'tensor(float16)':
frame_tensor = frame_tensor.astype(np.float16)
input_name = session.get_inputs()[0].name
outputs = session.run(None, {input_name: frame_tensor})
out = outputs[0]
boxes = []
confidences = []
scale_x = capture_size / input_width
scale_y = capture_size / input_height
# モデルの出力形式に応じた処理
if out.shape[-1] == 85:
for detection in out[0]:
x1, y1, x2, y2, conf, *class_scores = detection
class_scores = np.array(class_scores)
class_id = int(np.argmax(class_scores))
score = conf * class_scores[class_id]
if score < 0.27:
continue
x1 = int(x1 * scale_x)
y1 = int(y1 * scale_y)
x2 = int(x2 * scale_x)
y2 = int(y2 * scale_y)
box = [x1, y1, x2 - x1, y2 - y1]
boxes.append(box)
confidences.append(score)
else:
detections = out.transpose(0, 2, 1)[0]
for det in detections:
cx, cy, w, h = det[0:4]
scores = det[4:]
conf = np.max(scores)
classID = int(np.argmax(scores))
if conf < 0.27 or classID != 0:
continue
x1 = cx - w/2
y1 = cy - h/2
x2 = cx + w/2
y2 = cy + h/2
x1 = int(x1 * scale_x)
y1 = int(y1 * scale_y)
x2 = int(x2 * scale_x)
y2 = int(y2 * scale_y)
box = [x1, y1, x2 - x1, y2 - y1]
boxes.append(box)
confidences.append(float(conf))
return boxes, confidences
# --- 設定ファイル読み込み ---
def load_config(config_path="config.ini"):
config = configparser.ConfigParser()
if os.path.exists(config_path):
config.read(config_path, encoding="utf-8")
else:
config['Settings'] = {
'model_path': 'yolov5nu.onnx',
'capture_size': '320'
}
with open(config_path, 'w', encoding="utf-8") as configfile:
config.write(configfile)
return config['Settings']
# --- メイン処理 ---
def main():
config = load_config()
model_path = config.get('model_path', 'yolov5nu.onnx')
capture_size = config.getint('capture_size', 320)
# ONNX Runtimeのセッション設定
sess_options = ort.SessionOptions()
sess_options.intra_op_num_threads = 2
sess_options.inter_op_num_threads = 2
sess_options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL
providers = [
('DmlExecutionProvider', {'device_id': 0, 'tunable_op_enable': 1}),
'CPUExecutionProvider'
]
session = ort.InferenceSession(model_path, sess_options, providers=providers)
# モニター情報の取得(画面全体の解像度)
with mss.mss() as sct:
monitor = sct.monitors[1]
screen_width = monitor["width"]
screen_height = monitor["height"]
capture_x = (screen_width - capture_size) // 2
capture_y = (screen_height - capture_size) // 2
print("画面検出を開始します。終了する場合は 'q' キーを押してください。")
while True:
with mss.mss() as sct:
monitor_region = {"top": capture_y, "left": capture_x, "width": capture_size, "height": capture_size}
sct_img = sct.grab(monitor_region)
frame = np.array(sct_img)
frame = cv2.cvtColor(frame, cv2.COLOR_BGRA2BGR)
boxes, confidences = detect_targets(session, frame, capture_size)
# 各検出結果を描画
for box, conf in zip(boxes, confidences):
label = "Conf: {:.2f}".format(conf)
plot_one_box(box, frame, color=(128, 0, 0), label=label)
cv2.imshow("Screen Detection", frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cv2.destroyAllWindows()
if __name__ == '__main__':
main()
各処理のポイント
画面キャプチャ
mss を用いて画面の特定領域(ここでは画面中央の capture_size x capture_size の領域)をキャプチャし、NumPy 配列として扱います。ONNXモデルによる推論
キャプチャした画像をモデルの入力サイズにリサイズし、RGB変換・正規化・チャネル順の変更を行った後、ONNX Runtime で推論を実施します。
出力の形式に応じた処理を行い、検出された物体のバウンディングボックスと信頼度をリストにまとめます。描画
OpenCV を利用し、各検出結果に対してバウンディングボックスと信頼度ラベルを描画します。plot_one_box 関数で美しく描画する処理を実現しています。ループ処理
キャプチャ→推論→描画のループを回し、リアルタイムに検出結果を表示。q キーが押されるとループを抜け、ウィンドウを破棄します。
5. まとめ
本記事では、画面検出を行うシンプルなプログラムの作成方法をご紹介しました。実際にこのテンプレートを元に、さらなる改良(例えばオブジェクトに対してのエイムアシストを発生させる処理や、UIのカスタマイズなど)を施すことで、自分だけのツールに仕上げることが可能です。