Медиана Реализация Фильтра В Python


Я реализован медианный фильтр в Python для того, чтобы удалить соль и перец шума из изображений. Он работает нормально и все, но я хотел бы услышать ваши мнения или советы.

def median_filter(data, filter_size):
    temp = []
    indexer = filter_size // 2
    for i in range(len(data)):

        for j in range(len(data[0])):

            for z in range(filter_size):
                if i + z - indexer < 0 or i + z - indexer > len(data) - 1:
                    for c in range(filter_size):
                        temp.append(0)
                else:
                    if j + z - indexer < 0 or j + indexer > len(data[0]) - 1:
                        temp.append(0)
                    else:
                        for k in range(filter_size):
                            temp.append(data[i + z - indexer][j + k - indexer])

            temp.sort()
            data[i][j] = temp[len(temp) // 2]
            temp = []
    return data


Комментарии
2 ответа

Комментарий код

Peilonrays указывает на смешение с аутом тестирования, что является допустимым. Заявление if j ... должно быть внутри цикла for k .... Одним из результатов является то, что вы добавить разное количество элементов temp в зависимости от того, какие границы вы находитесь. Но есть лучшие способы, чтобы избежать из-за границы индексации, см. ниже.

Ваша самая большая ошибка в том, что вы пишете результат фильтра на изображение обрабатывается. Медианная фильтрация не может быть сделано на месте. При обновлении data[i][j]ты прочитаешь обновленное значение для расчета data[i][j+1]. Вам нужно выделить новый образ, и напишите результат здесь.

Я бы предложил не добавлять нули из-за границы пикселей на всех, потому что он вводит уклоном к выходному пикселей вблизи границы. Ярчайший пример-для пикселей близко к любому из углов. На углу пиксела, с ядром 3х3, вы будете иметь 4 пикселей изображения, охватываемые ядра. При добавлении 5 нулей для вне границ пикселов гарантирует, что на выходе будет 0. Для больших ядер это происходит в больше пикселей, конечно. Вместо этого, это легко просто удалить temp.append(0) высказывания, ведущие к предвзятым результатам. Другие варианты для чтения значений из других источников изображения, например зеркального изображения на границе или расширение изображения путем экстраполяции. Для медианной фильтрации это мало влияет, ИМО.

Вы установить temp = [] в самом начале вашей функции, а затем сбросить его, когда вы закончите его использования, в подготовке к следующему циклу. Вместо этого, как только инициализировать его внутри основного двойного цикла над пикселами:

for i in range(len(data)):
for j in range(len(data[0])):
temp = []
# ...

Ты обходишь i и j как изображение показателей, то за z и c или k для фильтрации ядра показателей. c и k имеют ту же функцию в двух разных петель, я предложил бы использовать ту же переменную для этого. z не очень вписывается в с c или k. Я бы выделил два имени, которые относятся друг к другу так, что i и j являются, например, m и n. Выбор имен переменных-это всегда очень ограничена, если это всего одна буква. Используя более длинные имена бы сделать этот код более ясным: например img_column, img_row, kernel_column, kernel_row.


Из-за проверки границ

На этом завершаются мои комментарии на ваш код. Теперь я хотел бы предложить некоторые альтернативы для проверки границ. Эти тесты стоят довольно дорого, когда выполняется для каждого пикселя -- это тест, который делается \$N к\$ раза (с \ФП\$ пикселов в изображении и $\к\$ пиксели в ядре). Возможно, в Python добавленную стоимость относительно невелика, это интерпретируемый язык, но на компилируемом языке эти тесты можно легко квалифицировать как удвоение времени обработки. Есть 3 альтернативы, которые я знаю. Я буду использовать border = filter_size // 2и предполагаю filter_size странно. Можно настроить все 3 метода даже по размеру фильтров.

Отдельные петли для изображения пограничных пикселей

Идея здесь заключается в том, что петлю над первой и последней border пикселей по каждому измерению обрабатываются отдельно от петли за основу изображения. Это позволяет избежать всех тестов. Но это требует некоторого дублирования кода (все во имя скорости!).

for i in range(border):
# here we loop over the kernel from -i to border+1
for i in range(border, len(data)-border):
# here we loop over the full kernel
for i in range(len(data)-border, len(data)):
# here we loop over the kernel from -border to len(data)-i

Конечно, в каждую из тех петель, подобный набор из 3 петель надо перебрать j. Таким образом, логику фильтра повторяется 9 раз. В компилируемых языках, где это наиболее эффективный способ, дублирование кода можно избежать с помощью встроенных функций или макросов. Я не знаю, как Python-функции вызова сравнивает кучу анализов из-за границы открыть, поэтому не могу комментировать целесообразность этого способ в Python.

Отдельного кода для границы пикселей

Идея здесь состоит в том, чтобы сделать из-за границы, проверяющие только для тех пикселей, которые находятся близко к границе изображения. Для точек вблизи границы, вы используете версию логика фильтрации с проверкой границ. Для точек в центре изображения (которых подавляющее большинство пикселей), то используйте второй вариант логика не выходит за рамки проверки.

for i in range(len(data)):
i_border = i < border or i >= len(data)-border
for j in range(len(data[0])):
j_border = j < border or j >= len(data)-border
if i_border or j_border:
# filtering with bounds checking
else:
# filtering without bounds checking

Обивка образ

Самое простое решение, а также самые гибкие в том, чтобы создать временный образ, который больше, чем входное изображение 2*border вдоль каждого измерения, и копию входного изображения в него. "Новые" пиксели могут быть заполнены нулями (чтобы повторить то, что ОП собирается делать), или значениями, полученными из входного изображения (например, путем зеркального отображения изображения на границе или экстраполируя в какой-то другой способ).

Теперь фильтр не должен проверять вне границ читает. Когда ядро фильтрации находится над любой из входных пикселей изображения, все образцы попадают в мягкие изображения.

Поскольку для этого типа фильтрации нужно создать новую вывода изображения в любом случае (это не возможно, чтобы вычислить его на месте, как я уже говорил), это не огромные затраты: исходное изображение теперь может быть повторно использован в качестве выходного изображения.

Это решение приводит к простейший код, позволяет за всякие границы, методы расширения без усложнения кода фильтрации, и часто приводит к слишком быстрый код.

3
ответ дан 14 апреля 2018 в 06:04 Источник Поделиться

Вы, кажется, есть несколько ошибок.



  1. if i + z - indexer < 0 or i + z - indexer > len(data) - 1:

    Если i и z являются 0, где indexer 1, то вы будете иметь 0 + 0 - 1 < 0. Это будет означать, что вы бы заменить данные (-1, j), (0, j) и (1, j) до 0. Начиная с 0 и 1, вероятно, содержат сведения это просто неправильно.



  2. if j + z - indexer < 0 or j + indexer > len(data[0]) - 1:
    temp.append(0)

    Это удаляет некоторые данные, это означает, что медиана сдвигается. Говорят, что вы должны иметь (0, 0, 0, 1, 2, 3)однако вы удалили первые три из-за этого вы бы (0, 1, 2, 3). Сейчас медиана 1 а не 0.



Ваш код будет проще, если вы:


  1. Сделал список окон, который будет содержать все индексы, которые вы хотите переместить.

  2. Есть если, чтобы проверить, если данные, что индекс находится вне границ.

  3. Если это запрещено по умолчанию 0.

  4. Если это не вне границ использования данных.

Это может стать:

def median_filter(data, filter_size):
temp = []
indexer = filter_size // 2
window = [
(i, j)
for i in range(-indexer, filter_size-indexer)
for j in range(-indexer, filter_size-indexer)
]
index = len(window) // 2
for i in range(len(data)):
for j in range(len(data[0])):
data[i][j] = sorted(
0 if (
min(i+a, j+b) < 0
or len(data) <= i+a
or len(data[0]) <= j+b
) else data[i+a][j+b]
for a, b in window
)[index]
return data

3
ответ дан 13 апреля 2018 в 03:04 Источник Поделиться