はじめに
相変わらずこちらの画像処理100本ノックをしています。
前回 -> 【Unity】ComputeShaderでグレースケールをしてみた【Q2】 - はなちるのマイノート
次回 -> 【Unity】ComputeShaderで大津の2値化をしてみる【Q4】 - はなちるのマイノート
今回は画像の2値化について取り上げていきたいとおもいます。
2値化とは
2値化とは白黒のみで画像を表現することです。
ただどんな条件で白,黒にさせるかはいろんな種類の方法があったりします。
今回は以下の手順で実現したいと思います。
まずはピクセルの色をグレースケール化します。
Y = 0.2126 R + 0.7152 G + 0.0722 B
この情報をもとに、ここでのグレースケールは0 ~ 1
なので128/255
より小さければを0
,それ以上なら1
とみなします。
※イメージ if(Y < 128 / 255){ B = 0; }else{ B = 1; }
コード
ComputeShader
はこんな感じ。
#pragma kernel Binarization RWTexture2D<float4> Result; Texture2D<float4> Texture; [numthreads(8,8,1)] void Binarization (uint3 id : SV_DispatchThreadID) { float gray = 0.2126 * Texture[id.xy].x + 0.7152 * Texture[id.xy].y + 0.0722 * Texture[id.xy].z; gray = (gray < 128.0 / 255.0) ? 0 : 1; Result[id.xy] = float4(gray, gray, gray, 1); }
つぎにC#のCPU側のコードはこんな感じ。
using UnityEngine; using UnityEngine.UI; public class Binarization : MonoBehaviour { [SerializeField] private ComputeShader _computeShader; [SerializeField] private Texture2D _tex; [SerializeField] private RawImage _renderer; private RenderTexture _result; 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の初期化 _result = new RenderTexture(_tex.width, _tex.height, 0, RenderTextureFormat.ARGB32); _result.enableRandomWrite = true; _result.Create(); // Binarizationのカーネルインデックス(0)を取得 var kernelIndex = _computeShader.FindKernel("Binarization"); // 一つのグループの中に何個のスレッドがあるか 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; } private void OnDestroy() { _result = null; } }
さいごに
2値化はグレースケールさえできていれば簡単でしたね。
またHLSLでは条件分岐は遅いらしいので、3項演算子の方がいいらしいです。(本当かはよく分かっていません)
次は大津の二値化なので、もう少し理論的なものになってきます。
もし興味があればそちらもどうぞ。
ではまた。