Перейти к содержимому
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.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Ога нашел, значит в проге уже Махаланобиса

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

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

15.png

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

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

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

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

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

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

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

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

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

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

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

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Да.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

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

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

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

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

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

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

Поделиться сообщением


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

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

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

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

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

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

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

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

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

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

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

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

и

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

Поделиться сообщением


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

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

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

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

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

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

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

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

К экзешнику надо бросить каскады: 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]

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Создайте учётную запись или войдите для комментирования

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

Создать учётную запись

Зарегистрируйтесь для создания учётной записи. Это просто!

Зарегистрировать учётную запись

Войти

Уже зарегистрированы? Войдите здесь.

Войти сейчас


  • Сейчас на странице   0 пользователей

    Нет пользователей, просматривающих эту страницу

×