В первом проекте было рассмотрено, как собрать механизм поворота/наклона и управлять им с помощью Raspberry Pi. Во втором было реализовано управление по локальной сети. В этом проекте с помощью системы компьютерного зрения реализуем автоматическое слежение за цветными предметами.
Кроме деталей, используемых ранее (Raspberry Pi 3 Модель B, модуль камеры и 2 сервопривода) нам понадобится ещё светодиод и резистор 221 Ом.
Для создания системы компьютерного зрения будет использоваться библиотека OpenCV. Это мой первый опыт работы с OpenCV, и я остался очень доволен этой библиотекой. OpenCV бесплатна для академического и коммерческого использования. Она имеет интерфейсы для C++, C, Python, Java и многих других языков, так же поддерживается работа на Windows, Linux, Mac OS, iOS и Android. В этом проекте для работы будет использоваться Python. OpenCV хорошо оптимизирован и позволяет создавать приложения компьютерного зрения для систем реального времени.
Я использую Raspberry Pi V3, с ОС Raspbian (Stretch), поэтому лучший способ установить OpenCV - это следовать прекрасному руководству, разработанному Адрианом Роузброком «Установка OpenCV 3 и Python на Raspberry Pi». Я попробовал несколько разных руководств, чтобы установить OpenCV и руководство Адриана на мой взгляд самое лучшее.
После установки всего необходимого, у Вас будет виртуальная среда OpenCV, готовая для проведения наших экспериментов. Давайте перейдем в нашу виртуальную среду и проверим, что OpenCV 3 правильно установлена. Адриан рекомендует запускать команду «source» при каждом новом запуске терминала, чтобы убедиться, что системные переменные установлены правильно.
source ~/.profileДалее запустим виртуальную среду:
workon cvЕсли всё установлено и настроено правильно, в терминале в начале строки будет написано «(cv)», примерно так:
(cv) pi@raspberry:~$Адриан обращает внимание, что виртуальная среда cv Python полностью независима и отделена от версии Python по умолчанию, включенной в сборку Raspbian Stretch. Таким образом, любые пакеты Python в глобальном каталоге не будут доступны для виртуальной среды cv. Аналогично, любые пакеты cv Python, не будут доступны для глобальной установки Python.
Теперь запустим интерпретатор Python:
pythonи убедимся, что используется версию 3.5 (или выше).
В интерпретаторе (появится «>>>») импортируем модуль cv2:
import cv2Если сообщения об ошибках не появляются, OpenCV правильно установлен в вашей виртуальной среде PYTHON.
Вы также можете проверить установленную версию OpenCV:
cv2.__version__Вид терминала после выполнения вышеописанных команд:
После установки OpenCV, давайте проверим работоспособность камеры. Если на данный момент вы не подключили и не настроили камеру, посмотрите в предыдущем проекте, как это сделать.
В среде разработки введите следующий (или скачайте файл simpleCamTest.py из репозитория GitHub):
import numpy as np import cv2 cap = cv2.VideoCapture(0) while(True): ret, frame = cap.read() frame = cv2.flip(frame, -1) # Flip camera vertically gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) cv2.imshow('frame', frame) cv2.imshow('gray', gray) if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows()
Приведенный выше код будет получать с камеры видеопоток, отображая его одновременно в цвете и в градациях серого.
Обратите внимание, что в коде сделан поворот изображения с камеры. Если Вы иначе закрепили камеру и поворот не нужен, закомментируйте или удалите в коде строку «flip»:
frame = cv2.flip(frame, -1) # Flip camera verticallyДля запуска этого скрипта, выполним команду:
python simpleCamTest.pyДля прерывания выполнения скрипта, нажмите Ctrl+C.
Для нахождения нужного объекта в кадре с камеры в данном случае будет использоваться информация о цвете. Поэтому нам нужно понимать, как OpenCV интерпретирует цвета.
OpenCV может работать с изображениями в различных цветовых пространствах. Это BGR, RGB, RGBA, BGRA, HSV, HLS, XYZ, LAB, Gray, YUV и т.д.
В модели RGB цвет каждого пикселя формируется комбинацией трёх базовых цветов: красный (red), зелёный (green) и синий (blue). Отличия между моделью RGB и BGR заключается только в порядке цветовых компонент. На данный момент очень часто для хранения и работы с изображениями используют не BGR, а RGB. Будьте внимательны, т.к. в OpenCV по умолчанию используется BGR. Так исторически сложилось - на ранних этапах разработки OpenCV, в видеокартах, камерах и программном обеспечении очень популярным был именно BGR.
В BGR для кодирования каждой компоненты используется один байт, т.е. значение может принимать значения в диапазоне от 0 до 255 (или от 0 до FF в шестнадцатеричном формате). Например, чистый синий пиксель на экране вашего компьютера будет иметь значение B 255, значение G 0 и значение R 0.
Цветовая модель HSV (Hue, Saturation, Value — тон, насыщенность, значение) является альтернативным представлением цветовой модели RGB, разработанной в 1970-х годах исследователями компьютерной графики для большего соответствия тому, как человечек воспринимает цвета.
Для нахождения и отслеживания цветных предметов с помощью OpenCV, мы будем использовать цветовую модель HSV. К примеру, нужно отследить желтый объект:
Что бы определиться, какой цвет в программе нужно искать, можно открыть изображение в какой-нибудь графической программе (графический редактор, видеоредактор и т.д.). Я использовал PowerPoint. В данном случае для указанного пикселя следующие значения цветовых компонент:
Синий - 71Полученные значения цвета в модели BGR (71, 234, 213) нужно преобразовать в HSV, с указанием верхней и нижней границ диапазона. Для этого будем использовать следующий код (файл bgr_hsv_converter.py):
import sys import numpy as np import cv2 blue = sys.argv[1] green = sys.argv[2] red = sys.argv[3] color = np.uint8([[[blue, green, red]]]) hsv_color = cv2.cvtColor(color, cv2.COLOR_BGR2HSV) hue = hsv_color[0][0][0] print("Lower bound is :"), print("[" + str(hue-10) + ", 100, 100]\n") print("Upper bound is :"), print("[" + str(hue + 10) + ", 255, 255]")
Для выполнения введите команду ниже, имеющую в качестве параметров значения BGR, найденные ранее:
python bgr_hsv_converter.py 71 234 213Программа выведет верхнюю и нижнюю границы цвета нашего объекта:
С цветом разобрались, теперь сделаем что бы OpenCV создал маску для нужного цветового диапазона. Скрипт colorDetection.py:
import cv2 import numpy as np # Read the picure - The 1 means we want the image in BGR img = cv2.imread('yellow_object.JPG', 1) # resize imag to 20% in each axis img = cv2.resize(img, (0,0), fx=0.2, fy=0.2) # convert BGR image to a HSV image hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) # NumPy to create arrays to hold lower and upper range # The “dtype = np.uint8” means that data type is an 8 bit integer lower_range = np.array([24, 100, 100], dtype=np.uint8) upper_range = np.array([44, 255, 255], dtype=np.uint8) # create a mask for image mask = cv2.inRange(hsv, lower_range, upper_range) # display both the mask and the image side-by-side cv2.imshow('mask',mask) cv2.imshow('image', img) # wait to user to press [ ESC ] while(1): k = cv2.waitKey(0) if(k == 27): break cv2.destroyAllWindows()
Для выполнения этого демонстрационного скрипта нужно что бы в папке был файл с изображением, в моем случае это файл «yellow_object.JPG». Исли у Вас другое имя файла, переименуйте его или в коде замените на нужное. Запускаем скрипт:
python colorDetection.pyНа экране появится два изображения:
Это исходное изображение и полученная маска.
Теперь, когда мы знаем, как получить маску для объекта, перейдём к отслеживать его движения. Для этого я адаптировал код «отслеживание мяча» Адриана Роузброка. Перед использованием моего кода, убедитесь, что у Вас установлена библиотека imutils. Это коллекция удобных функций OpenCV, созданная Адрианом, которая значительно упрощает выполнение нескольких основных задач (таких как изменение размера или отображение). Если её нет, для установки в среде Virtual Python введите команду:
pip install imutilsЗатем загрузите код ball_tracking.py с моего GitHub и выполните его с помощью команды:
python ball_traking.pyВ результате Вы увидите нечто похожее:
По сути, это тот же код, что и у Адриана, за исключением переворачивания изображения вот такой строкой кода:
frame = imutils.rotate(frame, angle=180)Также обратите внимание, что использовались границы маски, которые мы получили на предыдущем шаге.
Теперь, когда мы познакомились с основами OpenCV, давайте установим светодиод на наш RPi и начнем взаимодействовать с нашими GPIO. Этот светодиод мы будем использовать для индикации, когда в кадре с камеры будут обнаружен объект нужного цвета.
Схема подключения светодиода очень проста:
Катод светодиода подключаем к GPIO 21, а анод к GND через резистор 220Ом.
Давайте проверим наш светодиод в нашей виртуальной среде cv Python.
Помните, что возможно, что RPi.GPIO не установлен в вашей виртуальной среде cv Python. Если нужно установить (не забудьт, что для cv Python в консоли вначале строки отображается «cv»), выполните следующую команду:
pip install RPi.GPIOДля управления светодиодом будет использоваться скрипт GPIO_LED_test.py:
import sys import time import RPi.GPIO as GPIO # initialize GPIO and variables redLed = int(sys.argv[1]) freq = int(sys.argv[2]) GPIO.setmode(GPIO.BCM) GPIO.setup(redLed, GPIO.OUT) GPIO.setwarnings(False) print("\n [INFO] Blinking LED (5 times) connected at GPIO {0} at every {1} second(s)".format(redLed, freq)) for i in range(5): GPIO.output(redLed, GPIO.LOW) time.sleep(freq) GPIO.output(redLed, GPIO.HIGH) time.sleep(freq) # do a bit of cleanup print("\n [INFO] Exiting Program and cleanup stuff \n") GPIO.cleanup()
Этот скрипт получает в качестве аргумента номер GPIO и частоту, с которой светодиод должен мигать. Светодиод будет мигать 5 раз, после чего программа завершиться. Обратите внимание, что перед завершением мы освободим GPIO.
Пример использования:
python LED_simple_test.py 21 1Теперь сделаем что бы при обнаружении объекта загорался светодиод. «Создаём» в скрипте светодиод:
import RPi.GPIO as GPIO redLed = 21 GPIO.setmode(GPIO.BCM) GPIO.setwarnings(False) GPIO.setup(redLed, GPIO.OUT)
Инициализируем (делаем изначально выключенным):
GPIO.output(redLed, GPIO.LOW) ledOn = False
Теперь внутри цикла, где отрисовывается круг, когда объект найден, включаем светодиод:
GPIO.output(redLed, GPIO.HIGH) ledOn = True
Полный код получившегося скрипта можно скачать по ссылке: object_detection_LED.py.
Запускаем скрипт:
python object_detection_LED.pyЗдесь результат. Обратите внимание, что светодиод (левый нижний угол) загорается при каждом обнаружении объекта:
Поэкспериментируем с объектами разного цвета и формы:
На данном этапе используется только светодиод, теперь задействуем механизм поворота/наклона, чтобы поворачивать камеру в сторону обнаруженного объекта.
Если у Вас ещё не собран или не настроен механизм поворота/наклона, сделайте это как описано в прошлых проектах (см. ссылки в самом начале).
Для теста работоспособности в cv Python запустим скрипт angleServoCtrl.py:
python angleServoCtrl.py 17 45Приведенная выше команда установит сервопривод, подключенный к GPIO 17 под углом 45 градусов.
Чтобы в кадре объект находился примерно по центру, нам при его обнаружении нужно знать его координаты.
За основу возьмем скрипт «object_detect_LED», использованный ранее, и изменим его, чтобы вывести координаты найденного объекта. В итоге получится такой скрипт objectDetectCoord.py.
В той части где, где происходило обнаружение объекта и отрисовка вокруг него круга с красной точкой внутри, добавлен вызов функции, которая выводит координаты объекта:
if radius > 10: # draw the circle and centroid on the frame, # then update the list of tracked points cv2.circle(frame, (int(x), int(y)), int(radius), (0, 255, 255), 2) cv2.circle(frame, center, 5, (0, 0, 255), -1) # print center of circle coordinates mapObjectPosition(int(x), int(y)) # if the led is not already on, turn the LED on if not ledOn: GPIO.output(redLed, GPIO.HIGH) ledOn = True
Функция вывода координат:
def mapObjectPosition (x, y): print ("[INFO] Object Center coordenates at X0 = {0} and Y0 = {1}".format(x, y))
При выполнении скрипта, в терминале будут выводиться координаты центра найденного объекта:
Теперь эти координаты мы будем использовать для управления сервоприводами.
Давайте будем считать, что наш объект находится в центре, если координаты найденного центра объекта находятся в диапазонах:
220 < х < 280Когда полученные координаты будут выходить за пределы этого диапазона, мы будем соответствующим образом управлять механизмом поворота/наклона, чтобы компенсировать это отклонение. Для этого добавим функцию «mapServoPosition (x, y)»:
def mapServoPosition (x, y): global panAngle global tiltAngle if (x < 220): panAngle += 10 if panAngle > 140: panAngle = 140 positionServo (panServo, panAngle) if (x > 280): panAngle -= 10 if panAngle < 40: panAngle = 40 positionServo (panServo, panAngle) if (y < 160): tiltAngle += 10 if tiltAngle > 140: tiltAngle = 140 positionServo (tiltServo, tiltAngle) if (y > 210): tiltAngle -= 10 if tiltAngle < 40: tiltAngle = 40 positionServo (tiltServo, tiltAngle)
Обратите внимание, что «x» и «y», используемые в качестве параметров в этой функции, это координаты центра найденного объекта. На основе этих координат определяем какой сервопривод и на какой угол нужно установить, затем вызываем функцию «positionServo (panServo, panAngle)».
Например, предположим, что текущий угол наклона примерно равен 120 градусам, а y=50. Это означает, что объект где-то в верхней части изображения. Чтобы это скомпенсировать, нам нужно уменьшить угол наклона (допустим, до 100 градусов), чтобы центр объекта оказался в центре изображения с камеры. Для наглядности:
Обратите внимание, что изображение с камеры не зеркальное. Это означает, что, если Вы провернёте камеру влево, то объект на изображении переместиться вправо.
Функцию «positionServo(servo, angle)» можно реализовать так:
def positionServo (servo, angle): os.system("python angleServoCtrl.py " + str(servo) + " " + str(angle)) print("[INFO] Positioning servo at GPIO {0} to {1} degrees\n".format(servo, angle))
Обратите внимание, что скрипт angleServoCtrl.py должен находиться в том же каталоге, что и objectDetectTrack.py. Полный код можно скачать с моего GitHub: objectDetectTrack.py
Ниже приведен пример работы нашего проекта:
Все файлы проекта Вы можете скачать из моего репозитория на GitHub: OpenCV-Object-Face-Tracking. В этом репозитории некоторые файлы от следующего проекта, над которым работаю: