使用Numpy复现Gaussian Blur遇难题:图像变暗与Alpha通道困惑
Numpy实现高斯模糊的问题:图像变暗与Alpha通道处理
我尝试用Numpy实现类似cv2.GaussianBlur的高斯模糊,但遇到两个问题:
- 随着标准差
sigma增大,图像会逐渐变暗 - 结果和OpenCV输出不一致,观察到
cv2.GaussianBlur会保留Alpha通道不做卷积,而我不确定如何处理Alpha通道
我的实现代码如下:
# 1D 高斯概率密度函数 def gauss(x, sigma): g= (1/(((2*np.pi)**0.5)*sigma))* np.exp((-x**2)/(2*sigma**2)) return g
def gaussian_filter(image, sigma, padding=True): ks= 2*(3*sigma)+1 # 核的尺寸,参考经验公式 image_result= np.zeros((image.shape[0], image.shape[1], image.shape[2])) if padding == True: # 填充以保持输出尺寸与输入一致 pad= int((ks-1)/2) image= np.pad(image, ((pad,pad),(pad,pad),(0,0))) k= np.arange(-(ks-1)/2, ks/2) # 生成中心为0的核尺寸数组 kernel= gauss(k, sigma) # 计算1D高斯值 kernel= kernel[:, np.newaxis] kernel= kernel@kernel.T # 利用2D高斯是两个1D高斯乘积的性质生成核 kernel=kernel[:,:, np.newaxis] h= kernel # 扩展核的通道数以匹配图像通道 for i in range(image.shape[2]-1): kernel= np.concatenate((kernel, h), axis=2) for i in range(image_result.shape[0]): # 卷积计算 for j in range(image_result.shape[1]): temp= image[i:i+ks, j:j+ks, :] temp= temp*kernel image_result[i,j] = (1/(ks*ks))*np.sum(np.sum(temp, axis= 0), axis=0) ''' ad= np.ones((image_result.shape[0], image_result.shape[1],1)) # 不确定Alpha通道处理方式,曾尝试设为最大值 image_result= np.concatenate((image_result[:,:,0:3],ad), axis=2) ''' return image_result
我的结果图显示:随着sigma增大,图像亮度明显降低。
问题解决思路
1. 图像变暗的原因与修复
代码中使用1/(ks*ks)进行归一化是错误的——高斯核的总和并不等于ks*ks,尤其是当sigma变化时,高斯核的权重总和会偏离1。正确的做法是将高斯核归一化到总和为1,这样卷积后的像素值不会被过度缩放导致变暗。
修改方式:
- 在生成2D高斯核后,添加归一化步骤:
kernel = kernel / np.sum(kernel) - 同时移除卷积步骤中的
(1/(ks*ks))缩放因子,因为核已经归一化过了。
2. Alpha通道的正确处理
OpenCV的cv2.GaussianBlur处理带Alpha通道的图像时,默认只会模糊RGB通道,Alpha通道保持原始值(用于控制透明度,通常不需要模糊)。因此我们可以拆分图像通道分别处理:
- 提取前3个RGB通道进行高斯模糊
- 直接复制原图像的Alpha通道到结果中
修改后的完整代码
import numpy as np def gauss(x, sigma): return (1/(((2*np.pi)**0.5)*sigma)) * np.exp((-x**2)/(2*sigma**2)) def gaussian_filter(image, sigma, padding=True): # 计算核尺寸,确保为奇数 ks = int(2 * (3 * sigma) + 1) if ks % 2 == 0: ks += 1 pad = int((ks - 1) / 2) # 拆分RGB和Alpha通道 if image.shape[2] == 4: rgb = image[..., :3] alpha = image[..., 3:4] else: rgb = image alpha = None # 对RGB通道处理 image_result_rgb = np.zeros_like(rgb) if padding: rgb_padded = np.pad(rgb, ((pad,pad),(pad,pad),(0,0))) else: rgb_padded = rgb # 生成归一化的2D高斯核 k = np.arange(-pad, pad + 1) kernel_1d = gauss(k, sigma) kernel_2d = np.outer(kernel_1d, kernel_1d) kernel_2d = kernel_2d / np.sum(kernel_2d) # 核归一化到总和为1 # 扩展核到3通道 kernel = np.repeat(kernel_2d[..., np.newaxis], 3, axis=2) # 卷积计算 for i in range(image_result_rgb.shape[0]): for j in range(image_result_rgb.shape[1]): temp = rgb_padded[i:i+ks, j:j+ks, :] temp = temp * kernel image_result_rgb[i,j] = np.sum(temp, axis=(0,1)) # 合并Alpha通道 if alpha is not None: image_result = np.concatenate([image_result_rgb, alpha], axis=2) else: image_result = image_result_rgb return image_result
额外优化说明
- 核尺寸计算时增加了奇偶校验,确保核是奇数尺寸(符合卷积填充的常规要求)
- 使用
np.outer替代矩阵乘法生成2D核,代码更简洁 - 用
np.repeat替代循环扩展核的通道数,效率更高 - 卷积时直接使用
np.sum(temp, axis=(0,1)),减少嵌套sum的冗余计算
内容的提问来源于stack exchange,提问作者DeadAsDuck
相关产品推荐
相关产品推荐

