如何使用3x3卷积核平滑图像

3

我正在尝试对一张图片进行平滑处理,通过遍历其像素,计算3x3补丁区域的平均值,然后将该平均值应用到此补丁中的所有9个像素。

代码:

    import matplotlib.pyplot as plt
    import numpy as np
    import cv2 as cv
    from PIL import Image


    # 1. Load Image
    name = 'ebay.png'
    img = cv.imread(name) #import image

    h, w = img.shape[:2]


    # 2. Smooth with kernel size 3

    for y in range(0, w, 3):
        for x in range(0, h, 3):     
            px1 = img[x][y] #0/0
            px2 = img[x][y+1] #0/1
            px3 = img[x][y+2] #0/2
            px4 = img[x+1][y] #1/0
            px5 = img[x+1][y+1] #1/1
            px6 = img[x+1][y+2] #1/2
            px7 = img[x+2][y] #2/0
            px8 = img[x+2][y+1] #2/1
            px9 = img[x+2][y+2] #2/2

            average = np.average(px1 + px2 + px3 + px4 + px5 + px6 + px7 + px8 + px9)    

            img[x][y] =  average  #0/0
            img[x][y+1] = average   #0/1
            img[x][y+2] = average   #0/2
            img[x+1][y] = average   #1/0
            img[x+1][y+1] = average   #1/1
            img[x+1][y+2] = average  #1/2
            img[x+2][y] = average  #2/0
            img[x+2][y+1] = average   #2/1
            img[x+2][y+2] = average  #2/2

# 3. Transform the resulting image into pgm format and save result        

new_image = Image.fromarray(img)
new_image.save('new.png')

# 4. Show image

new_image.show()

However this just makes my new image just very pixely and not smooth at all. 

我觉得我在这里做错了什么:
average = np.average(px1 + px2 + px3 + px4 + px5 + px6 + px7 + px8 + px9)

由于我现在只使用px5作为平均值,所以新图像看起来更好(但仍然不是非常流畅)。请查看下面的图像:

原始图像: Original Image

我的代码目前正在做什么: What my code is doing right now...

当我使用px5作为平均值时的结果: Result when I am using px5 as average

将所有px相加并除以9的结果: enter image description here


你正在计算值的总和的平均值。当然,这等于值的总和。你应该先求和再除以9。但是首先要将其转换为较大的类型。添加9个uint8值可能会导致结果溢出。 - Cris Luengo
2
接下来,仅将平均值写入中间像素,而不是邻域中的所有3x3像素,并将其写入单独的输出图像。您不能在原地执行此操作,因为它会影响后面像素的结果。 - Cris Luengo
@CrisLuengo 谢谢。但是你说的“转换为更大的类型”是什么意思?我该怎么做? - Leonard Michalas
1
在模256算术中(在使用uint8进行计算时),200 + 200 = 145。我不知道从numpy数组中提取的数字会发生什么,但看起来这是你的问题。 float(px1)等应该解决问题。 - Cris Luengo
2个回答

4

我这里有两个问题,感谢 @Ernie Yang 和 @Cris Luengo 的帮助,让我成功解决了。

1) 我的平均值计算出现了问题,是因为像素值相加时溢出了。这就是为什么结果看起来很奇怪,因为它在回绕。所以我不得不进行更改:

average = np.average(px1 + px2 + px3 + px4 + px5 + px6 + px7 + px8 + px9)

to:

average = px1/9. + px2/9. + px3/9. + px4/9. + px5/9. + px6/9. + px7/9. + px8/9. + px9/9.

2) 然而,这并没有使我的图像变得更加平滑,因为我只是将块内所有9个像素的平均值分配给它们。所以这导致图片呈现出像素化的效果,并不平滑。因此,我不得不将平均值结果仅写入中央像素,而不是写入邻域内3x3个像素的所有像素。我还必须将结果写入一个单独的输出图像。你不能在原地进行这种操作,因为它会影响后面像素的结果。

正确的代码示例:

    import matplotlib.pyplot as plt
    import numpy as np
    import cv2 as cv
    from PIL import Image
    import scipy.ndimage as ndimage
    from scipy.ndimage.filters import gaussian_filter


    # 1. Load Image
    name = 'ebay.png'
    img = cv.imread(name) #import image


    h, w = img.shape[:2]
    smoothedImage = cv.imread(name) #initialize second image


    # 2. Smooth with with kernel size 3

    for y in range(0, w-2):
        for x in range(0, h-2):     
            px1 = img[x][y] #0/0
            px2 = img[x][y+1] #0/1
            px3 = img[x][y+2] #0/2
            px4 = img[x+1][y] #1/0
            px5 = img[x+1][y+1] #1/1
            px6 = img[x+1][y+2] #1/2
            px7 = img[x+2][y] #2/0
            px8 = img[x+2][y+1] #2/1
            px9 = img[x+2][y+2] #2/2

            average = px1/9. + px2/9. + px3/9. + px4/9. + px5/9. + px6/9. + px7/9. + px8/9. + px9/9.

            smoothedImage[x+1][y+1] = average   #1/1

    # 3. Transform the resulting image into pgm format and save result        

    new_image = Image.fromarray(smoothedImage)
    new_image.save('new.png')

    # 4. Show image

    new_image.show()

原始图片: enter image description here

平滑后的图片: enter image description here

编辑:

大家好,我从午睡中醒来了。 我有一些有趣的想法,现在是我改进后的代码:

import matplotlib.pyplot as plt
import numpy as np
import cv2 as cv
from PIL import Image
import scipy.ndimage as ndimage
from scipy.ndimage.filters import gaussian_filter


# 1. Load Image
name = 'ebay.png'
img = cv.imread(name) #import image
h, w = img.shape[:2]
kernel = 5
radius = (kernel-1)/2

img2 = np.zeros((h, w, 3), dtype = 'uint8') #new image to paint on

    def pxIsInImgRange(x, y):
        if (0<=x) and (x < w): 
                if (0<=y) and (y < h):
                    return True
        return False

    # 2. Smoothing the shit out

    for x in range (-radius, w+radius):
        for y in range (-radius, h+radius):

            if pxIsInImgRange(x,y): 

                    px = 0

                    for vx2 in range (-radius, radius+1):
                        for vy2 in range (-radius, radius+1):
                            x2 = x + vx2
                            y2 = y + vy2
                            if pxIsInImgRange(x2,y2):

                                px = px + (img[y2][x2]/float((kernel*kernel)))
                            else:
                                px = px + 0 


                    img2[y][x] = px

    # 3. Save image                

    new_image = Image.fromarray(img2)
    new_image.save('new.png')

    # 4. Show image

    new_image.show()

使用5个内核的新结果:

在此输入图片描述


2

谢谢您的快速回复。然而,将所有像素加起来,再除以9并不能解决我的问题。请查看更新后的问题。 - Leonard Michalas
尝试这样做:average2 = [np.average([px1 , px2 , px3 , px4 , px5 , px6 , px7 , px8 , px9])]*3 - Ernie Yang
结果与我的原始代码完全相同 :/ - Leonard Michalas
/9 的问题在于它们似乎是无符号的8位整数。因此,在进行求和时会发生溢出。这就是为什么结果看起来很奇怪,因为它正在环绕。 - Ernie Yang
2
尝试删除new.png文件,我发现我的更新有些问题,所以我在寻找旧文件。此外,这个代码需要工作:average = px1/9. + px2/9. + px3/9. + px4/9. + px5/9. + px6/9. + px7/9. + px8/9. + px9/9. - Ernie Yang
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接