DevGang
Авторизоваться

Введение в обработку изображений в Python с OpenCV 

В этом уроке мы узнаем, как мы можем выполнять обработку изображений с использованием языка Python. Мы не собираемся ограничиваться одной библиотекой или структурой; однако есть одна, которую мы будем использовать чаще всего, - библиотека Open CV. Мы начнем с небольшого разговора об обработке изображений, а затем перейдем к рассмотрению различных приложений / сценариев, где обработка изображений может оказаться полезной. Итак, начнем!

Что такое обработка изображений?

Важно знать, что именно представляет собой обработка изображений и какова ее роль в общей картине, прежде чем углубляться в ее принципы. Обработка изображений чаще всего называется «Цифровая обработка изображений», а область, в которой она часто используется, - «Компьютерное зрение». Не смущайтесь - мы поговорим об этих терминах и о том, как они связаны. Оба алгоритма обработки изображений и алгоритмы Computer Vision (CV) принимают изображение в качестве входных данных; однако при обработке изображения вывод также является изображением, тогда как в компьютерном зрении выводом могут быть некоторые особенности / информация об изображении.

Зачем нам это нужно?

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

Например, давайте предположим, что мы пытались создать классификатор котов. Наша программа будет принимать изображение в качестве входных данных и затем сообщать нам, содержит ли изображение кошку или нет. Первым шагом для построения этого классификатора было бы собрать сотни фотографий кошек. Одна общая проблема заключается в том, что все скопированные нами изображения не будут иметь одинаковый размер, поэтому перед передачей их в модель для обучения нам потребуется изменить их размер / предварительно обработать до стандартного размера.

Это только одна из многих причин, по которым обработка изображений необходима для любого приложения компьютерного зрения.

Предпосылки

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

Чтобы следовать этому уроку, вы должны определенно знать, как именно изображение представляется в памяти. Каждое изображение представлено набором пикселей, то есть матрицей значений пикселей. Для изображения в градациях серого значения пикселей варьируются от 0 до 255, и они представляют интенсивность этого пикселя. Например, если у вас есть изображение размером 20 x 20, оно будет представлено матрицей 20 x 20 (всего 400-пиксельных значений).

Если вы имеете дело с цветным изображением, вы должны знать, что оно будет иметь три канала - Красный, Зеленый и Синий (RGB). Следовательно, было бы три таких матрицы для одного изображения.

Установка

Примечание. Поскольку мы собираемся использовать OpenCV через Python, подразумевается, что на вашей рабочей станции уже установлен Python (версии 3).

Windows

pip install opencv-python

MacOS

brew install opencv3 --with-contrib --with-python3

Linux

sudo apt-get install libopencv-dev python-opencv

Чтобы проверить, была ли ваша установка успешной или нет, выполните следующую команду в оболочке Python или в командной строке:

import cv2

Некоторые основы, которые вы должны знать

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

Для этой статьи мы будем использовать следующее изображение:

Примечание. Исходный размер, который мы используем, составляет около 1180x786.

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

Поиск деталей изображения

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

import cv2

img = cv2.imread('rose.jpg')

print("Image Properties")
print("- Number of Pixels: " + str(img.size))
print("- Shape/Dimensions: " + str(img.shape))

Выход:

Image Properties
- Number of Pixels: 2782440
- Shape/Dimensions: (1180, 786, 3)

Разделение изображения на отдельные каналы

Теперь мы разделим изображение на его красные, зеленые и синие компоненты с помощью OpenCV и отобразим их:

from google.colab.patches import cv2_imshow

blue, green, red = cv2.split(img) # Split the image into its channels
img_gs = cv2.imread('rose.jpg', cv2.IMREAD_GRAYSCALE) # Convert image to grayscale

cv2_imshow(red) # Display the red channel in the image
cv2_imshow(blue) # Display the red channel in the image
cv2_imshow(green) # Display the red channel in the image
cv2_imshow(img_gs) # Display the grayscale version of image

Для краткости мы просто покажем изображение в градациях серого.

Изображение в градациях серого:

Thresholding изображения (сегментация)

Концепция сегментации довольно проста. Как обсуждалось выше в представлении изображения, значениями пикселей могут быть любые значения от 0 до 255. Допустим, мы хотим преобразовать изображение в двоичное изображение, то есть назначить пикселю значение 0 или 1. Для этого мы можем выполнить сегментацию. Например, если значение Threshold(T) равно 125, тогда всем пикселям со значениями больше 125 будет присвоено значение 1, а всем пикселям со значениями, меньшими или равными этому, будет присвоено значение 0. Давайте сделаем это через код, чтобы получить лучшее понимание.

Изображение, используемое для Thresholding:

import cv2

# Read image
img = cv2.imread('image.png', 0)

# Perform binary thresholding on the image with T = 125
r, threshold = cv2.threshold(img, 125, 255, cv2.THRESH_BINARY)
cv2_imshow(threshold)

Выход:

Как вы можете видеть, в результирующем изображении были установлены две области: черная область (значение пикселя 0) и белая область (значение пикселя 1). Оказывается, установленный нами порог был прямо посередине изображения, поэтому значения черного и белого делятся там.

Приложения

1: удаление шума с изображения

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

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

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

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

import numpy as np

