【Unity】Texture2Dにロードした画像をJPEG変換したらグレーになった話

Android

 最近は初心者 vs Unityのメモリマネジメントの戦いを日々繰り返しております。今回はTexture2DにLoadした画像をJPEGにEncodeして、ファイルに保存した時、うまくいく保存できている場合とグレー一色になり、うまくいかない場合があったので、その原因と対処方法について説明します。

Goal

適切にバッファを確保してEncodeToJPG()を正しく使う!




サンプルアプリ説明

 実験に使ったアプリについて説明します。アプリは基本、以下のブログで紹介したアプリと同じものです。

 こちらのアプリでShootボタンが押されたコールバック処理を改造して、撮影した画像をJPEGに変換して、ファイルとして保存する処理を以下の通り追加しました。

    // 取り込みサイズは4032×3024
    Color32[] tempbuff = new Color32[4032 * 3024];
    Texture2D TextureTmp;
    public void OnClickShoot()
    {
        // カメラを停止
        webCam.Pause();
        // 画像取り込み
        webCam.GetPixels32(tempbuff);
        // jpegファイル生成用Texture生成
        TextureTmp = new Texture2D(webCam.height, webCam.width, TextureFormat.ARGB32, false);
        // カメラのピクセルデータを設定
        TextureTmp.SetPixels32(tempbuff);
        // TextureをApply
        TextureTmp.Apply();
        // Encode
        byte[] bin = TextureTmp.EncodeToJPG();
        // データを保存
        try {
            File.WriteAllBytes(Application.persistentDataPath+"test.jpg", bin);
        } catch (Exception e) {
            Debug.Log("Write error");
        }
        // 保存が終わったら削除
        bin = null;
        Destroy(TextureTmp);
    }

 取り込んだ画像はカメラから取り込んだデータなので、カメラの取り付け向きだけ回転していますが、今回は話をシンプルにするので回転したまま保存します。Jpegファイルの生成はEncodeToJPG()で生成します。生成後、File.WriteAllBytes()でファイルを書き出して、不要になったTexture2DDestoryします。

アプリを動かしてみる

 早速アプリを動作させて、保存されたファイルを見てみます。期待通り書き出されていることがわかります。

 では、サイズを小さく、960×720に変更して撮影してみるとどうなるでしょうか。サイズを変更するにはWebCamTextureのインスタンス生成時に指定するサイズを変更すればよいです。

webCam = new WebCamTexture(960,720);

 撮影すると以下のようになります。。。

 なぜ、サイズを小さくすると、正しく撮影できないか、非常にハマりました。多分、詳しい方はコードの悪い点をすぐに気づかれるかと思いますが、初心者は視点が初心者なので、解決まではかなり遠回りしてしまいました。。。

原因と対策

 今回の原因はカメラの画像を取得するバッファのサイズが正しくなかったことが原因となります。カメラの起動時にサイズをリクエストしますが、そのサイズ通りのバッファサイズでないと、Texture2DのEncodeToJPG()が正しく動作しないようです。以下の通り、バッファ確保のサイズを変更します。

    // 取り込みサイズは4032×3024→960×720に変更すること
    Color32[] tempbuff = new Color32[960 * 720];

 このようにコードを変更して動かすと、正しく撮影できることを確認しました。

 

まとめ

 サイズを小さくしただけで画像がグレーになり、非常にあせりました。原因はこのサイズだと、WebCamTextureが正しく画像取得できないんじゃないんだろうかとか。色フォーマットがダメなんじゃないかとか、初心者前回でまったく明後日の方向を調査していました。。
 GetPixels32()では取得側でバッファを確保できるので、大きめに確保しておけばいいかと思っていたのですが、こんなところに落とし穴があるとは思いませんでした。。。

コメント