python-3.x - 如何计算像素周围像素的平均值?

标签 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

最佳答案

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

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

此对象创建称为装箱(获取 native Numpy 数据并将其打包/装箱到 Python 对象数据结构中)。以及拆箱(获取 Python 数据,提取其中包含的实际值(例如数字)并将其打包到 native Numpy 数组中)。从 Python 读取/写入 Numpy 数组中的值总是涉及装箱和拆箱,这就是为什么它非常慢,您应该始终使用原生 Numpy 方法来操作您的数据. Numpy 不是我们可以像对待任何随机访问 Python 列表一样对待的通用“数组”。 Numpy 旨在使用其自己的内置函数进行向量/矩阵运算!事实上,你甚至不应该为 X in some_ndarray 执行 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 数组元素。这不是为了那个。

祝 future 的读者好运。


编辑:顺便说一句,要回答原来的问题...要去除椒盐噪声,您应该使用中值过滤器而不是框模糊。

输入:

input

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

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

blur

3x3 中值模糊:

img = cv2.medianBlur(img, 3)

median

关于python-3.x - 如何计算像素周围像素的平均值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49001470/

相关文章:

python - QWidget::setLayout:尝试在 ProgramWindow ""上设置 QLayout "",该窗口已经有布局

python - 从 2D 数组中沿 axis=0 滑动的滑动窗口给出具有动态重叠的 3D 数组

python - 仅在内部使用派生类型的 F2PY 包装过程

Python 笔画宽度变换

c++ - OpenCV - 两个完全不同的图像使用 BFMatcher 进行更多匹配

c++ - 如何在opencv中声明存储0到1之间的浮点值的Mat

未安装的 python 模拟和库

python - Django接受QueryParams(必须是可调用错误)

python - 退出脚本后如何返回到 python shell?

python - timedelta 不支持的类型