Морфология: эрозия и дилатация

Морфология — это «лепка» формы на чёрно-белых масках: то сжать объект, то нарастить.

Морфологические операции — преобразования формы объектов на бинарном (чёрно-белом) изображении с помощью маленького шаблона — структурного элемента.

Где это нужно

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

  • Эрозия: пиксель остаётся объектом (1), только если все его соседи тоже объект. Иначе становится фоном. Эффект — объект «худеет», мелкие точки шума исчезают.
  • Дилатация: пиксель становится объектом, если хоть один сосед — объект. Эффект — объект «толстеет», дырки и разрывы заполняются.

Реализуем руками

img = [
    [0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0],
    [0,0,1,1,1,0,0],
    [0,0,1,1,1,0,0],
    [0,0,1,1,1,0,0],
    [0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0],
]

def show(m):
    for r in m:
        print("".join("#" if v else "." for v in r))

def neighbors(m, y, x):
    return [m[y + dy][x + dx] for dy in (-1, 0, 1) for dx in (-1, 0, 1)]

def erode(m):
    H, W = len(m), len(m[0])
    out = [[0] * W for _ in range(H)]
    for y in range(1, H - 1):
        for x in range(1, W - 1):
            out[y][x] = 1 if all(neighbors(m, y, x)) else 0
    return out

def dilate(m):
    H, W = len(m), len(m[0])
    out = [[0] * W for _ in range(H)]
    for y in range(1, H - 1):
        for x in range(1, W - 1):
            out[y][x] = 1 if any(neighbors(m, y, x)) else 0
    return out

print("Исходник (квадрат 3x3):")
show(img)
print("Эрозия (сжался до 1x1):")
show(erode(img))
print("Дилатация (разбух до 5x5):")
show(dilate(img))

Вывод:

Исходник (квадрат 3x3):
.......
.......
..###..
..###..
..###..
.......
.......
Эрозия (сжался до 1x1):
.......
.......
.......
...#...
.......
.......
.......
Дилатация (разбух до 5x5):
.......
.#####.
.#####.
.#####.
.#####.
.#####.
.......

Эрозия «съела» внешний слой пикселей — квадрат 3×3 ужался до точки. Дилатация, наоборот, нарастила слой — стал 5×5. Эффекты противоположны.

Открытие и закрытие

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

ОперацияПорядокЗачем
Открытие (opening)эрозия → дилатацияубрать мелкий шум, не меняя размер объекта
Закрытие (closing)дилатация → эрозиязаполнить дырки и разрывы внутри объекта

Открытие сначала «съедает» мелкие точки шума эрозией, а потом дилатация возвращает крупный объект к прежнему размеру. Закрытие работает зеркально — латает дыры. Эти операции — рабочая лошадка очистки масок перед поиском контуров и подсчётом объектов.

Итог

  • Эрозия сжимает объект (нужны все соседи) — убирает мелкий шум.
  • Дилатация наращивает (достаточно одного соседа) — заполняет дыры.
  • Открытие = эрозия+дилатация (чистит шум), закрытие = дилатация+эрозия (латает дыры).
  • Работают на бинарных масках, чистят результат пороговой бинаризации.
Проверьте себя
1. Что делает эрозия с бинарной маской?
AНаращивает объект и заполняет дыры
BСжимает объект и убирает мелкий шум: пиксель остаётся, только если все соседи — объект
CИнвертирует маску
DПоворачивает объект
2. Какая комбинация заполняет мелкие дырки внутри объекта, сохраняя его размер?
AОткрытие (эрозия → дилатация)
BЗакрытие (дилатация → эрозия)
CДвойная эрозия
DИнверсия
3. На каких изображениях обычно работают морфологические операции?
AТолько на цветных RGB
BНа бинарных (чёрно-белых) масках, например после пороговой бинаризации
CТолько на 16-битных
DНа любых, кроме серых
Поддержать проект