【Unity】カメラの映像をAndroid実機に保存する

Android
Unityで作るiPhone/Androidアプリ入門 | 山崎 透, 野口 基之 |本 | 通販 | Amazon
Amazonで山崎 透, 野口 基之のUnityで作るiPhone/Androidアプリ入門。アマゾンならポイント還元本が多数。山崎 透, 野口 基之作品ほか、お急ぎ便対象商品は当日お届けも可能。またUnityで作るiPhone/Androidアプリ入門もアマゾン配送商品なら通常配送無料。

 カメラの映像をUnityで表示する手順についてはこちらの記事でまとめましたが、今回はその映像データをJPG形式でAndroidの実機に保存する手順について説明します。ゲーム内で撮影した写真を端末に保存するような場合を想定しています。

Goal

AndroidのUnityアプリでカメラ映像を画像として保存する!




カメラ映像を表示させる準備

 今回は4:3の縦映像を指定したファイル名で保存するサンプルを作成していきます。レイアウトは撮影ボタン、保存ボタン、取り消しボタン、ファイル名指定Textを準備します。取り合えず適当にこんな感じで配置しておきます。

 カメラ起動スクリプトはどこでもいいですが、今回は撮影ボタンのStart()で実行しました。アプリ起動と同時にカメラが起動するようになっています。カメラの縦横サイズも4:3となるように適当に設定しています。使っている環境によってはサイズが合わなかったり、向きが異なる可能性もあります。その時は回転をやめたり、サイズを小さくしてみたりしてみてください。

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

public class ShootButton : MonoBehaviour
{
    public RawImage RawImage;
    public GameObject Text;
    WebCamTexture webCam;

    // Start is called before the first frame update
    void Start()
    {
        // WebCamTextureのインスタンスを生成
        webCam = new WebCamTexture();

        //RawImageのテクスチャにWebCamTextureのインスタンスを設定
        RawImage.texture = webCam;

        //90度回転
        Vector3 angles = RawImage.GetComponent<RectTransform>().eulerAngles;
        angles.z = -90;
        RawImage.GetComponent<RectTransform>().eulerAngles = angles;
        Vector2 size;
        size = RawImage.GetComponent<RectTransform>().sizeDelta;
        size.x = RawImage.GetComponent<RectTransform>().sizeDelta.y;
        size.y = RawImage.GetComponent<RectTransform>().sizeDelta.x;
        RawImage.GetComponent<RectTransform>().sizeDelta = size;


        //縦横のサイズを要求
        webCam.requestedWidth = 3024;
        webCam.requestedHeight = 4032;

        //カメラ表示開始
        webCam.Play();
    }

    // Update is called once per frame
    void Update()
    {
    }

    public void OnClick()
    {

    }
}

 カメラの画像が表示されるか試してみます。無事Androidの実機でカメラ画像が表示されることを確認しました。

映像を保存する(撮影)

 次にShootボタンを押されたタイミングで表示している画像を一時停止してその後、saveボタンで保存します。カメラを停止するのはPause()を実行します。

    public void OnClick()
    {
        // カメラを停止
        webCam.Pause();
    }

 Pasuseでカメラを止めた後、Saveボタンで表示されている映像を保存します。保存は以下のサンプルの通り、Texture2Dを使用します。File.WriteAllBytesでファイルを指定したフォルダに書き出すのですが、PCはApplication.dataPathを指定することで、Assetsフォルダのパス、AndroidはApplication.persistentDataPathを指定することで、Android\data\アプリファイル名\filesのパスを指定することができます。

    public void OnClick()
    {
        // インスタンス取得
        webCam = ShootButton.GetComponent<ShootButton>().webCam;
        // Texture2Dを新規作成
        Texture2D texture = new Texture2D(webCam.width, webCam.height, TextureFormat.ARGB32, false);
        // カメラのピクセルデータを設定
        texture.SetPixels(webCam.GetPixels());
        // TextureをApply
        texture.Apply();

        // Encode
        byte[] bin = texture.EncodeToJPG();
        // Encodeが終わったら削除
        Object.Destroy(texture);

        // ファイルを保存
#if UNITY_ANDROID
        File.WriteAllBytes(Application.persistentDataPath + "/test.jpg", bin);
#else
        File.WriteAllBytes(Application.dataPath + "/test.jpg", bin);
#endif
    }

 ここでさらなる問題が発生します。保存したデータが-90度回転しています。そもそもここではWebCamTextureのインスタンスからデータを引っ張ってきているので、回転させた表示領域は関係ないです。回転して保存されるのは当たり前です。
 Texture2Dを回転させる方法やWebcamTextureを回転させる方法は見つからなかったので、強引にPixelデータを回転させる処理を作って対応しました。

    private Color[] _rotateImg(Color[] coler, int width, int height, int rotate)
    {
        Color[] rotatepix = new Color[width * height];
        int startposi = width* height-width;
        int posi = 0;

        if (rotate == 0) {
            for (int i = 0; i < rotatepix.Length; i++) {
                rotatepix[i] = coler[i];
            }
        } else if (rotate == 90) {
            startposi = width -1;
            for (int j = 0; j < width; j++) {
                for (int i = startposi; i < rotatepix.Length ; i += width) {
                    rotatepix[posi] = coler[i];
                    posi++;
                }
                startposi--;
            }
        } else if (rotate == 180) {
            for (int i = (rotatepix.Length-1); i >= 0; i--) {
                rotatepix[rotatepix.Length-1-i] = coler[i];
            }
        } else if (rotate == 270) {
            startposi = width * height - width;
            for (int j = 0; j < width; j++) {
                for (int i = startposi; i >= 0; i -= width) {
                    rotatepix[posi] = coler[i];
                    posi++;
                }
                startposi++;
            }
        }

        return rotatepix;
    }

 いまだに何でこれで回転できているのか、わからないですし、汚いコードですが、予定通りに回ったので、良しとします。

まとめ

 Unityでカメラから取り込んだ映像を写真として扱うにあたって、回転に特にハマりました。画像として保存するとなると、UIを回転する要領では扱えないので、ピクセルデータを直接配置変更する必要がありました。(私がやり方を知らないだけかもしれませんが。。。)
 あと、サイズや回転など決め打ちでやっていますが、アプリを色々な機器で動かすとなると、Native コードできちんと情報を取得しておく必要がありそうです。

Unityで作るiPhone/Androidアプリ入門 | 山崎 透, 野口 基之 |本 | 通販 | Amazon
Amazonで山崎 透, 野口 基之のUnityで作るiPhone/Androidアプリ入門。アマゾンならポイント還元本が多数。山崎 透, 野口 基之作品ほか、お急ぎ便対象商品は当日お届けも可能。またUnityで作るiPhone/Androidアプリ入門もアマゾン配送商品なら通常配送無料。

コメント

タイトルとURLをコピーしました