うら干物書き

ゲームを作っています。

【Unity】WebCamTextureを使ってカメラが映している画像情報を取得するよ

 カメラで撮影している画像情報を取得したいなぁ。
 いえ、Unity上のカメラじゃないですよ。

 iOSやAndroidの端末にあるカメラで映している画像情報が欲しいのです。
 望むことなら、エディター上でもWebカメラで映している画像情報を取りたいな。
 でもネイティブコードを書くのは大変そうだし、どうしよう……。

 そんなときは、WebCamTexture
 ネイティブコードを書かなくても、パパっと簡単にカメラで映している画像情報が取得できるみたい!

 さっそく使ってみましょう。


この記事にはUnity5.6.2f1を使用しています。

WebCamTextureを使おう

docs.unity3d.com

 では以下のスクリプトにWebCamTextureを使って、カメラが映している画像情報を取得する流れを記述してみますよ。

public class TestCamera : MonoBehaviour
{
    private IEnumerator Start()
    {
        yield return null;
    }   
} // class TestCamera

WebCamTexture.devicesを使って、接続されているカメラを探すよ!

 さて、まずはそもそもカメラがあるかを調べなくちゃいけませんよね。
 WebCamTexture.devicesを使って存在するカメラを探してみましょう。
docs.unity3d.com

private IEnumerator Start()
{
    if( WebCamTexture.devices.Length == 0 )
    {
        Debug.LogFormat( "カメラのデバイスが無い様だ。撮影は諦めよう。" );
        yield break;
    }
}

 とりあえず、無ければそれまでという形でいきます。

カメラの利用の許可が出ているか調べてみるよ!

 カメラがあったとしても、カメラの使用許可が出ていないと使えませんよね。
 Application.RequestUserAuthorization()を使って許可のリクエストを出し、Application.HasUserAuthorizationで確認してみましょう。
docs.unity3d.com

 ドキュメントを読む限り、リクエストの方はWebPlayer用だよ的なことが書かれているけど、今回は一応呼んでおきましょう。

private IEnumerator Start()
{
    if( WebCamTexture.devices.Length == 0 )
    {
        Debug.LogFormat( "カメラのデバイスが無い様だ。撮影は諦めよう。" );
        yield break;
    }

    yield return Application.RequestUserAuthorization( UserAuthorization.WebCam );
    if( !Application.HasUserAuthorization( UserAuthorization.WebCam ) )
    {
        Debug.LogFormat( "カメラを使うことが許可されていないようだ。市役所に届けでてくれ!" );
        yield break;
    }
}

WebCamTextureを作ってみるよ!

 さて、前準備は整えたので、テクスチャの解像度を指定してWebCamTextureを作ってみます。
docs.unity3d.com

public class TestCamera : MonoBehaviour
{
    [SerializeField]
    private int     m_width     = 1920;
    [SerializeField]
    private int     m_height    = 1080;

    private WebCamTexture   m_webCamTexture     = null;


    private IEnumerator Start()
    {
        if( WebCamTexture.devices.Length == 0 )
        {
            Debug.LogFormat( "カメラのデバイスが無い様だ。撮影は諦めよう。" );
            yield break;
        }

        yield return Application.RequestUserAuthorization( UserAuthorization.WebCam );
        if( !Application.HasUserAuthorization( UserAuthorization.WebCam ) )
        {
            Debug.LogFormat( "カメラを使うことが許可されていないようだ。市役所に届けでてくれ!" );
            yield break;
        }

        // とりあえず最初に取得されたデバイスを使ってテクスチャを作りますよ。
        WebCamDevice userCameraDevice = WebCamTexture.devices[ 0 ];
        m_webCamTexture = new WebCamTexture( userCameraDevice.name, m_width, m_height );
    }
} // class TestCamera

 今回はデバイスの名前を指定していますが、指定しない場合には最初に検出されたデバイス名を使うみたいです。
 他にもフレームレートも指定できるみたいです。

さあ、撮影を開始するよ!

 あとは、画面上でUIのRawImageに先ほど作ったWebCamTextureを設定して動かしてみましょう。

using UnityEngine;
using UnityEngine.UI;
using System.Collections;

public class TestCamera : MonoBehaviour
{
    [SerializeField]
    private int     m_width     = 1920;
    [SerializeField]
    private int     m_height    = 1080;
    [SerializeField]
    private RawImage    m_displayUI = null;

