unity3d shader之Roberts,Sobel,Canny 三種邊緣檢測方法


方法其實都差不多,就是用兩個過濾器,分別處理兩個分量

Sobel算子

先說Sobel算子
 
GX為水平過濾器,GY為垂直過濾器,垂直過濾器就是水平過濾器旋轉90度。
過濾器為3x3的矩陣,將與圖像作平面卷積。
如果不存在邊則兩個點顏色很接近,過濾器返回一個較小的值,否則就可判斷出邊緣的存在。
當前點為中間點

具體計算如下:

 
求出圖像的每一個像素的橫向及縱向灰度值通過以下公式結合,來計算該點灰度的大小
 

 

本shdaer將G值作為顏色輸出

Roberts算子

Roberts算子與之相似
過濾器是2x2的矩陣
過濾器如下:
 
當前點為左上角的點
具體計算如下:

 

Canny算子

過濾器是2x2的矩陣
 

 



以sobel為例:
看看shader的實現


在frag函數中


float3 lum = float3(0.2125,0.7154,0.0721);
轉化為luminance亮度值的變量

           

 

獲取當前點的周圍的點,並與luminance點積,求出亮度值(黑白圖)
float mc00 = dot(tex2D (_MainTex, i.uv_MainTex-fixed2(1,1)/_Size).rgb, lum);
float mc10 = dot(tex2D (_MainTex, i.uv_MainTex-fixed2(0,1)/_Size).rgb, lum);
float mc20 = dot(tex2D (_MainTex, i.uv_MainTex-fixed2(-1,1)/_Size).rgb, lum);
。。。。。。。        
由於CG函數tex2DSize函數(獲取圖片長寬的像素數)在unity中不能用,我也不知道用什么函數來替代它,就弄了個外部變量_Size方便調節。
如果有什么函數能代替tex2DSize函數各位看官一定要告訴我。

   

 

根據過濾器矩陣求出GX水平和GY垂直的灰度值
float GX = -1 * mc00 + mc20 + -2 * mc01 + 2 * mc21 - mc02 + mc22;

float GY = mc00 + 2 * mc10 + mc20 - mc02 - 2 * mc12 - mc22;

 

G = sqrt(GX*GX+GY*GY);
標准灰度公式
G = abs(GX)+abs(GY);

近似灰度公式

 

c = length(float2(GX,GY));
length的內部算法就是灰度公式的算法,歐幾里得長度

float length(float3 v)
{
  return sqrt(dot(v,v));
}

c即是最終輸出

讓我們看看效果:

可以看見對於這種簡單的卡通,三種算法都非常清晰,sobel和roberts稍好一點。

再看看復雜一些的圖片

 

Roberts的紋路非常清晰,有一些噪聲

Sobel比Roberts還要清晰,噪聲相對少些

canny已經沒法看了,噪聲太多,邊緣判斷的不清楚

 

 

綜上,從結果來看,sobel算子的實現效果最好

 

下面給出sobel的shader:

Shader "Custom/sobel" {
	Properties {
		_MainTex ("MainTex", 2D) = "white" {}
		_Size("Size", range(1,2048)) = 256//size
		
	}
	SubShader {
		pass{
		Tags{"LightMode"="ForwardBase" }
		Cull off
		CGPROGRAM
		#pragma vertex vert
		#pragma fragment frag
		#include "UnityCG.cginc"

		float _Size;
		sampler2D _MainTex;
		float4 _MainTex_ST;
		struct v2f {
			float4 pos:SV_POSITION;
			float2 uv_MainTex:TEXCOORD0;
			
		};

		v2f vert (appdata_full v) {
			v2f o;
			o.pos=mul(UNITY_MATRIX_MVP,v.vertex);
			o.uv_MainTex = TRANSFORM_TEX(v.texcoord,_MainTex);
			return o;
		}
		float4 frag(v2f i):COLOR
		{
			float3 lum = float3(0.2125,0.7154,0.0721);//轉化為luminance亮度值
			//獲取當前點的周圍的點
			//並與luminance點積,求出亮度值(黑白圖)
			float mc00 = dot(tex2D (_MainTex, i.uv_MainTex-fixed2(1,1)/_Size).rgb, lum);
			float mc10 = dot(tex2D (_MainTex, i.uv_MainTex-fixed2(0,1)/_Size).rgb, lum);
			float mc20 = dot(tex2D (_MainTex, i.uv_MainTex-fixed2(-1,1)/_Size).rgb, lum);
			float mc01 = dot(tex2D (_MainTex, i.uv_MainTex-fixed2(1,0)/_Size).rgb, lum);
			float mc11mc = dot(tex2D (_MainTex, i.uv_MainTex).rgb, lum);
			float mc21 = dot(tex2D (_MainTex, i.uv_MainTex-fixed2(-1,0)/_Size).rgb, lum);
			float mc02 = dot(tex2D (_MainTex, i.uv_MainTex-fixed2(1,-1)/_Size).rgb, lum);
			float mc12 = dot(tex2D (_MainTex, i.uv_MainTex-fixed2(0,-1)/_Size).rgb, lum);
			float mc22 = dot(tex2D (_MainTex, i.uv_MainTex-fixed2(-1,-1)/_Size).rgb, lum);
			//根據過濾器矩陣求出GX水平和GY垂直的灰度值
			float GX = -1 * mc00 + mc20 + -2 * mc01 + 2 * mc21 - mc02 + mc22;
			float GY = mc00 + 2 * mc10 + mc20 - mc02 - 2 * mc12 - mc22;
		//	float G = sqrt(GX*GX+GY*GY);//標准灰度公式
			float G = abs(GX)+abs(GY);//近似灰度公式
//			float th = atan(GY/GX);//灰度方向
			float4 c = 0;
//			c = G>th?1:0;
//			c = G/th*2;
			c = length(float2(GX,GY));//length的內部算法就是灰度公式的算法,歐幾里得長度

			return c;
		}
		ENDCG
		}//

	} 
}


                                            -----------------------by  wolf96  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM