Я просто хочу предупредить всех, кто найдет эту страницу. По сути, никто никогда не должен делать somenumpyarray[y,x]
для прямого доступа к значениям пикселей по одному. Каждый раз, когда вы вводите что-то подобное, Numpy должен создавать 4 новых объекта Python (объект tuple
, содержащий значения RGB, и три отдельных объекта int
для каждого значения R/G/B). Это связано с тем, что в Python все является объектом (даже числа являются объектами), а это означает, что данные нельзя «просто прочитать непосредственно из Numpy». Вместо этого должны быть созданы фактические объекты Python, а данные Numpy (такие как числа) копируются в эти объекты каждый раз, когда вы пытаетесь прочитать что-нибудь из Numpy в Python.
Это 4 создания объекта Python на пиксель, который вы пытаетесь прочитать из массива. Для изображения 1080p, если вы ТОЛЬКО читаете каждый пиксель ОДИН РАЗ, это 8 294 400 объектов. Но приведенный выше код проверяет матрицу 5x5 (25 пикселей) вокруг каждого пикселя, так что получается 207 360 000 объектов! Безумие!
Это создание объекта известно как упаковка (взятие собственных данных Numpy и их упаковка/упаковка в структуру данных объекта Python). А также распаковка (сбор данных Python, извлечение фактического значения, которое они содержат (например, число), и упаковка их в собственный массив Numpy). Чтение/запись значений в массиве Numpy из Python всегда включает в себя упаковку и распаковку, поэтому это чрезвычайно медленно, и вместо этого вы должны всегда использовать собственные методы Numpy для работы с вашими данными. . Numpy НЕ является «массивом» общего назначения, который мы должны рассматривать как любой список Python с произвольным доступом. Numpy предназначен для векторных/матричных операций с использованием собственных встроенных функций! Фактически, вы никогда не должны даже делать for X in some_ndarray
, потому что итерация вызывает такой же медленный процесс упаковки (каждый элемент X
в таком цикле извлекается из Numpy и упаковывается).
В любом случае... то, что вы пытаетесь реализовать, представляет собой «размытие коробки» 5x5, что означает среднее значение всех соседних пикселей в пределах квадратного радиуса 5x5.
Поэтому вам следует использовать нативную библиотеку C++, которая выполняет все это в чистой, чистой оперативной памяти, вообще не задействуя Python. Одной из таких библиотек является OpenCV, которая принимает ndarray (пиксели вашего изображения) и внутренне считывает непосредственно из ОЗУ, принадлежащего ndarray, и изначально работает непосредственно с каждым пикселем.
Вот код:
import cv2
path = "noisy.jpg"
img = cv2.imread(path)
img = cv2.blur(img, (5,5)) # This is now your box-blurred image.
Тесты на изображении 1920x1080:
- Ваш исходный код (который до смерти злоупотребляет массивом Numpy и создает более 200 миллионов новых объектов Python): 87,80000 секунд (и это даже не считая злоупотребления ОЗУ, которое происходит при создании более 200 миллионов объектов)
- Вместо этого используется OpenCV: 0,02992 секунды (в 2935 раз быстрее)
Никогда, никогда не обращайтесь к элементам массива Numpy напрямую. Он не для этого предназначен.
Удачи всем будущим читателям.
Редактировать: Кстати, чтобы ответить на исходный вопрос... чтобы удалить шум соли и перца, вы должны использовать медианные фильтры вместо размытия прямоугольника.
Ввод:
а>
Размытие прямоугольника 5x5 (также известное как среднее/среднее размытие):
img = cv2.blur(img, (5,5))
а>
Среднее размытие 3 x 3:
img = cv2.medianBlur(img, 3)
а>
person
Mitch McMabers
schedule
05.10.2019