В предыдущем уроке мы рассмотрели, как управлять сервоприводами Поворота / Наклона для размещения PiCam. Теперь мы будем использовать наше устройство, чтобы помочь камере автоматически отслеживать цветные объекты, как вы можете видеть ниже:
Это мой первый опыт работы с OpenCV, и я должен признаться, я влюблен в эту фантастическую «библиотеку компьютерного зрения с открытым исходным кодом».
OpenCV бесплатный для академического и коммерческого использования. Он имеет интерфейсы C ++, C, Python и Java и поддерживает Windows, Linux, Mac OS, iOS и Android. В моей серии обучающих программ OpenCV мы сосредоточимся на Raspberry Pi (так, Raspbian как OS) и Python. OpenCV был разработан для вычислительной эффективности и с большим вниманием к приложениям реального времени. Таким образом, он идеально подходит для физических вычислений!
Шаг 1: Список деталей проекта
- Raspberry Pi V3 - US$ 32.00 / Raspberry Pi V3 - US$ 36.99
- 5 Megapixels 1080p Sensor OV5647 Mini Camera Video Module - US$ 13.00 / 5 Megapixels 1080p Sensor OV5647 Mini Camera Video Module - US$ 6.40
- TowerPro SG90 9G 180 degrees Micro Servo (2 X)- US$ 4.00 / TowerPro SG90 9G 180 degrees Micro Servo (2 X)- US$ 2.45
- Mini Pan/Tilt Camera Platform Anti-Vibration Camera Mount w/ 2 Servos (*) - US$ 8.00 / Mini Pan/Tilt Camera Platform Anti-Vibration Camera Mount w/ 2 Servos (*) - US$ 5.89
- Красный Светодиод
- Резистор 220 Ом
- Резистор 1 кОм (2) - Опционально
- Прочее: металлические детали, ленты и т. Д. (В случае, если вы создадите механизм Поворота / Наклона
(*) вы можете купить полную платформу Поворота / Наклона с сервоприводами или построить свой собственный.
Шаг 2: Установка пакета OpenCV 3
Я использую Raspberry Pi V3, обновленный до последней версии Raspbian (Stretch), поэтому лучший способ установить OpenCV - следовать отличному учебному пособию, разработанному Адрианом Розброком: Raspbian Stretch: Установка OpenCV 3 + Python на Raspberry Pi V3.
Я попробовал несколько разных руководств по установке OpenCV на моем Pi. Учебник Адриана - лучший. Я советую вам сделать то же самое, следуя его указаниям шаг за шагом.
После того, как вы закончите установку по учебнику Адриана, у Вас должна быть виртуальная среда OpenCV, готовая запускать наш эксперимент на Raspberry Pi.
Перейдем к нашей виртуальной среде и убедитесь, что OpenCV 3 правильно установлен.
Адриан рекомендует запускать команду «источник» каждый раз, когда вы открываете новый терминал, чтобы обеспечить правильную настройку системных переменных.
source ~/.profile
Затем перейдем к нашей виртуальной среде:
workon cv
Если вы видите текст (cv), предшествующий вашему запросу, то вы находитесь в виртуальной среде cv:
(cv) pi@raspberry:~$
Адриан обращает внимание, что виртуальная среда cv Python полностью независима и секвестрирована из стандартной версии Python, включенной в загрузку Raspbian Stretch. Таким образом, любые пакеты Python в каталоге глобальных пакетов сайтов не будут доступны для виртуальной среды cv. Аналогично, любые пакеты Python, установленные в сайтах-пакетах cv, не будут доступны для глобальной установки Python.
Теперь введите интерпретатор Python:
Python
и убедитесь, что вы используете версию 3.5 (или выше)
Внутри интерпретатора (появится «>>>») импортируйте библиотеку OpenCV:
import cv2
Если сообщений об ошибках не выходят, значит OpenCV правильно установлен на ВАШЕЙ PYTHON ВИРТУАЛЬНОЙ СРЕДЕ.
Вы также можете проверить какая версия OpenCV установлена:
cv2.__version__
Должен появиться 3.3.0 (или более высокая версия, которая может быть выпущена в будущем). Вышеупомянутый терминал PrintScreen показывает предыдущие шаги.
Шаг 3: Тестирование Вашей Камеры
Как только вы установите OpenCV в свой RPi, давайте проверим, работает ли ваша камера правильно.
Я предполагаю, что у вас есть PiCam, уже установленный на вашем Raspberry Pi.
Введите ниже код Python в вашу среду IDE:
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()
Вышеприведенный код будет захватывать видеопоток, который будет создан вашим PiCam, и отображать его как в цвете BGR, так и в режиме Gray.
Обратите внимание, что я повернул камеру по вертикали из-за того, что она собрана. Если это не ваше дело, прокомментируйте или удалите командную строку «flip».
Вы также можете загрузить код из GitHub: simpleCamTest.py
Для выполнения введите команду:
python simpleCamTest.py
Чтобы завершить программу, вы должны нажать клавишу [q] tor [Ctrl] + [C] на клавиатуре
На рисунке показан результат.
Чтобы узнать больше о OpenCV, вы можете изучить за учебник: -video-python-opencv-tutorial
Шаг 4: Обнаружение цвета в Python с OpenCV
Одна вещь, которую мы попытаемся выполнить, будет - обнаружение и отслеживание определенного цветового объекта. Для этого нам нужно понять немного больше о том, как OpenCV интерпретирует цвета.
Анри Данг написал отличный учебник по распознаванию цвета в Python с OpenCV.
Обычно наша камера будет работать с цветовым режимом RGB, что можно понять, думая об этом как о всех возможных цветах, которые могут быть сделаны из трех цветных огней красного, зеленого и синего. Мы будем работать здесь с BGR (синий, зеленый, красный).
Как описано выше, с BGR пиксель представлен тремя параметрами: синим, зеленым и красным. Каждый параметр обычно имеет значение от 0 до 255 (или от 0 до FF в шестнадцатеричном формате). Например, чистый синий пиксель на экране вашего компьютера будет иметь значение B 255, значение G 0 и значение R 0.
OpenCV работает с цветовой моделью HSV (Hue, Saturation, Value), что является альтернативным представлением цветовой модели RGB, разработанной в 1970-х годах исследователями компьютерной графики, чтобы в большей степени соответствовать тому, как человеческое видение воспринимает цветовые атрибуты:
Отлично. Итак, если вы хотите отслеживать определенный цвет с помощью OpenCV, Вы должны определить его с помощью модели HSV.
Предположим, что я должен отследить желтый объект, как пластиковый кубик, показанный выше картинки. Легкость в том, чтобы найти его элементы BGR. Вы можете использовать любую конструкторскую программу для ее поиска (я использовал PowerPoint). В моем случае я обнаружил:
- Синий: 71
- Зеленый: 234
- Красный: 213
Затем мы должны преобразовать модель BGR (71, 234, 213) в модель HSV, которая будет определена с верхними и нижними границами диапазона. Для этого давайте запустим следующий код:
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]")
Вы также можете скачать код из GitHub: bgr_hsv_converter.py
Для выполнения введите команду ниже, имеющую в качестве параметров значения BGR, найденные ранее:
python bgr_hsv_converter.py 71 234 213
Программа напечатает верхнюю и нижнюю границы цвета нашего объекта.
В этом случае:
lower bound: [24, 100, 100]
и
upper bound: [44, 255, 255]
Терминал PrintScreen покажет результат.
Последнее, но не финал, давайте посмотрим, как OpenCV может «mask» наш объект, как только мы определили его цвет:
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()
Вы также можете загрузить код из GitHub: colorDetection.py
Для выполнения введите следующую команду, имеющую в вашем каталоге фотографию с вашим целевым объектом (в моем случае: yellow_object.JPG):
python colorDetection.py
На приведенном выше рисунке будет отображаться исходное изображение («image») и как будет отображаться объект («mask») после применения маски.
Шаг 5: Отслеживание движения объекта
Теперь, когда мы знаем, как «выбрать» наш объект с помощью маски, давайте проследим его движение в реальном времени с помощью камеры. Для этого я написал код на основе Adrian Rosebrock's Ball Tracking with OpenCV.
Я настоятельно рекомендую вам подробно прочитать учебник Адриана.
Сначала проверьте, установлена ли библиотека imutils. Это коллекция удобных функций OpenCV от Adrian, чтобы сделать несколько основных задач (например, изменение размера или переворот-экрана) намного проще. Если библиотека не установлена, введите команду ниже, чтобы установить библиотеку в среду Virtual Python:
pip install imutils
Затем загрузите код ball_tracking.py из GitHub и выполните его с помощью команды:
python ball_traking.py
В результате вы увидите нечто похожее на gif ниже:
В принципе, это тот же код, что и Adrian's, если только «вертикальный вертикальный поворот», который я получил со строчкой:
frame = imutils.rotate(frame, angle=180)
Также обратите внимание, что использовались границы маски, которые мы получили на предыдущем шаге.
Шаг 6: Тестирование GPIO
Теперь, когда мы поиграли с основами OpenCV, давайте установим светодиод на наш RPi и начнем взаимодействовать с нашими GPIO.
Следуйте приведенной выше электрической схеме: катод светодиода будет подключен к GPIO 21 и анод к GND через 220-омный резистор.
Давайте проверим наш светодиод внутри нашей виртуальной среды Python.
Помните, что возможно, что RPi. GPIO не установлен в вашей виртуальной среде Python! Чтобы устранить эту проблему, как только вы зашли (не забудьте подтвердить, что (cv) находится в вашем терминале), вам нужно использовать pip для его установки в вашу виртуальную среду:
pip install RPi.GPIO
Давайте используем скрипт python для выполнения простого теста:
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.
Итак, чтобы выполнить скрипт, вы должны ввести параметры, светодиод GPIO и частоту.
Например:
python LED_simple_test.py 21 1
Вышеуказанная команда будет мигать 5 раз красным светодиодом, подключенным к «GPIO 21» через каждые «1» секунд.
Файл GPIO_LED_test.py можно загрузить с GitHub.
На приведенном выше экране терминала отображается результат (и, конечно же, вы должны убедиться, что светодиод мигает.
Теперь давайте работать с OpenCV и некоторыми базовыми материалами GPIO.
Шаг 7: Распознавание цветов и взаимодействие с GPIO
Давайте начнем интегрировать наш код OpenCV для взаимодействия с GPIO. Мы начнем с последнего вариант кода OpenCV, мы будем интегрировать в него библиотеку GPIO-RPI, поэтому мы запускаем красный светодиод в то время, когда наш цветной объект будет найден камерой. Код, используемый на этом шаге, был основан на большом учебнике Эдриана OpenCV, RPi.GPIO и GPIO Zero на Raspberry Pi:
Первое, что нужно сделать, это «создать» наш светодиод, подключив его к конкретному GPIO:
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
Давайте загрузим полный код из GitHub: object_detection_LED.py
Запустите код с помощью команды:
python object_detection_LED.py
Вот результат. Обратите внимание, что светодиод (левый нижний угол) горит каждый раз, когда объект обнаружен:
Попробуйте использовать разные объекты (цвет и формат). Вы увидите, что как только цвет будет соответствовать границам маски, светодиод будет включен.
Видео, приведенное ниже, показывает некоторые варианты. Обратите внимание, что будут обнаружены только желтые объекты, которые остаются внутри цветового диапазона, включив светодиод. Объекты с иными цветами игнорируются.
Мы используем только светодиод здесь, как объяснено на последнем шаге. У меня был уже собрана поворотно / наклонная площадка, когда я сделал видео, так что игнорировать его. В следующем шаге мы будем работать с механизмом Поворота / Наклона.
Шаг 8: Механизм Поворота / Наклона
Теперь, когда мы поэкспериментировали с основами OpenCV и GPIO, давайте установим наш механизм Поворота / Наклона.
Для получения дополнительной информации ознакомитесь с уроком: ПОВОРОТНО-НАКЛОННАЯ ПЛАТФОРМА НА RASPBERRY PI И PYTHON.
Сервоприводы должны быть подключены к внешнему источнику питания 5 В, имеющему свой Контакт данных (в моем случае, их желтая проводка) подключаться к Raspberry Pi GPIO, как показано ниже:
- GPIO 17 ==> Tilt Servo
- GPIO 27 ==> Pan Servo
Не забудьте соединить GND вместе ==> Raspberry Pi - Серводвигатели - внешний источник питания)
Опционально у вас может быть, резистор 1 кОм, между Raspberry Pi GPIO и выводом ввода данных сервера. Это защитит ваш RPi в случае проблемы с сервомеханизмом.
Давайте также воспользуемся возможностью и протестируем наши сервомоторы внутри нашей виртуальной среды Python.
Давайте используем скрипт Python для выполнения некоторых тестов с нашими драйверами:
from time import sleep
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
def setServoAngle(servo, angle):
pwm = GPIO.PWM(servo, 50)
pwm.start(8)
dutyCycle = angle / 18. + 3.
pwm.ChangeDutyCycle(dutyCycle)
sleep(0.3)
pwm.stop()
if __name__ == '__main__':
import sys
servo = int(sys.argv[1])
GPIO.setup(servo, GPIO.OUT)
setServoAngle(servo, int(sys.argv[2]))
GPIO.cleanup()
Ядром приведенного выше кода является функция setServoAngle (servo, angle). Эта функция принимает в качестве аргументов, номер GPIO сервопривода и значение угла, в котором должен быть установлен сервопривод. Когда вход этой функции является «углом», мы должны преобразовать его в эквивалентный рабочий цикл.
Чтобы выполнить сценарий, вы должны ввести в качестве параметров, сервопривод GPIO и angle.
Например:
python angleServoCtrl.py 17 45
Вышеуказанная команда будет позиционировать сервопривод, подключенный к GPIO 17 («наклон») с 45 градусами в «высоте». Аналогичную команду можно использовать для управления поворотного привода (положение до 45 градусов в «азимуте»:
python angleServoCtrl.py 27 45
Файл angleServoCtrl.py можно загрузить с GitHub.
Шаг 9: Поиск позиции объекта в реальном времени
Идея здесь состоит в том, чтобы поместить объект в середину экрана с помощью механизма Поворота / Наклона. Плохая новость заключается в том, что для начала мы должны знать, где объект находится в реальном времени. Но хорошая новость в том, что это очень легко, как только мы узнаем координаты центра объекта.
Во-первых, давайте возьмем использованный ранее код «object_detect_LED» и изменим его, чтобы распечатать координаты x, y основанного объекта.
Загрузите с GitHub код: objectDetectCoord.py
«Ядро» кода - это часть, где мы находим объект и наносим на него круг с красной точкой в центре.
# only proceed if the radius meets a minimum size
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
Давайте будем «экспортировать» координаты центра для функции mapObjectPosition (int (x), int (y)) для печати своих координат. Ниже функция:
def mapObjectPosition (x, y):
print ("[INFO] Object Center coordenates at X0 = {0} and Y0 = {1}".format(x, y)
Запустив программу, мы увидим на нашем терминале координаты положения (x, y), как показано выше. Переместите объект и наблюдайте за координатами. Мы поймем, что x идет от 0 до 500 (слева направо), а y идет от o до 350 (сверху вниз). См. Приведенные выше снимки.
Отлично! Теперь мы должны использовать эти координаты в качестве отправной точки для нашей системы отслеживания Поворота / Наклона.
Шаг 10: Система отслеживания местоположения объекта
Мы хотим, чтобы наш объект всегда находился на экране. Итак, давайте определим, например, что наш объект будет «центрирован», если:
220 < x < 280
И
160 < y < 210
Вне этих границ мы должны переместить наш механизм Поворота / Наклона, чтобы компенсировать отклонения. Исходя из этого, мы можем построить функцию mapServoPosition (x, y), как показано ниже. Обратите внимание, что «x» и «y», используемые в качестве параметров в этой функции, те же, что мы использовали ранее для печати центрального положения:
# position servos to present object at center of the frame
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 (servo, angle). Например, предположим, что y-позиция равна «50», что означает, что наш объект находится почти в верхней части экрана, что можно перевести, что «взгляд камеры» направлен вниз (скажем, угол наклона 120 градусов) Таким образом, мы должны «уменьшить» угол наклона (скажем, до 100 градусов), поэтому «взгляд камеры» будет двигаться «вверх», и объект будет перемещаться «вниз» на экране (у увеличится, скажем, 190). На приведенной выше диаграмме показан пример с точки зрения геометрии.
Подумайте, как будет работать камера Наклона. обратите внимание, что экран не заркалит, что означает, что, если вы переместите объект в «лево», он будет перемещаться по экрану «справа», когда вы находитесь в оппозиции к камере.
Функция 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 должен находиться в том же каталоге, что и objectDetectTrac.py
Полный код можно скачать с GitHub: objectDetectTrack.py
Ниже gif показан пример работы нашего проекта:
Шаг 11: Вывод
Как всегда, я надеюсь, что этот проект поможет другим найти свой путь в захватывающий мир электроники!
Подробнее и окончательный код, пожалуйста, посетите депозитарий GitHub: OpenCV-Object-Face-Tracking
На следующем уроке, мы рассмотрим «Распознавание лица»: