Искусство — это способ видеть, и говорят, что видеть — значит верить, но верно и обратное, верить — значит видеть, и действительно трудно представить себе жизнь в этом мире без дара зрения — наших глаз, как прекрасно быть младенцем, когда наши глаза только открываются, мы видим мир и начинаем узнавать и видеть мир вокруг нас, но со временем тот же самый замечательный опыт становится обыденным. Но по мере того, как мы прогрессируем с технологиями, мы находимся на грани, когда машины также способны видеть и понимать его. В настоящее время это не кажется научной фантастикой, если вы просто разблокируете свой телефон своим лицом, но история развития машинного зрения насчитывает более 20 лет.
Первый формальный шаг в этой области был сделан еще в 1999 году по инициативе Intel, когда все исследования проводились в рамках OPEN CV (Open Source computer vision), изначально написанного на C++, с его первым крупным релизом 1.0 в 2006 году, вторым в 2009 году, третьим в 2015 году и четвертым только в 2018 году. Теперь OpenCV имеет интерфейсы C++, Python и Java и поддерживает Windows, Linux, Mac OS, iOS и Android. Поэтому его можно легко установить на Raspberry Pi с Python и средой Linux. А Raspberry Pi с OpenCV и подключенной камерой можно использовать для создания множества приложений обработки изображений в реальном времени, таких как распознавание эмоций на лицах, распознавание жестов, определение пола и возраста людей, обнаружение масок на лицах и т. д.
Итак, в данной статье мы рассмотрим базовую обработку изображений с помощью библиотеки OpenCV, но аналогичную обработку изображений можно выполнять и с помощью системы Matlab если кто поклонник этого программного обеспечения. Но прежде чем приступить к изучению обработки изображений с помощью OpenCV, важно знать, что такое изображения и как люди и машины воспринимают эти изображения.
Что такое изображения?
Изображения представляют собой двумерное представление видимого спектра света. А видимый спектр света — это всего лишь часть электромагнитного спектра, лежащая между инфракрасным и ультрафиолетовым спектрами.
Как формируются изображения? Это происходит когда свет отражается от объекта на пленку, сенсор или сетчатку.
Именно так работают наши глаза: они используют барьер, чтобы блокировать большую часть света, оставляя небольшое отверстие, через которое может проходить свет, называемое апертурой, и оно формирует очень сфокусированное изображение и является рабочей моделью для камеры с точечной диафрагмой. Однако в камере с точечной диафрагмой есть проблема: в апертуру будет попадать такое же количество света, что может не подходить для пленки или сформированного изображения. Кроме того, мы не можем получить сфокусированное изображение, поэтому для фокусировки изображения нам нужно перемещать пленку вперед и назад, но во многих ситуациях это проблематично.
Или мы можем решить эту проблему с помощью линз, это позволяет нам контролировать размер диафрагмы, и в фотографии это известно как fStop. Как правило, чем меньше значение fStop, тем лучше для фотографии.
Размер диафрагмы также позволяет нам получить приятную глубину резкости, называемую в фотографии боке . Это позволяет нам получить размытый фон, пока мы фокусируемся на изображении.
Как компьютер хранит изображения
Вы, возможно, слышали о различных форматах изображений, таких как .PNG, .JPEG и т. д. Все это цифровое представление нашего аналогового мира, компьютеры делают это, переводя изображение в цифровой код для хранения, а затем интерпретируя файл обратно в изображение для отображения. Но в основе они используют общую платформу для хранения изображений, и то же самое верно и для openCV.
По умолчанию OpenCV использует для своих изображений цветовое пространство RGB (красный, зеленый и синий), где каждая координата пикселя (x, y) содержит 3 значения, варьирующиеся для интенсивности в 8-битной форме, т. е. (0-255, 28).
Смешение различных интенсивностей каждого цвета дает нам полный спектр, поэтому в живописи или искусстве эти три цвета считаются основными цветами, а все остальные — вторичными, потому что большинство вторичных цветов могут быть образованы основными цветами. Например, для желтого цвета у нас есть следующие значения: Красный — 255; Зеленый — 255; Синий — 0.
Теперь изображения хранятся в многомерных массивах . В программировании массив — это ряд коллекций объектов. И здесь мы имеем дело с тремя типами массивов: 1D, 2D и 3D, где «D» означает размерность.
Цветные изображения хранятся в трехмерных массивах, где третье измерение представляет цвета RGB (которые мы рассмотрим позже), и вместе они формируют различные интенсивности пикселей для изображения, в то время как черно-белые изображения хранятся в двухмерных массивах, и также существует два типа черно-белых изображений: изображения в оттенках серого и двоичные изображения.
Изображения в градациях серого формируются из оттенков серого двумерного массива [(0,255), (0,255)], тогда как бинарные (двоичные) изображения состоят из пикселей либо черного, либо белого цвета.
Почему машине трудно распознавать изображения
Компьютерное зрение само по себе является сложной задачей, вы можете себе представить, как сложно дать машине чувство зрения, распознавания и идентификации. Существуют следующие факторы, которые делают компьютерное зрение таким сложным.
- Ограничения сенсора и объектива камеры;
- Варианты точек зрения;
- Изменение освещения;
- Масштабирование;
- Окклюзии;
- Вариации классов объектов;
- Неоднозначные изображения/Оптические иллюзии.
Применение и использование OpenCV
Несмотря на трудности, у Computer Vision есть много историй успеха. Это:
- Роботизированная навигация – беспилотные автомобили;
- Распознавание и обнаружение лиц;
- Поисковая система поиска изображений;
- Чтение номерных знаков;
- Распознавание почерка;
- Snapchat и фильтры для лица;
- Распознавание объектов;
- Отслеживание мяча и игрока в спорте;
- И многое другое!
Установка OpenCV с Python и Anaconda
Библиотека OpenCV написана на C++, но работать с ней на C++ очень сложно, поэтому мы решили работать с ней на языке высокого уровня, таком как Python. Кроме того, реализация OpenCV на Python имеет дополнительные преимущества , поскольку Python — один из самых простых языков для начинающих. Он чрезвычайно эффективен для приложений в области науки о данных и машинного обучения, а также хранит изображения в массивах NumPy, что позволяет нам довольно легко выполнять некоторые очень мощные операции.
Базовое программирование будет полезно при наличии курса математики на уровне средней школы, веб-камеры, Python 2.7 или 3.6 (предпочтительно пакет Anaconda).
Шаг 1. Загрузите и установите пакет Anaconda Python
Перейдите по ссылке: https://www.anaconda.com/download и выберите версию, соответствующую вашему компьютеру: Windows, Linux или Mac. Вы можете выбрать версию Python 2.7 или Python 3.7 для 64-битных или 32-битных систем, но в настоящее время большинство систем являются 64-битными.
Дистрибутив python Anaconda поставляется вместе со Spyder studio, jupyter notebooks и anaconda prompt, что делает python очень удобным в использовании. Мы будем использовать spyder studio для выполнения примеров.
Выбор между python 2.7 или 3.7 полностью нейтрален, но, тем не менее, для примеров мы будем использовать python 3.7. Также он также дает ожидаемые результаты для основных математических операций, таких как (2/5=2.5), в то время как python 2.7 оценит его как 2. Также print рассматривается как функция в python 3.7 (print(“hello”)), поэтому он дает практические рекомендации программистам.
Шаг 2. Создание виртуальной платформы с OpenCV
Мы собираемся установить OpenCV, создав виртуальную платформу для Spyder с помощью командной строки Anaconda и файла YML, загруженного здесь.
С помощью YML-файлов мы установим все необходимые пакеты и библиотеки, однако, если вы хотите установить какие-либо дополнительные пакеты, вы можете легко сделать это через командную строку Anaconda, выполнив команду этого пакета.
Перейдите к значку поиска Windows и найдите Anaconda prompt terminal. Вы можете найти его в папке Anaconda, которую вы только что установили.
Затем вам нужно найти загруженный файл YML, и отсюда у вас есть два варианта: либо изменить каталог вашего терминала на место, куда загружен ваш файл YML, либо скопировать ваш файл YML в каталог, где установлена ваша Anaconda (в большинстве случаев это будет на диске C:\). После копирования вашего файла YML в указанное место выполните следующую команду в командной строке.
1 |
conda env create –f virtual_platform_windows.yml |
Поскольку моя система работает на Windows, YML-файл и команда соответствуют Windows, однако вы можете внести изменения в соответствии со своей системой, заменив Windows на Linux или Mac соответственно.
Примечание: Если при извлечении пакета возникает ошибка, сначала установите pytorch и numpy, а затем выполните указанную выше команду.
Теперь найдите навигатор Anaconda, там будет выпадающее меню “Applications on ___” «Приложения на ___», в нем выберите виртуальную среду, а затем оттуда запустите Spyder studio.
Вот и все, вы готовы начать!
Открытие и сохранение изображений в OpenCV
Здесь мы объясняем некоторые основные команды и терминологию для использования Python в OpenCV. Мы узнаем о трех основных функциях в OpenCV - imread, imshow и imwrite.
1 |
#comments in python are given by # symbol |
Вначале импортируйте opencv в python с помощью команды
1 |
import cv2 |
Загрузите изображение с помощью функции «imread», указав путь к изображению.
1 |
image =cv2.imread('input.jpg') |
Теперь это изображение загружено и сохранено в Python как переменная, которую мы назвали image.
Теперь, чтобы отобразить нашу переменную изображения, мы используем функцию 'imshow', а первым параметром функции imshow является заголовок, отображаемый в окне изображения, и его нужно ввести в (' '), чтобы представить имя в виде строки.
1 |
cv2.imshow('hello world',image) |
waitkey позволяет нам вводить информацию, когда открыто окно изображения. Если оставить это поле пустым, оно просто будет ожидать нажатия любой клавиши, прежде чем продолжить. Указав числа (кроме 0), мы можем указать задержку, в течение которой окно будет оставаться открытым (здесь время в миллисекундах).
1 |
cv2.waitKey() |
«destroyAllWindows» закрывает все открытые окна. Если не установить этот параметр, ваша программа зависнет.
1 |
cv2.destroyAllWindows() |
Теперь давайте посмотрим, как хранятся изображения в Open CV, для этого мы будем использовать NumPy. NumPy — это библиотека для программирования на Python, позволяющая добавлять поддержку больших многомерных массивов и матриц.
1 2 3 4 5 6 7 8 9 |
import cv2 #importing numpy import numpy as np image=cv2.imread('input.jpg') cv2.imshow('hello_world', image) # функция shape очень полезна, когда мы смотрим на размеры массива, она возвращает кортеж, который дает размер изображения print(image.shape) cv2.waitKey() cv2.destroyAllWindows() |
вывод консоли - (183, 275, 3). Два измерения изображения составляют 183 пикселя в высоту и 275 пикселей в ширину, а 3 означает, что есть три других компонента (R, G, B), которые составляют это изображение (это показывает, что цветные изображения хранятся в трехмерных массивах).
Теперь давайте распечатаем каждое измерение изображения, добавив следующие строки кода:
1 2 |
print('Height of image:',(image.shape[0],'pixels')) print('Width of image:',(image.shape[1],'pixels')) |
вывод консоли: Height of image: (183, 'pixels')
Width of image: (275, 'pixels')
Сохранение отредактированного изображения в OpenCV
Мы используем функцию «imwrite» для указания имени файла и изображения, которое необходимо сохранить.
1 2 |
cv2.imwrite('output.jpg',image) cv2.imwrite('output.png',image) |
Первый аргумент — это имя файла, который мы хотим сохранить, {чтобы прочитать или сохранить файл, мы используем (' '), чтобы обозначить его как строку}, а второй аргумент — это имя файла.
OpenCV позволяет сохранять изображение в различных форматах.
Изображение в оттенках серого в OpenCV
Серая шкала — это процесс, при котором изображение преобразуется из полноцветного в оттенки серого (черно-белое).
В opencv многие функции перед обработкой делают изображения серыми. Это делается потому, что это упрощает изображение, действуя почти как шумоподавление и уменьшая время обработки, поскольку в изображении становится меньше информации (поскольку серые изображения хранятся в двумерных массивах).
1 2 3 4 5 6 7 8 9 10 |
import cv2 # загрузка нашего входного изображения image=cv2.imread('input.jpg') cv2.imshow('original', image) cv2.waitKey() #мы используем cvtcolor для преобразования в оттенки серого gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) cv2.imshow('grayscale', gray_image) cv2.waitKey() cv2.destroyALLWindows() |
Более простой способ преобразовать изображение в оттенки серого — просто добавить аргумент 0 в функции imread к имени изображения.
1 2 3 4 5 |
import cv2 grey_image=cv2.imread('input.jpg',0) cv2.imshow('grayscale',grey_image) cv2.waitKey() cv2.destroyAllWindows() |
Теперь давайте посмотрим размерность каждого изображения с помощью функции shape.
1 2 3 4 5 6 7 8 9 10 11 |
import cv2 import numpy as np image=cv2.imread('input.jpg') print(image.shape) cv2.imshow('original', image) cv2.waitKey() gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) cv2.imshow('grayscale', gray_image) print(gray_image.shape) cv2.waitKey() cv2.destroyALLWindows() |
Вывод на консоль: - (183, 275, 3) – для цветного изображения
(183, 275) – для изображения в оттенках серого
Таким образом, ясно видно, что цветные изображения представлены трехмерными массивами, а изображения в оттенках серого — двумерными массивами.
Цветовые пространства
Цветовые пространства — это способ хранения изображений. RGB, HSV, CMYK — это различные цветовые пространства, фактически, это просто способы представления цвета.
RGB – красный, зеленый и синий.
HSV – оттенок, насыщенность и значение.
А CMYK обычно используется в струйных принтерах.
Цветовое пространство RGB или BGR
Цветовое пространство OpenCV по умолчанию — RGB. RGB — это аддитивная цветовая модель, которая генерирует цвета путем комбинирования синего, зеленого и красного цветов различной интенсивности/яркости. В OpenCV мы используем 8-битную глубину цвета.
- Красный (0-255)
- Синий (0-255)
- Зеленый (0-255)
Однако OpenCV на самом деле хранит цвет в формате BGR.
Интересный факт: мы используем порядок BGR в компьютерах из-за того, как беззнаковые 32-битные целые числа хранятся в памяти, они все равно в конечном итоге сохраняются как RGB. Целое число, представляющее цвет, например:- 0X00BBGGRR будет сохранено как 0XRRGGBB.
HSV (оттенок, насыщенность и значение/яркость) — это цветовое пространство, которое пытается представить цвета, которые воспринимает человек. Оно хранит цветовую информацию в цилиндрическом представлении цветовых точек RGB.
Оттенок – значение цвета (0-179)
Насыщенность – яркость цвета (0-255)
Значение – яркость или интенсивность (0-255)
Формат цветового пространства HSV полезен при сегментации цвета. В RGB фильтрация определенного цвета непроста, однако HSV значительно упрощает настройку цветовых диапазонов для фильтрации определенного цвета, как мы его воспринимаем.
Оттенок представляет цвет в HSV, значение оттенка варьируется от 0 до 180, а не 360, поэтому он не завершает полный круг и поэтому отображается иначе, чем стандарт.
Фильтры цветовой гаммы:
- Красный – (165-15)
- Зеленый – (45-75)
- Синий – (90-120)
Как мы знаем, изображения хранятся в цветовом пространстве RGB (красный, зеленый и синий), поэтому OpenCV показывает нам то же самое, но первое, что нужно помнить о формате RGB OpenCV, это то, что на самом деле это BGR, и мы можем это узнать, посмотрев на форму изображения.
1 2 3 4 5 6 7 8 9 10 11 12 |
import cv2 import numpy as np image = cv2.imread('input.jpg') #значение B,G,R для первого пикселя 0,0 B,G,R=image[0,0] print(B,G,R) print(image.shape) #теперь, если мы применим это к изображению в оттенках серого gray_img=cv2.cvtColor(image,cv2.COLOR_BGR2GRAY) print(gray_img.shape) #значение пикселя gray_image для пикселя 10,50 print(gray_img[10,50]) |
Вывод на консоль: print(B,G,R) - 6 11 10
print(image.shape) - (183, 275, 3)
print(gray_img.shape) - (183, 275)
print(gray_img[10,50]) - 69
Теперь в изображении в оттенках серого есть только два измерения, поскольку мы помним, что цветное изображение хранится в трех измерениях, причем третье измерение — это (R,G,B), тогда как в изображении в оттенках серого присутствуют только два измерения, поскольку (R,G,B) отсутствует и для определенного положения пикселя мы получаем только одно значение, тогда как в цветном изображении мы получаем три значения.
Еще одно полезное цветовое пространство — HSV.
1 2 3 4 5 6 7 8 9 |
import cv2 image=cv2.imread('input.jpg') hsv_image=cv2.cvtColor(image,cv2.COLOR_BGR2HSV) cv2.imshow('HSV image',hsv_image) cv2.imshow('Hue channel',hsv_image[:,:,0]) cv2.imshow('saturation channel',hsv_image[:,:,1]) cv2.imshow('value channel',hsv_image[:,:,2]) cv2.waitKey() cv2.destroyAllWindows() |
После запуска кода вы увидите четыре изображения, три из которых представляют собой отдельные каналы, а одно — объединенное изображение HSV.
Изображение канала оттенка довольно темное, поскольку его значение изменяется только от 0 до 180.
Также обратите внимание, что функция imshow пытается показать вам изображение RGB или BGR, но преобразование HSV перекрывает его.
Кроме того, канал значений будет похож на оттенки серого изображения из-за его яркости.
Изучение отдельных компонентов RGB-изображения
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import cv2 image=cv2.imread('input.jpg') #функция split в opencv разделяет изображение по индексу каждого цвета B,G,R=cv2.split(image) cv2.imshow("Red",R) cv2.imshow("Green",G) cv2.imshow("Blue",B) #создание исходного изображения путем слияния отдельных цветовых компонентов merged=cv2.merge([B,G,R]) cv2.imshow("merged",merged) #усиление синего цвета merged=cv2.merge([B+100,G,R]) cv2.imshow("merged with blue amplify",merged) #представление формы отдельных цветовых компонентов. # вывод будет иметь только два измерения: высоту и ширину, поскольку третий элемент компонента RGB представлен индивидуально print(B.shape) print(R.shape) print(G.shape) cv2.waitKey(0) cv2.destroyAllWindows() |
Вывод на консоль: #размеры изображения из функции shape
(183, 275)
(183, 275)
(183, 275)
Преобразование изображения в отдельный компонент RGB
В коде ниже мы создали матрицу нулей с размерами изображения HxW, ноль возвращает массив, заполненный нулями, но с такими же размерами.
Функция Shape очень полезна, когда мы смотрим на размер изображения, и здесь мы сделали нарезку этой функции. Так что shape[:2] захватит все до обозначенных точек, т.е. до вторых обозначенных точек, которые будут высотой и шириной изображения, поскольку третья представляет компонент RGB изображения, и она нам здесь не нужна.
1 2 3 4 5 6 7 8 9 10 |
import cv2 import numpy as np image = cv2.imread('input.jpg') B,G,R = cv2.split(image) zeros=np.zeros(image.shape[:2],dtype="uint8") cv2.imshow("RED",cv2.merge([zeros,zeros,R])) cv2.imshow("Green",cv2.merge([zeros,G,zeros])) cv2.imshow("Blue",cv2.merge([B,zeros,zeros])) cv2.waitKey(0) cv2.destroyAllWindows() |
Представление изображения в виде гистограммы
Гистограммное представление изображения — это метод визуализации компонентов изображения.
Следующий код позволяет анализировать изображение с помощью цветовой гистограммы его объединенных и отдельных цветовых компонентов.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
import cv2 import numpy as np #нам нужно импортировать matplotlib для создания гистограмм import matplotlib.pyplot as plt image=cv2.imread('input.jpg') histogram=cv2.calcHist([image],[0],None,[256],[0,256]) #мы строим гистограмму, ravel() выравнивает наш массив изображений plt.hist(image.ravel(),256,[0,256]) plt.show() #просмотр отдельных цветовых каналов color=('b','g','r') #мы знаем, что нужно разделить цвет и построить каждый в histogram for i, col in enumerate (color): histogram2=cv2.calcHist([image],[i],None,[256],[0,256]) plt.plot(histogram2,color=col) plt.xlim([0,256]) plt.show() |
Давайте разберем функцию calcHist с каждым из ее отдельных параметров.
1 |
cv2.calcHist(images, channels, mask, histsize, ranges) |
Images: это исходное изображение типа uint 8 или float 32. Его следует указывать в квадратных скобках, т. е. «[img]», которые также указывают на его массив второго уровня, поскольку изображение для OpenCV — это данные в форме массива.
Channels: также указывается в квадратных скобках. Это индекс канала, для которого мы вычисляем гистограмму, например, если входные данные — изображение в оттенках серого, его значение равно [0], для цветных изображений можно указать [0], [1] или [2], чтобы вычислить гистограмму синего, зеленого и красного каналов соответственно.
Mask: изображение маски. Чтобы найти гистограмму полного изображения, укажите «none». Но если вы хотите найти гистограмму определенной области изображения, вам придется создать для нее изображение маски и указать его в качестве маски.
Histsize: это представляет наш счет BIN. Необходимо указать в квадратных скобках для полной шкалы, которую мы проходим [256].
Ranges: это наш диапазон, обычно [0,256].
Рисование изображений и фигур с помощью OpenCV
Ниже приведены несколько примеров рисования линий, прямоугольников, многоугольников, окружностей и т. д. в OpenCV.
1 2 3 4 5 6 7 8 |
import cv2 import numpy as np #создание черного квадрата image=np.zeros((512,512,3),np.uint8) #мы также можем создать его в черно-белом варианте, однако никаких изменений не будет image_bw=np.zeros((512,512),np.uint8) cv2.imshow("black rectangle(color)",image) cv2.imshow("black rectangle(B&W)",image_bw) |
Линия
1 2 3 4 5 6 |
#создаем линию поверх черного квадрата #cv2.line(image, starting coordinates, ending coordinates, color, thickness) #drawing a diagonal line of thickness 5 pixels image=np.zeros((512,512,3),np.uint8) cv2.line(image,(0,0),(511,511),(255,127,0),5) cv2.imshow("blue line",image) |
Прямоугольник
1 2 3 4 5 6 |
#создаем прямоугольник поверх черного квадрата #cv2.rectangle(image,starting coordinates, ending coordinates, color, thickness) #drawing a rectangle of thickness 5 pixels image=np.zeros((512,512,3),np.uint8) cv2.rectangle(image,(30,50),(100,150),(255,127,0),5) cv2.imshow("rectangle",image) |
Круг
1 2 3 4 5 |
#создание круга над черным квадратом #cv2.circle(image,center,radius,color,fill) image=np.zeros((512,512,3),np.uint8) cv2.circle(image,(100,100),(50),(255,127,0),-1) cv2.imshow("circle",image) |
Полигон
1 2 3 4 5 6 7 8 |
#создание многоугольника image=np.zeros((512,512,3),np.uint8) #lets define four points pts=np.array([[10,50], [400,60], [30,89], [90,68]], np.int32) #lets now reshape our points in form required by polylines pts=pts.reshape((-1,1,2)) cv2.polylines(image, [pts], True, (0,255,255), 3) cv2.imshow("polygon",image) |
Текст
1 2 3 4 5 6 7 |
#вывод текста с помощью opencv #cv2.putText(image,'текст для отображения',нижняя левая начальная точка, шрифт,размер шрифта, цвет, толщина) image=np.zeros((512,512,3),np.uint8) cv2.putText(image,"hello world", (75,290), cv2.FONT_HERSHEY_COMPLEX,2,(100,170,0),3) cv2.imshow("hello world",image) cv2.waitKey(0) cv2.destroyAllWindows() |
Компьютерное зрение и OpenCV — очень обширные темы, но это руководство может стать хорошей отправной точкой для изучения OpenCV и обработки изображений.