LoginSignup
2
0

Deep写輪眼とUEをTCPで連携させる

Last updated at Posted at 2023-12-02

概要

注意点

  • Deep写輪眼の環境構築方法の詳細は公式をご確認ください。
  • Deep写輪眼とUEはそれぞれ独立して動作します。Deep写輪眼はコンソールから実行します。一つのexeにまとめることや、Steam等で配布することは考慮していません。有識者~~~~

使用例

こういうことができます。
https://x.com/iochiryo/status/1731032980649607325?s=20

Deep写輪眼の検出結果を送信する

Deep写輪眼を動かす

Deep写輪眼をローカル環境で動かせる状態にしてください。リポジトリ内の「simple_demo.py」が実行できれば問題ないです。

環境構築方法

※ 2021年頃に書いたので内容が最新でない可能性があります。バージョン等は適宜読み替えてください。
わかってる人は読み飛ばしてください。公式の方が詳しいと思います。
手元のPCのGPU等によって若干変わります。一例として読んでください。
ChatGPT4が結構丁寧に教えてくれます。

Cuda cuDNNのインストール

CUDA Toolkit 11.0をダウンロードインストールします
https://developer.nvidia.com/cuda-11.0-download-archive

cuDNNをインストールします
https://developer.nvidia.com/rdp/cudnn-archive

Download cuDNN v8.0.5 (November 9th, 2020), for CUDA 11.0をクリック
cuDNN Library for Windows (x86) をダウンロードします。
フォルダを解凍後、ダウンロードしたデータのcuda配下のすべてのフォルダを以下の位置にコピーします。

C:\ProgramFile\NVIDIA GPU Computing Toolkit\CUDA\v11.0\

Anacondaの設定

以下のページからAnacondaをdowloadしてinstallしてください
https://www.anaconda.com/products/individual

64-Bit Graphical Installer (457 MB)をinstallします
Install中に
「add Anaconda to the system Pass environment variable」
のチェックボックスにチェックを入れます。

必要なパッケージのインストール

「コマンドプロンプト」を起動し以下のコマンドを順に実行します。

以下のコマンドを順に実行し必要なパッケージをインストールします。わかる人は仮想環境作ったらいいと思います。

python -m pip install --upgrade --user pip
conda install -c conda-forge opencv       
conda install tensorflow-gpu 
pip install numpy
pip install sockets

以上で環境構築が完了です。

動作確認

simple_demo.py をダブルクリックして起動します。
起動しないときは以下のコマンドを実行してください。

python simple_demo.py

ウィンドウが開きウェブカメラの映像が出たらDeep写輪眼の起動準備は終了です。

TCPで送信できるようにする。

「simple_demo.py」を検出結果を送信できるように書き換えます。
TCPに関する説明はこのあたりを参照したらいいと思います。
https://qiita.com/t_katsumura/items/a83431671a41d9b6358f

最終的に以下の部分で検出結果を送信しています。

conn.send(bytes(labels[class_id][0], 'utf-8'))#ラベルの送信

書き換えた後のコードは次の通りになります。並立でUEを動かす都合上パフォーマンスを下げてます。configの値は公式のREADMEを参照してください。

30010のポートを使用することを想定しています。

simple_demo_unreal.pyを実行すると「WaitForStart」のログが出て停止します。
後述の手順を行い受信できる状態にしたUE側を起動すると、諸々の処理が実行され接続後に起動します。
公式の「simple_demo.py」が動作できる環境なら動作します。

simple_demo_unreal.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import argparse
import csv
import time
import copy

import cv2 as cv
import numpy as np
import tensorflow as tf

# socket サーバを作成
import socket


def get_args():
    parser = argparse.ArgumentParser()

    parser.add_argument("--device", type=int, default=0)
    parser.add_argument("--file", type=str, default=None)
    parser.add_argument("--fps", type=int, default=10)
    parser.add_argument("--width", help='cap width', type=int, default=960)
    parser.add_argument("--height", help='cap height', type=int, default=540)

    parser.add_argument("--model", default='model/EfficientDetD0/saved_model')
    parser.add_argument("--score_th", type=float, default=0.75)
    parser.add_argument("--skip_frame", type=int, default=0)

    args = parser.parse_args()

    return args


def run_inference_single_image(image, inference_func):
    tensor = tf.convert_to_tensor(image)
    output = inference_func(tensor)

    output['num_detections'] = int(output['num_detections'][0])
    output['detection_classes'] = output['detection_classes'][0].numpy()
    output['detection_boxes'] = output['detection_boxes'][0].numpy()
    output['detection_scores'] = output['detection_scores'][0].numpy()
    return output


def main():
    # 引数解析 #################################################################
    args = get_args()
    cap_device = args.device
    cap_width = args.width
    cap_height = args.height
    fps = args.fps
    skip_frame = args.skip_frame

    model_path = args.model
    score_th = args.score_th

