高斯滤波后组合各种图像通道产生白色图像

Combining various image channels after gaussian filtering produces white image

我正在尝试为尺寸为 (256, 320, 4) 的图像实现高斯滤波器。

我首先为此生成了一个高斯内核,然后在 4 个通道中的每个通道上分别执行卷积,即在所有 256*320 灰度图像上。执行此操作后,我希望将图像组合成彩色图像。

但是,当我这样做时,它似乎没有按预期工作。期望看到原始图像的模糊版本,其模糊程度取决于 sigma 的值。但是,当我 运行 代码时,我只是得到 一个白色图像 ,没有任何模糊。

from PIL import Image
image = imageio.imread('graf_small.png')
print(image.shape)

def gaussian_filter(image, s):     
    
    probs = [np.exp(-z*z/(2*s*s))/np.sqrt(2*np.pi*s*s) for z in range(-3*s,3*s+1)] 
    kernel = np.outer(probs, probs) 
    channels = image.shape[2]
    final_output = np.ndarray((image.shape[0],image.shape[1], image.shape[2]))
    
    for i in range(4):
    
        channels = image.shape[2]
        im = np.ndarray((image.shape[0],image.shape[1]))
        print(channels)
        im[:,:] = image[:,:,i]
        #  generate a (2k+1)x(2k+1) gaussian kernel with mean=0 and sigma = s
        probs = [np.exp(-z*z/(2*s*s))/np.sqrt(2*np.pi*s*s) for z in range(-3*s,3*s+1)] 
        kernel = np.outer(probs, probs)
        # Cross Correlation
        # Gather Shapes of Kernel + Image + Padding
        xKernShape = kernel.shape[0]
        yKernShape = kernel.shape[1]
        xImgShape = im.shape[0]
        yImgShape = im.shape[1]


        strides= 1
        padding= 6

    # Shape of Output Convolution
        xOutput = int(((xImgShape - xKernShape + 2 * padding) / strides) + 1)
        yOutput = int(((yImgShape - yKernShape + 2 * padding) / strides) + 1)
        output = np.zeros((xOutput, yOutput))

        # Apply Equal Padding to All Sides
        if padding != 0:
            imagePadded = np.zeros((im.shape[0] + padding*2, im.shape[1] + padding*2))
            imagePadded[int(padding):int(-1 * padding), int(padding):int(-1 * padding)] = im
            #print(imagePadded)
        else:
            imagePadded = image

        # Iterate through image
        for y in range(image.shape[1]):
            # Exit Convolution
            if y > image.shape[1] - yKernShape:
                break
            # Only Convolve if y has gone down by the specified Strides
            if y % strides == 0:
                for x in range(image.shape[0]):
                    # Go to next row once kernel is out of bounds
                    if x > image.shape[0] - xKernShape:
                        break
                    try:
                        # Only Convolve if x has moved by the specified Strides
                        if x % strides == 0:
                            output[x, y] = (kernel * imagePadded[x: x + xKernShape, y: y + yKernShape]).sum()
                    except:
                        break
        final_output[:,:,i] = output[:,:]

final_output =np.dstack((final_output[:,:,0],final_output[:,:,1],final_output[:,:,2],final_output[:,:,3]))
        #print(merged.shape)
    return final_output

为了测试函数,调用了一个辅助函数>


def plot_multiple(images, titles, colormap='gray', max_columns=np.inf, share_axes=True):
    """Plot multiple images as subplots on a grid."""
    assert len(images) == len(titles)
    n_images = len(images)
    n_cols = min(max_columns, n_images)
    n_rows = int(np.ceil(n_images / n_cols))
    fig, axes = plt.subplots(
        n_rows, n_cols, figsize=(n_cols * 4, n_rows * 4),
        squeeze=False, sharex=share_axes, sharey=share_axes)

    axes = axes.flat
    # Hide subplots without content
    for ax in axes[n_images:]:
        ax.axis('off')
        
    if not isinstance(colormap, (list,tuple)):
        colormaps = [colormap]*n_images
    else:
        colormaps = colormap

    for ax, image, title, cmap in zip(axes, images, titles, colormaps):
        ax.imshow(image, cmap=cmap)
        ax.set_title(title)
        
    fig.tight_layout()

image = imageio.imread('graf_small.png')
sigmas = [2]
blurred_images = [gaussian_filter(image, s) for s in sigmas]
titles = [f'sigma={s}' for s in sigmas]

