Как Мы Спасли Глаза С Помощью Opencv

Материалы этого поста задержались в публикации на 4 месяца.

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

Предыстория в Эта статья , где мы обещали выложить продолжение.

Рассказ будет о том, как работает (или не работает) наше приложение, решать читателю.

Какое приложение? Мы — команда проекта Viewaide (ранее EyeDoc) и пишем программное обеспечение, которое с помощью веб-камеры определяет параметры усталости глаз и отображает уведомления, задача которых — снизить риск ухудшения зрения из-за длительной работы за монитором.

.

Лучше один раз увидеть, чем 100 раз услышать.

Вы можете скачать и попробовать его на эта ссылка , как говорится, «бесплатно, без смс».

Помимо программного обеспечения у нас есть еще часть веб-сервиса, но обо всем по порядку.

О том, как нам больно осознавать, что монитор вреден для глаз, рассказано в предыдущая статья , но вкратце хотелось бы сказать, что мы начали заниматься такими вещами не ради прибыли (хотя кто бы от нее отказался), а ради решения собственной проблемы.

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

Чтобы не быть голословным, немного цифр.



Как мы спасли глаза с помощью OpenCV

Синдром компьютерного зрения (CSF) — это временное состояние, возникающее в результате постоянной фокусировки глаз на дисплее в течение длительного времени.

К некоторые источники Около 75% людей, работающих за компьютером более 3 часов в день, испытывают зрительные симптомы этого синдрома.

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

Это также может привести к развитию более серьезных заболеваний глаз.

Что уж говорить, если около 40 миллионов американцев страдают синдромом сухого глаза.

Симптомы сухости глаз являются причиной № 1 посещения офтальмолога в США.

Изучив более подробно материал по этой теме в Интернете, пообщавшись с врачами-офтальмологами, мы узнали, что усталость глаз можно определить по нескольким параметрам:

  • щурясь
  • частота моргания
  • среднее расстояние между монитором и пользователем и т. д.
Это именно те факторы, по которым можно определить, устали ли у человека глаза.

И впоследствии игнорирование этих мелочей может даже ухудшить ситуацию.

Честно говоря, идея пришла не после тщательного исследования рынка и анализа современных IT-трендов, а после того, как мы подумали: «Что можно закодировать, чтобы было кручеЭ» Год назад обработка изображений казалась крутой.

Потом мы задумались: «Веб-камера всегда перед носом, неужели она нужна только для разговоров по скайпуЭ» Мне хотелось создать не только что-то крутое, но и полезное.

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

Выбор инструментов разработки пал на Qt + OpenCV .

Мне понравился Qt не только потому, что он имел красивый зеленый цвет, с удобным редактором кода и подсветкой синтаксиса, но и потому, что он давал людям с опытом C++ возможность создавать кроссплатформенные приложения.

Теперь есть надежда, что можно будет написать полноценный мобильные приложения .

Ждать и смотреть.

Если за GUI отвечал Qt, то для работы с изображениями нужно было что-то другое.

OpenCV — это библиотека, написанная энтузиастами компьютерного зрения из Intel. Он имеет очень подробную документацию и множество примеров в Интернете.

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



Задача №1 Получить изображения глаз с помощью веб-камеры.

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

Но в этом случае достаточно получить просто изображение глаз.

Мы пропустим получение потокового видео с веб-камеры; в сети их много.

Примеры .

Реализован поиск объектов по изображению в OpenCV Метод Виолы-Джонса .

Тема распознавания образов не склонна к частым инновациям, метод был представлен в 2001 году и спустя 13 лет по-прежнему лидирует в своей области.

Если кратко, то метод вставляет в изображение так называемые примитивы Хаара, которые представляют собой набор элементарных комбинаций темных и светлых областей, и если на изображении найдена область, в которую уместилось достаточное количество примитивов, то объект найденный.

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

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

Несмотря на все оптимизации по скорости метода, поиск лица в видео в реальном времени невозможен на бюджетном ПК.

Или это все еще возможно?

Как мы спасли глаза с помощью OpenCV

Итак, задача — найти глаза пользователя с помощью веб-камеры.

Возьмем изображение с разрешением 640х480.

  
  
  
  
  
  
   

