Jump to content
Compvision.ru
Smorodov

Программа распознавания лиц методом главных компонент

Recommended Posts

Flame. В статье Распознавание с помощью PCA делается на Евклидовом расстоянии. Но в конце указывается что изменить, чтобы использовать расстояние Махаланобиса. Вот кусок этого текста:

Improving Eigenface

Having a framework like this for training and testing will make it easier for you to add improvements to Eigenface and to test their effects.

One of the first improvements you might want to add is to change the way distance is measured. The original eigenface paper used Euclidean distances between points, and that's the distance basis I've used in findNearestNeighbors(). But a different basis, called Mahalanobis distance (after its inventor) usually gives better results.

One of the things that happens when you project a face image onto the PCA subspace is that each dimension receives a certain amount of stretch. The amount of stretch isn't the same, though, in every direction. The directions that correspond to the largest eigenvalues get stretched far more than the directions associated with smaller eigenvalues. Because Euclidean distance ignores this stretching, using it to measure distance is approximately the same as using only one eigenvector and ignoring the rest!

It's easy to switch from Euclidean to Mahalanobis distance. Just change line 15, in findNearestNeighbors(), from

distSq += d_i*d_i;

to

distSq += d_i*d_i/eigenValMat->data.fl;

Switching to Mahalanobis distance eliminates the mismatch error mentioned above, bringing recognition accuracy up to 100% for these three subjects.

Share this post


Link to post
Share on other sites

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

15.png

тут получается аж 21, если мы подаем незнакомое лицо.

с чем это может быть связано? у меня если даже незнакомое подаешь на проверку всеравно там не больше 2х...

Если много фоток взять в обучающую выборку, то соответственно и расстояния уменьшаются до вектора...

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

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

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

я тут другое придумал:

1. Обучаем алгоритм например по 4 фотки для каждого человека. Условие: Фотки одного человека должны идти по порядку.

2. Загружаем тестовое фото любого человека из выборки, которое не использовалось в обучении, например 7.

3. Находим расстояния в пространстве от вектора тестовой фотки до всех других объектов по порядку.

4. Складываем расстояния до обьектов одного человека.

5. Полученную сумму сравниваем с другими такими суммами. Она будет меньше чем другие.

А если тестовое фото будет вообще другого человека не из выборки, то сумма будет больше той суммы в пункте 5.

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

Вроде так алгоритм должен лучше работать, как думаете?

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

Да.

Share this post


Link to post
Share on other sites

Попробовал так сделать, значения слишком большие получаются

Вот для изначальной реализации взятой из вашей программы на билдере:

2WZCfX1h.jpg

Соответственно минимальные значения для каждой тестовой фотки:


Мои:         Хабр:

0.0329157    0.49

0.109273     0.85

0.0582627    0.47

0.16966      2.08

Если добавить деление на сумму всех чисел для собсвенных векторов:

float summ_eigen = 0;


    for(int i = 0; i < N_People * N_Samples; i++)

    {

        summ_eigen += EigenVals->data.fl[i];

    }


    for(int i = 0; i < N_People * N_Samples; i++)

    {

        EigenVals->data.fl[i] = EigenVals->data.fl[i] / summ_eigen;

    }

Получается так: (значения min):

75V2iAXr.jpg

Видимо буду тогда делать по своему алгоритму с суммами, там все лучше получается

Share this post


Link to post
Share on other sites

Flame, а как ты реализовал сам отсев неприсутствующих в БД? Примерный алгоритм или как изменить программу. Можешь поделиться?

Share this post


Link to post
Share on other sites

JoQeR, Смотри пост 56, вот примерно так все и будет думаю. только я еще не оттестировал нормально

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

http://www.compvision.ru/forum/index.php?showtopic=81&view=findpost&p=6327

http://www.bytefish.de/blog/fisherfaces

там есть описание про работу с разной освещенностью

http://dailyburrito.com/projects/facerecog/FaceRecReport.html

а тут есть с разным направлением головы.

http://www.face-rec.org/algorithms/

стоит обратить внимание на разделы PCA, ICA, LDA, EP, Algorithms Comparisons ну и вообще.

  • Like 1

Share this post


Link to post
Share on other sites

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

возможно есть какие то базы звёзд? (или кто то ограбил паспортный стол).

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

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites
Я тоже думал на эту тему (где раздобыть много-много фоток), была мысль facebook сканера, но подумал о своем интернет трафике, и о требуемом объеме прокачиваемых данных и отказался (хотя может я чего то не знаю, и можно получить фотки напрямую не прочесывая все архивы).

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

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

