Обнаружение лица Viola-Jones требует 180 тыс. Функций - PullRequest
78 голосов
/ 10 ноября 2009

Я реализую адаптацию Алгоритма обнаружения лица Виолы-Джонса . Этот метод основан на размещении подкадра размером 24x24 пикселя на изображении и последующем размещении прямоугольных элементов внутри него в каждой позиции с любым возможным размером.

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

Rectangle features

Они утверждают, что исчерпывающий набор составляет более 180 КБ (раздел 2):

Учитывая, что базовое разрешение детектора составляет 24x24, исчерпывающий набор прямоугольных элементов довольно велик - более 180 000. Обратите внимание, что в отличие от основы Хаара, множество прямоугольников функции переполнены.

Следующие утверждения прямо не изложены в документе, поэтому они являются предположениями с моей стороны:

  1. Есть только 2 элемента с двумя прямоугольниками, 2 элемента с тремя прямоугольниками и 1 элемент с четырьмя прямоугольниками. Логика этого заключается в том, что мы наблюдаем разницу между выделенными прямоугольниками, а не явно цвет или яркость или что-либо в этом роде.
  2. Мы не можем определить тип объекта A как блок пикселей 1x1; он должен быть не менее 1х2 пикселей. Кроме того, тип D должен иметь размер не менее 2x2 пикселей, и это правило соответствует другим функциям.
  3. Мы не можем определить тип объекта A как блок пикселей 1x3, так как средний пиксель не может быть разделен, и вычитание его из себя идентично блоку пикселей 1x2; этот тип объекта определен только для четной ширины. Кроме того, ширина типа объекта C должна делиться на 3, и это правило выполняется в соответствии с другими объектами.
  4. Мы не можем определить объект с шириной и / или высотой 0. Поэтому мы повторяем значения x и y до 24 минус размер объекта.

Исходя из этих предположений, я насчитал исчерпывающий набор:

const int frameSize = 24;
const int features = 5;
// All five feature types:
const int feature[features][2] = {{2,1}, {1,2}, {3,1}, {1,3}, {2,2}};

int count = 0;
// Each feature:
for (int i = 0; i < features; i++) {
    int sizeX = feature[i][0];
    int sizeY = feature[i][1];
    // Each position:
    for (int x = 0; x <= frameSize-sizeX; x++) {
        for (int y = 0; y <= frameSize-sizeY; y++) {
            // Each size fitting within the frameSize:
            for (int width = sizeX; width <= frameSize-x; width+=sizeX) {
                for (int height = sizeY; height <= frameSize-y; height+=sizeY) {
                    count++;
                }
            }
        }
    }
}

Результат 162,336 .

Единственный способ, которым я нашел приблизительный подход к «более чем 180 000», о котором говорят Виола и Джонс, - это отбросить предположение № 4 и внести ошибки в код. Это предполагает изменение четырех строк соответственно на:

for (int width = 0; width < frameSize-x; width+=sizeX)
for (int height = 0; height < frameSize-y; height+=sizeY)

В результате получается 180,625 . (Обратите внимание, что это эффективно предотвратит соприкосновение элементов с правой и / или нижней частью подкадра.)

Теперь, конечно, вопрос: допустили ли они ошибку в своей реализации? Имеет ли смысл рассматривать объекты с нулевой поверхностью? Или я вижу это неправильно?

Ответы [ 5 ]

38 голосов
/ 11 ноября 2009

При ближайшем рассмотрении, ваш код выглядит правильно для меня; это заставляет задуматься, не было ли у оригинальных авторов ошибки «один за другим». Я думаю, что кто-то должен посмотреть, как это реализует OpenCV!

Тем не менее, одно предложение, чтобы облегчить понимание, - это перевернуть порядок петель для , сначала пройдя все размеры, а затем зацикливаясь на возможных местах, учитывая размер:

#include <stdio.h>
int main()
{
    int i, x, y, sizeX, sizeY, width, height, count, c;

    /* All five shape types */
    const int features = 5;
    const int feature[][2] = {{2,1}, {1,2}, {3,1}, {1,3}, {2,2}};
    const int frameSize = 24;

    count = 0;
    /* Each shape */
    for (i = 0; i < features; i++) {
        sizeX = feature[i][0];
        sizeY = feature[i][1];
        printf("%dx%d shapes:\n", sizeX, sizeY);

        /* each size (multiples of basic shapes) */
        for (width = sizeX; width <= frameSize; width+=sizeX) {
            for (height = sizeY; height <= frameSize; height+=sizeY) {
                printf("\tsize: %dx%d => ", width, height);
                c=count;

                /* each possible position given size */
                for (x = 0; x <= frameSize-width; x++) {
                    for (y = 0; y <= frameSize-height; y++) {
                        count++;
                    }
                }
                printf("count: %d\n", count-c);
            }
        }
    }
    printf("%d\n", count);

    return 0;
}

с теми же результатами, что и предыдущие 162336


Чтобы проверить это, я проверил случай окна 4x4 и вручную проверил все случаи (легко посчитать, так как формы 1x2 / 2x1 и 1x3 / 3x1 совпадают только с поворотом на 90 градусов):