    private WebCamTexture   m_webCamTexture     = null;


    private IEnumerator Start()
    {
        if( WebCamTexture.devices.Length == 0 )
        {
            Debug.LogFormat( "カメラのデバイスが無い様だ。撮影は諦めよう。" );
            yield break;
        }

        yield return Application.RequestUserAuthorization( UserAuthorization.WebCam );
        if( !Application.HasUserAuthorization( UserAuthorization.WebCam ) )
        {
            Debug.LogFormat( "カメラを使うことが許可されていないようだ。市役所に届けでてくれ!" );
            yield break;
        }

        // とりあえず最初に取得されたデバイスを使ってテクスチャを作りますよ。
        WebCamDevice userCameraDevice = WebCamTexture.devices[ 0 ];
        m_webCamTexture = new WebCamTexture( userCameraDevice.name, m_width, m_height );

        m_displayUI.texture = m_webCamTexture;

        // さあ、撮影開始だ!
        m_webCamTexture.Play();
    }
} // class TestCamera

f:id:urahimono:20170706220809g:plain

 おお、いい感じにPCに取り付けたWebカメラの映像が表示されています。
 これは僕が作ったノートの映像ですね。

ちょっとだけUIを改良するよ!

 あとせっかくなので、UIを少しだけ改良しましょう。
 PlayボタンStopボタンを追加してみました。

public void OnPlay()
{
    if( m_webCamTexture == null )
    {
        return;
    }

    if( m_webCamTexture.isPlaying )
    {
        return;
    }

    m_webCamTexture.Play();
}

public void OnStop()
{
    if( m_webCamTexture == null )
    {
        return;
    }

    if( !m_webCamTexture.isPlaying )
    {
        return;
    }

    m_webCamTexture.Stop();
}

f:id:urahimono:20170706220810g:plain

Androidで使えるかな

 さあ、このシーンをAndroidにビルドしてみましょう。
 上手くいくといいけど。
f:id:urahimono:20170706215601j:plain

 わーい、あっさり出来ました。
 簡単ですね!

iOSで使えるかな

 では引き続きiOSにもビルドしてみましょう。
 さあ、どうでしょうか。
f:id:urahimono:20170706215616p:plain

 この後、即クラッシュ!
 なぜなのか。

iOS10以降では一手間必要だ!

 インターネットで検索したところ、こんな情報がありました。
qiita.com

 んー、カメラを使うにはInfo.plistにカメラ許可用のキーを設定する必要があるみたいですね。
 Xcodeで設定してあげればいいんですが、僕のこのPCはWindows。Xcodeはありませんわ!
 iOS用のビルドはCloudBuildを使って作っているんですよね……。

 PostProcessBuildAttributeを使って、自動でInfo.plistに書き込むようにするしかないね!
 Editorフォルダをつくって、その中にPostProcessBuild用のスクリプトを作ってっと。

f:id:urahimono:20170706215659p:plain

using UnityEngine;
using UnityEditor;
using UnityEditor.iOS.Xcode;
using UnityEditor.Callbacks;

public class PostBuilder
{
    [PostProcessBuildAttribute()]
    public static void OnPostProcessBuild( BuildTarget i_buildTarget, string i_pathToBuiltProject )
    {
        if( i_buildTarget == BuildTarget.iOS )
        {
            OnPostProcessBuild_iOS( i_buildTarget, i_pathToBuiltProject );
        }
    }

    private static void OnPostProcessBuild_iOS( BuildTarget i_buildTarget, string i_pathToBuiltProject )
    {
        var plistPath   = System.IO.Path.Combine( i_pathToBuiltProject, "Info.plist" );
        var plist       = new PlistDocument();
        plist.ReadFromFile( plistPath );

        plist.root.SetString( "NSCameraUsageDescription", "" );

        plist.WriteToFile( plistPath );
    }
} // class PostBuilder

 さあ、これでiOSビルドに再チャレンジですよ。

f:id:urahimono:20170706220006j:plain

 やったー、うまくいきましたよー。

締めの言葉

 WebCamTextureを使って簡単にカメラの画像が取得できました。
 iOS,Androidそしてエディタ上全てで共通処理で出来るので楽ちんです。
 フレームレート的に動画として使うのは、ちょっと難しいかな。