сvHaarDetectObjects(frame,left_eye_cascade,storage,1.1,3,CV_HAAR_DO_CANNY_PRUNING,cvSize(22,22);

Цель этой функции — найти левый глаз.

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

Кроме того, будет много ненужных триггеров.

Поэтому было решено, что начать нужно с поиска лица.



сvHaarDetectObjects(frame,face_cascade,storage,1.1,3,CV_HAAR_DO_CANNY_PRUNING,cvSize(80,80);

Теперь он работает быстрее.

Что изменилось? Последний аргумент функции определяет минимальный размер искомого объекта.

Многочисленные примеры OpenCV показывают, что для глаз нужно искать область размером не менее 22х22 пикселей, а для лица можно обойтись и 80х80. Тут становится понятно, почему поиск по лицу происходит быстрее: пройти всё изображение площадью 22х22 далеко не так быстро, как 80х80. Для сравнения, на моей машине Intel Core 2 Duo 2,2 ГГц, ОЗУ 2 Гб, функция поиска по глазам выполняется в среднем за 900 миллисекунд, а поиск по лицу за 200 миллисекунд. Как вы знаете, лицо не квадратное (если только наш пользователь не Губка Боб).

Сделаем минимальную область поиска прямоугольной.

При таких цифрах время работы уже составляет 160 миллисекунд.

сvHaarDetectObjects(frame,face_cascade,storage,1.1,3,CV_HAAR_DO_CANNY_PRUNING,cvSize(80,120);

Что это за третий аргумент, равный 1,1? Это шаг, с которым расширяется «окно поиска».

То есть если нет лица размером 80х120, то размер умножается на 1,1. А что, если увеличить его не на 10%, а на 20%? Неплохо, время работы уже 100 миллисекунд.

сvHaarDetectObjects(frame,face_cascade,storage,1.2,3,CV_HAAR_DO_CANNY_PRUNING,cvSize(80,120);

И еще одна мысль.

Зачем нам изображение размером 640x480? Если уменьшить его в 2 раза (теперь 320х240), то можно будет искать нужный объект быстрее.

Уменьшив изображение, мы видим, что лицо по-прежнему видно так же хорошо.

Логично, что скорость выросла в 2 раза, время работы 50 миллисекунд.

Как мы спасли глаза с помощью OpenCV



Как мы спасли глаза с помощью OpenCV



сvHaarDetectObjects(frame,face_cascade,storage,1.2,3,CV_HAAR_DO_CANNY_PRUNING,cvSize(40,60);

Поиск по лицу работает более-менее сносно, но глаза нам еще нужны.

Нет смысла искать их по всему лицу.

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

Эту идею легко проиллюстрировать.



Как мы спасли глаза с помощью OpenCV

Именно в этих областях мы будем искать глаза.

Согласитесь, что объем работы стал существенно меньше по сравнению с объемом работы при поиске по всему изображению.

К тому же вероятность ложного срабатывания уже мала.

Нужно ли искать лицо в каждом кадре? Едва ли.

Основная цель – найти глаза.

Чтобы не усложнять жизнь процессору, усложним ее себе.

Когда глаз найден, возьмите прямоугольник, в котором он находится, и увеличьте площадь в 2 раза.

Затем накладываем эту область на следующий кадр видео с веб-камеры, и вуаля! При достаточно высоком fps (а мы просто постарались ускорить обработку каждого кадра) с большой долей вероятности попадётся наш взгляд именно в этой области.



Как мы спасли глаза с помощью OpenCV

На скриншоте показаны основные этапы оптимизированного глазного поиска.

Если глаза не найдены, то после нескольких попыток нужно вернуться к поиску лица.

Теперь функция поиска со всеми ее компонентами занимает в среднем 30 миллисекунд процессорного времени, что в 30 раз меньше, чем подход «в лоб».



Задача №2 Определение расстояния до монитора.

Нам очень помогли решить эту проблему.

Эта статья .

В этом примере автор дает нам достаточно простую геометрию, демонстрируя работу веб-камеры:

Как мы спасли глаза с помощью OpenCV

В этом случае мы видим, что треугольники ABC и ECD подобны.

Проведем две высоты от точки С и удалим всю ненужную информацию.



Как мы спасли глаза с помощью OpenCV

Теперь, глядя на геометрическую версию веб-камеры, мы понимаем, что соотношение сторон AB/CF равно соотношению ED/CH. Давайте посчитаем это соотношение.

Это делается очень просто.

Разместим перед веб-камерой линейку длиной 10 см так, чтобы она вписывалась в кадр от края до края.

После этого измерьте расстояние от камеры до линейки.

Наш результат будет равен 16 см.

Отношение расстояния к длине линейки соответственно 16/10 = 1,6. Если область лица полностью войдет в кадр, мы получим расстояние, равное 24 см (1,6*15).

Почему 15 см? Потому что это средняя длина лица человека.

Ошибка из-за среднего значения практически незаметна (если, опять же, вы не Губка Боб).

Теперь дело за мелочами.

Определить расстояние нужно, если лицо занимает менее 100% кадра.

Для этого достаточно определить количество сантиметров в одном проценте.

А это 24/100=0,24. Таким образом, рассчитываем, что расстояние до монитора = 24 + (100% — процент места, занимаемого лицом в кадре) * 0,24. Фактически мы берем за ориентир не область лица, а расстояние между глазами.

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

И вообще, все дело в глазах.



Задание №3. Выявление прищуривания/моргания.

Вообще, прищуривание – очень субъективный параметр.

И об этом можно было бы и не задуматься, если бы не высказывания родителей в детстве «не щурись, юзернейм».

Тем не менее, о его реализации поговорить необходимо, поскольку из этого следует распознавание морганий.

Вопрос первый: чем отличается полностью открытый глаз от полузакрытого или закрытого? Расстояние между веками.

Это значит, что нам нужно его искать.

На изображении веки представлены тонкой каймой перехода цвета.

Тогда решение состоит в том, чтобы найти границу на изображении.

Кстати, хочу порекомендовать очень занимательную книгу.

Марр Д.

- Видение.

Информационный подход к изучению представления и обработки зрительных образов .

Он описывает довольно интересный подход к человеческому зрению в соответствии с теорией информации.

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

Самый популярный детектор границ — Детектор края Кенни .

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



Как мы спасли глаза с помощью OpenCV

Классический поиск ребер мы начинаем с применения фильтра Гаусса, который помогает избавиться от шума.

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

Следующий шаг — найти края.

Края — это не что иное, как резкие цветовые переходы.

Для удобства можно преобразовать изображение в монохромное.

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

Чтобы найти резкий переход яркости, воспользуемся дифференциальным оператором (лучше всего для этой операции подходит Оператор Лапласа .

Также используется оператор Собеля).

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

Если заменить «сумму значений пикселей» на слово «функция», то понятно, что нас интересуют места, где она резко меняется.

Именно для таких ситуаций товарищи Лейбниц и Ньютон придумали дифференциальное исчисление.



сvLaplace(eye, dst, 9);

В OpenCV есть готовая реализация этого оператора с простой реализацией.

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

Чтобы удалить ненужные участки в области выделения, да и вообще облегчить себе жизнь, можно бинаризировать изображение (иными словами, оставить только 2 цвета: черный и белый, без оттенков).

Для этого можно использовать пороговое преобразование.

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



cvThreshold(dst,dst,threshold,255,CV_THRESH_BINARY);

Здесь порог — это наше среднее значение.

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

Давайте поэтапно проиллюстрируем описанный выше процесс.



Как мы спасли глаза с помощью OpenCV

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

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



Как мы спасли глаза с помощью OpenCV



Задача №4 – Определение уровня освещенности.

Уровень освещенности также играет важную роль, если вы беспокоитесь о своем зрении.

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

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

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

Вряд ли эту проблему можно полностью устранить с помощью веб-камеры.

Но мы все же попытались реализовать частичное решение.

Итак, что вам нужно сделать, чтобы добиться этого? Для начала нам нужно разделить изображение на 2 части: лицо и все остальное, то есть фон.

Мы сравним яркость этих двух картинок.

Мы легко можем преобразовать изображение в монохромное, поскольку для нас не имеет значения, какую насыщенность считать, общую или разделенную на три канала (RGB).

Наш следующий шаг — построение гистограмм из двух изображений.

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

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

Далее условно делим пиксели на светлые и темные.

Из каждого изображения выделяют 4 группы.

Теперь, при определенном анализе и сравнении этих 4 групп, мы можем сделать несколько выводов: когда все количество темных пикселей существенно преобладает как на лице, так и на заднем плане, то мы работаем в темной комнате, и только монитор производит свечение.

Когда количество светлых пикселей на лице значительно больше, чем на фоне, это означает, что за монитором есть еще один источник света, светящий прямо нам в лицо.

Это может быть как настольная лампа, так и солнце за окном.

Пример разделения изображения на лицо и фон с последующим построением гистограмм представлен ниже.



Как мы спасли глаза с помощью OpenCV

В этом случае пользователь находится в темной комнате, а настольная лампа светит прямо в лицо, что не правильно.

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

Мы попытались сделать это несколькими способами.

Однако гистограммы оказались наиболее эффективным способом прогнозирования уровня освещенности.

Нельзя не сказать, насколько занимательен процесс испытания всей этой радости.

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

Алгоритмы определенно нуждаются в постоянной оптимизации.

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

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

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

Здесь Viewaide дает подсказки невнимательным.

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

На данный момент доступна полноценная версия для Windows, а также бета-версия для Mac. В течение месяца мы планируем выпустить готовое приложение на Mac и Linux. А если хабра-сообществу будет интересно, то будут опубликованы 2 статьи о тонкостях портирования Qt-приложения с Windows на Mac и Linux. Мы будем признательны за любую критику.

И если вы еще не скачали Viewaide, сейчас самое время сделай это .

Теги: #Обработка изображений #здоровье глаз #здоровье глаз #OpenCV #Qt #Viewaide #Алгоритмы #Обработка изображений

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.