2x1 shapes:
        size: 2x1 => count: 12
        size: 2x2 => count: 9
        size: 2x3 => count: 6
        size: 2x4 => count: 3
        size: 4x1 => count: 4
        size: 4x2 => count: 3
        size: 4x3 => count: 2
        size: 4x4 => count: 1
1x2 shapes:
        size: 1x2 => count: 12             +-----------------------+
        size: 1x4 => count: 4              |     |     |     |     |
        size: 2x2 => count: 9              |     |     |     |     |
        size: 2x4 => count: 3              +-----+-----+-----+-----+
        size: 3x2 => count: 6              |     |     |     |     |
        size: 3x4 => count: 2              |     |     |     |     |
        size: 4x2 => count: 3              +-----+-----+-----+-----+
        size: 4x4 => count: 1              |     |     |     |     |
3x1 shapes:                                |     |     |     |     |
        size: 3x1 => count: 8              +-----+-----+-----+-----+
        size: 3x2 => count: 6              |     |     |     |     |
        size: 3x3 => count: 4              |     |     |     |     |
        size: 3x4 => count: 2              +-----------------------+
1x3 shapes:
        size: 1x3 => count: 8                  Total Count = 136
        size: 2x3 => count: 6
        size: 3x3 => count: 4
        size: 4x3 => count: 2
2x2 shapes:
        size: 2x2 => count: 9
        size: 2x4 => count: 3
        size: 4x2 => count: 3
        size: 4x4 => count: 1
8 голосов
/ 21 июля 2010

все. В документах Виолы и Джонса все еще есть некоторая путаница.

В их статье CVPR'01 четко указано, что

"Более конкретно, мы используем три виды функций. Значение двухугольник - это разница между суммой пикселей в двух прямоугольных областях. Регионы имеют одинаковый размер и форма и горизонтально или вертикально смежный (см. рисунок 1). Трехугольник вычисляет сумму в двух внешних прямоугольники вычитаются из суммы в центральный прямоугольник. Наконец а четырехугольный элемент ".

В статье IJCV'04 сказано точно то же самое. Итак, всего 4 функции . Но как ни странно, на этот раз они заявили, что исчерпывающий набор функций - 45396! Похоже, это не окончательная версия. Здесь я предполагаю, что там были введены некоторые дополнительные ограничения, такие как min_width, min_height, соотношение ширины / высоты и даже положение.

Обратите внимание, что обе статьи можно загрузить на его веб-странице .

3 голосов
/ 10 ноября 2009

Не прочитав всю статью, формулировка вашей цитаты торчит у меня

Учитывая, что базовое разрешение детектор 24х24, исчерпывающий набор из прямоугольников довольно большой, более 180 000 Обратите внимание, что в отличие от Основа Хаара, набор прямоугольников функции переполнены.

«Набор элементов прямоугольника переполнен» «Исчерпывающий набор»

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

edit: или использование какого-то алгоритма машинного обучения, как намекает абстракция. Исчерпывающий набор подразумевает все возможности, а не только «разумные».

2 голосов
/ 10 ноября 2009

Нет никаких гарантий, что любой автор какой-либо статьи прав во всех своих предположениях и выводах. Если вы считаете, что предположение № 4 верно, то сохраните это предположение и опробуйте свою теорию. Вы можете быть более успешным, чем оригинальные авторы.

1 голос
/ 12 апреля 2017

Неплохое наблюдение, но они могут неявно заполнять нулевой кадр 24x24 или «переполнять» и начинать использовать первые пиксели, когда он выходит за границы, как при поворотных сдвигах, или, как сказал Бретон, они могут рассматривать некоторые функции как тривиальные функции ", а затем отказаться от них с AdaBoost.

Кроме того, я написал версии вашего кода на Python и Matlab, чтобы я мог сам тестировать код (его легче отлаживать и отслеживать), поэтому я публикую их здесь, если кто-нибудь когда-нибудь найдет их полезными.

Python:

frameSize = 24;
features = 5;
# All five feature types:
feature = [[2,1], [1,2], [3,1], [1,3], [2,2]]

count = 0;
# Each feature:
for i in range(features):
    sizeX = feature[i][0]
    sizeY = feature[i][1]
    # Each position:
    for x in range(frameSize-sizeX+1):
        for y in range(frameSize-sizeY+1):
            # Each size fitting within the frameSize:
            for width in range(sizeX,frameSize-x+1,sizeX):
                for height in range(sizeY,frameSize-y+1,sizeY):
                    count=count+1
print (count)

Matlab:

frameSize = 24;
features = 5;
% All five feature types:
feature = [[2,1]; [1,2]; [3,1]; [1,3]; [2,2]];

count = 0;
% Each feature:
for ii = 1:features
    sizeX = feature(ii,1);
    sizeY = feature(ii,2);
    % Each position:
    for x = 0:frameSize-sizeX
        for y = 0:frameSize-sizeY
            % Each size fitting within the frameSize:
            for width = sizeX:sizeX:frameSize-x
                for height = sizeY:sizeY:frameSize-y
                    count=count+1;
                end
            end
        end
    end
end

display(count)
...