plot_multiple(blurred_images, titles)

似乎所有问题都是您在 float64 中获取图像,但 matplot 需要 uint8 才能显示它。

imageio 将其作为正确的图像保存在文件中,但带有警告 "Lossy conversion from float64 to uint8"

这两个问题都可以解决转换为 uint8

    return final_output.astype(np.uint8)

几乎没有小改动的完整工作代码

  • 我删除了 dstack
  • 我需要 size = output.shape[:2]final_output[:size[0],:size[1],i] = output[:,:]
import imageio
import numpy as np
import matplotlib.pyplot as plt


def gaussian_filter(image, s):     
    
    probs = [np.exp(-z*z/(2*s*s))/np.sqrt(2*np.pi*s*s) for z in range(-3*s,3*s+1)] 
    kernel = np.outer(probs, probs) 

    channels = image.shape[2]
    print('channels:', channels)
    
    final_output = np.ndarray((image.shape[0],image.shape[1], image.shape[2]))
    
    for i in range(channels):
    
        im = image[:,:,i]
        
        #  generate a (2k+1)x(2k+1) gaussian kernel with mean=0 and sigma = s
        probs = [np.exp(-z*z/(2*s*s))/np.sqrt(2*np.pi*s*s) for z in range(-3*s,3*s+1)] 
        kernel = np.outer(probs, probs)
        # Cross Correlation
        # Gather Shapes of Kernel + Image + Padding
        xKernShape = kernel.shape[0]
        yKernShape = kernel.shape[1]
        xImgShape = im.shape[0]
        yImgShape = im.shape[1]

        strides= 1
        padding= 6

        # Shape of Output Convolution
        xOutput = int(((xImgShape - xKernShape + 2 * padding) / strides) + 1)
        yOutput = int(((yImgShape - yKernShape + 2 * padding) / strides) + 1)
        output = np.zeros((xOutput, yOutput))

        # Apply Equal Padding to All Sides
        if padding != 0:
            imagePadded = np.zeros((im.shape[0] + padding*2, im.shape[1] + padding*2))
            imagePadded[int(padding):int(-1 * padding), int(padding):int(-1 * padding)] = im
            #print(imagePadded)
        else:
            imagePadded = image

        # Iterate through image
        for y in range(image.shape[1]):
            # Exit Convolution
            if y > image.shape[1] - yKernShape:
                break
            # Only Convolve if y has gone down by the specified Strides
            if y % strides == 0:
                for x in range(image.shape[0]):
                    # Go to next row once kernel is out of bounds
                    if x > image.shape[0] - xKernShape:
                        break
                    try:
                        # Only Convolve if x has moved by the specified Strides
                        if x % strides == 0:
                            output[x, y] = (kernel * imagePadded[x: x + xKernShape, y: y + yKernShape]).sum()
                    except:
                        break
                    
        size = output.shape[:2]
        
        final_output[:size[0],:size[1],i] = output[:,:]

    return final_output.astype(np.uint8)


def plot_multiple(images, titles, colormap='gray', max_columns=np.inf, share_axes=True):
    """Plot multiple images as subplots on a grid."""
    assert len(images) == len(titles)
    n_images = len(images)
    n_cols = min(max_columns, n_images)
    n_rows = int(np.ceil(n_images / n_cols))
    fig, axes = plt.subplots(
        n_rows, n_cols, figsize=(n_cols * 4, n_rows * 4),
        squeeze=False, sharex=share_axes, sharey=share_axes)

    axes = axes.flat
    # Hide subplots without content
    for ax in axes[n_images:]:
        ax.axis('off')
        
    if not isinstance(colormap, (list,tuple)):
        colormaps = [colormap]*n_images
    else:
        colormaps = colormap

    for ax, image, title, cmap in zip(axes, images, titles, colormaps):
        ax.imshow(image, cmap=cmap)
        ax.set_title(title)
        
    fig.tight_layout()
    plt.show()

# --- main --

image = imageio.imread('test/lenna.png')
print('shape:', image.shape)

sigmas = [2, 3, 5]
blurred_images = [gaussian_filter(image, s) for s in sigmas]
titles = [f'sigma={s}' for s in sigmas]

plot_multiple(blurred_images, titles)

for number, image in enumerate(blurred_images, 1):
    imageio.imsave(f'output-{number}.png', image)

来自维基百科的原始图片Lenna

结果: