Python, Программирование

Как бороться с сонливостью за рулём с помощью Python

Время прочтения: 8 мин.

Обнаружение сонливости водителя продиктовано потребностью безопасности – разработка приложения для обнаружения в режиме реального времени позволит избежать серьезных происшествий в тот момент, когда водитель переутомлен. По разным оценкам, около 20% всех уличных происшествий связаны с переутомлением, а на некоторых оживленных улицах – до 50%. Таким образом, совершенствование технологий распознавания и предотвращения сна за рулем может стать серьезным вызовом в области улучшения систем предотвращения аварий. При обнаружении сонливости, необходимо в тот же момент предупредить водителя о возможных неприятностях. Подобное обнаружение достигается при помощи детектирования состояния глаз водителя.

Использование библиотек языка программирования Python позволяет выполнить программную реализацию системы обнаружения сонливости водителя, которое позволяет определять, как долго у конкретного человека (водителя) были закрыты глаза. Если глаза были закрыты в течение определенного времени, то следует предположить, что водитель начинает засыпать, и включить звуковой сигнал, чтобы разбудить водителя и привлечь его внимание.

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

Классификация состояния глаз осуществляется при помощи методов компьютерного зрения. Чтобы начать реализацию, необходимо создать новый *.py-файл, открыть его в текстовом редакторе или среде разработки для языка Python (например, в IDLE) и выполнить подключение необходимых библиотек. Исходный код функции воспроизведения приведен в листинге 1.

Листинг 1. Функция воспроизведения уведомления
def runAlarm(path):
	# воспроизвести уведомление
	playsound.playsound(path)

Далее необходимо определить функцию findEAR, которая используется для вычисления отношения расстояний между вертикальными признаками глаза и расстояниями между горизонтальными признаками глаза (листинг 2).

Листинг 2. Функция вычисления EAR
def findEAR(eye):
	# вычислить евклидовы расстояния между двумя наборами
	# вертикальных признаков глаза (x, y)-координат
	A = dist.euclidean(eye[1], eye[5])
	B = dist.euclidean(eye[2], eye[4])
	# то же самое для горизонтальных признаков
	C = dist.euclidean(eye[0], eye[3])
	# вычислить EAR
	ear = (A + B) / (2.0 * C)
	# вернуть его
	return ear

Возвращаемое значение соотношения сторон глаза будет приблизительно константным и положительным, когда глаз открыт. Затем значение будет быстро уменьшаться до нуля во время моргания.

Если глаз закрыт, соотношение сторон глаза снова останется примерно постоянным, но будет намного меньше, чем соотношение в тот момент, когда глаз открыт. Визуализация приведена на рисунке 1.

Рисунок 1 – Визуализация глазных ориентиров. Вверху слева: глаз открыт. Вверху справа: глаз закрыт. Внизу: график соотношения сторон с течением времени. Падение соотношения сторон глаза указывает на моргание.

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

Листинг 3. Пороговые значения 
# определить две константы:
# одну для соотношения сторон глаза, чтобы определить моргание,
# а затем вторую константу для количества последовательных кадров.
# глаз должен быть ниже порогового значения, чтобы сработал сигнал тревоги
THRESHOLD_EAR = 0.3
FRAMES_EAR = 48
# инициализировать счетчик кадров, а также логическое значение,
# используемое для индикации срабатывания сигнализации
COUNTER = 0
ALARM_ON = False

В начале листинга 3 определена переменная THRESHOLD_EAR. Если соотношение сторон глаза упадет ниже этого порога, то необходимо начать подсчет количества кадров, на протяжении которых человек закрыл глаза.

Если количество кадров, в которых человек закрыл глаза, превышает FRAMES_EAR, необходимо издать звуковой сигнал.

Экспериментально было обнаружено, что значение THRESHOLD_EAR, равное 0,3, хорошо работает в различных ситуациях.

Значение FRAMES_EAR также экспериментально было установлено на 48 – если человек закрывает глаза на 48 последовательных кадров, то будет воспроизводиться звуковой сигнал. Можно сделать детектор более чувствительным, уменьшив FRAMES_EAR – аналогично, менее чувствительным, увеличив его.

В листинге 3 также определена переменная COUNTER – общее количество последовательных кадров, на протяжении которых соотношение сторон глаза меньше THRESHOLD_EAR. Если COUNTER превышает FRAMES_EAR, то следует обновить логическое значение ALARM_ON. Библиотека dlib поставляется с детектором лица на основе гистограммы ориентированных градиентов вместе с предсказателем лицевых признаков. Соответствующие экземпляры создаются в следующем блоке кода (листинг 4).

Листинг 4. Детектор лица и предсказатель признаков лица
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor(args["shape_predictor"])

Признаки (ориентиры) лица, созданные dlib, представляют собой индексируемый список (рисунок 2). Следовательно, чтобы извлечь области глаз из набора лицевых признаков, необходимо знать правильные индексы срезов массива (листинг 5).

Листинг 5. Выделение индексов признаков глаз
(leftEyeSt, leftEyeEnd) = face_utils.FACIAL_LANDMARKS_IDXS["left_eye"]
(rightEyeSt, rightEyeEnd) = face_utils. FACIAL_LANDMARKS_IDXS["right_eye"]

