Распознавание лиц Виолы-Джонс: 180 тысяч функций

85

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

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

Особенности прямоугольника

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

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

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

  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 . (Обратите внимание, что это эффективно предотвратит соприкосновение элементов с правой и / или нижней частью подрамника.)

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

Пол Ламмерцма
источник
Почему я получаю count = 114829 при запуске вашего кода?
Ники
Почему ваши петли x / y начинаются с 1? Я предполагаю, что x / y - это верхняя левая координата прямоугольника функции. Тогда не должны ли x / y начинаться с 0/0?
Ники
Помимо того, начинается ли он с 0 или 1, заканчивается на x < sizeпредположении №4: я хочу, чтобы функция оставалась в подкадре, но имела размер не менее 1x1. Что касается того, не должен ли размер функции выходить за пределы подкадра, ну, возможно, это тоже предположение.
Paul Lammertsma
Точно так же, если бы я начал x с 0, он должен был бы дойти до x < size - 1, поэтому нет никакого выигрыша.
Paul Lammertsma
Я сделал миллион петель. мне это кажется неправильным. <size будет препятствовать тому, чтобы x когда-либо стал 24, начиная с 0, вы получите 0 ... 23, с размером в 1 пиксель прямоугольник никогда не выйдет за пределы кадра.
Breton

Ответы:

41

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

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

#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
Амро
источник
Убедительно. Настолько убедительно, что я почти уверен, что мы правы. Я отправил автору письмо по электронной почте, чтобы узнать, не допустил ли я фундаментальную ошибку в своих рассуждениях. Посмотрим, найдется ли у занятого парня время ответить.
Paul Lammertsma
имейте в виду, что эта штука вышла уже пару лет, и с тех пор было сделано много улучшений
Amro
26
Оригинальный документ, в котором было указано 180k, взят из материалов конференции 2001 года по компьютерному зрению и распознаванию образов. В переработанной статье, принятой в 2003 г. и опубликованной в Международном журнале компьютерного зрения в 2004 г., на стр. 139 (конец раздела 2): «исчерпывающий набор прямоугольников довольно велик, 160 000». Похоже, мы были правы!
Paul Lammertsma
4
Отлично, спасибо за обновление. Для тех, кому интересно, я нашел ссылку на статью IJCV'04: Lear.inrialpes.fr/people/triggs/student/vj/viola-ijcv04.pdf
Амро
Да это оно. 160к, а не 180к.
Paul Lammertsma
9

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

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

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

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

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

Лаома из Сингапура
источник
3

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

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

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

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

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

Бретонский
источник
Я должен включить сноску после слова «overcomplete»: «Полная основа не имеет линейной зависимости между базовыми элементами и имеет то же количество элементов, что и пространство изображения, в данном случае 576. Полный набор из 180 000 тысяч функций во много раз превосходит- завершено. " Они явно не избавляются от классификаторов без поверхности, они используют AdaBoost, чтобы определить, что «очень небольшое количество этих функций может быть объединено, чтобы сформировать эффективный классификатор». Хорошо, поэтому объекты с нулевой поверхностью будут немедленно отброшены, но зачем их вообще рассматривать?
Paul Lammertsma
Что ж, это звучит как рассуждение кого-то, кто действительно увлекается теорией множеств.
Breton
Согласен, исчерпывающий набор подразумевает все возможности. Но учтите, что если вы возьмете от 1 до 24 для x и width <= x, функция расширит на 1 пиксель за пределы подкадра!
Paul Lammertsma
Вы уверены, что ваш код не изобилует ошибками "по одному"? Я только что присмотрелся, и у вас наверняка есть забавный способ написать цикл for.
Breton
Я должен уточнить это - я просто немного подумал, и если у вас есть прямоугольник высотой 1 пиксель, высотой 2 пикселя, высотой 3 пикселя, вплоть до 24 пикселей, у вас есть 24 вида прямоугольников, все которые помещаются в подкадр высотой 24 пикселя. Какие свесы?
Breton
2

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

Майкл Диллон
источник
Эксперименты показывают, что он работает точно так же. Я считаю, что AdaBoost просто отбрасывает эти дополнительные функции с нулевой поверхностью в первом цикле, но я на самом деле не рассматривал это.
Paul Lammertsma
Виола и Джонс - очень известные имена в области компьютерного зрения. Фактически, именно эта статья считается основополагающей. Все делают ошибки, но доказано, что именно этот алгоритм работает очень хорошо.
Дима
1
Определенно, и я совершенно не сомневаюсь в их методе. Это эффективно и работает очень хорошо! Теория верна, но я считаю, что они могли ошибочно обрезать свой детектор на один пиксель и включить ненужные элементы с нулевой поверхностью. Если нет, я призываю вас продемонстрировать 180 тысяч функций!
Paul Lammertsma
Дело в том, что все люди. Все совершают ошибки. Когда громкое имя допускает ошибки, они часто скрываются из поколения в поколение, потому что люди боятся подвергнуть сомнению полученную мудрость. Но настоящая наука следует научным методам и никому не поклоняется, независимо от того, насколько велико их имя. Если это наука, то простые смертные могут приложить усилия, понять, как она работает, и адаптировать ее к своим обстоятельствам.
Майкл Диллон
Посмотрим; Я отправил автору письмо по электронной почте.
Paul Lammertsma
1

Неплохое наблюдение, но они могут неявно обнулить кадр 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)
Бојан Матовски
источник
Почему вы используете 5 функций, только 4 размещены в основном вопросе. Но все равно спасибо за версию на питоне.
Kasparov92
0

В своей оригинальной статье 2001 года они только заявляют, что используются три типа функций:

мы используем три вида функций

Также

Регионы имеют одинаковый размер и форму.

Поскольку каждый вид имеет две ориентации, разумно предположить, что они используют в общей сложности 6 функций (по крайней мере, для вычисления общего числа функций): 2 двухпрямоугольных объекта, 2 трех прямоугольных объекта и 2 четырехугольных объекта. Исходя из этого предположения, действительно существует более 180000 функций:

feature_types = [(1,2), (2,1), (1,3), (3,1), (2,2), (2,2)]
window_size = (24,24)

total_features = 0
for f_type in feature_types:
    for f_height in range(f_type[0], window_size[0] + 1, f_type[0]):
        for f_width in range(f_type[1], window_size[1] + 1, f_type[1]):
            total_features += (window_size[0] - f_height + 1) * (window_size[1] - f_width + 1)
            
print(total_features)
# 183072

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

Андреас К.
источник