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

Я внедряю адаптацию алгоритма распознавания лиц Виолы-Джонса. Этот метод основан на размещении внутри изображения подкадра размером 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. (Обратите внимание, что это эффективно предотвратит соприкосновение элементов с правой и / или нижней частью подрамника.)

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


person Paul Lammertsma    schedule 10.11.2009    source источник
comment
Почему я получаю count = 114829 при запуске вашего кода?   -  person Niki    schedule 10.11.2009
comment
Почему ваши петли x / y начинаются с 1? Я предполагаю, что x / y - это верхняя левая координата прямоугольника функции. Тогда не должны ли x / y начинаться с 0/0?   -  person Niki    schedule 10.11.2009
comment
Помимо того, начинается ли он с 0 или 1, заканчивается на x < size, это связано с предположением №4: я хочу, чтобы функция оставалась в подкадре, но имела размер не менее 1x1. Что касается того, не должны ли размеры объекта выходить за пределы подкадра, ну, возможно, это тоже предположение.   -  person Paul Lammertsma    schedule 10.11.2009
comment
Точно так же, если бы я начал x с 0, он должен был бы увеличиться до x < size - 1, поэтому нет никакого выигрыша.   -  person Paul Lammertsma    schedule 10.11.2009
comment
Я сделал миллион петель. мне это кажется неправильным. ‹Size будет препятствовать тому, чтобы x когда-либо стал равным 24, начиная с 0, вы получите 0 ... 23, с размером в 1 пиксель прямоугольник никогда не выйдет за пределы кадра.   -  person Breton    schedule 10.11.2009
comment
Что ж, если x / y начинаются с 0, я получаю 162336. Если я откажусь от предположения №4 и позволю ширине / высоте начинаться с 0, я получу 212256. Интересно, как они получили 180k ...   -  person Niki    schedule 10.11.2009
comment
Ха-ха, молодец, теперь мы все на одной сцене! Хорошая мысль, Бретон, размер никогда не достигнет 24, как есть. Я еще раз посмотрю.   -  person Paul Lammertsma    schedule 10.11.2009
comment
Ага, я тоже не могу довести его до 180 000. вот моя паршивая попытка. jsbin.com/imase/edit (нажмите вкладку вывода для результата, вкладку javascript для исходного кода)   -  person Breton    schedule 10.11.2009
comment
@Breton: Я могу кодировать, чтобы продемонстрировать 180k +, отказавшись от предположения №4 и добавив в код ошибки, которые мы обсуждали: jsbin.com/ibahe/edit   -  person Paul Lammertsma    schedule 10.11.2009
comment
Спасибо, что подняли эту статью, я никогда раньше о ней не слышал. Это действительно здорово.   -  person Ron    schedule 10.11.2009


Ответы (6)


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

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

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

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

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

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

person Laoma from Singapore    schedule 21.07.2010

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

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

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

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

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

person Breton    schedule 10.11.2009
comment
Я должен включить сноску после переполнения: полная основа не имеет линейной зависимости между базовыми элементами и имеет такое же количество элементов, как и пространство изображения, в данном случае 576. Полный набор из 180 000 тысяч функций во много раз переполнен. Они явно не избавляются от классификаторов без поверхности, они используют AdaBoost, чтобы определить, что очень небольшое количество этих функций может быть объединено в эффективный классификатор. Хорошо, поэтому объекты с нулевой поверхностью будут немедленно отброшены, но зачем вообще их рассматривать? - person Paul Lammertsma; 10.11.2009
comment
Что ж, это звучит как рассуждения кого-то, кто действительно увлекается теорией множеств. - person Breton; 10.11.2009
comment
Согласен, исчерпывающий набор подразумевает все возможности. Но учтите, что если вы возьмете от 1 до 24 для x и width ‹= x, функция расширит на 1 пиксель за пределы подкадра! - person Paul Lammertsma; 10.11.2009
comment
Вы уверены, что в вашем коде нет ни одной ошибки? Я только что присмотрелся, и у вас наверняка есть забавный способ написать цикл for. - person Breton; 10.11.2009
comment
Я должен уточнить это - я просто немного подумал, и если у вас есть прямоугольник высотой 1 пиксель, высотой 2 пикселя, высотой 3 пикселя и высотой до 24 пикселей, у вас есть 24 вида прямоугольников, все из которые помещаются в подкадр высотой 24 пикселя. Какие свесы? - person Breton; 10.11.2009
comment
Ты прав; циклы for были неряшливыми. Я перепутал размеры с расположением элемента. Я редактировал это в ОП. Вы правы и насчет свеса: его нет. Единственный способ воспроизвести 180k + - это установить для циклов for ширина и высота, начинающиеся с 0. - person Paul Lammertsma; 10.11.2009
comment
Итак, в итоге, похоже, что Виола и Джонс сочли свой избыточный набор прямоугольных элементов, включив в него элементы с нулевой поверхностью. Вам это кажется логичным? - person Paul Lammertsma; 10.11.2009
comment
В основном, за исключением того, что, как указывает выше, вы начинаете свои координаты x / y с 1 вместо 0, что может объяснить ваше расхождение. - person Breton; 10.11.2009

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

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

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

В своей оригинальной статье 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.

person Andreas K.    schedule 23.06.2020