# Adding salt & pepper noise to an image
def salt_pepper(prob):
      # Extract image dimensions
      row, col = img_gs.shape

      # Declare salt & pepper noise ratio
      s_vs_p = 0.5
      output = np.copy(img_gs)

      # Apply salt noise on each pixel individually
      num_salt = np.ceil(prob * img_gs.size * s_vs_p)
      coords = [np.random.randint(0, i - 1, int(num_salt))
            for i in img_gs.shape]
      output[coords] = 1

      # Apply pepper noise on each pixel individually
      num_pepper = np.ceil(prob * img_gs.size * (1. - s_vs_p))
      coords = [np.random.randint(0, i - 1, int(num_pepper))
            for i in img_gs.shape]
      output[coords] = 0
      cv2_imshow(output)

      return output

# Call salt & pepper function with probability = 0.5
# on the grayscale image of rose
sp_05 = salt_pepper(0.5)

# Store the resultant image as 'sp_05.jpg'
cv2.imwrite('sp_05.jpg', sp_05)

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

Шумное изображение:

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

Арифметический фильтр с Sharpening Kernel
# Create our sharpening kernel, the sum of all values must equal to one for uniformity
kernel_sharpening = np.array([[-1,-1,-1],
                              [-1, 9,-1],
                              [-1,-1,-1]])

# Applying the sharpening kernel to the grayscale image & displaying it.
print("\n\n--- Effects on S&P Noise Image with Probability 0.5 ---\n\n")

# Applying filter on image with salt & pepper noise
sharpened_img = cv2.filter2D(sp_05, -1, kernel_sharpening)
cv2_imshow(sharpened_img)

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

Выход арифметического фильтра:

Фильтр средней точки
from scipy.ndimage import maximum_filter, minimum_filter

def midpoint(img):
    maxf = maximum_filter(img, (3, 3))
    minf = minimum_filter(img, (3, 3))
    midpoint = (maxf + minf) / 2
    cv2_imshow(midpoint)

print("\n\n---Effects on S&P Noise Image with Probability 0.5---\n\n")
midpoint(sp_05)

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

Выход фильтра средней точки:

Контрагармонический средний фильтр

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

def contraharmonic_mean(img, size, Q):
    num = np.power(img, Q + 1)
    denom = np.power(img, Q)
    kernel = np.full(size, 1.0)
    result = cv2.filter2D(num, -1, kernel) / cv2.filter2D(denom, -1, kernel)
    return result

print("\n\n--- Effects on S&P Noise Image with Probability 0.5 ---\n\n")
cv2_imshow(contraharmonic_mean(sp_05, (3,3), 0.5))

Результирующее изображение от применения фильтра Contraharmonic Mean к изображению с шумом соли и перца показано ниже. При сравнении с исходным изображением в градациях серого мы видим, что оно воспроизводило почти то же самое изображение, что и исходное. Его уровень интенсивности / яркости одинаков, и он также выделяет яркие пятна на розе. Следовательно, мы можем сделать вывод, что фильтр с контрастным антигармоническим эффектом очень эффективен для борьбы с шумом соли и перца.

Результат работы контрагармонического среднего фильтра:

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

2: Обнаружение края с помощью Canny Edge Detector

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

Ранее в этом уроке мы говорили о классификаторе кошек, давайте рассмотрим этот пример и посмотрим, как обработка изображений играет в этом важную роль.

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

Поскольку мы используем классификатор cat в качестве примера, будет справедливо, что мы будем использовать изображение cat в будущем. Ниже изображение, которое мы будем использовать:

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

# Declaring the output graph's size
plt.figure(figsize=(16, 16))

# Convert image to grayscale
img_gs = cv2.imread('cat.jpg', cv2.IMREAD_GRAYSCALE)
cv2.imwrite('gs.jpg', img_gs)

# Apply canny edge detector algorithm on the image to find edges
edges = cv2.Canny(img_gs, 100,200)

# Plot the original image against the edges
plt.subplot(121), plt.imshow(img_gs)
plt.title('Original Gray Scale Image')
plt.subplot(122), plt.imshow(edges)
plt.title('Edge Image')

# Display the two images
plt.show()

Результат обнаружения края:

Как вы можете видеть, часть изображения, которая содержит объект, который в данном случае является кошкой, была расставлена ​​точками / отделена посредством обнаружения края. Теперь вам должно быть интересно, что такое Canny Edge Detector и как он это сделал; так что давайте обсудим это сейчас.

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

В конце он выполняет гистерезисный порог; мы сказали выше, что есть скачок в значении первой производной на ребре, но мы не указали, «насколько высоким» должен быть скачок, чтобы его можно было классифицировать как ребро - это называется порогом! Ранее в этом уроке мы обсуждали, что такое простой порог. Гистерезисный порог является улучшением в том, что он использует два пороговых значения вместо одного. Причина этого заключается в том, что если пороговое значение слишком высокое, мы можем пропустить некоторые фактические ребра (истинные негативы), а если значение слишком низкое, мы получим множество точек, классифицированных как ребра, которые на самом деле не являются ребрами (ложные срабатывания). Одно пороговое значение установлено высоким, а другое низким. Все точки, которые находятся выше «верхнего порогового значения», обозначены как ребра, затем оцениваются все точки, которые находятся выше нижнего порогового значения, но ниже верхнего порогового значения; точки, которые близки или являются соседями точек, которые были определены как ребра, также идентифицируются как ребра, а остальные отбрасываются.

Это базовые концепции / методы, которые алгоритм Canny Edge Detector использует для идентификации краев на изображении.

Источник:

#Python
Комментарии
Чтобы оставить комментарий, необходимо авторизоваться

Присоединяйся в тусовку

В этом месте могла бы быть ваша реклама

Разместить рекламу