Используя эти индексы, можно легко выделить области глаз с помощью списковых срезов. Теперь можно запустить ядро ​​детектора сонливости (листинг 6).


Рисунок 2 – Визуализация лицевых признаков.

Листинг 6. Запуск детектора сонливости водителя
# запустить захват видеопотока с веб-камеры
vs = VideoStream(src=args["webcam"]).start()
time.sleep(1.0)

# цикл по кадрам из видеопотока
while True:
	# захватить кадр из потокового видеофайла, изменить его размер
        # и преобразовать в каналы в оттенки серого
	frame = vs.read()
	frame = imutils.resize(frame, width=450)
	gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

	# обнаружить лица при помощи детектора
	rects = detector(gray, 0)

Вначале создается экземпляр потока VideoStream, который использует индекс веб-камеры ноутбука для захвата видео. Также здесь необходимо сделать паузу на одну секунду, чтобы дать сенсору камеры включиться. В цикле считываются кадры видеопотока один за другим, и затем они подвергаются предварительной обработке (размер каждого кадра изменяется до ширины 450 пикселей; каждый кадр преобразуется в оттенки серого). Детектор лиц dlib применяется для поиска и определения местоположения лица (лиц) на изображении.

Следующим шагом будет применение функции распознавания признаков на лице, чтобы локализовать каждую из важных областей лица. Каждое из обнаруженных лиц перебирается в цикле (на самом деле предполагается, что есть только одно лицо – водителя, но этот цикл может быть использован для видео с более чем одним лицом). Для каждого из обнаруженных лиц применяется детектор лицевых признаков dlib, и результат преобразуется в numpy-массив. Используя срезы массива, можно извлечь (x, y)-координаты левого и правого глаза соответственно. Зная (x, y)-координаты для обоих глаз, легко затем вычислить их соотношения сторон. Для лучшей оценки эти значения усредняются. Затем можно визуализировать каждую из областей глаз на кадре с помощью функции cv2.drawContours – это часто бывает полезно, когда необходимо убедиться, что глаза правильно обнаруживаются и локализуются.

Наконец, все готово для того, чтобы можно было проверить, не начинает ли водитель в видеопотоке проявлять симптомы сонливости.

Вначале проверяется, находится ли соотношение сторон глаза ниже порога «blink / closed» («моргание / закрытие»).

Если это так, то увеличивается счетчик, общее количество последовательных кадров, в которых у водителя были закрыты глаза.

Если значение счетчика превышает установленное максимальное число кадров, то можно предположить, что водитель начинает засыпать.

Далее выполняется еще одна проверка, чтобы увидеть, включен ли звуковой сигнал – если нет, то он включается.

Затем обрабатывается воспроизведение звукового сигнала при условии, что при выполнении скрипта был указан путь к файлу —alarm. Особое внимание здесь уделяется созданию отдельного потока, отвечающего за вызов звукового сигнала, чтобы гарантировать, что основная программа не заблокируется до тех пор, пока не закончится воспроизведение звука.

После этого в кадре отображается текст «DROWSINESS ALERT!» («ПРЕДУПРЕЖДЕНИЕ О СОНЛИВОСТИ»).

Наконец, обрабатывается случай, когда соотношение сторон глаза больше, чем значение порога, что указывает на то, что глаза открыты. Если глаза открыты, то счетчик сбрасывается, а звуковое уведомление не воспроизводится.

При использовании классификатора сначала настраивается камера, которая отслеживает все лица на видеокадре из потока (рисунок 3).

Рисунок 3 – Поиск лиц во входном видеопотоке.

Если лицо найдено, то выполняется определение признаков глаз на лице и извлекаются области глаз (рисунок 4).

Рисунок 4 – Выделение глаз на лице водителя.

Теперь, когда на кадре выделены области глаз, можно вычислить соотношение сторон глаза (EAR – Eye Aspect Ratio), чтобы определить, закрыты ли глаза (рисунок 5).

Рисунок 5 – Вычисление EAR.

Если соотношение сторон глаза указывает на то, что глаза были закрыты достаточно долгое время, то вызывается звуковое уведомление, предназначенное для того, чтобы разбудить уснувшего водителя (рисунок 6).

Рисунок 6 – Вызов звукового уведомления.

Сегодня все больше и больше профессий требуют концентрации в долгосрочной перспективе. Водители должны внимательно следить за дорожным движением, чтобы быстро реагировать на неожиданные ситуации. Усталость водителя обычно становится мгновенной причиной нескольких дорожно-транспортных происшествий. Таким образом, было разработано приложение, которое способно обнаруживать сонливость водителя и сообщать самому водителю о недопустимости подобного состояния в момент движения.

Детектор сонливости может работать в различных условиях, в том числе под прямыми солнечными лучами при движении по дороге и при слабом или искусственном освещении в бетонном гараже.  На данный момент у разработанного приложения обнаруживается несколько ограничений. Самое большое из них – неспособность обнаружить глаза при слабом освещении. Большинство случаев сонливости приходится на ночное время, и эффективность программы в темноте невысока. Другим ограничением является задержка по времени между счетом, в течение которого глаза были закрыты, и сигналом тревоги. За счет использования более качественного оборудования можно повысить общую эффективность системы.

Советуем почитать