はじめに
前回はComputeShader
を用いて肌色を検出する記事を書きました。
今回はこれをOpenCV plus Unity
をという無料のアセットを用いて実現してみようと思います。
このアセットはオープンソースプロジェクトOpenCVSharp
をベースとして作られているものです。
ただこのアセットはかなりマイナーどころで、こっちのアセットの方がかなり有名で情報もいっぱいあります。
こちらはネイティブプラグインを用いてC++
で実装されているようです。
本当は下のアセットを使いたいところですが、学生である私にはそんなお金はないのでOpenCV Plus Unity
を使っていきたいと思います。
肌色を検出する
細かいことは以下の記事に書かせてもらったので、コードを実際にみていきます。
private readonly static Scalar SKIN_LOWER = new Scalar(0, 30, 60); private readonly static Scalar SKIN_UPPER = new Scalar(20, 150, 255); private void ExtractSkinColor(WebCamTexture tex) { //WebCamTextureからOpenCVで使われる形式Matに変換 Mat mat = OpenCvSharp.Unity.TextureToMat(tex); Mat hsvMat = new Mat(); // BGRからHSVに変換 Cv2.CvtColor(mat, hsvMat, ColorConversionCodes.BGR2HSV); // 肌色で抽出して2値化 Mat binary = hsvMat.InRange(SKIN_LOWER, SKIN_UPPER); _renderer.texture = OpenCvSharp.Unity.MatToTexture(binary); }
一番重要なところはこちらだと思います。
mat = hsvMat.InRange(SKIN_LOWER, SKIN_UPPER);
InRange
メソッドによりSKIN_LOWER <= 対象の画像 < SKIN_UPPER
とそれ以外で2値化します。
前回の記事では色はそのままにしていましたが、この方法では2値化してしまうところだけは注意ですね。
コード
Texture2D
がWebカメラからの画像になるように付け足すとこんな感じ。
using OpenCvSharp; using UnityEngine; using UnityEngine.UI; public class ExtractSkin : MonoBehaviour { [SerializeField] private RawImage _renderer; private readonly static Scalar SKIN_LOWER = new Scalar(0, 60, 80); private readonly static Scalar SKIN_UPPER = new Scalar(10, 160, 240); private int _width = 1920; private int _height = 1080; private int _fps = 30; private WebCamTexture _webcamTexture; private void Start() { WebCamDevice[] devices = WebCamTexture.devices; _webcamTexture = new WebCamTexture(devices[0].name, this._width, this._height, this._fps); _webcamTexture.Play(); } void OnDestroy() { if (_webcamTexture != null) { if (_webcamTexture.isPlaying) _webcamTexture.Stop(); _webcamTexture = null; } } private void Update() => ExtractSkinColor(_webcamTexture); private void ExtractSkinColor(WebCamTexture tex) { //WebCamTextureからOpenCVで使われる形式Matに変換 Mat mat = OpenCvSharp.Unity.TextureToMat(tex); Mat hsvMat = new Mat(); // BGRからHSVに変換 Cv2.CvtColor(mat, hsvMat, ColorConversionCodes.BGR2HSV); // 肌色で抽出して2値化 Mat binary = hsvMat.InRange(SKIN_LOWER, SKIN_UPPER); _renderer.texture = OpenCvSharp.Unity.MatToTexture(binary); } }
比較
FPS的にはComputeShader
を使った方が勝っているようでした。
あくまで私が実践した実装方法においての話ですが、Unityの場合はComputeShader
が最適化の要なのかもしれませんね。
追記
前回みたく色をつけたり、背景はそのままで肌色だけ色を変えたいしたい場合は以下の知識を使えばいけるはずです。
まずMatにはいくつかの種類(Type)があることに注意してください。
OpenCVのMatのタイプ一覧表 | 404 Motivation Not Found
いじっていなければ普通のカラーの画像はCV_8UC3
,2値化した画像はCV_8UC1
です。
例えばCV_8UC1
からCV_8UC3
に変換したければ以下のコードを書けばOKです。
Cv2.CvtColor(binary, mat, ColorConversionCodes.GRAY2BGR);
加えてType
が一致していれば画像の足し合わせは+
演算子で行えます。
mat = mat1 + mat2;
さいごに
このアセットを使えば簡潔にかけるのは確かです。
ただOpenCV
を使ったとしても上手に実装しなければ重くなってしまうことも事実です。(特に全部のピクセルになんらかの操作を加えるために2重for文なんかしたら一瞬で重くなる)
うまく適材適所で使ってみてください。
あとはOpenCV for Unity
でも試してみたかった・・・。