はじめに
画像処理100本ノックの続きをやっていきましょう。
前回 -> 【Unity】ComputeShaderでモーションフィルタを実装してみる【Q12】 - はなちるのマイノート
次回 -> 【Unity】ComputeShaderで微分フィルタを実装してみる【Q14】 - はなちるのマイノート
今回はMax-Minフィルタについて取り組んでいきます。
Max-Minフィルタとは
グレースケールに対してフィルタ内の画素の最大値と最小値の差を出力するフィルタです。
エッジフィルタの一つみたいですね。
実装
ComputeShader
はこちら。
#pragma kernel MaxMinFilter RWTexture2D<float4> Result; Texture2D<float4> Texture; [numthreads(32,16,1)] void MaxMinFilter (uint3 id : SV_DispatchThreadID) { float4 rgb2gray = float4(0.2126, 0.7152, 0.0722, 0); float gray[9] = { dot(Texture[id.xy + int2(-1, 1)],rgb2gray), dot(Texture[id.xy + int2(0, 1)],rgb2gray), dot(Texture[id.xy + int2(1, 1)],rgb2gray), dot(Texture[id.xy + int2(-1, 0)],rgb2gray), dot(Texture[id.xy],rgb2gray), dot(Texture[id.xy + int2(1, 0)],rgb2gray), dot(Texture[id.xy + int2(-1, -1)],rgb2gray), dot(Texture[id.xy + int2(0, -1)],rgb2gray), dot(Texture[id.xy + int2(1, -1)],rgb2gray) }; float maxValue = max(gray[0],max(gray[1], max(gray[2], max(gray[3], max(gray[4], max(gray[5], max(gray[6], max(gray[7], gray[8])))))))); float minValue = min(gray[0],min(gray[1], min(gray[2], min(gray[3], min(gray[4], min(gray[5], min(gray[6], min(gray[7], gray[8])))))))); float target = maxValue - minValue; Result[id.xy] = float4(target, target, target, 1); }
C#側のコードはこちら。
using UnityEngine; using UnityEngine.UI; public class MaxMinFilter : 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(); // カーネルインデックスを取得 var kernelIndex = _computeShader.FindKernel("MaxMinFilter"); // 一つのグループの中に何個のスレッドがあるか 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; } }
さいごに
最近知ったのですがiPhoneの多くではでは512個の並列処理ができるみたいらしく、今回は(32, 16, 1)にしてみました。
携帯端末でそれだけあるなら、おそらくパソコンならもっとできるのかもしれませんね。
どれくらいを目安にしていけば良いのかは少し調査してみようと思います。
ではまた。