はじめに
画像処理100本ノックの続きをやっていきましょう。
前回 -> 【Unity】ComputeShaderで平均プーリングをする【Q7】 - はなちるのマイノート
次回 -> 【Unity】ComputeShaderでガウシアンフィルタを実装してみる【Q9】 - はなちるのマイノート
今回はMaxプーリングについて取り組んでいきます。
Maxプーリングとは
Maxプーリングとは8*8
ピクセルといったグリッド単位で画像を分割してその領域内を最大値で埋めることです。
ほとんどの仕組みは前回の平均プーリングと同じなので、細かいところはそちらを参照してみてください。
コード
ComputeShader
はこちら。
#pragma kernel MaxPooling RWTexture2D<float4> Result; Texture2D<float4> Texture; int2 Grid; [numthreads(8,8,1)] void MaxPooling (uint3 id : SV_DispatchThreadID) { float3 maxValue = float3(0,0,0); for(int i = 0; i < Grid.x; i++){ for(int j = 0; j < Grid.y; j++){ int2 index = int2((id.x * Grid.x) + i, (id.y * Grid.y) + j); maxValue.x = max(maxValue.x, Texture[index.xy].x); maxValue.y = max(maxValue.y, Texture[index.xy].y); maxValue.z = max(maxValue.z, Texture[index.xy].z); } } for(int i = 0; i < Grid.x; i++){ for(int j = 0; j < Grid.y; j++){ int2 index = int2((id.x * Grid.x) + i, (id.y * Grid.y) + j); Result[index.xy] = float4(maxValue.x, maxValue.y, maxValue.z, 1); } } }
CPU側のコードはこちら。
using UnityEngine; using UnityEngine.UI; public class MaxPooling : 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; } // グリッドを「8ピクセル*8ピクセル」で分割 int[] grid = new int[] { 8, 8 }; // RenderTextueの初期化 var result = new RenderTexture(_tex.width, _tex.height, 0, RenderTextureFormat.ARGB32); result.enableRandomWrite = true; result.Create(); // MaxPoolingのカーネルインデックス(0)を取得 var kernelIndex = _computeShader.FindKernel("MaxPooling"); // 一つのグループの中に何個のスレッドがあるか 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); _computeShader.SetInts("Grid", grid); // GPUの処理を実行する _computeShader.Dispatch(kernelIndex, (_tex.width / (int)threadSize.x) / grid[0], (_tex.height / (int)threadSize.y) / grid[1], (int)threadSize.z); // テクスチャを適応 _renderer.texture = result; } }
さいごに
最大値を求めるときにif文を用いる方法もありますが、HLSLではif文は遅いらしいです。
そこで今回はmax
という組み込み関数を用いています。(実際に早くなっているか分かりませんが)
HLSLリファレンスまとめ
次は画像処理の代表格?だと勝手に思っているフィルタ関連になっていきます。
たたみ込みなどの今までと違ってより理論的な分野になりますが、興味があるかたは是非みてみてください。
ではまた。