一覧に戻る

Unity3DプラグインのプラグインAPI

2018.07.08Unity3Dプラグイン

プラグインAPIは、ネイティブポインタとしてカメラフレームへのアクセスを提供します。したがって、C#またはC++、Objective C、Javaのようなネイティブコードを使用して処理することができます。

UNITYインターフェース

プラグインを有効にするには、GameObject(ゲームオブジェクト)にPluginスクリプトを追加する必要があります。このイベントは別のスレッドから呼び出すことができることに注意してください。また、On Camera Frame Availableイベントをサブスクライブすることで、新しいカメラフレームの追加処理の準備ができたときに通知を受けることができます。

その後、コールバックへのパラメータとして、フレームに関するすべての情報を含む "Wikitude.CameraFrame"クラスを受け取ります。

public class CameraFrame
{
    public long ID;
    public long ColorTimestamp;
    public ColorCameraFrameMetadata ColorMetadata;
}

ColorMetadataには、サイズやフォーマットなどのカメラフレームに関する追加情報が含まれています。

public struct ColorCameraFrameMetadata {
        public float HorizontalFieldOfView;
        public int Width;
        public int Height;
        public CaptureDevicePosition CameraPosition;
        public FrameColorSpace ColorSpace;
        public int TimestampScale;
    }

ColorDataリストには、カメラフレームに含まれる各プレーンのフレームデータを格納するために使用される実際のネイティブメモリへの生のIntPtrが含まれています。
ネイティブポインターは、OnCameraFrameAvailableの呼び出し中のみ有効で、ポインターデータを決して削除または変更しないでください。 このポインタを独自のネイティブプラグインに渡すか、 C#では"Marshal.Copy"関数を使用してデータを転送することができます。

using UnityEngine;
using System;
using Wikitude;
using System.Runtime.InteropServices;

public class PluginController : MonoBehaviour
{
    public void OnCameraFrameAvailable(Frame frame) {

        // Example of how to transfer data from native memory
        // to a C# array

        byte[] data = new byte[frame.DataSize];
        Marshal.Copy(frame.Data, data, 0, frame.DataSize);
    }
}

サンプルの説明

プラグインAPIのバーコードサンプルは、一般向けのバーコードライブラリであるZXing.NetをUnityに実装する例と、処理するカメラフレームを送信するためのプラグインAPIの使用方法を示します。
PluginController.csスクリプトはサンプルを制御します。エディタに登録され、 "OnCameraFrameAvailable"を受信します。まず、スキャンを実行するために使用できる新しいスレッドを作成します。古いデバイスではスキャンに時間がかかりすぎてアプリが応答しないように見えることがあるため、これが必要になります。
Updateメソッドでは、新しいスキャン結果があるかどうかだけを確認し、そうであれば表示します。

void Update() {
    if (_scanningInProgress) {
        return;
    }

    if (_scanningResult != null) {
        ResultText.text = _scanningResult;
        _scanningResult = null;
    }
}

"OnCameraFrameAvailable"イベントがトリガされると、スキャンが既に進行中であるかどうかをまずチェックします。 そうでない場合は、生データを「Color32」配列に変換し、新しいスキャンを開始します。

public void OnCameraFrameAvailable(CameraFrame frame) {
    if (_scanningInProgress) {
        return;
    }

    var metadata = frame.ColorMetadata;
    var data = new Color32[metadata.Width * metadata.Height];
    if (metadata.ColorSpace == FrameColorSpace.RGBA) {
        var rawBytes = new byte[frame.ColorData[0].DataSize];
        Marshal.Copy(frame.ColorData[0].Data, rawBytes, 0, (int)frame.ColorData[0].DataSize);

        for (int i = 0; i < metadata.Width * metadata.Height; ++i) {
            data[i] = new Color32(rawBytes[i * 4], rawBytes[i * 4 + 1], rawBytes[i * 4 + 2], rawBytes[i * 4 + 3]);
        }
    } else if (metadata.ColorSpace == FrameColorSpace.RGB) {
        /* conversion code */
    } else if (metadata.ColorSpace == FrameColorSpace.YUV_420_NV12 ||
               metadata.ColorSpace == FrameColorSpace.YUV_420_NV21 ||
               metadata.ColorSpace == FrameColorSpace.YUV_420_YV12) {
        /* conversion code */
    }

    // Trigger a new scan
    Monitor.Pulse(_monitor);
}

最後に、新しいスキャンをトリガするときに、Decodeメソッドを呼び出してスキャン結果を戻します。AutoRotateオプションを有効にすると、風景モードとポートレートモードの両方でスキャンが機能します。

private string Decode(Color32[] colors, int width, int height) {
    var reader = new BarcodeReader();
    reader.AutoRotate = true;
    var result = reader.Decode(colors, width, height);
    if (result != null) {
        return result.Text;
    } else {
        return null;
    }
}

上記のコードスニペットは、わかりやすくするために簡略化されています。スレッドの同期化とエラー処理、およびその他の統合の詳細については、実際のソースコードを参照してください。