Статья не содержит описания важных достижений, просьба относиться к ней как к DIY поделке. Когда искал ответ на вопрос не нашел (плохо искал) решения с применением openCV, а так же двух и более камер для наблюдения за объектами.
Сегодня в сети можно найти множество статей посвященных распознаванию, отслеживанию объектов и дальнейшей обработке полученной информации. Для моего проекта требуется определение положения объекта по "трем осям". То есть помимо определения координат x и y - с чем прекрасно справляется одна вебкамера (камера 1), необходимо получить "глубину" - координаты, которые покажут удаление объекта от камеры 1.
Все задумывалось как тест камер на реальной задаче. Чтобы было не так скучно, перевел всё в игру для двоих, что порадовало домашних.
На картинке ниже объяснение работы скрипта. Камера 1 отслеживает координаты объекта и переводит их в управление мышью на экране. Камера 2 ослеживает зону, при попадании в которую производится клик левой кнопки мыши.
Использовались 2 вебкамеры logitech c270, проектор, компьютер, поросенок.
Скрипт на python, кликер на unity. Кликер при попадании в круг добавляет игроку очко и считает общее количество ходов.
Ниже стандартный код, распознавания объекта по цвету. Комментарии оставил только к тем блокам, которые добавил. Так как основной код расжеван в сети много раз.
import cv2
from collections import deque
import argparse
import pyautogui
#работаем с двумя камерами
camera = cv2.VideoCapture(0)
camera1 = cv2.VideoCapture(1)
ap = argparse.ArgumentParser()
ap.add_argument("-v", "--video",
help="path to the (optional) video file")
ap.add_argument("-b", "--buffer", type=int, default=64,
help="max buffer size")
args = vars(ap.parse_args())
colorLower = (4, 100, 100)
colorUpper = (24, 255, 255)
pts = deque(maxlen=args["buffer"])
while True:
(grabbed, frame) = camera.read()
(grabbed, frame1) = camera1.read()
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
hsv1 = cv2.cvtColor(frame1, cv2.COLOR_BGR2HSV)
mask1 = cv2.inRange(hsv1, colorLower, colorUpper)
mask1 = cv2.erode(mask1, None, iterations=2)
mask1 = cv2.dilate(mask1, None, iterations=2)
cnts = cv2.findContours(mask1.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)[-2]
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv, colorLower, colorUpper)
mask = cv2.erode(mask, None, iterations=2)
mask = cv2.dilate(mask, None, iterations=2)
cnts1 = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)[-2]
center = None
# only proceed if at least one contour was found
if len(cnts) > 0:
# find the largest contour in the mask, then use
# it to compute the minimum enclosing circle and
# centroid
c = max(cnts, key=cv2.contourArea)
((x, y), radius) = cv2.minEnclosingCircle(c)
M = cv2.moments(c)
center = (int(M["m10"] / M["m00"]), int(M["m01"] / M["m00"]))
#КРУГ-РАДИУС
# 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(frame1, (int(x), int(y)), int(radius),
(0, 255, 255), 2)
cv2.circle(frame1, center, 5, (0, 0, 255), -1)
#дублируем движение объекта, курсором мыши
pyautogui.moveTo(int(x)*2, int(y)*2)
if len(cnts1) > 0:
# find the largest contour in the mask, then use
# it to compute the minimum enclosing circle and
# centroid
c = max(cnts1, key=cv2.contourArea)
((x, y), radius) = cv2.minEnclosingCircle(c)
M = cv2.moments(c)
center = (int(M["m10"] / M["m00"]), int(M["m01"] / M["m00"]))
#КРУГ-РАДИУС
# 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)
#клик, когда шарик попадает в зону "клика" второй камеры
if int(x)>290:
pyautogui.click()
#задержка чтобы наш поросенок отлетел от стены, иначе накрутит много очков
cv2.waitKey(700)
# update the points queue
pts.appendleft(center)
#линия настройки, на картинке получаемой с камеры контроля "клика"
cv2.line(frame, (320, 0), (320, 512), (0, 255, 0), thickness=2)
cv2.line(frame, (295, 0), (295, 512), (0, 255, 0), thickness=2)
#cv2.imshow("Frame", frame)
#cv2.imshow("Frame1", frame1)
key = cv2.waitKey(1) & 0xFF
# if the 'q' key is pressed, stop the loop
if key == ord("q"):
break
# cleanup the camera and close any open windows
camera.release()
cv2.destroyAllWindows()
Почему использовался плюшевый поросенок? Изначально все затачивалось под тенисный мячик. И планировалось как игра на двоих с тенисными ракетками. Но камера 2 не успевает "засечь" мячик и сделать клик. Поросенок по цвету отлично подошел для отслеживания.
Почему на стену проецировалось изображение с проектора? Изначально была мысль делать большую мишень из бумаги, но узнав, что крепиться она будет на новые обои, мысль зарубили на корню. Но тут есть плюсы, можно использовать проектор для любых игр кликеров.
Я в ВК https://vk.com/zxgamevr ссылка на Git (если нужен скрипт по получению цвета для отслеживания) https://github.com/zxgame0/PuzikFly