среда, 3 октября 2012 г.

Python+OpenCv распознавание лиц и определение расстояния до них (версия простая )

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


Мы живём в счастливую для лентяев эпоху - вам уже не нужно купаться в сложной математической теории. Всё сделано за вас, по крайней мере на таком элементарном уровне. Распознавание лиц происходит по признакам Хаара. "Эталон" лица уже давно создан и хорошо обучен, находится он в файле haarcascade_frontalface_alt.xml . Там находится, так называемая, модель лица, а именно "Идеальное" лицо в виде примитивов Хаара :

Это бинаризированая диаграмма яркостей. У любого лица посередине светло(нос), а по краям уходит в тёмное, а потом снова на светлое( щеки). Таких признаков у лица много, и они перечислены в haarcascade_frontalface_alt.xml. А наша программка будет их искать.

 Первый бой (вывод видео на экран) :

  Для начала давайте на экран выведем видео поток. С помощью OpenCV это делается элементарно.




import cv2

cam = cv2.VideoCapture(0)
while 1:
     _,frame =cam.read()
     cv2.imshow("features", frame)
     if cv2.waitKey(10) == 0x1b: # ESC
         print 'ESC pressed. Exiting ...'
         break

Работает? Классно!
Код прост:
Я создаю объект камеры, которая подхватит первую попавшуюся(ваша web-ка)
И всё последующее время я буду читать 1 кадр из камеры и выводить его на экран, каждые десять миллисекунд опрашивать клавиатуру, и, если нажата клавиша с кодом 27(Esc) ,то выйду из бесконечного цикла прямо в ОС.



Битва вторая( использование микроскопа по назначению):

Использовать OpenCV для вывода видео на экран - забивание гвоздей микроскопом. Не будем столь брутальны - начнём  распознавание лиц:



import sys
import cv,cv2
import numpy
cascade = cv.Load('haarcascade_frontalface_alt.xml')

def detect(image):
 bitmap = cv.fromarray(image)
 faces = cv.HaarDetectObjects(bitmap, cascade, cv.CreateMemStorage(0))
 if faces:
  for (x,y,w,h),n in faces:
   pass
   cv2.rectangle(image,(x,y),(x+w,y+h),(255,255,255),2)
 return image

if __name__ == "__main__":
    cam = cv2.VideoCapture(0)
    while 1:
        _,frame =cam.read()
        frame = numpy.asarray(detect(frame))
        cv2.imshow("features", frame)
        if cv2.waitKey(10) == 0x1b: # ESC
            print 'ESC pressed. Exiting ...'
            break

HaarDetectObjects принимает 3 параметра ( где_искать,что искать, где_хранить_промежуточные_данные)
где_хранить_промежуточные_данные - область памяти, которую можно выделить функцией CreateMemStorage.

Уже что-то.

Последний бой ( проявите каплю своего творчества):

Если, проделав это всё, вы гордо скажите, что вы программист, я плюну вам в лицо. Что вы сделали? Ну хоть малость своих мыслей тут? Мы использовали стандартные функции для совершения достаточно тривиальных действий - ни капли математического аппарата. Давайте-же прибавим! А что тут можно сделать? Определим расстояние до лица!
Попытаемся понять, как работает наша камера:



Обратите внимание, что треугольники ACB и DCE подобны. Что это значит?
Уберём из рисунка лишнее и добавим необходимое:

А значит это то , что отношение DE/CH равно отношению AB/FC. Поднеся линейку в 10 сантиметров к веб-камере так, чтоб она поместилась полностью, и измерив расстояние до веб-камеры от линейки, мы можем узнать отношение высоты матрицы нашей камеры к её фокусному расстоянию.( у меня это 1.6) .
Средняя высота лица человека - 15 сантиметров.


Получив из OpenCV размер квадрата лица, просто вычислить, сколько процентов всей высоты кадра оно занимает. Несложно додуматься, что Если лицо влезает в камеру всеми своими 15 сантиметрами , то до веб-камеры - (AB/FC)*12. Если лицо занимает меньше 100% пространства, то оно более удалено. Давайте определим , насколько...





Из рисунка видно, что переместив объект из точек CD в точки EF, его отображение(GH) будет занимать не 100% , а N%(где N<=100). Из свойства подобия треугольников видно, что отношение GH/AB = KL/CD. А ,значит, для наглядности ,я могу упростить модель до треугольника и трапеции:
Что нам отсюда известно?
Мы знаем, отношение AB/FE - это есть наше фокальное расстояние, обозначим его как focal.
реальные размеры AB.

Нам надо узнать расстояние FG.
Треугольники AFB и DFC подобны, значит FE/FG=AB/DC. Из этого равенства видно, что если FG увеличивается в M раз, то либо DC увеличивается в M раз, либо AB уменьшается в M раз. AB уменьшится не может - это реальный размер лица, он всегда 15 сантиметров, А вот DC - может, это размер лица на фотографии, чем лицо дальше, тем размер его меньше. Исходя из этого, можно сделать вывод, что если DC уменьшилось в M раз( или на N%) ,то и FG уменьшилось так же в M раз( или на N%). Если DC у нас равно AB, то GF = EF, если DC меньше AB в M раз (илиN%),то и FG


Теперь ближе к коду:

import sys
import cv,cv2
import numpy
cascade = cv.Load('haarcascade_frontalface_alt.xml')
c=1.6
Sr=15

def detect(image):
 bitmap = cv.fromarray(image)
 faces = cv.HaarDetectObjects(bitmap, cascade, cv.CreateMemStorage(0))
 if faces:
  for (x,y,w,h),n in faces:  
   k=float(w)/bitmap.cols
   S = Sr*c/k
   cv2.rectangle(image,(x,y),(x+w,y+h),(255,255,255),3)
   cv2.putText(image,'S=%s'%(S),(x,y-10), cv2.FONT_HERSHEY_PLAIN, 1.0,(255,255,255))
 return image

if __name__ == "__main__":
    cam = cv2.VideoCapture(0)
    while 1:
        _,frame =cam.read()
        frame = numpy.asarray(detect(frame))
        cv2.imshow("features", frame)
        if cv2.waitKey(1) == 0x1b: # ESC
            print 'ESC pressed. Exiting ...'
            break





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

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

2 комментария: