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

Команда Pip для установки библиотеки —

pip install opencv-python

давайте посмотрим, как читать, отображать и сохранять изображение, над которым мы хотим выполнить некоторую обработку. Следующие шаги будут включать следующие три функции: cv2.imread(), cv2.imshow(), и cv2.imwrite(). Вы также узнаете, как отображать изображения с помощью Matplotlib. Итак, приступим.

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

Откройте любую понравившуюся IDE Python и создайте новый файл с именем «Playwithimages.py», где .py представляет собой расширение Python. И напишите следующий код, чтобы открыть изображение с помощью openCV

import numpy as np
import cv2
img = cv2.imread('cat.jpg',1)
cv2.imshow('image',img)
cv2.waitKey(0)
cv2.destroyAllWindows()

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

Мы сокращаем numpy до np для нашего удобства, и с этого момента python будет распознавать np как numpy. То же самое можно сделать и с другими библиотеками. Вторая строка кода импортирует библиотеку OpenCV для использования в обработке изображений. Третья строка создает переменную с именем img, и с помощью функции cv2.imread() мы читаем сохраненное изображение кота.

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

Функция cv2.imread() имеет два аргумента. Первое относится к изображению, которое необходимо прочитать, а второе относится к тому, как изображение должно быть прочитано. Второй аргумент может принимать значения 0,1 и -1. 0 загрузит изображение в оттенках серого, -1 загрузит изображение без изменений, а 1 загрузит изображение в цвете, а прозрачность изображения будет игнорироваться. По умолчанию выбирается 1, если второй аргумент не указан.

Как только изображение будет прочитано, четвертая строка покажет/отобразит изображение с помощью функции cv2.imshow(). Функция также имеет два аргумента. Первый аргумент — это заголовок окна, в котором мы хотим отобразить изображение. Если окно не существует, будет создано окно с таким именем. Второе — это имя переменной, содержащей изображение, в данном случае это img, которое мы создали в предыдущей строке.

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

Команда cv2.imshow() всегда должна быть поставлена ​​с командой cv2.waitkey(). cv2.waitKey() — это функция привязки клавиатуры, единственным аргументом которой является время в миллисекундах. Функция ожидает указанное количество миллисекунд. Если передается 0, он ожидает бесконечно, пока не будет нажата клавиша. Его также можно настроить для обнаружения определенных нажатий клавиш, таких как клавиша «q» для выхода. Обратите внимание, что эта функция также обрабатывает многие другие события графического интерфейса, поэтому ее обязательно использовать для фактического отображения изображения.

cv2.destroyAllWindows(), как следует из названия, уничтожит все созданные нами окна. Если вы хотите уничтожить какое-либо конкретное окно, передайте точное имя окна в качестве аргумента этой функции.

import numpy as np
import cv2
img = cv2.imread('cat.jpg')
cv2.imshow('image',img)
i = cv2.waitKey(0) 
if i == 27: 
    cv2.destroyAllWindows()
elif i == ord('s'):
    cv2.imwrite('cat_saved.jpg',img)
cv2.destroyAllWindows()

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

Функция cv2.imwrite() имеет два аргумента. В первом указывается имя сохраняемого файла и в нужном формате. В этом случае мы сохраняем файл как «cat_saved.jpg», а переменная, содержащая изображение, которое мы хотим сохранить, является нашим вторым аргументом. Это завершает первую часть нашей цели.

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

import numpy as np
import cv2
from matplotlib import pyplot as plt
img = cv2.imread('cat.jpg',0)
plt.imshow(img, cmap = 'gray', interpolation = 'bicubic')
plt.xticks([]), plt.yticks([])  # to hide tick values on X and Y axis 
plt.show()

Первые две строки аналогичны предыдущему коду. Третья строка импортирует пакет pyplot из matplotlib, а четвертая читает изображение в оттенках серого. Следующая ссылка объясняет значение различных аргументов, строка 5: https://matplotlib.org/api/_as_gen/matplotlib.pyplot.imshow .html

