Вступление
В этом руководстве мы узнаем, как выполнять обработку изображений с помощью языка Python. Мы не собираемся ограничиваться одной библиотекой или фреймворком; однако есть одна, которую мы будем использовать чаще всего, - это библиотека Open CV. Мы начнем с разговора об обработке изображений, а затем перейдем к рассмотрению различных приложений / сценариев, в которых обработка изображений может пригодиться. Итак, приступим!
Что такое обработка изображений?
Важно знать, что такое обработка изображений и какова ее роль в общей картине, прежде чем углубляться в то, как это делается. Обработку изображений чаще всего называют «обработкой цифровых изображений», а область, в которой она часто используется, - «компьютерное зрение». Не путайте - мы поговорим об обоих этих терминах и о том, как они связаны. И алгоритмы обработки изображений, и алгоритмы компьютерного зрения (CV) принимают изображение в качестве входных данных; однако при обработке изображений вывод также является изображением , тогда как при компьютерном зрении выводом могут быть некоторые характеристики / информация об изображении.
Зачем нам это нужно?
Данные, которые мы собираем или генерируем, в основном представляют собой необработанные данные, то есть они не подходят для использования в приложениях напрямую по ряду возможных причин. Поэтому нам нужно сначала его проанализировать, выполнить необходимую предварительную обработку, а затем использовать.
Например, предположим, что мы пытались создать классификатор кошек. Наша программа будет принимать изображение в качестве входных данных, а затем сообщать нам, содержит ли изображение кошку или нет. Первым шагом к созданию этого классификатора будет сбор сотен изображений кошек. Одна из распространенных проблем заключается в том, что все изображения, которые мы скопировали, не будут иметь одинаковый размер / размеры, поэтому перед передачей их модели для обучения нам нужно будет изменить размер / предварительно обработать их все до стандартного размера.
Это лишь одна из многих причин, почему обработка изображений важна для любого приложения компьютерного зрения.
Предпосылки
Прежде чем идти дальше, давайте обсудим, что вам нужно знать, чтобы с легкостью следовать этому руководству. Во-первых, у вас должны быть базовые знания программирования на любом языке. Во-вторых, вы должны знать, что такое машинное обучение и основы его работы, поскольку в этой статье мы будем использовать некоторые алгоритмы машинного обучения для обработки изображений. В качестве бонуса было бы полезно, если бы у вас были какие-либо знания или базовые знания Open CV, прежде чем продолжить это руководство. Но этого не требуется.
Одна вещь, которую вы обязательно должны знать, чтобы следовать этому руководству, - это то, как именно изображение представляется в памяти. Каждое изображение представлено набором пикселей, то есть матрицей значений пикселей. Для изображения в градациях серого значения пикселей находятся в диапазоне от 0 до 255, и они представляют интенсивность этого пикселя. Например, если у вас есть изображение размером 20 x 20, оно будет представлено матрицей 20 x 20 (всего 400 пикселей).
Если вы имеете дело с цветным изображением, вы должны знать, что у него будет три канала - красный, зеленый и синий (RGB). Следовательно, для одного изображения будет три таких матрицы.
Монтаж
Примечание. Поскольку мы собираемся использовать OpenCV через Python, подразумевается, что на вашей рабочей станции уже установлен Python (версия 3).
Окна
$ pip install opencv-python
MacOS
$ brew install opencv3 --with-contrib --with-python3
Linux
$ sudo apt-get install libopencv-dev python-opencv
Чтобы проверить, была ли ваша установка успешной, выполните следующую команду либо в оболочке Python, либо в командной строке:
import cv2
Некоторые основы, которые вам следует знать
Прежде чем мы перейдем к использованию обработки изображений в приложении, важно понять, какие операции попадают в эту категорию и как их выполнять. Эти операции, наряду с другими, будут использоваться позже в наших приложениях. Итак, приступим к делу.
В этой статье мы будем использовать следующее изображение:
{.ezlazyload}
Примечание : изображение было масштабировано для отображения в этой статье, но исходный размер, который мы используем, составляет около 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
Для краткости мы просто покажем изображение в градациях серого.
Изображение в градациях серого:
{.ezlazyload}
Пороговое значение изображения
Концепция пороговой обработки довольно проста. Как обсуждалось выше в представлении изображения, значениями пикселей могут быть любые значения от 0 до 255. Допустим, мы хотим преобразовать изображение в двоичное изображение, т.е. присвоить пикселю значение 0 или 1. Для этого мы можем выполнить пороговое значение. Например, если значение Threshold (T) равно 125, то всем пикселям со значениями больше 125 будет присвоено значение 1, а всем пикселям со значениями, меньшими или равными этому, будет присвоено значение 0. Давайте сделаем. это через код, чтобы лучше понять.
Изображение, используемое для определения порога:
{.ezlazyload}
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)
Выход:
{.ezlazyload}
Как вы можете видеть, в результирующем изображении были созданы две области, то есть черная область (значение пикселя 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)
Хорошо, мы добавили шум к нашему изображению розы, и теперь это выглядит так:
Шумное изображение:
{.ezlazyload}
Теперь давайте применим к нему разные фильтры и запишем наши наблюдения, то есть насколько хорошо каждый фильтр снижает шум.
Арифметический фильтр с ядром повышения резкости
# 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)
Изображение, полученное в результате применения арифметического фильтра к изображению с шумом соли и перца, показано ниже. При сравнении с исходным изображением в градациях серого мы видим, что оно слишком осветляет изображение и также не может выделить яркие пятна на розе. Отсюда можно сделать вывод, что арифметический фильтр не устраняет шум соли и перца.
Выход арифметического фильтра:
{.ezlazyload}
Фильтр средней точки
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)
Изображение, полученное в результате применения фильтра средней точки к изображению с шумом соли и перца, показано ниже. При сравнении с исходным изображением в градациях серого мы видим, что, как и метод ядра, описанный выше, слишком сильно увеличивает яркость изображения; однако он способен выделить яркие пятна на розе. Таким образом, мы можем сказать, что это лучший выбор, чем арифметический фильтр, но все же он не восстанавливает исходное изображение полностью.
Выход фильтра средней точки:
{.ezlazyload}
Контрагармонический средний фильтр
Примечание . Реализации этих фильтров можно легко найти в Интернете, и то, как именно они работают, выходит за рамки этого руководства. Мы будем смотреть на приложения с абстрактного / более высокого уровня.
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 Filter к изображению с шумом соли и перца, показано ниже. При сравнении с исходным изображением в оттенках серого мы видим, что оно воспроизводит практически то же изображение, что и исходное. Его уровень интенсивности / яркости такой же, и он также выделяет яркие пятна на розе. Следовательно, мы можем сделать вывод, что фильтр контрагармонических средних очень эффективен в борьбе с шумом соли и перца.
Выходной сигнал среднего контрагармонического фильтра:
{.ezlazyload}
Теперь, когда мы нашли лучший фильтр для восстановления исходного изображения из зашумленного изображения, мы можем перейти к следующему приложению.
# 2: Обнаружение края с помощью Canny Edge Detector
Изображение розы, которое мы использовали до сих пор, имеет постоянный фон, то есть черный, поэтому мы будем использовать другое изображение для этого приложения, чтобы лучше показать возможности алгоритма. Причина в том, что если фон постоянный, это делает задачу обнаружения краев довольно простой, а мы этого не хотим.
Мы говорили о классификаторе кошек ранее в этом руководстве, давайте рассмотрим этот пример и посмотрим, как обработка изображений играет в этом неотъемлемую роль.
В алгоритме классификации изображение сначала сканируется на предмет «объектов», т.е. когда вы вводите изображение, алгоритм находит все объекты в этом изображении, а затем сравнивает их с характеристиками объекта, который вы пытаетесь найти. В случае классификатора кошки он будет сравнивать все объекты, найденные на изображении, с характеристиками изображения кошки, и, если совпадение найдено, он сообщает нам, что входное изображение содержит кошку.
Поскольку мы используем классификатор кошек в качестве примера, будет справедливо использовать изображение кошки в будущем. Ниже приведено изображение, которое мы будем использовать:
Изображение, используемое для обнаружения краев:
{.ezlazyload}
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()
Выход обнаружения края:
{.ezlazyload}
Как вы можете видеть, часть изображения, содержащая объект, которым в данном случае является кошка, была разбита точками / разделена с помощью обнаружения краев. Теперь вам должно быть интересно, что такоеCanny Edge Detector и как он сделал это; так что давайте обсудим это сейчас.
Чтобы понять вышесказанное, необходимо обсудить три ключевых шага. Во-первых, он выполняет уменьшение шума на изображении аналогично тому, как мы обсуждали ранее. Во-вторых, он использует первую производную для каждого пикселя, чтобы найти края. Логика, лежащая в основе этого, заключается в том, что в точке, где существует край, происходит резкое изменение интенсивности, что вызывает всплеск в значении первой производной, что делает этот пиксель «краевым пикселем».
В конце он выполняет пороговое значение гистерезиса; мы сказали выше, что есть всплеск в значении первой производной на краю, но мы не указали, «насколько высоким» должен быть пик, чтобы его можно было классифицировать как край - это называется порогом! Ранее в этом руководстве мы обсуждали, что такое простая установка пороговых значений. Пороговое значение гистерезиса является улучшением по сравнению с этим, оно использует два пороговых значения вместо одного. Причина этого в том, что если пороговое значение слишком велико, мы можем пропустить некоторые фактические края (истинные негативы), а если значение слишком низкое, мы получим много точек, классифицированных как края, которые на самом деле не являются краями (ложные срабатывания ). Одно пороговое значение установлено высоким, а другое - низким. Все точки, которые выше «высокого порогового значения», идентифицируются как края, затем оцениваются все точки, которые выше нижнего порогового значения, но ниже верхнего порогового значения; точки, которые находятся рядом с точками, которые были идентифицированы как ребра, или являются их соседями, также идентифицируются как ребра, а остальные отбрасываются.
Это основные концепции / методы, которые алгоритм Canny Edge Detector использует для определения краев изображения.
Заключение
В этой статье мы узнали, как установить OpenCV, самую популярную библиотеку для обработки изображений в Python, на различные платформы, такие как Windows, MacOS и Linux, а также как проверить, что установка прошла успешно.
Мы продолжили обсуждение того, что такое обработка изображений и ее использование в области машинного зрения машинного зрения. Мы говорили о некоторых распространенных типах шума и о том, как мы можем удалить его с наших изображений с помощью различных фильтров, прежде чем использовать изображения в наших приложениях.
Кроме того, мы узнали, как обработка изображений играет неотъемлемую роль в высокопроизводительных приложениях, таких как обнаружение объектов или классификация. Обратите внимание, что эта статья была лишь верхушкой айсберга, и Digital Image Processing предлагает гораздо больше в магазине, что невозможно охватить в одном руководстве. Прочитав это, вы сможете глубже погрузиться в изучение других передовых концепций, связанных с обработкой изображений. Удачи!