GPUで自動二値化を実装中です

Boltzmanです.
まだ完成してはいませんが, 中間報告です.
現在, GPUを用いた大津の手法による自動閾値決定二値化アルゴリズムを実装中です.
GPUプログラミングは初めてなので, どのようなコードがGPUと相性がいいのか模索しながらの実装となっています.
とりあえず現時点で一応動きはしたものを以下に載せます.

<br />
// Each #kernel tells which function to compile; you can have many kernels<br />
#pragma kernel CSMain</p>
<p>// Create a RenderTexture with enableRandomWrite flag and set it<br />
// with cs.SetTexture</p>
<p>StructuredBuffer&lt;int&gt; histogram;<br />
RWStructuredBuffer&lt;float2&gt; result; //x:thresh, y:variable</p>
<p>float num1, num2;  //画素数<br />
float sum1, sum2;  //和<br />
float m1, m2;      //平均</p>
<p>[numthreads(8,8,1)]<br />
void CSMain (uint3 id : SV_DispatchThreadID)<br />
{<br />
	float w = 0;<br />
	uint i=0;<br />
	num1=0;<br />
	num2=0;<br />
	sum1=0;<br />
	sum2=0;<br />
	m1=0;<br />
	m2=0;</p>
<p>	for(i=0; i&lt;id.x; i++){<br />
		num1 += histogram[i];<br />
		sum1 += histogram[i]*i;<br />
	}<br />
	m1 = sum1/num1;</p>
<p>	for(i=id.x; i&lt;256; i++){<br />
		num2 += histogram[i];<br />
		sum2 += histogram[i]*i;<br />
	}<br />
	m2 = sum2/num2;</p>
<p>	w = num1 * num2 * (m1 &#8211; m2) * (m1 &#8211; m2);</p>
<p>	if(isfinite(w)){<br />
		result[id.x] = float2( (float)id.x, w);<br />
	}<br />
	else{<br />
		result[id.x] = float2((float)id.x, 0);<br />
	}</p>
<p>}<br />

まだまだ改良の余地が多く, 現在全体を改めて見直している最中です. 特に, 上のコードではnumやsumを愚直に計算してしまっているため, この辺りを何とかしたいです.
また本当はif文を使いたくなかったのですが, m1, m2を計算する際にゼロ割が生じるケースがあり, それによって分離度がNaNになってしまうと最大値を求める際に支障をきたすため, NaNを消すためにif文を導入しました. 他に良い方法はないだろうか…

最大値の算出は

<br />
// Each #kernel tells which function to compile; you can have many kernels<br />
#pragma kernel CSMain</p>
<p>// Create a RenderTexture with enableRandomWrite flag and set it<br />
// with cs.SetTexture<br />
RWStructuredBuffer&lt;float2&gt; data;</p>
<p>[numthreads(8,8,1)]<br />
void CSMain (uint3 id : SV_DispatchThreadID)<br />
{<br />
	float2 w = float2(1, 0);<br />
	w = lerp(data[id.x*2], data[id.x*2+1], step(data[id.x*2].y, data[id.x*2+1].y));</p>
<p>	AllMemoryBarrierWithGroupSync();<br />
	data[id.x] = w;<br />
}<br />

このようになっていますが, こちらもやはり全体を書き換えている最中です.
上のコードでは, 2n番目と2n+1番目を比較して, 大きいほうをn番目に格納し, それを繰り返すことで最大値がn=0に格納されるようにしています.
saidaiti
このコードで初めてHLSLの同期を使いました. 同期って他のやつの進行を待つことになるので, 一番遅い奴に足並みがそろうことになります. なのであまり使わないほうがいいのかと思いましたが, コードを調べてみたら割とがっつり使っているものが多かったので気にしなくてもいいのでしょうか…

上のコードを見直すと同時に, ラベリング処理の並列化についても少し調べています.
論文を見てみるとCPUを並列させたほうが速いらしいです. しかしながら, 今までの処理をGPUでやっているため, ラベリングをCPUで計算するとなるとデータをCPUへ移さなければならず, そのコストを考えて総合的に評価するとGPUのほうが速くなる可能性があります.
一度GPUでやると抜け出せなくなりますね…

そういえばGPUは流体力学と相性がいいそうです. 私は詳しくないのですが, 流体に詳しい人から少しだけ話を聞きました. まあ紹介程度に.

引き続き実装頑張ります.

Posted on