Оттенки серого и бинарные изображения

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

Вы можете удивиться, почему шкала серого не может быть достаточным упрощением. Причина в том, что шкала серого по-прежнему имеет как минимум 255 значений. Пороговое значение изображения может фундаментально упростить изображение, преобразуя все в белое или черное в зависимости от порогового значения. У нас есть значения пикселей в диапазоне от 0 до 255.

Предположим, что мы хотим, чтобы порог был равен 127, тогда все, что было 127 и меньше, будет преобразовано в 0 (или черное), а все, что выше 127, будет преобразовано в 255 (или белое).

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

Функция OpenCV, используемая для преобразования цветного изображения в оттенки серого, называется cv2.cvtColor().

gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

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

Примечание. Цвет преобразуется из BGR в серый. Это связано с тем, что OpenCV считывает цветные изображения в формате BGR, а не в формате RGB. Если вам по какой-либо причине нужно изображение RGB, вы можете использовать эту же команду со вторым аргументом как cv2.COLOR_BGR2RGB.

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

th1, threshold = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)

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

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

th2=cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY,11,2)

Здесь первый аргумент img — это имя переменной изображения, для которого необходимо задать пороговое значение. Второй аргумент 255 представляет собой значение, которое необходимо применить, если значение пикселя превышает вычисленное пороговое значение. Третий аргумент cv2.ADAPTIVE_THRESH_MEAN_C указывает, что порог вычисляется с использованием среднего значения вокруг пикселя. cv2.THRESH_BINARY определяет тип порогового значения, которое должно выполняться после вычисления порога. Третий аргумент указывает размер блока вокруг пикселя, который используется для вычисления среднего значения. В этом случае для расчета среднего значения используется сетка 11 × 11 вокруг каждого пикселя. Последний аргумент указывает общую отрицательную коррекцию, которую необходимо применить после вычисления среднего значения. В этом случае порог для каждого пикселя будет [mean(11×11 сетка вокруг пикселя) — 2].

Уменьшение и масштабирование

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

import cv2
import numpy as np
img = cv2.imread('cat.jpg')
res = cv2.resize(img,None,fx=0.1, fy=0.1, interpolation = cv2.INTER_AREA)
res = cv2.resize(img,None,fx=10, fy=10, interpolation = cv2.INTER_AREA)

Попробуйте поэкспериментировать, изменив коэффициенты масштабирования, и сравните различные результаты, которые вы получите! Вы можете заметить, что в функции cv2.resize() есть аргумент, называемый интерполяцией. Когда размер изображения изменяется, нам нужно вычислить значения пикселей нового изображения из старого. Этот процесс называется интерполяцией, и есть несколько способов сделать это; INTER_AREA — один из таких аргументов, который помогает нам достичь этого.

Использование cv2.INTER_AREA дает пикселизированное изображение при масштабировании. Чтобы получить лучшее изображение при увеличении, попробуйте более сложные методы интерполяции, такие как cv2.INTER_CUBIC.

Размытие по Гауссу

Как и в случае с одномерными сигналами, изображения также можно фильтровать с помощью различных фильтров нижних частот (ФНЧ) и фильтров верхних частот (ФВЧ). LPF помогает удалить шум, размыть изображение и т. д. HPF помогает определить края любого изображения.

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

Для этой цели используется следующая функция:

cv2.GaussianBlur(image, (kernel_width,kernel_height), standard_deviation)

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

Преобразование BGR в HSV

Компьютерное зрение и графика используют несколько цветовых пространств. Например, некоторые часто используемые включают RGB (красный, зеленый, синий) и CMYK (голубой, пурпурный, желтый, ключевой), которые используют различные комбинации основных цветов для создания спектра других цветов.

Часто мы сталкиваемся со сценариями, когда нас интересуют определенные цветные объекты на изображении. Это может быть выполнено более эффективно в альтернативных цветовых пространствах, таких как HSV.

