[Unity] カメラ画像の輝度を取得する

カメラから映し出された画像から輝度を求める方法の紹介です。
Unityで照度センサみたいなものを実装できないかと考えたのがきっかけです。

実装方針

カメラ画像の輝度を求めるには、画像のピクセルデータを取得する必要があります。
結論から言うと、カメラの描画先(TargetTexture)にRenderTextureを設定し、このRenderTextureからピクセルデータを取得できます。
ピクセルデータを取得する方法はこちらの記事で紹介しています。

注意点としては、ディスプレイに描画しているカメラ(TargetTextureがnull)にはこの方法は適用できないことです。
TargetTextureにRenderTextureを指定すると、ディスプレイにカメラ画像が描画されなくなってしまうからです。
そのため、ディスプレイに描画しているカメラに適用したい場合は前者と同じCameraがアタッチされたGameObjectを同じTransformで持たせる必要があります。

実装手順

1.RenderTexture作成

輝度の取得対象となるRenderTextureをAssets配下に作成します。
既に存在している場合はそれを使いまわしても構いません。

lightsense1

このRenderTextureに設定する解像度は輝度の計算量に直結します。
計算量を抑えたい場合は解像度を低めに設定、逆に正確な輝度を求めたい場合は高めに設定すると良いでしょう。
今回は32×32ピクセルの解像度に設定します。

lightsense2

2.Cameraオブジェクトの配置

RenderTextureへ描画する専用のCameraがアタッチされたGameObjectを作成します。
今回はMain Cameraの子オブジェクトに対してCameraを持たせることにします。
CameraのTargetTextureには1.のRenderTextureを指定してください。

lightsense3

3.輝度取得スクリプト実装&追加

以下スクリプトを2.のGameObjectにアタッチします。

using UnityEngine;
using System.Collections;

// 照度を計算するスクリプト
public class LightSensor : MonoBehaviour {
    public Camera dispCamera;
    private Texture2D targetTexture;

    public float lightValue;

    // Use this for initialization
    IEnumerator Start() {
        var tex = dispCamera.targetTexture;
        targetTexture = new Texture2D(tex.width, tex.height, TextureFormat.ARGB32, false);

        while ( true ) {
            // RenderTextureキャプチャ
            RenderTexture.active = dispCamera.targetTexture;

            yield return new WaitForEndOfFrame();

            targetTexture.ReadPixels(new Rect(0, 0, tex.width, tex.height), 0, 0);
            targetTexture.Apply();


            // 照度を取得する
            lightValue = GetLightValue(targetTexture);
        }
    }

    // 画像全体の照度計算
    float GetLightValue(Texture2D tex) {
        var cols = tex.GetPixels();

        // 平均色計算
        Color avg = new Color(0, 0, 0);
        foreach ( var col in cols ) {
            avg += col;
        }
        avg /= cols.Length;

        // 照度計算
        return 0.299f * avg.r + 0.587f * avg.g + 0.114f * avg.b;
    }
}

dispCameraには2.のCameraをドラッグ&ドロップで指定してください。

lightsense4

これでうまく実行できればLightSensor.lightValueから輝度を参照できるようになります。

輝度はRenderTextureの平均色を求め、そこからRGB→輝度変換式により求めています。
LightSensor.lightValueを実際に表示してみると以下の動画のようになります。
(表示方法の説明は割愛させていただきます)

カメラ画像の視野角をうまく調整すればカメラが向いている先の色を取得できたりします。
太陽のフレア効果の演出などに使えるかもしれませんね。