はなちるのマイノート

Unityをメインとした技術ブログ。自分らしくまったりやっていきたいと思いますー!

【Unity】ComputeShaderでモーションフィルタを実装してみる【Q12】

はじめに

画像処理100本ノックの続きをやっていきましょう。

github.com


前回 -> 【Unity】ComputeShaderで移動平均フィルタ(平均化フィルタ)を実装してみる【Q11】 - はなちるのマイノート

次回 ->

今回はモーションフィルタについて取り組んでいきます。

f:id:hanaaaaaachiru:20200214210302p:plain

モーションフィルタとは

これは対角方向の平均値を取るフィルタです。

以下のようなフィルタを使えばOK。

f:id:hanaaaaaachiru:20200214205244p:plain

コード

ComputeShaderはこちら。

#pragma kernel MotionFilter

RWTexture2D<float4> Result;
Texture2D<float4> Texture;

[numthreads(8,8,1)]
void MotionFilter (uint3 id : SV_DispatchThreadID)
{
    float3x3 filter = (1.0 / 3.0) * float3x3(0, 0, 1, 0, 1, 0, 1, 0, 0);

    float4 upperLeft = Texture[id.xy + int2(-1, 1)] * filter[0][2];
    float4 up = Texture[id.xy + int2(0, 1)] * filter[1][2];
    float4 upperRight = Texture[id.xy + int2(1, 1)] * filter[2][2];
    float4 left = Texture[id.xy + int2(-1, 0)] * filter[0][1];
    float4 middle = Texture[id.xy] * filter[1][1];
    float4 right = Texture[id.xy + int2(1, 0)] * filter[2][1];
    float4 lowerLeft = Texture[id.xy + int2(-1, -1)] * filter[0][0];
    float4 down = Texture[id.xy + int2(0, -1)] * filter[1][0];
    float4 lowerRight = Texture[id.xy + int2(1, -1)] * filter[2][0];

    Result[id.xy] = upperLeft + up + upperRight + left + middle + right + lowerLeft + down + lowerRight;
}


CPU側のコードはこちら。

using UnityEngine;
using UnityEngine.UI;

public class MotionFilter : MonoBehaviour
{
    [SerializeField] private ComputeShader _computeShader;
    [SerializeField] private Texture2D _tex;
    [SerializeField] private RawImage _renderer;

    struct ThreadSize
    {
        public uint x;
        public uint y;
        public uint z;

        public ThreadSize(uint x, uint y, uint z)
        {
            this.x = x;
            this.y = y;
            this.z = z;
        }
    }

    private void Start()
    {
        if (!SystemInfo.supportsComputeShaders)
        {
            Debug.LogError("Comppute Shader is not support.");
            return;
        }

        // RenderTextueの初期化
        var result = new RenderTexture(_tex.width, _tex.height, 0, RenderTextureFormat.ARGB32);
        result.enableRandomWrite = true;
        result.Create();

        // AveragingFilterのカーネルインデックス(0)を取得
        var kernelIndex = _computeShader.FindKernel("MotionFilter");

        // 一つのグループの中に何個のスレッドがあるか
        ThreadSize threadSize = new ThreadSize();
        _computeShader.GetKernelThreadGroupSizes(kernelIndex, out threadSize.x, out threadSize.y, out threadSize.z);

        // GPUにデータをコピーする
        _computeShader.SetTexture(kernelIndex, "Texture", _tex);
        _computeShader.SetTexture(kernelIndex, "Result", result);

        // GPUの処理を実行する
        _computeShader.Dispatch(kernelIndex, _tex.width / (int)threadSize.x, _tex.height / (int)threadSize.y, (int)threadSize.z);

        // テクスチャを適応
        _renderer.texture = result;
    }
}

さいごに

モーションフィルタは正直使い道がよくわからないですね・・・。

次はMax-Minフィルタということで、今回よりは変化があって面白いかもしれません。

ではまた。