Как применить простые ядра к изображениям с помощью Python и OpenCV

Я пытался изучить компьютерное зрение с помощью Python и OpenCV, и я всегда натыкался на термины ядро ​​ и свертка.

Сначала я попытался полагаться на эти гифки и некоторые краткие объяснения, но я часто путаюсь с их использованием, поэтому решил взглянуть на них получше.

В этой статье я поделюсь кое-чем из того, что я узнал о ядрах и свертках, исследуя некоторые из их основных приложений, таких как размытие, повышение резкости, дистилляция и эрозия.

Ядра и свертки

Как и я, вы можете подумать, что это как-то связано с кукурузой, но это не так.

Ядра в компьютерном зрении - это матрицы, используемые для свертки наших данных. Давай попробуем сломать это.

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

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

Таким образом, в матрице 3x3 на каждый пиксель влияют только пиксели вокруг него, тогда как более дальние пиксели 7x7 изменили бы его.

Хорошо, поэтому, чтобы применить его к изображению, мы должны: расположить его над заданным пикселем, используя его точку привязки, выполнить некоторую операцию между значениями в ядрах и значениями пикселей, определить новое значение для этого пикселя и переместить к следующему.

Резюмируя:

  • Мы можем фильтровать и изменять изображения, взаимодействуя с их пикселями;
  • Это взаимодействие может происходить с извилинами;
  • Эти свертки используют ядра для описания того, как будут затронуты пиксели;
  • Ядра представляют собой область для каждой операции, значения / веса и точку привязки;

Руки вверх

Хватит разговоров, давайте посмотрим, как мы можем использовать эти ядра.
Мы будем использовать OpenCV, Numpy и Matplotlib.

import cv2
import numpy as np
import matplotlib.pyplot as plt

После импорта библиотек мы можем построить исходное изображение, чтобы знать, что меняется.

image = cv2.imread('Images/6.jpg')
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
fig, ax = plt.subplots(1, figsize=(12,8))
plt.imshow(image)

Круто, теперь давайте сделаем ядро ​​для размытия изображения. Мы будем использовать Numpy, чтобы построить матрицу единиц 3x3 и разделить ее на 9.

>>> kernel = np.ones((3, 3), np.float32) / 9
>>> kernel
...
array([[0.11111111, 0.11111111, 0.11111111],
       [0.11111111, 0.11111111, 0.11111111],
       [0.11111111, 0.11111111, 0.11111111]], dtype=float32)

Мы можем применить его к изображению с помощью .filter2D.

img = cv2.filter2D(image, -1, kernel)
fig, ax = plt.subplots(1, figsize=(12,8))
plt.imshow(img)

Давайте попробуем это с ядром Sharpen.

kernel = np.array([[0, -1, 0],
                   [-1, 5, -1],
                   [0, -1, 0]])

Используя .filter2D, мы можем применять линейные фильтры к любому ядру, которое захотим. Но есть и более удобные способы достижения этих результатов.

Легкий путь

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

Мы можем использовать .blur, чтобы применить размытие рамки, и нам просто нужно передать изображение и размер ядра.

image = cv2.imread('Images/6.jpg')
image = cv2.blur(img, (5,5))
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
fig, ax = plt.subplots(1, figsize=(12,8))
plt.imshow(image)

Гауссово и медианное размытие:

image = cv2.imread('Images/6.jpg')
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
fig, ax = plt.subplots(1, figsize=(12,16))
ax = plt.subplot(211)
g = cv2.GaussianBlur(image, (3,3), 0)
plt.imshow(g)
ax = plt.subplot(212)
m = cv2.medianBlur(image, 3)
plt.imshow(m)

Расширения и эрозии

Ядра также могут помочь расширять и размывать изображения - кажется, это полезно при работе с изображениями с текстом.

image = cv2.imread('Images/9.png')
# convert to black and white
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) 
r ,image = cv2.threshold(image, 150, 255, cv2.THRESH_BINARY)
# create kernel
kernel = np.ones((5,5), np.uint8)
fig, ax = plt.subplots(1, figsize=(16,12))
# original
ax = plt.subplot(232)
plt.imshow(image, cmap='Greys')
plt.title('original')
# erosion
e = cv2.erode(image, kernel)
ax = plt.subplot(234)
plt.imshow(e, cmap='Greys')
plt.title('erosion')
# dilation
d = cv2.dilate(image, kernel)
ax = plt.subplot(235)
plt.imshow(d, cmap='Greys')
plt.title('dilation')
# morphological gradient (dilation - erosion)
m = cv2.morphologyEx(image, cv2.MORPH_GRADIENT, kernel)
ax = plt.subplot(236)
plt.imshow(m, cmap='Greys')
plt.title('dilation - erosion')

В этом приложении ядра являются структурирующим элементом, определяющим форму и распространение применяемого эффекта. Несмотря на то, что вам нужно создать матрицу, OpenCV имеет некоторые функции для облегчения этого процесса.

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

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

Ресурсы:
2D-фильтр OpenCV;
Сглаживание OpenCV;
Фильтрация OpenCV;
Морфологические преобразования OpenCV;

Дальше:
Шумоподавление OpenCV;
Обнаружение краев OpenCV;