Smorodov 579 Жалоба Опубликовано December 29, 2013 Реализовал измеритель пульса по изображению с камеры (pdf-ка в архиве с исходниками). Работает не очень устойчиво (думаю из-за того что переключаются каналы разделенные при помощи ICA), но при удачном исходе на разложении Фурье виден острый четкий пик. Предлагаю желающим поэксперименировать Там встроен детектор лица, но и так работает Исходники (используются: OpenCV и Eigen): HeartRateMeasure.rar UPD: добавил комментариев и немного подправил исходники. Только что откопал интересный ресурс по теме: http://people.csail.mit.edu/mrub/vidmag/ с исходниками на MATLAB и видеороликами (обязательно посмотрите ). И еще исходники на питоне: https://github.com/thearn/webcam-pulse-detector 2 Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Nuzhny 243 Жалоба Опубликовано December 30, 2013 *Выдох* Круууто! Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Smorodov 579 Жалоба Опубликовано January 22, 2015 Статья на хабре: http://habrahabr.ru/post/232515/ Появились также сишные исходники на github-е. Ссылки не привожу, т.к. гуглятся на раз. Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
And36 0 Жалоба Опубликовано April 24, 2017 Привет всем умным людям! Пришло время и добрался до измерителя пульса с веб-камеры. Как и раньше прогнал на Матлабе = все ок. Перевожу на VS+OpenCV. Прошу помощи по 2-м вопросам: 1. Что посмотреть/почитать по C++ такого, чтобы чуть легче стало переводить Матлабовские алгоритмы в VS+OpenCV. 2. Пока не могу понять почему получаю разные результаты в коде: for( int n=0; n<ires->height; n++ ) { uchar* ptr = (uchar*) ( ires->imageData) + n * ires->widthStep; cvInRangeS(rim, cvScalar(n*8), cvScalar(n*8+8), hv); // creating mask for Rim pixels cvMul(gim, hv, mres, 0.00390625); // implementing Rim-mask for Gim pixels 0.00390625 (нормировка=1/256) for( int m=0; m<ires->width; m++ ) { cvInRangeS(mres, cvScalar(m*8), cvScalar(m*8+8), mv); // creating mask for Gim pixels // ptr[m] = cv::sum(cv::Mat(mv)).val[0]; // sum of sorted pixels ptr[m] = cvCountNonZero(mv); } } Имея 2 изображения rim и gim, хотелось бы получить третье ires, которое является распределением амплитуд по диапазонам: в цикле выделяю узкий диапазон амплитуд в первом (rim), делаю для выделенных значений маску (mres), которую применяю ко второму(gim). В получившемся втором (gim) аналогично выделяю узкий диапазон пикселей и хочу подсчитать их количество для каждого комплексного диапазона. В случае sum вроде близко к нужному результату, но все-равно не так, как в Матлабе получается, а в случае CountNonZero вообще какая-то лажа = почти все смещено в один узкий диапазон (никакого распределения). Может кто подскажет, почему CountNonZero как-то странно считает кол-во ненулевых пикселей для каждого заданного диапазона? CountNonZero.bmp Sum.bmp Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Nuzhny 243 Жалоба Опубликовано April 24, 2017 Какая-то мешанина из разных версий API и мне как-то не очевидна логика. Но да ладно. Я когда-то интересовался и исходной работой, и исходниками Smorodov'а. Немного их отрефакторил под себя, добавил стороннее motion magnification. Получился очень сырой проект HeartRateMeasure. В планах на правах хобби: 1. Разобраться со скачками, о которых писал выше Smorodov. 2. Добавить вычисление пульса не только по цвету, но и по движению. 3. Сделать нормальный трекинг лица по face features. Для этого можно взять dlib, можно OpenFace. После этого можно будет уже для измерения брать не средний цвет лица или анализировать optical flow, а смотреть на участки кожи, близко к которым расположены крупные артерии. Вот. Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
mrgloom 242 Жалоба Опубликовано April 25, 2017 Недавно наткнулся еще на 1 реализацию https://habrahabr.ru/post/232515/#comment_10188184 Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Nuzhny 243 Жалоба Опубликовано April 25, 2017 Там, вроде, пульс не считают. Но да, видел. Факт заброшенности настораживает. Большое обсуждение на Reddit. Есть уже приложения и под Андроид с открытыми исходниками, но всё это на уровне баловства. И точность страдает при высокой вычислительной нагрузке. Так что копать есть куда. Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Nuzhny 243 Жалоба Опубликовано August 8, 2017 Появилось немного времени и обновил проект. Пытался разобраться с непонятными скачками пульса. Для этого я считал не одно значение с экспоненциальным сглаживанием, а добавил микстуру из 6 Гауссианов. Почему 6? 1. 3 штуки на ошибку с выбором независимой компоненты, выбираем мы 1 из 3-х. 2. В выбранной независимой компоненте я считаю 2 максимума. В итоге 3 * 2 = 6. В результате в одном из Гауссианов получается достаточно точное значение пульса и вес у него неплохой. Вот. 1 Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Smorodov 579 Жалоба Опубликовано August 8, 2017 А гауссианы жестко сидят на каналах, или они просто собирают все максимумы подряд и затем сортируется кто куда идет ? Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Nuzhny 243 Жалоба Опубликовано August 8, 2017 Последнее. Там всё по классической схеме: есть один Гауссиан, которому скармливаются значения. Как только они выходят за рамки нормального распределения для первого Гауссиана, создаётся для них второй и т.д. пока не получится 6 штук (может вообще не получиться). Чем чаще значения выпадают на конкретный гауссиан, тем больше его вес. Ну и внутри каждого Гауссиана среднее и дисперсия обновляются экспоненциальным сглаживанием, что должно отслеживать постепенное изменение пульса. Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Nuzhny 243 Жалоба Опубликовано August 14, 2017 Добавил вычисление цвета только по коже. За детектор кожи взял Decision tree от mrgloom. Вопрос к нему: почему именно Decision tree? Мне бы что-нибудь самое быстрое и не сильно лажающее. Можно, конечно, самому опытов напроводить, но интересно, почему первоначально был выбран именно этот вариант. EM будет не точнее и быстрее? Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
mrgloom 242 Жалоба Опубликовано August 14, 2017 Изначально decision tree, т.к. этот метод интерпретируемый, т.е. там дерево из threshold'ов которые выучены из данных, чтобы не было таких вот хардкодов threshold'ов или in range как тут: http://bytefish.de/blog/opencv/skin_color_thresholding/ https://github.com/WillBrennan/SkinDetector И да их сначала можно выучить, а потом для скорости захардкодить) Такой же вопрос почему в grabcut используется GMM? по сути нам там без разницы какой метод будет выдавать probability пикселя? https://github.com/opencv/opencv/blob/c8783f3e235f81edb97279fafabcf12ec43ccc9f/modules/imgproc/src/grabcut.cpp Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Nuzhny 243 Жалоба Опубликовано August 14, 2017 3 hours ago, mrgloom said: Изначально decision tree, т.к. этот метод интерпретируемый, т.е. там дерево из threshold'ов которые выучены из данных, чтобы не было таких вот хардкодов threshold'ов или in range как тут: http://bytefish.de/blog/opencv/skin_color_thresholding/ И да их сначала можно выучить, а потом для скорости захардкодить) Такой же вопрос почему в grabcut используется GMM? по сути нам там без разницы какой метод будет выдавать probability пикселя? https://github.com/opencv/opencv/blob/c8783f3e235f81edb97279fafabcf12ec43ccc9f/modules/imgproc/src/grabcut.cpp Один раз обучить и сохранить - да. Почему EM, например? Или GMM - это похожая степь. На первый взгляд кажется, что области цвета кожи должны быть достаточно локальными в цветовом пространстве. Почему бы не описать их эллипсоидами? Кажется, что так и должно быть, при этом мы как раз и получим самые настоящие вероятности. Разумеется, в HSV области будут более компактными, чем в RGB, а в Lab ещё лучше. Но для скорости лучше остаться в RGB. Собственно, так оно и есть: Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Smorodov 579 Жалоба Опубликовано August 14, 2017 Собственно есть изящный метод как раз с эллипсом: cv::Mat Src = imread("image.jpg", 1); cv::Mat mask = Mat::zeros(Src.size(), CV_8UC1); Mat crcb; skinCrCbHist = Mat::zeros(Size(256, 256), CV_8UC1); cvtColor(Src, crcb, CV_BGR2YCrCb); ellipse(skinCrCbHist, Point(113, 155.6), Size(23.4, 15.2), 43.0, 0.0, 360.0, Scalar(255, 255, 255), -1); double t = (double)getTickCount(); for (size_t i = 0; i < Src.rows; i++) { for (size_t j = 0; j < Src.cols; j++) { Scalar v; v = crcb.at<Vec3b>(i, j); int s=skinCrCbHist.at<uchar>(v[1], v[2]); // проверяем попали ли в эллипс, если да, то кожа if (s > 0) { mask.at<uchar>(i, j) = 255; } } } и довольно неплохо работает. Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Nuzhny 243 Жалоба Опубликовано August 14, 2017 12 minutes ago, Smorodov said: Собственно есть изящный метод как раз с эллипсом: Сдаётся мне, что код у тебя довольно расисткий, на первом попавшемся негре не отработал. Ай-ай-ай! Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Smorodov 579 Жалоба Опубликовано August 14, 2017 Ну, можно для негров отдельный эллипс организовать Распределения можно взять здесь: https://www.cs.rutgers.edu/~elgammal/pub/skin.pdf Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Nuzhny 243 Жалоба Опубликовано August 15, 2017 Возвращаясь к моему первому вопросу по коже и деревьям: оказалось, что деревья довольно быстрые (что логично), конкуренты отстают нормально. Но всё равно медленновато и есть вопросы по качеству. Кожи на моём лице с дешёвой вебки из ноутбука находит совсем мало. Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
mrgloom 242 Жалоба Опубликовано August 15, 2017 Гауссианы это хорошо, но одной отделаться не получится. Может попробую потом что то такое: http://scikit-learn.org/stable/modules/mixture.html Насчёт вебки скорее всего надо переучить, не знаю насколько репрезентативен этот датасет: https://archive.ics.uci.edu/ml/datasets/Skin+Segmentation И еще кстати как минус decision tree я не знаю как можно выкрутить recall на полную, т.е. это может быть полезно в зачах типа детектирования лиц, как первый дешевый шаг который снижается пространство для поиска с recall=1.0. Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Nuzhny 243 Жалоба Опубликовано August 15, 2017 5 minutes ago, mrgloom said: Гауссианы это хорошо, но одной отделаться не получится Это да, особенно если брать цвета разных рас. Но тот же стандартный EM из OpenCV умеет несколько эллипсоидов. Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Nuzhny 243 Жалоба Опубликовано August 22, 2017 Нашёл чувака, который занимается пульсом. Его работы впечатляют больше, чем успехи из MIT. К сожалению, исходники являются собственностью Phillips и их не потестить. Вот. Насколько я понимаю, главным его нововведением является замена PCA (PCA у MIT, ICA у Smorodov) на POS (Plane Orthogonal to the Skin-tone direction). Что и дало такую точность. 2 Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Nuzhny 243 Жалоба Опубликовано January 31, 2018 Практика. Молодцы, вывели свою работу на рынок. Эх! Хочу безусловный доход, чтобы была возможность самому больше времени уделять таким интересным штукам. Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
mrgloom 242 Жалоба Опубликовано January 31, 2018 Какого качества камеру надо чтобы нормально 'увидеть эффект'? Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Nuzhny 243 Жалоба Опубликовано February 2, 2018 Не знаю. У меня пока получается, что на видео с неплохого фотоаппарата с 60 fps пульс считается хуже, чем при съёмке обычным китайским телефоном. Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Nuzhny 243 Жалоба Опубликовано March 15, 2018 Подумалось, что для вычисления пульса по лицу надо не искать лицо Хааром и вычислять участки с кожей, а делать что-то посовременнее. К тому же пульс можно считать двумя способами: как по частоте изменения цвета кожи, так и по микродвижениям головы в такт сердечным сокращениям. Для последнего в статье из MIT используют просто оптический поток для произвольных точек. Сразу напрашивается мысль, как можно убить двух зайцев одним ударом: и более точно определять лицо с кожей и находить движение точек. Для этого искать face landmarks - ключевых точек на лице. По ним можно определить и область лица (кожа) с углом поворота, а также, сравнивая их на соседних кадрах, находить микрокачания головы. И на основании этих двух измерений уже выдавать пульс. Как считаете, подход рабочий? И второй вопрос, технический. Соответственно нужен быстрый алгоритм поиска лица и ключевых точек на нём. Желательно, чтобы он работал точнее древнего Хаара и не требовал установки монструозных нейросетевых фреймворков, был кроссплатформенным типа OpenCV (тот же TensorFlow слабо кроссплатформенный, CUDA версия под Windows не заводится), всё работало без Питона и на С++. Если удастся ограничиться только OpenCV и остаться в его рамках (это я о dlib), то будет здорово. Модуль opencv_dnn работает здорово, OpenCL версия тоже подтягивается. Вот. Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Smorodov 579 Жалоба Опубликовано March 15, 2018 Так это, MTCNN уже всюду портировали и на чистом OpenCV + BLAS и еще один видел, по детекту лиц и 5 ключевых точек, в маленьком разрешении работает близко к реалтайм. Еще есть face parsing, там же и датасет можно найти: https://www.sifeiliu.net/face-parsing в реалтайме вряд ли-пойдет, но для записанных видео, вполне (не пробовал, могу ошибаться). Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах