Отслеживание взгляда — это процесс измерения либо точки взгляда (куда вы смотрите), либо движения глаза относительно головы. Айтрекер – это устройство для измерения положения и движения глаз.

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

В общем, процессы «обнаружения» представляют собой классификации на основе машинного обучения, которые классифицируют между объектными и необъектными изображениями. Например, есть ли на картинке лицо или нет, и где оно, если оно есть.

Для классификации нужен классификатор. Доступны классификаторы лица и глаз (каскады Хаара), которые поставляются с библиотекой OpenCV, вы можете скачать их из официального репозитория GitHub: Eye Classifier, Face Classifier.

Хотя в конечном итоге мы будем отслеживать глаза на видео, мы начнем с изображения, поскольку оно намного быстрее, а код, который работает с изображением, будет работать и с видео, потому что любое видео — это всего лишь N изображений (кадров) в секунду. Итак, скачайте где-нибудь портрет или используйте для этого свою фотографию. Я буду использовать стоковое изображение.

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

ОБНАРУЖЕНИЕ ДИАГРАММЫ

Теперь мы обнаружили глаза, следующим шагом будет обнаружение радужной оболочки. Для этого ищем самый «круглый» объект в области глаза. К счастью, в OpenCV уже есть функция, которая делает именно это! Он называется и работает следующим образом: сначала он применяет к изображению детектор краев, из которого он делает контуры, а из сделанных контуров пытается вычислить «коэффициент круглости», то есть насколько этот контур похож на круг.

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

getLeftmostEye возвращает только тот прямоугольник, из которого верхняя левая позиция является самой левой. Ничего особенного.

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

Давайте подробно рассмотрим, что ожидает функция HoughCircles:

  • входное изображение: входное изображение
  • круги: круги, которые он нашел
  • метод: метод, который будет применяться
  • DP: Обратное отношение разрешения аккумулятора
  • MinDist: минимальное расстояние между центром одного круга и другим
  • порог: Порог детектора края
  • minArea: какова минимальная площадь круга на изображении?
  • minRadius: Каков минимальный радиус круга на изображении?
  • MaxRadius: Каков максимальный радиус круга на изображении?
import cv2
import numpy as np

cap = cv2.VideoCapture('Eye-Motion.mp4')

while True:
    _, frame = cap.read()
    cv2.imshow('Frame', frame)
    roi = frame[69:395, 137:716]
    rows, col, _ = roi.shape
    gray_roi = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)
    gray_roi = cv2.GaussianBlur(gray_roi,(7,7), 0) # Removing Noises

    _, thres = cv2.threshold(gray_roi, 3, 255, cv2.THRESH_BINARY_INV)
    contours, _ = cv2.findContours(thres,cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # Bounday of white sapce
    contours = sorted(contours, key = lambda x: cv2.contourArea(x), reverse = True)  # it will sort the contours of big to small
    for cnt in contours:
        (x, y, w, h) = cv2.boundingRect(cnt)
        #cv2.drawContours(roi, cnt, -1,(0,  0, 255), 3) # here we only print the biggest area
        cv2.rectangle(roi, (x, y), (x+w, y+h),(255, 0, 0), 2)
        cv2.line(roi, (x+ int(w/2),0), (x + int(w/2), rows), (0, 255, 0), 2)
        cv2.line(roi, (0, y + int(w / 2)), (col, y + int(w / 2)), (0, 255, 0), 2)
        break

    cv2.imshow("Threshold",thres)
    cv2.imshow("Gray_Frame",gray_roi)
    cv2.imshow("Roi",roi)
    key = cv2.waitKey(30)
    if key == 27:
        break
cv2.destroyAllWindows()

Ну вот и все… Как говорит сама функция, она может обнаружить много кругов, а нам нужен только один. Итак, давайте выберем тот, который принадлежит глазному яблоку. Для этого я выбрал очень глупую эвристику: выберите круг, в котором больше «черных» пикселей! Другими словами, круг, сумма пикселей внутри которого минимальна.

Чтобы узнать, находится ли пиксель внутри пикселя или нет, мы просто проверяем, не превышает ли евклидово расстояние между местоположением пикселя и центром окружности радиус окружности. Кусок пирога.

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