Перейти к содержимому
Compvision.ru

Nuzhny

Пользователи
  • Количество публикаций

    1 427
  • Зарегистрирован

  • Посещение

  • Days Won

    176

Сообщения, опубликованные пользователем Nuzhny


  1. С глюками согласен - исправил.

    Вот, добавил простенький алгоритм отсева отделившихся точек:

    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]


  2. Если честно, меня очень удивляют твои затруднения. Элементарные знания языка и среды разработки должны быть - как же ты собираешься разбираться в гораздо более сложных алгоритмах?

    Visual Studio надо открывать hgfhgfhgf.sln. В Solution Explorer будет отображаться дерево проекта с исходниками.

    Ммм, ты знаешь что такое bat файлы, "командная строка", параметры функции main? Надо тебе разобраться с этими понятиями.


  3. Что означает "после преобразования С++" я даже предположить не могу.

    "hgfhgfhgf" - это я по привычке дал имя случайным образом.

    В папке release есть bat файл. Его можно редактировать в блокноте. Открой его, посмотри какие параметры там указаны. Их и меняй. Если запустить сам exe без bat файла из командной строки, то он выведет доступные опции и параметры.

    Как оно работает:

    1. Неподвижная камера смотрит куда-нибудь, сцена без движения.

    2. Начинается движение, небольшое, объект меньше четверти размера кадра.

    3. Он должен выделиться кружком.


  4. бы очень признателен за компиляцию blobtrack.exe )))

    Хоть как то сдвинусь с мертвой точки!

    Вот здесь. Там есть папка release, запускай через bat-файл, в котором прописаны параметры используемых алгоритмов. Их можно (и нужно!) менять, при разных комбинациях параметров будет разное качество и скорость работы програмы. Видео берёт с первой камеры в системе.


  5. Проблема все та же... Как поместить движущийся объект в квадратик и измерить его скорость. С координатами сам покумекаю. Главное первых два шага!
    Проблема в другом: найти на стационарном изображении движущийся объект - нетрудно. Трудности начнутся, когда ты будешь крутить камерой: смазывание изображения, интерлейсинг, артефакты сжатия... Это для начала.

    Потом: раз камера движется, то и задний план будет меняться, сразу отметаются самые популярные алгоритмы вычитания фона. Если ты всё таки захочешь их оставить, то надо будет использовать весьма точное сопоставление и наложение кадров (фактически это задача панорамирования).

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

    Короче говоря, это весьма непросто.

    P.S. Если хочешь, могу скомпилировать пример blobtrack.exe, чтобы он ничего лишнего не требовал (странно, что у тебя это не получается). Видео брать с камеры или из файла?


  6. Программирование контроллера по сравнению с задачей отслеживания объекта поворотной камерой - это ерунда. Даже на стационарной камере точное и быстрое сопровождение сделать непросто. Готовые решения ты вряд ли найдёшь.

    Посмотри здесь четвёртый ролик - это фича очень немногих систем видеонаблюдения. Так что тебе придётся хорошенько над этим подумать.


  7. можно ли обращаться к элементу IplImage и напрямую в него записывать данные.

    Можно.

    можно ли с ним работать как с двухмерным массивом.

    Можно.

    Это же обычная структура. Посмотри на её поля их значения - сразу всё станет ясно.


  8. Что такое CRT я думаю ты знаешь. Существует 2 способа собрать .exe:

    1. прилинковать к нему CRT в виде статических lib;

    2. ничего не прилинковывать, CRT используется из dll (например для 2008-й студии это msvcp80.dll, msvcr80.dll,...).

    В первом случае тебе ничего тащить с программой не надо, а во втором эти самые dll. Преимущества и недостатки есть у каждого подхода. Соответственно многие инсталляторы тащат с собой vcredist - майкрософтовский инсталлятор CRT. Или кидают эти dll в папку с программой. В OpenCV 1.1 эти dll лежат в папке bin.


  9. Гм, с какой камеры? blobtrack.exe на входе из командной строки получает имя avi-файла с видео (а также параметры алгоритмов). С камерой он не работает. Ты можешь его скомпилировать? Почему стандартный не запускается? Может быть ему просто надо подсунуть майкрософтовскую CRT в ддлках? Или поставить vcredist?


  10. На самом деле проблема сидит вот здесь:

    .......

    Мне этот "throw" не перехватить ничем, даже SetUnhandledExceptionFilter не может с этим справиться...

    Хорошая была библиотека, а теперь ей пользоваться практически невозможно.

    Ну исходники же открыты - можно исправить.

    P.S. Я пока с версии 1.1 не ухожу, неохота.


  11. Хочу попробовать использовать GStrimer в OpenCV для видео захвата с камеры мобильного устройства nokia n810. OpenCV вроде поддерживает GStrimer. Может кто знает как его можно использовать в OpenCV ? А то я что то не нашёл нормальной документации по этому вопросу!
    Документации я тоже не видел, но в исходниках OpneCV 1.1 есть функция cvCreateCapture_GStreamer, которая включается при определённом макросе HAVE_GSTREAMER. Определить его, конечно, можно, после перекомпилировать highgui. Может и поможет, надо пробовать.

  12. 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. Принимаются от всех заинтересованных лиц идеи по улучшению алгоритма. Идеи (а не реализация по примерам) действительно интересны.


  13. Как в примере 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

    Если качество видео достаточно высокое, то сопровождение должно работать корректно.


  14. Давай попробуем разобраться что есть что.

    1. Нам подаётся видеопоток, в котором содержатся или не содержатся интересующие нас объекты - лица.

    2. Лица надо как-то обнаружить: каскады Хаара, поиск регионов с текстурой, похожей на кожу и т.п. Поиск надо производить на каждом кадре?

    3. Если поиск производится на каждом кадре, то надо определить какое лицо на кадре t соответствует лицу на кадре t+1. Есть варианты: по координатам, по размеру, найти точки с особенностями и сравнивать их и т.д. Соответствие между лицами на соседних кадрах найдено? Это и есть сопровождение.

    4. Допустим, нам надо найти первое попавшееся лицо и сопровождать только его в видеопотоке. Тут можно воспользоваться алгоритмом Лукаса-Канаде. Находим алгоритмом из пункта 2 лицо, находим на нём характерные точки (например, как в примере lkdemo.exe). Сопровождаем точки с помощью Лукаса-Канаде; после их пропадания считаем, что лицо исчезло из поля зрения. Снова переходим к пункту 2.

    5. Фильтр Кальмана в компьютерном зрении применяется для сглаживания траектории движения объекта (лица), а также для предсказания его положения на следующем кадре. Тут необходимо отметить, что фильтр Кальмана предназначен для линейных моделей движения. Для нелинейного же применяется particle filter (как вариант particle filter + mean shift).


  15. А что же там сложного для переделки? Убирай всё, что касается Билдера (это будет интерфейс) и заменяй на стандартный OpenCV вывод.

    Вообще, в примерах OpenCV есть отличный пример Лукаса-Канаде.


  16. Как бы его переделать под 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]

×