UnityでOpenCVSharpを使う

## 1.　はじめに
UnityEditorで実行中にUSBカメラで録画する方法をまとめました．
無料/有料アセットありますが，今回はOpenCVSharpを使います．

VR用のシーン再生時にVR映像と現実の映像を同期して保存したいというのがモチベーションです．
なお，ビルド後使えるかは試していません．

## 2. 実行環境
- Windows 11 Pro
- Unity 2022.3.21f1
- Visual Studio 2022
- USB接続機器
  - VIVE Pro Eye (HTC)
  - キャプチャボード JVA06(j5create)
  - USBカメラ C920(Logicool)

## 3. 手順
### (1) NuGetForUnityダウンロード

この記事執筆地点ではバージョン4.2.0．

インストール手法は3種類あります．
unitypackageの場合はこちらからダウンロード↓

https://github.com/GlitchEnzo/NuGetForUnity/releases


### (2) import
ダウンロードしたunitypackageをインポート．
`Assets>NuGet`フォルダとメニューにNuGetが追加されます．

### (3) Nuget起動
メニュー`Nuget > Manage NuGet Packages`をクリックするとパッケージ検索ウィンドウが出現します．
先人の記事を調べるにOpenCvSharp4.WindowsかOpenCvSharp4.runtime.winを使うパターンがあるみたいです．
runtimeの方は別途OpenCvSharpのzipを入手する必要があるみたい？

### (4) OpenCVSharpインストール
今回はOpenCvSharp4.Windowsを使ってみます．
installボタンをぽちり．
Warningウィンドウがポップアップしますが，OKで大丈夫です．

<img width="50%" alt="スクリーンショット 2025-02-03 192924.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2654082/d6c385eb-3f96-4a59-4cf2-875ab5db60f1.png">

※dllをダウンロードしてAssets>Pluginsフォルダに入れるケースもヒットしましたが，Consoleにライブラリが二重で存在するみたいなエラーが出たので削除．(OpenCvShart4.runtime.winの場合は必要？)
一応手順を記すと，
1. OpenCVホームページでバージョン4.10.0 (2024-06-03 Released)のWindowsインストーラをダウンロード
https://opencv.org/releases/
2. pencv-4.10.0-windows.exe起動
3. 出力されたopencvフォルダの`build>bin>opencv_videoio_ffmpeg4100_64.dll`をAssets>Pluginsにコピー

OpenCVSharpExtern.dllを特定のフォルダに入れる手順を踏むパターン<sup>[2][3][4]</sup>もあるみたいですが，やっていません．
（USBカメラで録画する分には問題なかったので）

とりあえず，これだけでUnityでPlayする分にはOpenCVSharpが使えます．


### (4) USBカメラのデータを取得
USBカメラで録画し，aviファイルとして出力するスクリプトです．
VideoCapture()の引数のindexはHMDのカメラやFaceRigのような仮想カメラが入っている場合は0が使われているっぽいです．
0から探していって上手く映るのを探すといいと思います．
複数台カメラがある場合はPCに挿入した順序になるっぽいです．
```C#
using UnityEngine;
using OpenCvSharp;
using System.IO;
using System;

public class debug_OpenCvSharp : MonoBehaviour
{
    private VideoCapture capture;
    private VideoWriter writer;
    private Mat frame;
    private bool isRecording = false;
    // cameraIndex 0: 何も写らない(HMD?) 1: j5craete(type-C) :2 USBカメラ
    // NOTE: PCに接続した順番？
    private int cameraIndex = 2;
    private int frameWidth = 1920;
    private int frameHeight = 1080;
    private double fps = 60.0;
    private string outputFilePath;

    void Start()
    {
        // 接続されている機器を探す
        for (int i = 0; i < 20; i++)
        {

        }
        capture = new VideoCapture(cameraIndex);
        if (!capture.IsOpened())
        {
            Debug.LogError("カメラを開けませんでした。");
            return;
        }
        capture.Set(VideoCaptureProperties.FrameWidth, frameWidth);
        capture.Set(VideoCaptureProperties.FrameHeight, frameHeight);
        capture.Set(VideoCaptureProperties.Fps, fps);

        frame = new Mat();
        // desktopにoutput.aviというファイル名で保存
        outputFilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "output.avi");
        writer = new VideoWriter(outputFilePath, FourCC.MJPG, fps, new Size(frameWidth, frameHeight));

        if (!writer.IsOpened())
        {
            Debug.LogError("VideoWriterの初期化に失敗しました。");
            return;
        } else
        {
            Debug.Log("VideoWriter is opened.");
        }
    }

    void Update()
    {
        // Rキーで録画開始 Scene再生終了で録画終了
        if (Input.GetKeyDown(KeyCode.R))
        {
            isRecording = true;
            Debug.Log("録画開始: " + outputFilePath);
        }

        if (isRecording && capture.IsOpened())
        {
            capture.Read(frame);
            if (!frame.Empty())
            {
                writer.Write(frame);
            }
        }
    }

    void OnApplicationQuit()
    {
        isRecording = false;
        writer?.Release();
        capture?.Release();
        frame?.Dispose();
        Debug.Log("録画終了");
    }
}
```

## 4. おわりに
今回はすんなり導入できたので何か足りないのではないか，と逆に訝しんでいるところ...
ネットの海を漁る限りdllをアタッチするとか一手間加えているみたいなので，今後何か問題が起こったら追記します．

OpenCVに頼らない無課金の方法として，VideoKit (旧NatCam)<sup>[5]</sup>やOpenCV plus Unityアセット<sup>[6]</sup>というのもあるんだとか．
VideoKitはAPIキーを発行したりと面倒＆後々課金制とかになったら嫌なのでダウンロードしてやめました．
OpenCV plus UnityはメンテナンスがないのとUnsafe Codeを許可しないといけないのが何とも言えないところ．

## 参考文献
[1] toRisouP, "NuGetForUnityを使うときの注意点メモ", Qiita

https://qiita.com/toRisouP/items/6c735e5fc28522853e5b

[2] システム創成2021 CIC担当チーム 引継ぎ資料, "OpenCVSharp",

https://sysdp2021-t03cic.github.io/opencv/introduction/opencvsharp/

[3] iwasiblog, "Unity + OpenCvSharp (4.5.3)",

https://iwasi.hatenablog.jp/entry/2021/11/17/085215

[4] Fantastic Works, "UnityでOpenCVSharpを使う",

https://fantastic-works.com/archives/3118

[5] mattun, "Unityでカメラロールにアクセスしてみる", Zenn,

https://zenn.dev/matsuataru/articles/videokit-share

[6] きつね, "Unity+OpenCVに挑戦してみた", Note,

https://note.com/dotkitsunet/n/n3de7b7aff179