HSV немного отличается тем, что он определяется так же, как люди воспринимают цвет. HSV означает: оттенок, насыщенность и значение. Это цветовое пространство описывает цвета (H — оттенок или оттенок) с точки зрения их оттенка (S — насыщенность или количество серого) и соответствующее им значение яркости (значение V).

Обобщенная функция, используемая для преобразования цвета, имеет вид

cv2.cvtColor(input_image, flag)

где флаг определяет тип преобразования. Для BGR\HSV используем флаг cv2.COLOR_BGR2HSV.

Точно так же преобразование BGR \ Gray также может быть выполнено с использованием этого метода, где мы используем флаг cv2.COLOR_BGR2GRAY. Чтобы найти флаги для преобразования в другие цветовые пространства, перейдите по ссылке эта.

Собираем все вместе!

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

Чтобы сложить изображение, мы используем функцию np.concatenate() из библиотеки numpy. Это помогает, потому что изображения openCV всегда хранятся в виде пустых массивов, поэтому мы объединяем изображения, просто объединяя массивы.

Следующая команда будет размещать 3 изображения рядом друг с другом.

temp1 = np.concatenate((img1,img2,img3), axis=1)

Следующая команда наложит одно изображение поверх другого:

img_final = np.concatenate((temp1,temp2), axis=0)

Первым аргументом этой команды является список изображений, заключенных в круглые скобки и разделенных запятыми — (img1,img2,… и т. д.). Второй аргумент — это ось для конкатенации: ось 0 означает вертикальную конкатенацию, что означает, что одно изображение будет находиться поверх другого, а ось 1 означает горизонтальную конкатенацию, что означает, что изображения будут располагаться рядом.

Окончательный код

Теперь давайте выполним все 6 изученных нами манипуляций и сложим все изображения в сетку 2×3.

Примечание:

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

Изображение Грея состоит только из одного массива пикселей, но сетка BGR предполагает три массива (массив B, массив G и массив R). Поэтому мы используем команду cv2.merge((img_gray,img_gray,img_gray)) для получения серого изображения с тремя массивами. Это не влияет на сделанные нами манипуляции (преобразование из BGR в GRAY).

Когда мы сжимаем изображение, ширина и высота уменьшаются. Однако команда np.concatenate() ожидает, что все массивы будут одного размера. Таким образом, мы увеличим изображение на ту же величину, на которую мы его уменьшили (уменьшим до 0,1, а затем увеличим до 10). Это покажет вам пикселизированное изображение.

Все пиксели в изображении HSV будут иметь цветовое пространство HSV. Поскольку мы отображаем его в сетке BGR (поскольку большинство других изображений являются BGR), цвет изображения будет выглядеть крайне неестественно.

import numpy as np
import cv2
img1 = cv2.imread('cat.jpg')
h,w,bpp = np.shape(img1)
img_gray = cv2.cvtColor(img1,cv2.COLOR_BGR2GRAY)
img2=cv2.merge((img_gray,img_gray,img_gray))
th1, img3 = cv2.threshold(img2, 127, 255, cv2.THRESH_BINARY)
img4 = cv2.resize(img1,None,fx=0.1, fy=0.1, interpolation = cv2.INTER_AREA)
img4 = cv2.resize(img4,None,fx=10, fy=10, interpolation = cv2.INTER_AREA)
img5 = cv2.GaussianBlur(img1,(9,9),10)
img6 = cv2.cvtColor(img1,cv2.COLOR_BGR2HSV)
temp1 = np.concatenate((img1,img2,img3), axis=1)
temp2 = np.concatenate((img4,img5,img6), axis=1)
img_final = np.concatenate((temp1,temp2), axis=0)
cv2.imshow("result",img_final)
cv2.waitKey(0)
cv2.destroyAllWindows()

Ну вот!

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

Изображения по порядку: исходное изображение, изображение в градациях серого, изображение с пороговым значением, уменьшенное и увеличенное изображение, изображение с размытием по Гауссу и изображение в цветовом пространстве HSV.

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