Обработка изображений как 2D-сигналов
Расширяем DSP на два измерения: изображение — это сигнал, а фильтры — те же свёртки, только 2D.
Изображение — это двумерный сигнал
I[i][j]: яркость пикселя в строкеiи столбцеj. Фильтрация изображений — это 2D-свёртка с ядром-матрицей.
Всё, что мы изучили про одномерные сигналы, почти без изменений переносится на изображения — нужно лишь добавить второе измерение. Размытие, резкость, выделение краёв в фоторедакторах и в нейросетях компьютерного зрения — это 2D-свёртки. Понимание 1D-свёртки даёт прямой ключ к обработке изображений.
2D-свёртка: ядро скользит по картинке
Ядро — маленькая матрица (3×3, 5×5). Мы накладываем её на каждый пиксель, перемножаем перекрытые значения и суммируем — получаем новый пиксель. То же «скольжение и сумма», но в двух направлениях.
def conv2d(img, kernel):
H, W = len(img), len(img[0])
kh, kw = len(kernel), len(kernel[0])
pad = kh // 2
out = [[0] * W for _ in range(H)]
for i in range(H):
for j in range(W):
s = 0.0
for di in range(kh):
for dj in range(kw):
ii, jj = i + di - pad, j + dj - pad
if 0 <= ii < H and 0 <= jj < W:
s += img[ii][jj] * kernel[di][dj]
out[i][j] = round(s)
return out
# Маленькое изображение: яркий квадрат на тёмном фоне
img = [[10, 10, 10, 10],
[10, 90, 90, 10],
[10, 90, 90, 10],
[10, 10, 10, 10]]
blur = [[1/9, 1/9, 1/9],
[1/9, 1/9, 1/9],
[1/9, 1/9, 1/9]] # усреднение 3x3 = размытие
for row in conv2d(img, blur):
print(row)
Вывод:
[13, 24, 24, 13] [24, 46, 46, 24] [24, 46, 46, 24] [13, 24, 24, 13]
Резкая граница яркого квадрата «размазалась»: значения 10 и 90 смешались в промежуточные 13–46. Это 2D-ФНЧ — размытие. Ядро-усреднитель давит высокие пространственные частоты (резкие края).
Резкость и выделение краёв
Поменяем ядро — получим противоположные эффекты. Ядро резкости усиливает отличие пикселя от соседей; ядро Лапласа выделяет края (places где яркость резко меняется).
def conv2d(img, kernel):
H, W = len(img), len(img[0])
kh = len(kernel); pad = kh // 2
out = [[0] * W for _ in range(H)]
for i in range(H):
for j in range(W):
s = 0.0
for di in range(kh):
for dj in range(len(kernel[0])):
ii, jj = i + di - pad, j + dj - pad
if 0 <= ii < H and 0 <= jj < W:
s += img[ii][jj] * kernel[di][dj]
out[i][j] = round(s)
return out
img = [[10, 10, 10, 10],
[10, 90, 90, 10],
[10, 90, 90, 10],
[10, 10, 10, 10]]
edges = [[0, -1, 0],
[-1, 4, -1],
[0, -1, 0]] # оператор Лапласа — края
print("Края (Лаплас):")
for row in conv2d(img, edges):
print(row)
Вывод:
Края (Лаплас): [20, -70, -70, 20] [-70, 160, 160, -70] [-70, 160, 160, -70] [20, -70, -70, 20]
Большие по модулю значения (160, -70) появились именно на границах квадрата, где яркость резко меняется. Внутри однородных областей отклик был бы около нуля. Так детектор краёв «обводит» контуры объектов.
Как работает под капотом
У изображения есть пространственные частоты: плавные градиенты — низкие частоты, резкие края и мелкая текстура — высокие. Размытие (усредняющее ядро) — это 2D-ФНЧ, оно гасит высокие пространственные частоты. Резкость и края — 2D-ФВЧ, они выделяют высокие частоты. К изображениям применимо и 2D-ДПФ (и 2D-БПФ): его используют в сжатии (JPEG режет картинку на блоки 8×8 и применяет родственное преобразование DCT, отбрасывая малозаметные высокие частоты). А свёрточные нейросети (CNN) в компьютерном зрении — это, по сути, наборы обучаемых 2D-ядер: сеть сама подбирает ядра, выделяющие полезные признаки (края, углы, текстуры). То есть фундамент компьютерного зрения — ровно та свёртка, что мы реализовали.
Частые ошибки
- Не нормировать ядро размытия. Если сумма коэффициентов не равна 1, изображение потемнеет или пересветится.
- Игнорировать края изображения. У границ ядро «выходит» за картинку; нужна стратегия (нули, отражение, повтор краёв), иначе появятся артефакты.
- Путать размытие и понижение разрешения. Перед уменьшением картинки её сначала размывают (2D-антиалиасинг), иначе появится муар.
Итог
- Изображение — двумерный сигнал; фильтрация — 2D-свёртка с ядром-матрицей.
- Усредняющее ядро размывает (2D-ФНЧ), ядро Лапласа выделяет края (2D-ФВЧ).
- У картинок есть пространственные частоты; к ним применимо 2D-ДПФ (основа JPEG).
- Свёрточные нейросети — это обучаемые 2D-ядра; фундамент компьютерного зрения — свёртка.