曲率滤波的理论基础可以参考下曲率滤波的理论基础和应用,这篇博客介绍的思想完美的避开了一大堆数学公式,简直是我的福音,但还是要细看的,不然很容易忽略重点,我这里就简单的做个总结,所有图片(除了我最后画的那个丑丑的图片)均来源于前面这篇博客。
曲面上的一点有一切平面,此点处会有各个方向上的曲率,但是必然会有最大主曲率和最小主曲率,k1是最小主曲率,k2是最大主曲率,则平均曲率和高斯曲率的定义如下:
可展曲面可由其切平面局部估计而得。
前面我们已知,二维图像中的高斯曲率定义为两个主曲率的乘积,定理1告诉我们的是,任何可展曲面(虽然只有3个可展曲面)其任意点处的高斯曲率为零。因此,最小化其中任意一个主曲率,就相当于最小化高斯曲率!
以上结论即是高斯曲率滤波器的理论基础(我们即可实现高斯曲率的最小化而无需进行高斯曲率的显式计算):
切点的领域上必有一点也在切平面上,高斯滤波的作用是把不在这个切平面上的点可展到切平面上。
局部最小化主曲率绝对值的较小者,受限于相邻像素之间的关联性,因此,提出了一种域分解方法,以去除相邻像素之间的依赖性。(这里其实是保证在3*3的模块里相邻点没有相同的元素。)
选取的是三角构型用于表示曲面的切平面,下面以红色点x与其3×3邻域N(x)为例,如图6所示:
这是在此局部窗口中的一个三角切平面,得到此切平面之后,我们将红色点,即当前点拉长到绿色切平面上,就可以得到当前点到切平面的距离di,如图7所示:
实际上,以黑色圆形点为中心点,切平面过白色点的三角构型切平面还有3种,也就是说图8(a)中的切平面共有4种,如图9所示:
在一个3*3的模块中,三角构型切平面共有12种情况,但是仔细看图6和图7可以看出,到切平面的距离di的实际情况没有12种。以图10(a)为例说明一下,黑色圆点到切平面的距离实际上就是黑色圆点到 ”白色圆-黑色圆-白色圆” 线段的距离。
当前点到切平面的投影总共有8种,即当前点到切平面的距离共有8种,{di, i=1,2,…,8},这里使用8种投影距离的最小值作为投影算子。
from __future__ import division
import numpy as np
#曲率滤波
def update_gc(inputimg, i, j):
#对二维数组(图片)进行操作
#数组不取到最后一个像素点的原因,可以参考我后面画的图,因为在步长为2的情况下是取不到最后一个点的
inputimg_ij = inputimg[i:-1:2, j:-1:2]
#求di(根据公式写程序,基点是[i:-1:2, j:-1:2])
#d1中[i - 1:-2:2, j:-1:2] 也可以参考后面画的图,因为它是基点的左边一个点,是模块中心点正左方的点,所以取不到倒数第二个点
d1 = (inputimg[i - 1:-2:2, j:-1:2] + inputimg[i + 1::2, j:-1:2])/2.0
- inputimg[i:-1:2, j:-1:2]
d2 = (inputimg[i:-1:2, j - 1:-2:2] + inputimg[i:-1:2, j + 1::2])/2.0
- inputimg[i:-1:2, j:-1:2]
d3 = (inputimg[i - 1:-2:2, j - 1:-2:2] + inputimg[i + 1::2, j + 1::2])/2.0 - inputimg[i:-1:2, j:-1:2]
d4 = (inputimg[i - 1:-2:2, j + 1::2] + inputimg[i + 1::2, j - 1:-2:2])/2.0
- inputimg[i:-1:2, j:-1:2]
d5 = (inputimg[i - 1:-2:2, j:-1:2] + inputimg[i:-1:2, j - 1:-2:2]
-inputimg[i - 1:-2:2, j - 1:-2:2])
- inputimg[i:-1:2, j:-1:2]
d6 = (inputimg[i - 1:-2:2, j:-1:2] + inputimg[i:-1:2, j + 1::2]
- inputimg[i - 1:-2:2,j + 1::2])
- inputimg[i:-1:2, j:-1:2]
d7 = (inputimg[i:-1:2, j- 1:-2:2] + inputimg[i + 1::2, j:-1:2]
- inputimg[i + 1::2, j - 1:-2:2])
- inputimg[i:-1:2, j:-1:2]
d8 = (inputimg[i:-1:2, j + 1::2] + inputimg[i + 1::2, j:-1:2]
- inputimg[i + 1::2, j + 1::2])
- inputimg[i:-1:2, j:-1:2]
#找到di最小值并赋值给d
#条件满足则赋1不满足赋0,若d2
d = d1 * (np.abs(d1) <= np.abs(d2)) + d2 * (np.abs(d2) < np.abs(d1))
d = d * (np.abs(d) <= np.abs(d3)) + d3 * (np.abs(d3) < np.abs(d))
d = d * (np.abs(d) <= np.abs(d4)) + d4 * (np.abs(d4) < np.abs(d))
d = d * (np.abs(d) <= np.abs(d5)) + d5 * (np.abs(d5) < np.abs(d))
d = d * (np.abs(d) <= np.abs(d6)) + d6 * (np.abs(d6) < np.abs(d))
d = d * (np.abs(d) <= np.abs(d7)) + d7 * (np.abs(d7) < np.abs(d))
d = d * (np.abs(d) <= np.abs(d8)) + d8 * (np.abs(d8) < np.abs(d))
#list.append(d)
inputimg_ij[...] +=d
import scipy.misc
import numpy as np
#cf_update文件是上面的代码
from cf_update import update_gc
from skimage.color.adapt_rgb import adapt_rgb, each_channel
from PIL import Image
#找函数名,相当于路由
#字典类型 键值对 调用方法:updaterule.keys()调用键,updaterule.value()调用值
updaterule = { 'gc': update_gc}
@adapt_rgb(each_channel)
#定义一个带参数的函数
#inputimg调用的初始图片,filtertype是指调用的滤波器类型,total_iter迭代次数
def cf_filter(inputimg, filtertype, total_iter = 10, dtype = np.float32):
#断言 如果不满足括号内条件系统报错并提示后一句
assert(type(filtertype) is str), "input argument is not a string datatype!"
#判断调用的滤波器是否在路由中
assert(filtertype in updaterule.keys()), "filter type is not found!"
#不对原图片进行修改 复制一份进行操作
filteredimg = np.copy(inputimg.astype(dtype))
#获取到函数名
update = updaterule.get(filtertype)
#遍历
for iter_num in range(total_iter):
update(filteredimg, 1, 1)
update(filteredimg, 2, 2)
update(filteredimg, 1, 2)
update(filteredimg, 2, 1)
return filteredimg
im = Image.open('2.jpg').convert('L')
im.show()
#图片转数组对象
arr=np.array(im)
result=cf_filter(arr,'gc',total_iter = 3)
#array 反操作
pil_im = Image.fromarray(result)
pil_im.show()
for iter_num in range(total_iter):
update(filteredimg, 1, 1)
update(filteredimg, 2, 2)
update(filteredimg, 1, 2)
update(filteredimg, 2, 1)
return filteredimg
请先原谅我的作图能力为零,我会慢慢研究的,但是我现在没办法,只好这样画出来,以及我也知道我是小学生字体,emmm能看懂就行啦!
提到的模块就是上面那个3乘3的模块,这样可以遍历一张图片的每一个像素点,确保都能覆盖到(我这里是假设的像素为5乘5),从阴影部分也是能看出来每个像素块都被覆盖了。
首先是原图(就是我上一篇博客中加了椒盐噪声的结果图):
然后是迭代3次后的结果图:
然后是迭代10次:
最后是迭代20次:
高斯滤波在实践中10次就能看到结果,其最重要性能是保存可展曲面,对于不可展曲面其减少了绝对高斯曲率,自动平滑图像并保持边缘。
下一篇预告:(我也不知道是什么系列,就酱!)