во всяком случае интересно попробовать.

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

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

Share this post


Link to post
Share on other sites

Напрямую это допустим бежим по адресам, и аватары сразу собираем к себе.

Тут скелет паука можно найти: http://code.google.com/p/driller-cpp-web-crawler/

Базы данных паспортного стола это закрытая информация (разве что в метро купить :) ).

Share this post


Link to post
Share on other sites

Для борьбы с разной освещенностью иногда помогает функция cvEqualizeHist.

JoQeR, свою реализацию придумали или сделали как на хабре?

я то что в 56м посте писал плохо работает на других базах, переделал, стало лучше :)

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

Share this post


Link to post
Share on other sites

Мне пришла супер мысль по поводу источника лиц :)

http://www.youtube.com/results?search_query=takes+a+photo+every+day&oq=takes+a+photo+every+day&aq=f&aqi=g1g-s3&aql=&gs_l=youtube-reduced.3..0j0i10l3.14786.21780.0.22420.23.14.0.9.9.0.445.3519.0j6j2j4j1.13.0...0.0.TWeXB5_J2a8

и

можно еще запросы придумать, но смысл должен быть понятен.

Share this post


Link to post
Share on other sites
На паспортном контроле вроде используется метод с отношением расстояний между ключевыми точками лица (забыл как называются они); ну типа расстояние между зрачками, носом, ртом, и т п...

биометрия наверно.

но смысл должен быть понятен.

снимать самого себя каждый день?)

граббер попробую написать на silenium+python или вроде даже можно wget+скрипт какой нибудь.

Share this post


Link to post
Share on other sites

Нет, снимать себя каждый день это лень :)

Разве что к будильнику приделать фотик, чтобы с утра щелкал лицо (но это неадекватные кадры будут :) )

Граббер можно и не писать, есть RealDownloader он нормально грабит.

А награбленное можно через opencv разбирать покадрово flv вроде читался нормально.

Share this post


Link to post
Share on other sites

Наваял воровалку фоток с таких роликов (где голова видна полностью):

К экзешнику надо бросить каскады: haarcascade_frontalface_alt2.xml

main.cpp


#include <windows.h>
#include <iostream>
#include <vector>
#include <stdio.h>
#include "opencv2/core/core.hpp"
#include "opencv2/core/gpumat.hpp"
#include "opencv2/core/opengl_interop.hpp"
#include "opencv2/gpu/gpu.hpp"
#include "opencv2/ml/ml.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/contrib/contrib.hpp"
#include "opencv2/video/tracking.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "fstream"
#include "iostream"
using namespace std;
using namespace cv;
using namespace cv::gpu;
cv::gpu::CascadeClassifier_GPU cascade_gpu("haarcascade_frontalface_alt2.xml");
//-------------------------------------------------------------------------------------------------------------
vector<Rect> detect_faces(Mat& image)
{
vector<Rect> res;
bool findLargestObject = true;
bool filterRects = true;
int detections_num;
Mat faces_downloaded;
Mat im(image.size(),CV_8UC1);
GpuMat facesBuf_gpu;
if(image.channels()==3)
{
cvtColor(image,im,CV_BGR2GRAY);
}
else
{
image.copyTo(im);
}
GpuMat gray_gpu(im);

cascade_gpu.visualizeInPlace = false;
cascade_gpu.findLargestObject = findLargestObject;
detections_num = cascade_gpu.detectMultiScale(gray_gpu, facesBuf_gpu, 1.2,(filterRects || findLargestObject) ? 4 : 0,Size(image.cols/4,image.rows/4));


if(detections_num==0){return res;}

facesBuf_gpu.colRange(0, detections_num).download(faces_downloaded);
Rect *faceRects = faces_downloaded.ptr<Rect>();

for(int i=0;i<detections_num;i++)
{
res.push_back(faceRects[i]);
}
gray_gpu.release();
facesBuf_gpu.release();
return res;
}
//-----------------------------------------------------------------------------------------------------------------

