-
Количество публикаций
1 427 -
Зарегистрирован
-
Посещение
-
Days Won
176
Сообщения, опубликованные пользователем Nuzhny
-
-
Ты лучше окно call stack в момент ошибки приведи - там и будет ответ.
-
С глюками согласен - исправил.
Вот, добавил простенький алгоритм отсева отделившихся точек:
1. вычисляем центр масс распределения точек;
2. по нему корректируем размеры описывающего прямоугольника;
3. удаляем точки, не вошедшие в новый прямоугольник.
У меня вроде как работает нормально.
#include <algorithm>
#include <limits>
#include <highgui.h>
#include <cv.h>
#undef min
#undef max
////////////////////////////////////////////////////////////////////////////
//Поиск лица на кадре
bool detect_face(IplImage *gray_img, CvHaarClassifierCascade *haar_cascade, CvMemStorage *mem_storage, CvRect &face_rect)
{
cvClearMemStorage(mem_storage);
//Поиск лиц
CvSeq *faces = cvHaarDetectObjects(gray_img, haar_cascade, mem_storage, 1.1, 2, 0, cvSize(20, 20));
if (!faces || !faces->total)
return false;
//Возвращаем первое найденное
face_rect = *(CvRect *)cvGetSeqElem(faces, 0);
return true;
}
////////////////////////////////////////////////////////////////////////////
class LKData
{
private:
//Всякие данные
IplImage *curr_img;
IplImage *prev_img;
IplImage *curr_pyramid_img;
IplImage *prev_pyramid_img;
int points_count;
CvPoint2D32f *curr_points;
CvPoint2D32f *prev_points;
char *status;
CvTermCriteria tc;
int flags;
public:
LKData()
: curr_img(NULL), prev_img(NULL), curr_pyramid_img(NULL), prev_pyramid_img(NULL),
points_count(0), flags(0), curr_points(NULL), prev_points(NULL), status(NULL)
{
tc = cvTermCriteria(CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 20, 0.03);
cvNamedWindow("points", 1);
}
////////////////////////////////////////////////////////////////////////////
~LKData()
{
deinit();
cvDestroyWindow("points");
}
////////////////////////////////////////////////////////////////////////////
//Инициализация данных для сопровождения
void init(const IplImage *gray_img, const CvRect &face_rect)
{
deinit();
curr_img = cvCreateImage(cvSize(gray_img->width, gray_img->height), IPL_DEPTH_8U, 1);
prev_img = cvCreateImage(cvSize(gray_img->width, gray_img->height), IPL_DEPTH_8U, 1);
curr_pyramid_img = cvCreateImage(cvSize(gray_img->width, gray_img->height), IPL_DEPTH_8U, 1);
prev_pyramid_img = cvCreateImage(cvSize(gray_img->width, gray_img->height), IPL_DEPTH_8U, 1);
//Покроем прямоугольник лица равномерной сеткой из точек
const int step_x = face_rect.width / 16;
const int step_y = face_rect.height / 16;
points_count = (face_rect.width / step_x + 1) * (face_rect.height / step_y + 1);
curr_points = new CvPoint2D32f[points_count];
prev_points = new CvPoint2D32f[points_count];
status = new char[points_count];
size_t pc = 0;
for (int i = face_rect.y, stop_i = face_rect.y + face_rect.height; i < stop_i; i += step_y)
{
for (int j = face_rect.x, stop_j = face_rect.x + face_rect.width; j < stop_j; j += step_x)
{
curr_points[pc] = cvPoint2D32f((float)j, (float)i);
++pc;
}
}
points_count = (int)pc;
cvCopyImage(gray_img, curr_img);
cvFindCornerSubPix(curr_img, curr_points, points_count, cvSize(3, 3), cvSize(-1, -1), tc);
//Смена указателей на параметры
std::swap(prev_img, curr_img);
std::swap(prev_pyramid_img, curr_pyramid_img);
std::swap(prev_points, curr_points);
}
////////////////////////////////////////////////////////////////////////////
//Деинициализация данных для сопровождения
void deinit()
{
if (curr_img)
{
cvReleaseImage(&curr_img);
curr_img = NULL;
}
if (prev_img)
{
cvReleaseImage(&prev_img);
prev_img = NULL;
}
if (curr_pyramid_img)
{
cvReleaseImage(&curr_pyramid_img);
curr_pyramid_img = NULL;
}
if (prev_pyramid_img)
{
cvReleaseImage(&prev_pyramid_img);
prev_pyramid_img = NULL;
}
points_count = 0;
flags = 0;
if (curr_points)
{
delete []curr_points;
curr_points = NULL;
}
if (prev_points)
{
delete []prev_points;
prev_points = NULL;
}
if (status)
{
delete []status;
status = NULL;
}
}
////////////////////////////////////////////////////////////////////////////
//Сопровождение лица
bool tracking_face(IplImage *gray_img, CvRect &face_rect)
{
cvCopyImage(gray_img, curr_img);
//Вычисление нового положения точек с помощью пирамидального алгоритма анализа оптического потока Лукаса-Канаде
cvCalcOpticalFlowPyrLK(prev_img, curr_img, prev_pyramid_img, curr_pyramid_img,
prev_points, curr_points, points_count, cvSize(20, 20), 3, status, 0, tc, flags);
flags |= CV_LKFLOW_PYR_A_READY;
if (!points_count)
return false;
//Удаление не найденных точек, а также вычисление координат описывающего прямоугольника
float left = std::numeric_limits<float>::max();
float top = std::numeric_limits<float>::max();
float right = std::numeric_limits<float>::min();
float bottom = std::numeric_limits<float>::min();
//Центр масс
CvPoint2D32f mc = cvPoint2D32f(0.f, 0.f);
int k = 0;
for (int i = 0; i < points_count; ++i)
{
if (status[i])
{
curr_points[k] = curr_points[i];
if (curr_points[k].x < left)
left = curr_points[k].x;
if (curr_points[k].x > right)
right = curr_points[k].x;
if (curr_points[k].y < top)
top = curr_points[k].y;
if (curr_points[k].y > bottom)
bottom = curr_points[k].y;
mc.x += curr_points[k].x;
mc.y += curr_points[k].y;
++k;
}
}
points_count = k;
mc.x /= (float)points_count;
mc.y /= (float)points_count;
//Вычисление расстояния от центра масс, до ближайших сторон описывающего прямоугольника
float min_x = std::min(mc.x - left, right - mc.x);
float min_y = std::min(mc.y - top, bottom - mc.y);
//Границы описывающего прямоугольника пересчитываются с учётом полученных минимальных значений + небольшой отступ на всякий случай
left = mc.x - min_x - min_x / 4.f;
right = mc.x + min_x + min_x / 4.f;
top = mc.y - min_y - min_y / 4.f;
bottom = mc.y + min_y + min_y / 4.f;
//Удаление точек, которые не попали в новый прямоугольник
k = 0;
for (int i = 0; i < points_count; ++i)
{
if (curr_points[i].x > left &&
curr_points[i].x < right &&
curr_points[i].y > top &&
curr_points[i].y < bottom)
{
curr_points[k] = curr_points[i];
//Вывод точек
cvDrawCircle(gray_img, cvPoint((int)curr_points[k].x, (int)curr_points[k].y), 1, cvScalar(255, 0, 255));
++k;
}
}
points_count = k;
printf("points_count = %i\n", points_count);
face_rect.x = (int)left;
face_rect.y = (int)top;
face_rect.width = (int)(right - left);
face_rect.height = (int)(bottom - top);
//Смена указателей на параметры
std::swap(prev_img, curr_img);
std::swap(prev_pyramid_img, curr_pyramid_img);
std::swap(prev_points, curr_points);
cvShowImage("points", gray_img);
return true;
}
};
////////////////////////////////////////////////////////////////////////////
int main()
{
//Загружаем обученные данные для классификатора
CvHaarClassifierCascade *haar_cascade = (CvHaarClassifierCascade *)cvLoad("c:\\Program Files\\OpenCV_1_1\\data\\haarcascades\\haarcascade_frontalface_alt2.xml", 0, 0, 0);
if (!haar_cascade)
return 1;
CvMemStorage *mem_storage = cvCreateMemStorage(0);
//Захватываем видео (можно и с камеры) с лицами
#if 0
CvCapture *video = cvCaptureFromFile("e:\\video_bmp\\cam_face.avi");
#else
CvCapture *video = cvCaptureFromCAM(0);
#endif
if (!video)
{
cvClearMemStorage(mem_storage);
cvRelease((void **)&haar_cascade);
return 2;
}
cvNamedWindow("frame", 1);
IplImage *gray_img = NULL;
//Прямоугольник найденного лица
CvRect face_rect = cvRect(0, 0, 0, 0);
LKData lk_data;
enum detector_states //Состояния нашего детектора
{
find_face,
track_face
};
detector_states state = find_face;
for (IplImage *frame = cvQueryFrame(video); frame; frame = cvQueryFrame(video))
{
//Вся работа ведётся на изображении в градациях серого
if (!gray_img)
gray_img = cvCreateImage(cvSize(frame->width, frame->height), IPL_DEPTH_8U, 1);
cvCvtColor(frame, gray_img, CV_RGB2GRAY);
//Наш примитивный автомат
switch (state)
{
case find_face: //Поиск лица
if (detect_face(gray_img, haar_cascade, mem_storage, face_rect))
{
//Лицо найдено, инициализируем данные для сопровождения и меняем состояние
lk_data.init(gray_img, face_rect);
state = track_face;
}
break;
case track_face: //Сопровождение лица
if (!lk_data.tracking_face(gray_img, face_rect))
{
//Лицо потеряно, деинициализируем данные сопровождения и меняем состояние
lk_data.deinit();
state = find_face;
}
break;
}
cvDrawRect(frame, cvPoint(face_rect.x, face_rect.y), cvPoint(face_rect.x + face_rect.width, face_rect.y + face_rect.height), cvScalar(255, 0, 0));
cvShowImage("frame", frame);
if (cvWaitKey(10) > 0)
break;
}
cvReleaseCapture(&video);
cvDestroyWindow("frame");
cvClearMemStorage(mem_storage);
cvRelease((void **)&haar_cascade);
return 0;
}
////////////////////////////////////////////////////////////////////////////[/codebox] -
Если честно, меня очень удивляют твои затруднения. Элементарные знания языка и среды разработки должны быть - как же ты собираешься разбираться в гораздо более сложных алгоритмах?
Visual Studio надо открывать hgfhgfhgf.sln. В Solution Explorer будет отображаться дерево проекта с исходниками.
Ммм, ты знаешь что такое bat файлы, "командная строка", параметры функции main? Надо тебе разобраться с этими понятиями.
-
Что означает "после преобразования С++" я даже предположить не могу.
"hgfhgfhgf" - это я по привычке дал имя случайным образом.
В папке release есть bat файл. Его можно редактировать в блокноте. Открой его, посмотри какие параметры там указаны. Их и меняй. Если запустить сам exe без bat файла из командной строки, то он выведет доступные опции и параметры.
Как оно работает:
1. Неподвижная камера смотрит куда-нибудь, сцена без движения.
2. Начинается движение, небольшое, объект меньше четверти размера кадра.
3. Он должен выделиться кружком.
-
А вариант использования 2х или 3х камер с известной геометрией их расположения?Это совсем другая задача. Я ей никогда не занимался (хотя хочется - нет времени).
-
бы очень признателен за компиляцию blobtrack.exe )))Хоть как то сдвинусь с мертвой точки!
Вот здесь. Там есть папка release, запускай через bat-файл, в котором прописаны параметры используемых алгоритмов. Их можно (и нужно!) менять, при разных комбинациях параметров будет разное качество и скорость работы програмы. Видео берёт с первой камеры в системе.
-
Проблема в другом: найти на стационарном изображении движущийся объект - нетрудно. Трудности начнутся, когда ты будешь крутить камерой: смазывание изображения, интерлейсинг, артефакты сжатия... Это для начала.Проблема все та же... Как поместить движущийся объект в квадратик и измерить его скорость. С координатами сам покумекаю. Главное первых два шага!Потом: раз камера движется, то и задний план будет меняться, сразу отметаются самые популярные алгоритмы вычитания фона. Если ты всё таки захочешь их оставить, то надо будет использовать весьма точное сопоставление и наложение кадров (фактически это задача панорамирования).
Если вычитание фона не использовать, то надо будет придумать хороший алгоритма поиска объекта. Если объект - человек, то отмашка рук, изменяющаяся геометрия ног и другие факторы начнут очень сильно портить картину.
Короче говоря, это весьма непросто.
P.S. Если хочешь, могу скомпилировать пример blobtrack.exe, чтобы он ничего лишнего не требовал (странно, что у тебя это не получается). Видео брать с камеры или из файла?
-
Программирование контроллера по сравнению с задачей отслеживания объекта поворотной камерой - это ерунда. Даже на стационарной камере точное и быстрое сопровождение сделать непросто. Готовые решения ты вряд ли найдёшь.
Посмотри здесь четвёртый ролик - это фича очень немногих систем видеонаблюдения. Так что тебе придётся хорошенько над этим подумать.
-
можно ли обращаться к элементу IplImage и напрямую в него записывать данные.Можно.
можно ли с ним работать как с двухмерным массивом.Можно.
Это же обычная структура. Посмотри на её поля их значения - сразу всё станет ясно.
-
Я думаю, что можно попробовать натренировать классификатор на номер - должно получиться очень неплохо.
-
Что такое CRT я думаю ты знаешь. Существует 2 способа собрать .exe:
1. прилинковать к нему CRT в виде статических lib;
2. ничего не прилинковывать, CRT используется из dll (например для 2008-й студии это msvcp80.dll, msvcr80.dll,...).
В первом случае тебе ничего тащить с программой не надо, а во втором эти самые dll. Преимущества и недостатки есть у каждого подхода. Соответственно многие инсталляторы тащат с собой vcredist - майкрософтовский инсталлятор CRT. Или кидают эти dll в папку с программой. В OpenCV 1.1 эти dll лежат в папке bin.
-
Гм, с какой камеры? blobtrack.exe на входе из командной строки получает имя avi-файла с видео (а также параметры алгоритмов). С камерой он не работает. Ты можешь его скомпилировать? Почему стандартный не запускается? Может быть ему просто надо подсунуть майкрософтовскую CRT в ддлках? Или поставить vcredist?
-
Странно, у меня 2005-я студия поддерживает OpenMP из коробки.Странно, что компилятор Micro$oft Studio 8 (2005) не смог слинковать проект (не нашёлся vcomp.lib), а вот интеловский компилятор с задачей справился. -
На самом деле проблема сидит вот здесь:.......
Мне этот "throw" не перехватить ничем, даже SetUnhandledExceptionFilter не может с этим справиться...
Хорошая была библиотека, а теперь ей пользоваться практически невозможно.
Ну исходники же открыты - можно исправить.
P.S. Я пока с версии 1.1 не ухожу, неохота.
-
Какие ошибки? Может, просто соответствующие *.lib не подключил?
P.S. Русскоязычная студия - это ужас!
-
Советую посмотреть на реализацию стандартных примеров из OpenCV: blobtrack.exe и bgfg_codebook.exe. В основном тебе будет интересен первый пример.
-
Документации я тоже не видел, но в исходниках OpneCV 1.1 есть функция cvCreateCapture_GStreamer, которая включается при определённом макросе HAVE_GSTREAMER. Определить его, конечно, можно, после перекомпилировать highgui. Может и поможет, надо пробовать.Хочу попробовать использовать GStrimer в OpenCV для видео захвата с камеры мобильного устройства nokia n810. OpenCV вроде поддерживает GStrimer. Может кто знает как его можно использовать в OpenCV ? А то я что то не нашёл нормальной документации по этому вопросу! -
2gonzik: Ну в примерах же всё есть! Надо же просто скомпоновать. Нерадивый студент?
Вот, набросал на скорую руку, но сразу скажу, что не реализовал пункт 5: Если какая-то точка слишком далеко "оторвалась" от объекта (например, на расстояние, превышающее размер лица), то её тоже удаляем.
Сам попробуй, а то уже футбол начался, "Рубин" играет.
#include <algorithm>
#include <limits>
#include <highgui.h>
#include <cv.h>
#undef min
#undef max
#pragma comment(lib, "cv.lib")
#pragma comment(lib, "cxcore.lib")
#pragma comment(lib, "highgui.lib")
////////////////////////////////////////////////////////////////////////////
//Поиск лица на кадре
bool detect_face(IplImage *gray_img, CvHaarClassifierCascade *haar_cascade, CvMemStorage *mem_storage, CvRect &face_rect)
{
cvClearMemStorage(mem_storage);
//Поиск лиц
CvSeq *faces = cvHaarDetectObjects(gray_img, haar_cascade, mem_storage, 1.1, 2, 0, cvSize(20, 20));
if (!faces || !faces->total)
return false;
//Возвращаем первое найденное
face_rect = *(CvRect *)cvGetSeqElem(faces, 0);
return true;
}
////////////////////////////////////////////////////////////////////////////
class LKData
{
private:
//Всякие данные
IplImage *curr_img;
IplImage *prev_img;
IplImage *curr_pyramid_img;
IplImage *prev_pyramid_img;
int points_count;
CvPoint2D32f *curr_points;
CvPoint2D32f *prev_points;
char *status;
CvTermCriteria tc;
int flags;
public:
LKData()
: curr_img(NULL), prev_img(NULL), curr_pyramid_img(NULL), prev_pyramid_img(NULL),
points_count(0), flags(0), curr_points(NULL), prev_points(NULL), status(NULL)
{
tc = cvTermCriteria(CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 20, 0.03);
cvNamedWindow("points", 1);
}
////////////////////////////////////////////////////////////////////////////
~LKData()
{
deinit();
cvDestroyWindow("points");
}
////////////////////////////////////////////////////////////////////////////
//Инициализация данных для сопровождения
void init(const IplImage *gray_img, const CvRect &face_rect)
{
deinit();
curr_img = cvCreateImage(cvSize(gray_img->width, gray_img->height), IPL_DEPTH_8U, 1);
prev_img = cvCreateImage(cvSize(gray_img->width, gray_img->height), IPL_DEPTH_8U, 1);
curr_pyramid_img = cvCreateImage(cvSize(gray_img->width, gray_img->height), IPL_DEPTH_8U, 1);
prev_pyramid_img = cvCreateImage(cvSize(gray_img->width, gray_img->height), IPL_DEPTH_8U, 1);
//Покроем прямоугольник лица равномерной сеткой из точек
const int step_x = face_rect.width / 16;
const int step_y = face_rect.height / 16;
points_count = (face_rect.width / step_x + 1) * (face_rect.height / step_y + 1);
curr_points = new CvPoint2D32f[points_count];
prev_points = new CvPoint2D32f[points_count];
status = new char[points_count];
size_t pc = 0;
for (int i = face_rect.y, stop_i = face_rect.y + face_rect.height; i < stop_i; i += step_y)
{
for (int j = face_rect.x, stop_j = face_rect.x + face_rect.width; j < stop_j; j += step_x)
{
curr_points[pc] = cvPoint2D32f((double)j, (double)i);
++pc;
}
}
points_count = (int)pc;
cvCopyImage(gray_img, curr_img);
cvFindCornerSubPix(curr_img, curr_points, points_count, cvSize(3, 3), cvSize(-1, -1), tc);
//Смена указателей на параметры
std::swap(prev_img, curr_img);
std::swap(prev_pyramid_img, curr_pyramid_img);
std::swap(prev_points, curr_points);
}
////////////////////////////////////////////////////////////////////////////
//Деинициализация данных для сопровождения
void deinit()
{
if (curr_img)
{
cvReleaseImage(&curr_img);
curr_img = NULL;
}
if (prev_img)
{
cvReleaseImage(&prev_img);
prev_img = NULL;
}
if (curr_pyramid_img)
{
cvReleaseImage(&curr_pyramid_img);
curr_pyramid_img = NULL;
}
if (prev_pyramid_img)
{
cvReleaseImage(&prev_pyramid_img);
prev_pyramid_img = NULL;
}
points_count = 0;
flags = 0;
if (curr_points)
{
delete []curr_points;
curr_points = NULL;
}
if (prev_points)
{
delete []prev_points;
prev_points = NULL;
}
if (status)
{
delete []status;
status = NULL;
}
}
////////////////////////////////////////////////////////////////////////////
//Сопровождение лица
bool tracking_face(IplImage *gray_img, CvRect &face_rect)
{
cvCopyImage(gray_img, curr_img);
//Вычисление нового положения точек с помощью пирамидального алгоритма анализа оптического потока Лукаса-Канаде
cvCalcOpticalFlowPyrLK(prev_img, curr_img, prev_pyramid_img, curr_pyramid_img,
prev_points, curr_points, points_count, cvSize(20, 20), 3, status, 0, tc, flags);
flags |= CV_LKFLOW_PYR_A_READY;
if (!points_count)
return false;
//Удаление не найденных точек, а также вычисление координат описывающего прямоугольника
double left = std::numeric_limits<double>::max();
double top = std::numeric_limits<double>::max();
double right = std::numeric_limits<double>::min();
double bottom = std::numeric_limits<double>::min();
int k = 0;
for (int i = 0; i < points_count; ++i)
{
if (status[i])
{
curr_points[k] = curr_points[i];
//Вывод точек
cvDrawCircle(gray_img, cvPoint((int)curr_points[k].x, (int)curr_points[k].y), 1, cvScalar(255, 255, 255));
if (curr_points[k].x < left)
left = curr_points[k].x;
if (curr_points[k].x > right)
right = curr_points[k].x;
if (curr_points[k].y < top)
top = curr_points[k].y;
if (curr_points[k].y > bottom)
bottom = curr_points[k].y;
++k;
}
}
points_count = k;
printf("points_count = %i\n", points_count);
face_rect.x = (int)left;
face_rect.y = (int)top;
face_rect.width = (int)(right - left);
face_rect.height = (int)(bottom - top);
//Смена указателей на параметры
std::swap(prev_img, curr_img);
std::swap(prev_pyramid_img, curr_pyramid_img);
std::swap(prev_points, curr_points);
cvShowImage("points", gray_img);
return true;
}
};
////////////////////////////////////////////////////////////////////////////
int main()
{
//Загружаем обученные данные для классификатора
CvHaarClassifierCascade *haar_cascade = (CvHaarClassifierCascade *)cvLoad("c:\\Program Files\\OpenCV_1_1\\data\\haarcascades\\haarcascade_frontalface_alt2.xml", 0, 0, 0);
if (!haar_cascade)
return 1;
CvMemStorage *mem_storage = cvCreateMemStorage(0);
//Захватываем видео (можно и с камеры) с лицами
#if 0
CvCapture *video = cvCaptureFromFile("e:\\video_bmp\\cam_face.avi");
#else
CvCapture *video = cvCaptureFromCAM(0);
#endif
if (!video)
{
cvClearMemStorage(mem_storage);
return 2;
}
cvNamedWindow("frame", 1);
IplImage *gray_img = NULL;
//Прямоугольник найденного лица
CvRect face_rect = cvRect(0, 0, 0, 0);
LKData lk_data;
enum detector_states //Состояния нашего детектора
{
find_face,
track_face
};
detector_states state = find_face;
for (IplImage *frame = cvQueryFrame(video); frame; frame = cvQueryFrame(video))
{
//Вся работа ведётся на изображении в градациях серого
if (!gray_img)
gray_img = cvCreateImage(cvSize(frame->width, frame->height), IPL_DEPTH_8U, 1);
cvCvtColor(frame, gray_img, CV_RGB2GRAY);
//Наш примитивный автомат
switch (state)
{
case find_face: //Поиск лица
if (detect_face(gray_img, haar_cascade, mem_storage, face_rect))
{
//Лицо найдено, инициализируем данные для сопровождения и меняем состояние
lk_data.init(gray_img, face_rect);
state = track_face;
}
break;
case track_face: //Сопровождение лица
if (!lk_data.tracking_face(gray_img, face_rect))
{
//Лицо потеряно, деинициализируем данные сопровождения и меняем состояние
lk_data.deinit();
state = find_face;
}
break;
}
cvDrawRect(frame, cvPoint(face_rect.x, face_rect.y), cvPoint(face_rect.x + face_rect.width, face_rect.y + face_rect.height), cvScalar(255, 0, 0));
cvShowImage("frame", frame);
cvWaitKey(10);
}
cvClearMemStorage(mem_storage);
cvReleaseCapture(&video);
cvDestroyWindow("frame");
return 0;
}
////////////////////////////////////////////////////////////////////////////[/codebox]P.S. Принимаются от всех заинтересованных лиц идеи по улучшению алгоритма. Идеи (а не реализация по примерам) действительно интересны.
-
Как в примере lkdemo.exe:
1. На кадре найдено лицо:
а) заполняем массив точек лица каким-либо способом: равномерно по площади лица, случайным образом, cvGoodFeaturesToTrack;
б) если использовались первые 2 способа, то для массива точек вызываем cvFindCornerSubPix.
2. Для следующего кадра: вызываем cvCalcOpticalFlowPyrLK, добавляем к флагам CV_LKFLOW_PYR_A_READY.
3. Если полученное число точек равно нулю - цель потеряна, выход, переходим к пункту 1.
4. Удаляем из массива не найденные на очередном кадре точки на основании массива status из cvCalcOpticalFlowPyrLK.
5. Если какая-то точка слишком далеко "оторвалась" от объекта (например, на расстояние, превышающее размер лица), то её тоже удаляем.
6. Если точек не осталось или осталось слишком мало (например 1-2 точки на лицо), то считаем цель потерянной, выход, переходим к пункту 1.
7. Вычисляем прямоугольник, описывающий оставшиеся точки, выводим его.
8. Можно вывести на кадре траекторию движения лица: центры прямоугольников на каждом кадре; при желании траекторию можно обрабатывать фильтром Кальмана.
9. Переход к пункту 2.
Реализация практически всех пунктов есть в примере lkdemo.exe
Если качество видео достаточно высокое, то сопровождение должно работать корректно.
-
Давай попробуем разобраться что есть что.
1. Нам подаётся видеопоток, в котором содержатся или не содержатся интересующие нас объекты - лица.
2. Лица надо как-то обнаружить: каскады Хаара, поиск регионов с текстурой, похожей на кожу и т.п. Поиск надо производить на каждом кадре?
3. Если поиск производится на каждом кадре, то надо определить какое лицо на кадре t соответствует лицу на кадре t+1. Есть варианты: по координатам, по размеру, найти точки с особенностями и сравнивать их и т.д. Соответствие между лицами на соседних кадрах найдено? Это и есть сопровождение.
4. Допустим, нам надо найти первое попавшееся лицо и сопровождать только его в видеопотоке. Тут можно воспользоваться алгоритмом Лукаса-Канаде. Находим алгоритмом из пункта 2 лицо, находим на нём характерные точки (например, как в примере lkdemo.exe). Сопровождаем точки с помощью Лукаса-Канаде; после их пропадания считаем, что лицо исчезло из поля зрения. Снова переходим к пункту 2.
5. Фильтр Кальмана в компьютерном зрении применяется для сглаживания траектории движения объекта (лица), а также для предсказания его положения на следующем кадре. Тут необходимо отметить, что фильтр Кальмана предназначен для линейных моделей движения. Для нелинейного же применяется particle filter (как вариант particle filter + mean shift).
-
А что же там сложного для переделки? Убирай всё, что касается Билдера (это будет интерфейс) и заменяй на стандартный OpenCV вывод.
Вообще, в примерах OpenCV есть отличный пример Лукаса-Канаде.
-
SetImageROI
-
Если автор не против, то выложу переделку - делал как-то для себя, поиграть. Немного перегруппировал код, отформатировал по своему вкусу, но всё работает также как и в оригинале:Как бы его переделать под VS2008...#include "stdafx.h"
#include <highgui.h>
#include <cv.h>
#pragma comment(lib, "cv.lib")
#pragma comment(lib, "cxcore.lib")
#pragma comment(lib, "highgui.lib")
////////////////////////////////////////////////////////////////////////////
//function to find the biggest blob and to get the top left position
CvPoint2D32f findBigBlob(IplImage *mask)
{
int maxval = 0;
int minval = 100;
//начинаем поиск контуров
CvMemStorage *storage = cvCreateMemStorage(0);
CvContourScanner traverse = cvStartFindContours(mask, storage, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_NONE);
//находим контуры с длиной периметра между minval и maxval
CvSeq *mContour = 0;
for (CvSeq *contour = cvFindNextContour(traverse); contour; contour = cvFindNextContour(traverse))
{
int contourarea = (int)abs(cvContourArea(contour, CV_WHOLE_SEQ));
if (contourarea > maxval)
{
mContour = contour;
maxval = contourarea;
}
}
CvPoint2D32f ret_val = cvPoint2D32f(-1.0, -1.0);
if (maxval > minval)
{
//находим центр масс
CvMoments moments;
cvMoments(mContour, &moments, 0);
ret_val.x = (float)(moments.m10 / moments.m00);
ret_val.y = (float)(moments.m01 / moments.m00);
}
//освобождаем память
cvEndFindContours(&traverse);
cvReleaseMemStorage(&storage);
return ret_val;
}
////////////////////////////////////////////////////////////////////////////
//находим участки, подходящие нам по цвету (шайба)
void region_color_range(const IplImage *color, IplImage *mask, uchar bmin, uchar bmax, uchar gmin, uchar gmax, uchar rmin, uchar rmax)
{
uchar *dst_ptr = (uchar *)mask->imageData;
for (const uchar *src_ptr = (uchar *)color->imageData, *stop = src_ptr + color->imageSize; src_ptr != stop; src_ptr += color->nChannels)
{
uchar b = src_ptr[0];
uchar g = src_ptr[1];
uchar r = src_ptr[2];
//проверяем диапазон составляющих цвета
if (b <= bmax && b >= bmin &&
g <= gmax && g >= gmin &&
r <= rmax && r >= rmin)
*dst_ptr = 255;
else
*dst_ptr = 0;
++dst_ptr;
}
}
////////////////////////////////////////////////////////////////////////////
int main()
{
//параметры, для идентификации шайбы по цвету
uchar bmin = 0;
uchar bmax = 51;
uchar gmin = 120;
uchar gmax = 225;
uchar rmin = 102;
uchar rmax = 235;
CvMat *measurement = cvCreateMat(2, 1, CV_32FC1);
cvZero(measurement);
CvRNG rng = cvRNG(-1);
CvMat *state = cvCreateMat(4, 1, CV_32FC1); // (x, y, deltax, deltay)
cvRandArr(&rng, state, CV_RAND_NORMAL, cvRealScalar(0), cvRealScalar(0.1));
//стандартный набор функций для фильтра кальмана
CvKalman *kalman = cvCreateKalman(4, 2, 0); //4 переменных состояния, 2 переменных измерения
float deltatime = 50.f; //приращение времени
float transition_matrix[] = { 1, 0, deltatime, 0, 0, 1, 0, deltatime, 0, 0, 1, 0, 0, 0, 0, 1}; //transition matrix
memcpy(kalman->transition_matrix->data.fl, transition_matrix, sizeof(transition_matrix));
cvSetIdentity(kalman->measurement_matrix, cvRealScalar(1));
cvSetIdentity(kalman->process_noise_cov, cvRealScalar(1e-5));
cvSetIdentity(kalman->measurement_noise_cov, cvRealScalar(1e-1));
cvSetIdentity(kalman->error_cov_post, cvRealScalar(1));
//выбираем случайное начальное состояние
cvRandArr(&rng, kalman->state_post, CV_RAND_NORMAL, cvRealScalar(0), cvRealScalar(0.1));
cvNamedWindow("frame", 1);
cvNamedWindow("color_segm", 1);
cvNamedWindow("contours", 1);
CvCapture *video = cvCaptureFromAVI("vid1.avi");
IplImage *mask = NULL;
for (IplImage *frame = cvQueryFrame(video); frame; frame = cvQueryFrame(video))
{
if (!mask)
mask = cvCreateImage(cvSize(frame->width, frame->height), IPL_DEPTH_8U, 1);
//находим положение шайбы, используя поиск по цвету
region_color_range(frame, mask, bmin, bmax, gmin, gmax, rmin, rmax);
cvShowImage("color_segm", mask);
CvPoint2D32f position = findBigBlob(mask);
cvShowImage("contours", mask);
const CvMat *kalman_predict = cvKalmanPredict(kalman, 0);
float predict_x = kalman_predict->data.fl[0];
float predict_y = kalman_predict->data.fl[1];
if (position.x < 0.0)
{
//уточняем используя предсказание
measurement->data.fl[0] = predict_x;
measurement->data.fl[1] = predict_y;
}
else
{
//уточняем, используя данные измерений
measurement->data.fl[0] = position.x;
measurement->data.fl[1] = position.y;
}
//Предсказанное и измеренное положение центра объекта
cvDrawCircle(frame, cvPoint((int)position.x, (int)position.y), 3, cvScalar(255, 255, 255), -1, 8, 0);
cvDrawCircle(frame, cvPoint((int)predict_x, (int)predict_y), 3, cvScalar(0, 0, 0), -1, 8, 0);
//обновляем состояние фильтра
cvKalmanCorrect(kalman, measurement);
cvWaitKey(100);
cvShowImage("frame", frame);
}
cvReleaseCapture(&video);
if (mask)
cvReleaseImage(&mask);
cvDestroyWindow("frame");
cvDestroyWindow("color_segm");
cvDestroyWindow("contours");
return 0;
}
////////////////////////////////////////////////////////////////////////////[/codebox] -
У меня нормально проходит. Что именно говорит текст ошибки? Какая версия OpenCV?
Скорость работы
в OpenCV
Опубликовано · Report reply
Это из-за IPP на интелах такая скорость. Без IPP будет тормозить.