如何计算像素周围像素的平均值?

时间:2018-02-27 05:03:48

标签: python-3.x numpy image-processing noise-reduction

我正在尝试创建一个5x5均值滤镜,以从图像中去除一些盐和胡椒噪音。我把图像读成一个numpy数组。并尝试进行一些更改以计算像素邻居的平均值。我得到的结果非常糟糕,我似乎无法弄清楚为什么我的图像结果存在差距。

from PIL import Image
import numpy

image1 = 'noisy.jpg'
save1 = 'filtered.jpg'

def average(path, name):
    temp=Image.open(path)
    image_array = numpy.array(temp)
    new_image = []
    for i in range(0, len(image_array)):
        new_image.append([])
    n = 0
    average_sum = 0
    for i in range(0, len(image_array)):
        for j in range(0, len(image_array[i])):
            for k in range(-2, 3):
                for l in range(-2, 3):
                    if (len(image_array) > (i + k) >= 0) and (len(image_array[i]) > (j + l) >= 0):
                        average_sum += image_array[i+k][j+l]
                        n += 1

            new_image[i].append(int(round(average_sum/n)))
            average_sum = 0
            n = 0

    x = Image.fromarray(numpy.array(new_image), 'L')
    x.save(name)
    print("done")

average(image1, save1)

---------------------输入图片-----------------

Input Image

---------------------输出图片-----------------

enter image description here

1 个答案:

答案 0 :(得分:3)

我只想警告其他发现此页面的人。基本上,没有人应该somenumpyarray[y,x]来直接直接访问像素值。每次键入类似的内容时,Numpy必须创建4个新的Python对象(包含RGB值的tuple对象,以及每个R / G / B值三个单独的int对象)。那是因为在Python中,一切是一个对象(甚至数字都是对象),这意味着不能“直接从Numpy读取数据”。相反,必须创建实际的Python 对象,并且每次 都会将Numpy数据(例如数字)复制到这些对象中从Numpy读一些东西到Python。

您尝试从数组读取的每个像素有4个Python对象创建。对于1080p图像,如果仅读取一次8 294 400对象的每个像素。但是上面的代码检查每个像素周围的5x5(25像素)矩阵,因此创建了207360 000个对象!疯了!

此对象创建称为装箱(获取本机Numpy数据并将其打包/装箱在Python对象数据结构中)。取消装箱(获取Python数据,提取其中包含的实际值(例如数字)并将其打包到本机Numpy数组中)。从Python读取/写入Numpy数组中的值总是涉及装箱和拆箱,这就是为什么极其速度慢,并且您应该始终使用本地Numpy方法来处理数据的原因。对于我们来说,Numpy并不是一个通用的“数组”,就像对待任何随机访问的Python列表一样。 Numpy旨在使用其自身的内置函数进行矢量/矩阵运算!实际上,您甚至都不要执行for X in some_ndarray,因为迭代会调用相同的慢屁股拳击过程(这样的循环中的每个X项目都会从Numpy中提取并装箱)。

无论如何...您要实现的是5x5的“盒子模糊”,即在5x5正方形半径内所有附近像素的平均值。

因此,您应该使用本机C ++库,该库在纯净的RAM中完成所有工作,而完全不涉及Python。 OpenCV是这样的一个库,它接受ndarray(您的图像像素),并在内部直接从ndarray拥有的RAM中读取数据,并直接在本地对每个像素进行操作。

代码如下:

import cv2

path = "noisy.jpg"
img = cv2.imread(path)
img = cv2.blur(img, (5,5)) # This is now your box-blurred image.

1920x1080图像中的基准:

  • 您的原始代码(滥用Numpy数组导致死亡,并创建了超过2亿个新的Python对象): 87.80000秒(并且这还没有算出随着创建超过2亿个对象)
  • 改为使用OpenCV: 0.02992秒(快2935倍)

永远不要直接访问Numpy数组元素。不是这个意思。

祝所有未来的读者好运。


编辑:顺便说一句,要回答最初的问题...以消除盐和胡椒粉的噪音,您应该使用中位数过滤器,而不是盒子模糊。

输入:

input

5x5框模糊(又名均值/平均模糊):

img = cv2.blur(img, (5,5))

blur

3x3中值模糊:

img = cv2.medianBlur(img, 3)

median