介绍
课程的最后一个实验是处理雀斑,网上查找了很多方法,最后我选择了快速双边滤波。但是实验又不能直接调用 opencv 的库,因此,我参照了 这个博客,将用 C 写的快速双边滤波改用 Python 重新写了一遍。
快速双边滤波是啥呢,官方滴说,它是一种非线性的滤波方法。它最大的特点就是既使用了颜色的相似度,又利用了空间的距离相似度。也就是说,它在进行滤波的过程中,不光要考虑周围像素值与中点像素值的大小之差,还需要考虑空间上的距离,进而确定该点对中间点的影响因子。
至于为啥要用快速双边滤波呢,,因为不优化的话,实在是太慢了。。。有兴趣可以试一下。
优化代码
主要的优化思路如下,提前计算好高斯模板,提前计算好各灰度值的滤波模板
代码实现
完整的代码如下:
# -*- coding: UTF-8 -*-
import numpy as np
import cv2
import math
def bilateralFilter(img, radius, sigmaColor, sigmaSpace) :
B, G, R = cv2.split(img)
B_tran, G_tran, R_tran = cv2.split(img)
img_height = len(B)
img_width = len(B[0])
# 计算灰度值模板系数表
color_coeff = -0.5 / (sigmaColor * sigmaColor)
weight_color = [] # 存放颜色差值的平方
for i in range(256) :
weight_color.append(np.exp(i * i * color_coeff))
# 计算空间模板
space_coeff = -0.5 / (sigmaSpace * sigmaSpace)
weight_space = [] # 存放模板系数
weight_space_row = [] # 存放模板 x轴 位置
weight_space_col = [] # 存放模板 y轴 位置
maxk = 0
for i in range(-radius, radius+1) :
for j in range(-radius, radius+1) :
r_square = i*i + j*j
r = np.sqrt(r_square)
weight_space.append(np.exp(r_square * space_coeff))
weight_space_row.append(i)
weight_space_col.append(j)
maxk = maxk + 1
# 进行滤波
for row in range(img_height) :
for col in range(img_width) :
value = 0
weight = 0
for i in range(maxk) :
m = row + weight_space_row[i]
n = col + weight_space_col[i]
if m < 0 or n < 0 or m >= img_height or n >= img_width :
val = 0
else :
val = B[m][n]
w = np.float32(weight_space[i]) * np.float32(weight_color[np.abs(val - B[row][col])])
value = value + val * w
weight = weight + w
B_tran[row][col] = np.uint8(value / weight)
# 绿色通道
for row in range(img_height) :
for col in range(img_width) :
value = 0
weight = 0
for i in range(maxk) :
m = row + weight_space_row[i]
n = col + weight_space_col[i]
if m < 0 or n < 0 or m >= img_height or n >= img_width :
val = 0
else :
val = G[m][n]
w = np.float32(weight_space[i]) * np.float32(weight_color[np.abs(val - G[row][col])])
value = value + val * w
weight = weight + w
G_tran[row][col] = np.uint8(value / weight)
# 红色通道
for row in range(img_height) :
for col in range(img_width) :
value = 0
weight = 0
for i in range(maxk) :
m = row + weight_space_row[i]
n = col + weight_space_col[i]
if m < 0 or n < 0 or m >= img_height or n >= img_width :
val = 0
else :
val = R[m][n]
w = np.float32(weight_space[i]) * np.float32(weight_color[np.abs(val - R[row][col])])
value = value + val * w
weight = weight + w
R_tran[row][col] = np.uint8(value / weight)
cv2.imshow("beauty_after", cv2.merge([B_tran, G_tran, R_tran]))
cv2.imwrite("beauty_after.png", cv2.merge([B_tran, G_tran, R_tran]))
img = cv2.imread("beauty1.png")
cv2.imshow("original image", img)
# bilateralFilter(img, 5, 45, 100)
bilateralFilter(img, 3, 30, 80)
img = cv2.imread("beauty_after.png")
bilateralFilter(img, 3, 30, 80)
cv2.waitKey(0)
总结
三个维度的滤波不应该像我这样写,应该一起处理,我分成了三个通道进行处理,代码就显得有点长,但这样十分方便,如果有时间的话,还是不要像我这样写。
需要注意的一点是,这段代码在处理图片的效果上不是很好,相比于 opencv 的库,效果差了很多,尤其是处理大图片的时候,使用时需要注意。