Размытие, резкость и шумоподавление

Меняя ядро свёртки, мы получаем целое семейство эффектов: сгладить, подчеркнуть, очистить от шума.

Шум — случайные отклонения яркости пикселей, не относящиеся к самому объекту (зернистость при слабом свете, артефакты сенсора).

Резкость: усилить отличие от соседей

Резкость — почти противоположность размытию. Ядро резкости даёт большой положительный вес центру и отрицательные соседям: пиксель усиливается там, где он сильно отличается от окружения (на границах). Применим ядро резкости к картинке с яркой точкой по центру (используем clamp, чтобы остаться в 0..255).

def clamp(v):
    return max(0, min(255, v))

def conv2d(image, kernel, divisor=1):
    H, W = len(image), len(image[0])
    pad = len(kernel) // 2
    out = []
    for y in range(pad, H - pad):
        row = []
        for x in range(pad, W - pad):
            acc = 0
            for ky in range(len(kernel)):
                for kx in range(len(kernel)):
                    acc += image[y - pad + ky][x - pad + kx] * kernel[ky][kx]
            row.append(clamp(round(acc / divisor)))
        out.append(row)
    return out

image = [
    [50, 50, 50, 50, 50],
    [50, 50, 50, 50, 50],
    [50, 50, 200, 50, 50],
    [50, 50, 50, 50, 50],
    [50, 50, 50, 50, 50],
]
sharpen = [
    [ 0, -1,  0],
    [-1,  5, -1],
    [ 0, -1,  0],
]

print("Резкость:")
for r in conv2d(image, sharpen):
    print(r)

Вывод:

Резкость:
[50, 0, 50]
[0, 255, 0]
[50, 0, 50]

Яркая точка (200) стала ещё ярче (255 после обрезки), а соседи вокруг провалились в 0 — контраст на границе подчёркнут. Так работает усиление резкости: оно акцентирует перепады.

Гауссово размытие против простого

Простое усреднение даёт всем соседям равный вес — картинка размывается, но появляются грубые «прямоугольные» артефакты. Гауссово размытие взвешивает соседей по расстоянию: ближние важнее дальних. Ядро напоминает «колокол». Результат мягче и естественнее, поэтому в OpenCV почти всегда используют именно его.

# Идея: ядро Гаусса 3x3 (веса больше к центру)
[1, 2, 1]
[2, 4, 2]   делитель = 16
[1, 2, 1]

Шум и медианный фильтр

Особый враг — импульсный шум («соль и перец»): отдельные пиксели стали чисто белыми или чёрными. Усреднение тут плохо: оно размазывает белую точку по соседям, оставляя серое пятно. Лучше работает медианный фильтр: он берёт окрестность, сортирует значения и выбирает среднее по порядку (медиану). Одиночный выброс просто отбрасывается.

import statistics

# окрестность 3x3 вокруг пикселя, в центре — «соль» (255)
neighborhood = [48, 52, 50, 51, 255, 49, 50, 47, 53]

avg = round(statistics.mean(neighborhood))
med = statistics.median(neighborhood)
print("Среднее (размывает выброс):", avg)
print("Медиана (убирает выброс):  ", med)

Вывод:

Среднее (размывает выброс): 73
Медиана (убирает выброс):   50

Среднее «заразилось» выбросом 255 и подскочило до 73, исказив пиксель. Медиана же дала 50 — настоящее значение фона. Поэтому против «соли и перца» берут медиану, а не усреднение.

Итог

  • Резкость — ядро с большим центром и отрицательными краями; подчёркивает перепады.
  • Гауссово размытие взвешивает соседей по расстоянию — мягче простого усреднения.
  • Импульсный шум («соль и перец») лучше всего убирает медианный фильтр.
  • Медиана устойчива к выбросам, среднее — нет.
Проверьте себя
1. Почему медианный фильтр лучше усреднения против шума «соль и перец»?
AОн работает быстрее
BОн повышает разрешение
CОн выбирает медиану окрестности и просто отбрасывает одиночный выброс, а не размазывает его
DОн добавляет цвет
2. Чем гауссово размытие отличается от простого усреднения?
AОно делает картинку резче
BОно не использует ядро
CОно работает только с цветом
DОно взвешивает соседей по расстоянию (ближние важнее), давая более мягкий результат
3. Что характерно для ядра повышения резкости?
AБольшой положительный вес в центре и отрицательные у соседей
BВсе веса равны
CВсе веса нулевые
DТолько один вес по углу