You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 23:43:26