//-----------------------------------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------------------------------
int main ( int argc, char** argv )
{
// Буфер, где будет лежать имя файла
char szFile[2048];
OPENFILENAMEA ofn;
// Запулним структурку диалога открытия файла
ZeroMemory( &ofn , sizeof( ofn)); // Обнулили память
ofn.lStructSize = sizeof ( ofn ); // Записали размер структуры
ofn.hwndOwner = NULL ; // Нет окна - родителя
ofn.lpstrFile = szFile ; // прицепили буфер
ofn.lpstrFile[0] = '\0'; // конец строки у нас вначале (длина = 0)
ofn.nMaxFile = sizeof( szFile ); // максимальная длина имени файла
ofn.lpstrFilter = "All\0*.*\0FLV\0*.flv\0"; // фильтр расширений
ofn.nFilterIndex =1; // индекс текущего фильтра
ofn.lpstrFileTitle = NULL ; // какой то конкретный файл не ищем
ofn.nMaxFileTitle = 0 ; // и так понятно
ofn.lpstrInitialDir=NULL ; // начальная директория
ofn.Flags = OFN_PATHMUSTEXIST|OFN_FILEMUSTEXIST ; // флаги

GetOpenFileName(&ofn); // Здесь вылезает диалог

VideoCapture cap(szFile); // открываем файл

int i=0; // Счетчик кадров
Mat frame; // Здесь лежит захваченный кадр

// Для общности, на самом деле содержит одно лицо,
// т.к. установлен флаг findLargestObject = true; см. выше.
vector<Rect> Faces;

// усредненное положение и размер прямоугольника с лицом,
// мы знаем что оно есть на всех кадрах примерно в лдной и той же позиции
Rect AverageRect;

// Последовательность лиц часто начинается с титров
bool facesStart=false;
// Количество кадров в файле
double N=cap.get(CV_CAP_PROP_FRAME_COUNT);
cout << "N frames=" << N << endl;

// Количество усредняемых кадров
double n=0;

// Коэффициенты, регулирующие ширину и высоту рамки
double kx=1.1;
double ky=1.3;
// если нет детекта лиц на 10 кадрах подряд, значит последовательность кончилась
int no_detect=0;

int FaceN=0; // номер лица

Mat face;
Mat PrevFace;

char drive[1000];
char dir[1000];
char fname[1000];
char ext[1000];
_splitpath(szFile, drive, dir, fname, ext);

char dirname[1000];
sprintf(dirname,"faces_%s",fname);
CreateDirectory(dirname,NULL); // Сделаем новую директорию для фейсов
while(i<N)
{
cap >> frame;
Faces = detect_faces(frame);
if(!Faces.empty())
{
Faces[0].x-=(Faces[0].width*(kx-1))*0.5;
Faces[0].y-=(Faces[0].height*(ky-1))*0.5;
Faces[0].width*=kx;
Faces[0].height*=ky;
// Не дадим приблизиться рамке к краю ближе чем на 5 пикселей
if(Faces[0].x>10 && Faces[0].y>10 && Faces[0].x<(frame.cols-AverageRect.width-10) && Faces[0].y<(frame.rows-AverageRect.height-10) )
{
facesStart=true;
no_detect=0;

AverageRect.x=(Faces[0].x+n*AverageRect.x)/(n+1);
AverageRect.y=(Faces[0].y+n*AverageRect.y)/(n+1);

if(AverageRect.width==0){AverageRect.width=(Faces[0].width);}
if(AverageRect.height==0){AverageRect.height=(Faces[0].height);}

if(n<10){n++;}


if(face.empty()){face=Mat(Size(AverageRect.width,AverageRect.height),CV_8UC3);}
if(!face.empty()){PrevFace=face.clone();}
cv::resize(frame(Rect(AverageRect)),face,face.size());

}
}
else
{
no_detect++;
}
if(no_detect>10)
{
facesStart=false;
}

if(facesStart && !PrevFace.empty())
{
char name[1000];
double diff=cv::norm(mean(face-PrevFace));
//cout << "diff=" << diff << endl;
if(diff>10) // Здесь задается порог разницы соседних кадров
{
sprintf(name,"%s\\face_%d.jpg",dirname,FaceN++);
cout << name << endl;
imshow("face",face);
imwrite(name,face);
cvWaitKey(20);
}

rectangle(frame,AverageRect,CV_RGB(255,0,0));
}

imshow("frames",frame);
i++;
cvWaitKey(20);
}
cap.release();
}
[/code]

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

To Flame. На хабре прочитал статью, интересно, но решил, что взять и установить порог по расстоянию Махаланобиса и не ваять сложный велосипед (реализация есть в с++ на том же хабре + ссылку здесь выкладывали на полное руководство). Очень хотелось поэксперементить)

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now


  • Recently Browsing   0 members

    No registered users viewing this page.

×