#サーバーに接続    
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # IPアドレスとポートを指定
    s.bind(('127.0.0.1', 30010))
    # 1 接続
    s.listen(1)
    # connection するまで待つ
    print("WaitStartGame")
    conn, addr = s.accept()

    if args.file is not None:
        cap_device = args.file

    frame_count = 0

    # カメラ準備 ###############################################################
    cap = cv.VideoCapture(cap_device)
    cap.set(cv.CAP_PROP_FRAME_WIDTH, cap_width)
    cap.set(cv.CAP_PROP_FRAME_HEIGHT, cap_height)

    # モデルロード #############################################################
    DEFAULT_FUNCTION_KEY = 'serving_default'
    loaded_model = tf.saved_model.load(model_path)
    inference_func = loaded_model.signatures[DEFAULT_FUNCTION_KEY]

    # ラベル読み込み ###########################################################
    with open('setting/labels.csv', encoding='utf8') as f:
        labels = csv.reader(f)
        labels = [row for row in labels]
    print("InitiarizeDetection")

    while True:
        start_time = time.time()

        # カメラキャプチャ #####################################################
        ret, frame = cap.read()
        if not ret:
            continue
        frame_width, frame_height = frame.shape[1], frame.shape[0]
        debug_image = copy.deepcopy(frame)

        frame_count += 1
        if (frame_count % (skip_frame + 1)) != 0:
            continue

        # 検出実施 #############################################################
        frame = frame[:, :, [2, 1, 0]]  # BGR2RGB
        image_np_expanded = np.expand_dims(frame, axis=0)

        output = run_inference_single_image(image_np_expanded, inference_func)

        num_detections = output['num_detections']
        for i in range(num_detections):
            score = output['detection_scores'][i]
            bbox = output['detection_boxes'][i]
            class_id = output['detection_classes'][i].astype(np.int)

            if score < score_th:
                continue

            # 検出結果可視化 ###################################################
            x1, y1 = int(bbox[1] * frame_width), int(bbox[0] * frame_height)
            x2, y2 = int(bbox[3] * frame_width), int(bbox[2] * frame_height)

            cv.putText(
                debug_image, 'ID:' + str(class_id) + ' ' +
                labels[class_id][0] + ' ' + '{:.3f}'.format(score),
                (x1, y1 - 15), cv.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2,
                cv.LINE_AA)
            cv.rectangle(debug_image, (x1, y1), (x2, y2), (0, 255, 0), 2)

            print('StartConnect')
            conn.send(bytes(labels[class_id][0], 'utf-8'))#ラベルの送信

        # キー処理(ESC:終了) #################################################
        key = cv.waitKey(1)
        if key == 27:  # ESC
            break

        # FPS調整 #############################################################
        elapsed_time = time.time() - start_time
        sleep_time = max(0, ((1.0 / fps) - elapsed_time))
        time.sleep(sleep_time)

        cv.putText(
            debug_image,
            "Elapsed Time:" + '{:.1f}'.format(elapsed_time * 1000) + "ms",
            (10, 30), cv.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2, cv.LINE_AA)

        # 画面反映 #############################################################
        cv.imshow('NARUTO HandSignDetection Simple Demo', debug_image)
        # cv.moveWindow('NARUTO HandSignDetection Simple Demo', 100, 100)

    cap.release()
    cv.destroyAllWindows()


if __name__ == '__main__':
    main()

UE側で検出結果を受け取る

Deep写輪眼から送信された検出結果をUE側で受け取るようプロジェクト側を修正します。

ObjectDeliverの追加

Object Deliverプラグインを追加してください。

受信用Actorの設定

TcpSocketConnection Classを作成します。
TcpSocketConnection.png

以下のような処理を追加します。画像はUE4ですがUE5でも同じだと思います。

BP_Tcp.png

これをレベル上に置いておくとTickでDeep写輪眼の値を取得します。
ここではGI_DetectedLabelに検出結果が入ります。
検出結果がString型で「Ne(Rat)」のように入ってきます。
今回はGameInstanceに送ってあちこちで使っていますが、この辺はよしなにやってください。

BP_DetectConverter-Detect Converter.png

使い方

環境構築が済んでいる前提で説明をします。

  1. simple_demo_unreal.pyを実行します。
  2. 「WaitForStart」のログが出ていることを確認します
  3. UEプロジェクトをPlayします。Play in EditorではなくスタンドアローンでPlayしないと通信されない時があります。
  4. Deep写輪眼が起動します。しばらく時間がかかります。ログが「WaitForStart」から流れ始めれば正常に動作しています。
  5. Webカメラが起動します。
  6. 検出結果がUE側に送信されていることを確認します

応用例

Deep写輪眼を別のリポジトリに置き換えても同じ要領でUEと連携できます。
これはMediaPipeを使用して作った「ういびーむDetector」です。
絵は10秒で描きました。たすけてしぐれうい。

知りたいこと

機械学習関係の処理をUE内に埋め込むのどうやるんですか。
ゲームやろうとして「Anaconda入れてYAML叩いて環境構築してね」はハードル高い気がしてます。
Deep写輪眼まで含めて1つのexeにまとめてワンチャンSteamに公開するところまでいけないですか。onnxに対応したらしいですが、ほとんど情報が見つからないです。有識者~~~
https://historia.co.jp/archives/26238/

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0