双边过滤算法的hlsl实现
来源:互联网 发布:乐视网络电视app 编辑:程序博客网 时间:2024/06/14 13:29
我是基于这篇文章来实现的。
先说说最核心的公式:
i, j 代表当前的纹理坐标
g(i, j) 代表该纹理坐标上实际的输出的颜色值
k,l 代表(i, j) 周围的点的纹理坐标,即需要采样融合到(i, j)上的点的纹理坐标
代表k, l在一定范围内取值后,通过这个公式计算出来的值的累加和。(关于累加和的解释可以看这里)
这里的这个f(k, l) 表示的是(k, l)坐标上的点的颜色值。
w(i, j, k, l)表示权重系数。
权重系数为
定义域核:
和值域核:
的乘积:
定义域核中的,就是纹理坐标i 减去纹理坐标k ,再算平方。
只是一个固定参数,自己根据实际情况给他赋值即可,在我的项目里,这个参数取到20000,效果不错。
值域核中 f(i, j) 还有f(k, l)和上面核心的公式中的不同,这里的这个f(k, l) 表示的是一个算法f。在(k, l)纹理坐标上的点的rgb值相加之和。比如,对(k, l)纹理取样后的值保存在一个float4 AA中,则算法f 就等于 AA.r + AA.g + AA.b,f(i, j)也是一样。
公式解释完了之后,实现就容易了:
uniform Texture2D diffuseTexture;uniform float radius = 10; // 过滤半径,即k, l的取值范围uniform float2 tex_size; // 传入的纹理的尺寸SamplerState textureSampler{ AddressU = Clamp; AddressV = Clamp; Filter = Linear;};struct VertData{ float4 pos : SV_Position; float2 texCoord : TexCoord0;};float4 sample2rgba(float2 uv){float4 out_c = diffuseTexture.Sample(textureSampler, uv);// 因为采样取出来的值的范围是0~1de,而算法中是需要0~255的,因此这里做一下转换out_c.r = out_c.r * 255;out_c.g = out_c.g * 255;out_c.b = out_c.b * 255;// out_c.a = out_c.a * 255; // alpha不需要参与计算return out_c;}float4 main(VertData input) : SV_Target{float4 center = sample2rgba(input.texCoord);// (k, l)坐标下的采样颜色值float4 curPix;// 由于纹理坐标的取值范围是0~1的,相邻点的纹理坐标就无法计算了,因此这里把纹理坐标转成实际像素位置,这样偏移值直接+1,-1之类就行了float2 tex_coord = float2(input.texCoord.x * offset_x, input.texCoord.y * offset_y);float2 new_coord;float weight; // 权重float weight_sum = 0.0; // 权重累加和float4 c_cum; // 颜色值的累加和float temp1;float temp2;int k = 0, l = 0;for (k = -radius; k <= radius; ++k){for (l = -radius; l <= radius; ++l){// 计算出(k, l)的纹理坐标new_coord = float2(tex_coord.x + k, tex_coord.y + l);// 采样,因为new_coord为实际像素的位置,需要转换成0~1范围的坐标才能取样curPix = sample2rgba(float2(new_coord.x / offset_x, new_coord.y / offset_y));// 计算定义域核temp1 = -pow(tex_coord.x - new_coord.x, 2);temp1 += pow(tex_coord.y - new_coord.y, 2);temp1 /= sigma_space;// 计算值域核temp2 = -pow(center.r + center.g + center.b - curPix.r - curPix.g - curPix.b, 2);temp2 /= sigma_color;// 计算当前的权重temp1 = temp1 + temp2;weight = exp(temp1);// 颜色值的累加c_cum += curPix * weight;// 权重的累加weight_sum += weight;}}// 累加颜色除以累加权重,就是需要的颜色值了c_cum = c_cum / weight_sum;c_cum = c_cum / 255.0; // 算法是用0~255范围的来算的,所以这里需要再转回0~1的范围c_cum.a = 1.0; // 固定alpha值return c_cum;}
升级过的算法:
uniform Texture2D diffuseTexture;uniform float radius = 10; // 过滤半径,即k, l的取值范围uniform float2 tex_size; // 传入的纹理的尺寸SamplerState textureSampler{ AddressU = Clamp; AddressV = Clamp; Filter = Linear;};struct VertData{ float4 pos : SV_Position; float2 texCoord : TexCoord0;};float4 sample2rgba(float2 uv){ float4 out_c = diffuseTexture.Sample(textureSampler, uv); // 因为采样取出来的值的范围是0~1的,而算法中是需要0~255的,因此这里做一下转换 out_c.r = out_c.r * 255; out_c.g = out_c.g * 255; out_c.b = out_c.b * 255; // out_c.a = out_c.a * 255; // alpha不需要参与计算 return out_c;}float4 main(VertData input) : SV_Target{ float4 center = sample2rgba(input.texCoord); // (k, l)坐标下的采样颜色值 float4 curPix;// 由于纹理坐标的取值范围是0~1的,相邻点的纹理坐标就无法计算了,因此这里把纹理坐标转成实际像素位置,这样偏移值直接+1,-1之类就行了float2 tex_coord = float2(input.texCoord.x * offset_x, input.texCoord.y * offset_y);float2 new_coord;float weight; // 权重float weight_sum = 0.0; // 权重累加和float4 c_cum; // 颜色值的累加和float temp1;float temp2;int k = 0, l = 0;// 横向部分取值计算for (k = -radius; k <= radius; ++k){// 计算出(k, l)的纹理坐标new_coord = float2(tex_coord.x + k, tex_coord.y + l);// 采样,因为new_coord为实际像素的位置,需要转换成0~1范围的坐标才能取样curPix = sample2rgba(float2(new_coord.x / offset_x, new_coord.y / offset_y));// 计算定义域核temp1 = -pow(tex_coord.x - new_coord.x, 2);temp1 += pow(tex_coord.y - new_coord.y, 2);temp1 /= sigma_space;// 计算值域核temp2 = -pow(center.r + center.g + center.b - curPix.r - curPix.g - curPix.b, 2);temp2 /= sigma_color;// 计算当前的权重temp1 = temp1 + temp2;weight = exp(temp1);// 颜色值的累加c_cum += curPix * weight;// 权重的累加weight_sum += weight;}// 纵向部分取值计算k = 0;for (l = -radius; l <= radius; ++l){// 计算出(k, l)的纹理坐标new_coord = float2(tex_coord.x + k, tex_coord.y + l);// 采样,因为new_coord为实际像素的位置,需要转换成0~1范围的坐标才能取样curPix = sample2rgba(float2(new_coord.x / offset_x, new_coord.y / offset_y));// 计算定义域核temp1 = -pow(tex_coord.x - new_coord.x, 2);temp1 += pow(tex_coord.y - new_coord.y, 2);temp1 /= sigma_space;// 计算值域核temp2 = -pow(center.r + center.g + center.b - curPix.r - curPix.g - curPix.b, 2);temp2 /= sigma_color;// 计算当前的权重temp1 = temp1 + temp2;weight = exp(temp1);// 颜色值的累加c_cum += curPix * weight;// 权重的累加weight_sum += weight;}// 累加颜色除以累加权重,就是需要的颜色值了c_cum = c_cum / weight_sum;c_cum = c_cum / 255.0; // 算法是用0~255范围的来算的,所以这里需要再转回0~1的范围c_cum.a = 1.0; // 固定alpha值return c_cum;}
运行后,效率还行,虽然有些掉帧,但还是能接受的。
后记:
对于公式,相信还是有很多人跟我一样,一看就蒙圈了,但是网上的很多贴了公式的文章,都只是草草的解释了下公式,有很多对于大神而言算是基础的,或者说是常识的东西,就直接一笔带过了,这让我们这种算法小白来说,看的就比较费劲了。这里当然不是说怪大神们不好,像这种基础的,常识的东西本来就应该了然于心的,只是这一次我走了次弯路,无视算法,先去尝试直接翻译他们的实现,以至于问题产生问题,无法解决,而不懂的还是不懂。最后实在没有办法了,才去尝试看懂算法,看懂了之后,实现,调整就都顺利了。
最后再记一些过程中发现的一些东西:
opencv是个挺牛逼的开源第三方库。它里面有双边过滤的算法实现源码。这里有一篇详细讲解该代码的文章,这里
这里还有一篇用CUDA实现的,貌似是针对CUDA做过优化的算法实现,这里
- 双边过滤算法的hlsl实现
- 使用hlsl实现bump map的算法
- 双边滤波算法的原理、流程、实现及效果
- 双边滤波算法的原理、流程、实现及效果
- 双边滤波算法的原理、流程、实现及效果
- 双边滤波算法的原理、流程、实现及效果
- 双边滤波算法介绍与实现
- o(1)复杂度之双边滤波算法的原理、流程、实现及效果。
- o(1)复杂度之双边滤波算法的原理、流程、实现及效果。
- 算法整理(二)---快速排序的两种实现方式:双边扫描和单边扫描
- 复杂度之双边滤波算法的原理、流程、实现及效果。
- o(1)复杂度之双边滤波算法的原理、流程、实现及效果。
- R实现MapReduce的协同过滤算法
- R实现MapReduce的协同过滤算法
- R实现mapreduce的协同过滤算法
- 协同过滤算法的Python实现
- 系统过滤算法的原理及实现
- 双边滤波器的原理及实现
- Web前端总结-----CSS基础详解(一)
- Spring MVC 跳转问题
- 10 Minutes to pandas----十分钟搞定Pandas
- MOS管驱动电路——电机干扰与防护处理
- U3D 0822 碰撞体触发
- 双边过滤算法的hlsl实现
- HDU4296
- swift WKWebView 某些网址无法加载
- 漫步微积分二十四——定积分引言
- Alcatraz插件管理工具
- javascript设计模式-singleton(单例)模式
- C# 枚举
- 使用开源库 MagicalRecord
- 实时刷新 fis3 --无名小码农