Понимание реализации OpenCV LBP

Мне нужна помощь в распознавании лиц на основе LBP, и именно поэтому я пишу это.

У меня есть следующие вопросы, связанные с распознаванием лиц, реализованным в OpenCV:

  1. В lbpCascade_frontal_face.xml (это из opencv): что такое internalNodes, leafValues, tree,features и т. д.? Я знаю, что они используются в алгоритме. Но я не понимаю смысла каждого из них. Например, почему мы берем одну функцию, а не другую для определенного этапа? как мы решаем, какую функцию/узел выбрать?
  2. Что такое значения функций в LBP_frontal_face_classifier.xml? Я знаю, что это вектор из 4 целых чисел. Но как мне использовать эти функции? Я думал, что этап 0 имеет доступ к первой функции, но доступ не соответствует этому шаблону. Каков шаблон доступа к этим функциям?

  3. Все статьи в литературе дают только обзор высокого уровня. Их описания в основном состоят из расчета LBP по соседним пикселям. Но как эти значения LBP используются против этих элементов в классификаторе?

  4. Как интегральное изображение помогает в вычислении значения LBP пикселя? Я знаю, как используется HAAR. Мне нужно понять LBP.

Я читал какие-то газеты, статьи. Но ни один из них четко не описывает, как работает распознавание лиц на основе LBP, или алгоритм в деталях. Если кто-то хочет разработать программу распознавания лиц самостоятельно, какие шаги он должен выполнить — ни в одном документе это не описано.

Пожалуйста, помогите мне в этом, если можете. Буду признателен.


person Luniam    schedule 21.03.2014    source источник


Ответы (1)


Я отсылаю вас к моему собственному ответу из прошлого который слегка затрагивает тему, но не объясняет каскадный формат XML.

Давайте посмотрим на фальшивый, измененный для четкости пример каскада только с одной стадией и тремя функциями.

<!-- stage 0 -->
<_>
  <maxWeakCount>3</maxWeakCount>
  <stageThreshold>-0.75</stageThreshold>
  <weakClassifiers>
    <!-- tree 0 -->
    <_>
      <internalNodes>
        0 -1 3 -67130709 -21569 -1426120013 -1275125205 -21585
        -16385 587145899 -24005</internalNodes>
      <leafValues>
        -0.65 0.88</leafValues></_>
    <!-- tree 1 -->
    <_>
      <internalNodes>
        0 -1 0 -163512766 -769593758 -10027009 -262145 -514457854
        -193593353 -524289 -1</internalNodes>
      <leafValues>
        -0.77 0.72</leafValues></_>
    <!-- tree 2 -->
    <_>
      <internalNodes>
        0 -1 2 -363936790 -893203669 -1337948010 -136907894
        1088782736 -134217726 -741544961 -1590337</internalNodes>
      <leafValues>
        -0.71 0.68</leafValues></_></weakClassifiers></_>

Несколько позже....

<features>
  <_>
    <rect>
      0 0 3 5</rect></_>
  <_>
    <rect>
      0 0 4 2</rect></_>
  <_>
    <rect>
      0 0 6 3</rect></_>
  <_>
    <rect>
      0 1 4 3</rect></_>
  <_>
      <rect>
      0 1 3 3</rect></_>

...

Давайте сначала посмотрим на теги этапа:

  • The maxWeakCount for a stage is the number of weak classifiers in the stage, what is called in the comments a <!-- tree --> and what I call an LBP feature.
    • In this example, the number of LBP features in stage 0 is 3.
  • The stageThreshold is what the weights of the features must add up to at least for the stage to pass.
    • In this example the stage threshold is -0.75.

Переходя к тегам, описывающим функцию LBP:

  • The internalNodes are an array of 11 integers. The first two are meaningless for LBP cascades. The third is the index into the <features> table of <rect>s at the end of the XML file (A <rect> describes the geometry of the feature). The last 8 values are eight 32-bit values which together constitute the 256-bit LUT I spoke of in my earlier answer. This LUT is computed by the training process, which I don't fully understand myself.
    • In this example, the first feature of the stage references rectangle 3, which is described by the four integers 0 1 4 3.
  • The leafValues are the two weights (pass/fail) associated with a feature. Depending on the bit selected from the internalNodes during feature evaluation, one of those two weights is added to a total. This total is compared to the stage's <stageThreshold>. Then, bool stagePassed = (sum >= stageThreshold - EPS);, where EPS is 1e-5, determines whether the stage has passed or failed. The weights are also determined by the training process.
    • In this example the first feature's fail weight is -0.65 and the pass weight is 0.88.

Наконец, тег <feature>. Он состоит из массива <rect> тегов, содержащих 4 целых числа, описывающих геометрию объекта. Для окна обработки (в вашем случае 24 x 24) первые два целых числа описывают его x и y целых пикселей смещение в пределах окна обработки, а следующие два целых числа описывают ширину. и высота одного подпрямоугольника из 9, необходимых для оценки функции LBP.

По сути, тег <rect> ft.x ft.y ft.width ft.height </rect>, расположенный в окне обработки pW.widthxpW.height, проверяющий, присутствует ли лицо в pW.xxpW.y, соответствует...

http://i.stack.imgur.com/NL0XX.png

Затем, чтобы оценить LBP, достаточно прочитать интегральное изображение в точках p[0..15] и использовать p[BR]+p[TL]-p[TR]-p[BL] для вычисления интеграла девяти подпрямоугольников. центральный подпрямоугольник, R4, сравнивается с восемью другими, по часовой стрелке, начиная с R0, для получения 8-битного LBP (биты упакованы [msb 01258763 lsb]).

Этот 8-битный LBP затем используется в качестве индекса в (2 ^ 8 = 256)-битном LUT функции (<internalNodes>), выбирая один бит. Если этот бит равен 1, функция несовместима с лицом; если 0, это соответствует лицу. Затем возвращается соответствующий вес (<leafNode>) и добавляется к весам всех других признаков для получения общей суммы этапов. Затем это сравнивается с <stageThreshold>, чтобы определить, пройден ли этап или нет.

Если есть что-то еще, что я не объяснил достаточно хорошо, я могу уточнить.

person Iwillnotexist Idonotexist    schedule 21.03.2014
comment
Большое спасибо!!! Спасибо за ваш очень подробный ответ, который описывает много вещей. У меня есть еще несколько вопросов. Во-первых, как один бит вычисляется из LUT? Во-вторых, поскольку наш xml-файл создается на основе изображения 24x24, мы можем обнаружить только лица размером 24x24, верно? Вот почему мы масштабировали изображение. Допустим, у нас есть изображение 40x40, состоящее из одного большого лица и маленького лица размером 24x24 пикселя в углу. - person Luniam; 24.03.2014
comment
В этом случае LBP будет рассчитываться поблочно, а при поблочном расчете LBP маленькое изображение 24x24 никогда не будет обнаружено. Тогда как он может обнаружить это маленькое лицо? Я считаю, что вы могли бы определенно прояснить это. Пожалуйста помоги. - person Luniam; 24.03.2014
comment
@user550762 user550762 Бит извлекается следующим образом. uint32_t LUT[8]; int bit = (LUT[LBP >> 5] >> (LBP & 31))&1;, где LUT — соответствующая таблица из восьми целых чисел. - person Iwillnotexist Idonotexist; 24.03.2014
comment
@user550762 user550762 Каскад LBP обнаруживает в заданном масштабированном изображении только лица размером 24x24, плюс-минус 10% размера. Он также сообщает координату верхнего левого угла лица, а не его центр! Приведем практический пример: если у вас есть изображение 64 x 64 с одним лицом 40 x 40 в нижнем левом углу и одним лицом 24 x 24 в верхнем правом углу, каскад LBP обнаружит одно лицо в (row=0, col=40), и упустил бы лицо в (row=24, col=0). - person Iwillnotexist Idonotexist; 24.03.2014
comment
@ user550762 Ах да, и пока я этим занимаюсь, биты LBP вычисляются по отношению int(Rx) >= int(R4). Таким образом, LBP - это что-то вроде строк LBP = ((int(R0) >= int(R4)) << 7) | ((int(R1) >= int(R4)) << 6) | ((int(R2) >= int(R4)) << 5) | ((int(R5) >= int(R4)) << 4) | ((int(R8) >= int(R4)) << 3) | ((int(R7) >= int(R4)) << 2) | ((int(R6) >= int(R4)) << 1) | ((int(R3) >= int(R4)) << 0); - person Iwillnotexist Idonotexist; 24.03.2014
comment
Спасибо большое. Еще одна вещь: если у нас есть большое лицо, и мы можем определить лицо, используя поблочное вычисление LBP, зачем нам масштабировать изображение? Это дополнительные накладные расходы. - person Luniam; 24.03.2014
comment
@user550762 user550762 Потому что в целом невозможно оценить блочные интегралы для любого конкретного масштаба, используя таблицу интегралов, рассчитанную для одного. Подумайте об этом: если бы у вас был интеграл для масштаба x1, как именно вы использовали бы его для вычисления блочных интегралов масштаба x1,21? С другой стороны, обычно намного быстрее вычислять пирамиды изображений, особенно. поскольку такие вещи, как GPU, могут выполнять масштабирование за вас почти бесплатно. - person Iwillnotexist Idonotexist; 24.03.2014
comment
Все в порядке. Мой вопрос заключался в том, зачем нам вообще нужна пирамида, потому что я думал, что для больших изображений не работают пиксели, а работают блоки 24x24. Но я обнаружил, что для больших изображений они также работают в подокнах размером 24x24 пикселя. В вашей индексации LUT вы использовали некоторую формулу. Как вы определяете эту формулу? Почему вы используете 5, а не какие-то другие значения? Более того, что означают значения LUT? почему мы берем определенное значение, а затем берем определенный бит? как они связаны с кодом LBP? - person Luniam; 25.03.2014
comment
Поскольку нецелесообразно масштабировать функцию size и показания интеграла изображения, вместо этого изменяют размеры изображений таким образом, чтобы лица выглядели размером 24x24 в интересных масштабах. Например, если на изображении VGA (640x480) необходимо обнаружить лица размером 72x72, необходимо уменьшить размер кадра VGA в (72/24) = 3 раза, чтобы предполагаемые лица могли быть обнаружены с помощью тренированный каскад. Размер уменьшенного изображения должен быть размером ~213 x 160 (640/3 x 480/3), чтобы лица размером 72 x 72 выглядели как 24 x 24 (72/3 x 72/3). - person Iwillnotexist Idonotexist; 25.03.2014
comment
Формула представляет собой классический поиск по битовому индексу. Он разбивает 8-битный LBP на старшую 3-битную часть и младшую 5-битную часть. Старшая 3-битная часть может находиться в диапазоне от 0 до 2^3-1 == 7 и используется для выбора одного из восьми 32-битных целых чисел в интересующей нас LUT. Младшая 5-битная часть может находиться в диапазоне от 0 до 2^5-1 == 31 и используется для выберите один бит из тридцати двух в указанном выше целом числе. Поскольку отдельные биты не адресуются, бит извлекается путем сдвига целого числа вправо на значение 5-битного селектора, оставляя выбранный бит в битовой позиции 0, откуда он считывается с помощью & 1. - person Iwillnotexist Idonotexist; 25.03.2014
comment
@ user550762 Что касается того, что означают биты в LUT. Если с помощью моей процедуры извлечения битов вы извлекаете из LUT бит LBP'th и обнаруживаете, что он равен 1, то функция несовместима с лицом и оценивается по ее отрицательному вес. Если вы обнаружите, что извлеченный бит равен 0, то объект соответствует лицу и оценивается по его положительному весу. ИОВ, LUT{LBP} == 1 ? fail : pass. - person Iwillnotexist Idonotexist; 25.03.2014
comment
@ Iwillnotexist Idonotexist еще раз спасибо. Пока то, что вы сделали, прекрасно. Я ценю вашу помощь. По последнему вопросу о значении бита LUT. Я знаю, что делает алгоритм — берет конкретный бит и решает, какой вес взять. Но мой вопрос: почему мы рассматриваем эти биты для определения веса? Что на самом деле содержат эти 8 * 32 = 256 бит? Я думаю, это какая-то комбинация LBP - не уверен. - person Luniam; 26.03.2014
comment
@user550762 user550762 Эти биты вычисляются в процессе обучения и указывают, соответствует ли эта конкретная функция лицу (=0) или нет (=1). Например, я бы предположил, что LUT функции, расположенной над глазом, будет иметь 0 в бите 255. Это связано с тем, что при оценке LBP все окружающие участки кожи светлее, чем очень темный зрачок глаза, а это означает, что LBP является 11111111 двоичным = 255 десятичным, и поскольку это локальное распределение яркости согласуется с (частью) лица, бит по этому смещению в LUT равен 0. - person Iwillnotexist Idonotexist; 26.03.2014
comment
@ Iwillnotexist Idonotexist Большое спасибо. Я очень ценю вашу помощь. Я не могу отблагодарить вас за усилия, которые вы приложили, чтобы ответить на мои вопросы. Именно благодаря таким парням, как ты, другие могут что-то понять. Многим людям помогут ваши ответы. Еще раз спасибо. - person Luniam; 26.03.2014
comment
@IwillnotexistIdonotexist Не могли бы вы уточнить эту строку: uint32_t LUT[8]; int bit = (LUT[LBP >> 5] >> (LBP & 31))&1;? Я видел, что некоторые числа в файле могут быть отрицательными. Значит, мы должны просто слепо приводить их к uint8? Спасибо. - person warmspringwinds; 03.06.2015
comment
@warmspringwinds Ваша тяга верна: отрицательные значения просто означают, что MSB установлен. Возможно, мне сошло с рук использование int32_t, но я решил напрямую преобразовать массив в uint32_t, потому что я уверен, что сдвиги 31 прямо на uint32_t четко определены и предсказуемо дадут правильный результат на всех архитектурах. - person Iwillnotexist Idonotexist; 03.06.2015
comment
@IwillnotexistIdonotexist. Я просто написал весь скрипт и использовал обученный файл opencv lbp для фронтальных лиц. Я пытался протестировать его на изображениях лиц из vec файла OpenCV, на котором, как я думал, этот файл обучался, но все лица не проходят дальше 3-го этапа. Вы знаете, на каких лицах я могу протестировать свою реализацию? потому что я не уверен, проблема ли это в масштабе или в моей реализации. - person warmspringwinds; 03.06.2015
comment
@IwillnotexistIdonotexist, я прав, чтобы быть обнаруженным, лицо должно пройти все этапы каскада? - person warmspringwinds; 03.06.2015
comment
@warmspringwinds Да, все этапы должны быть пройдены, а лица должны быть в масштабе 24x24, чтобы их можно было обнаружить. - person Iwillnotexist Idonotexist; 03.06.2015
comment
@IwillnotexistIdonotexist Спасибо за помощь! Извините, что бомбил вас вопросом, но я борюсь с ним целый день. Вы уверены, что, когда вы говорите о lut: что 0 соответствует лицу, а «1» — нет? Или, может быть, вы можете связать рабочую реализацию, с которой я могу сравнить свой код? - person warmspringwinds; 04.06.2015
comment
@IwillnotexistIdonotexist Потому что в исходном коде Opencv я вижу эту вещь idx = (subset[c>>5] & (1 << (c & 31))) ? node.left : node.right;. Или он идентичен? - person warmspringwinds; 04.06.2015
comment
@warmspringwinds Мои описания взяты из моего понимания кода OpenCV. И вы действительно в нужном месте: обратите внимание, что node.left соответствует левому (первому) конечному значению; Это тот, который отрицательный, а также тот, который выбирается, если бит в LUT был установлен. Затем условное выражение оценивается как ненулевое (истина) и выбирается node.left. Значение правого (второго) листа положительное и выбирается, когда бит в LUT не установлен. - person Iwillnotexist Idonotexist; 04.06.2015
comment
@IwillnotexistIdonotexist . Спасибо. Я сделал все в Python, но это не работает. Сейчас я пытаюсь проверить это на изображениях в разных масштабах. Я просто взял лицо в большой ограничивающей рамке и уменьшил размер этой рамки на каждом шаге, а затем изменил размер до 24x24. По некоторым шкалам доходит до 17-й стадии, но ни одно из изображений не проходит. Это правильный способ проверить это? - person warmspringwinds; 04.06.2015
comment
@warmspringwinds Я обнаружил, что детектор лица LBP довольно чувствителен к размеру и представлению лица. Голова или лицо должны быть размером примерно 24x24 пикселя (от подбородка до макушки или линии роста волос), должны быть наклонены или повернуты не более чем на 10-15º относительно плоскости. Если вы получаете слишком ранние выходы, попробуйте отрезать последние 3-5 этапов (просто не запускать их) и посмотреть, находятся ли оставшиеся выжившие там, где вы их ожидаете. Если да, то возможно каскад, описанный в XML, просто слишком избирательен для ваших нужд. - person Iwillnotexist Idonotexist; 04.06.2015
comment
@IwillnotexistIdonotexist Большое спасибо за это! Однако он пропускает много лиц. Даже лица, которые сам Opencv обнаружил с помощью lbp. - person warmspringwinds; 04.06.2015
comment
@IwillnotexistIdonotexist Я только что написал скользящее окно и масштабирование. И я предполагаю, что проблема в том, что когда я нахожусь в масштабе лица на изображении, само изображение размыто в этом масштабе и не имеет достаточного контраста. Может ли это быть причиной? - person warmspringwinds; 04.06.2015
comment
@warmspringwinds Возможно. Каков ваш скользящий шаг? Для мелких масштабов шаг, на который вы двигаетесь, должен быть равен 1 (каждый пиксель). Стратегия, которую вы используете для изменения размера изображения, также является фактором; Если вы линейно измените размер большого изображения на крошечное, вы можете значительно усилить шум. Вместо этого вы должны pyrDown, пока вы не станете менее чем в 2 раза больше целевого размера, а затем линейно изменить размер до целевого размера. - person Iwillnotexist Idonotexist; 05.06.2015
comment
@IwillnotexistIdonotexist Мой шаг составляет 1 пиксель. Я также пробовал то, что вы рекомендовали, с гауссовой пирамидой, но все то же самое. Я предполагаю, что это проблема с моим кодом оценки этапа. Вы можете взглянуть на это? Было бы здорово, если бы вы указали, где могут быть возможные проблемы. Код действительно короткий. Если я заставлю это работать, я думаю, мы сможем обновить ваш ответ, чтобы помочь другим людям, которые могут застрять. gist.github.com/warmspringwinds/516772c7ddedf5b579b9 - person warmspringwinds; 05.06.2015
comment
@IwillnotexistIdonotexist Я нашел ошибку :) Извините за беспокойство. Что вы думаете об обновлении своего ответа позже? - person warmspringwinds; 05.06.2015
comment
@warmspringwinds Если вы сделаете редактирование со вкусом, я буду рад одобрить его и получить +2 репутации! - person Iwillnotexist Idonotexist; 05.06.2015
comment
@IwillnotexistIdonotexist Я обновил ответ. Я надеюсь, что вы найдете это полезным. Теперь у меня есть другая проблема: когда я делаю обнаружение по нескольким шкалам, я получаю большое количество ложных срабатываний. Я видел, что у OpenCV есть параметр minNeighbors в их детекторе. Я предполагаю, что они оставляют только те прямоугольники, у которых достаточно соседей. Не могли бы вы немного подробнее рассказать об этом, если знаете об этом? - person warmspringwinds; 28.06.2015
comment
Просто хочу сказать спасибо за этот ответ, он был невероятно полезен при попытке понять каскадный файл lbp. Я хотел упомянуть, однако, я встречал много людей, говорящих, что первые два значения в узле ничего не значат в lbp, но на самом деле они имеют значение. это node.left и node.right, и они являются индексами в leafValues. если есть только один узел, то они равны 0 и -1, но если у вас больше узлов, они будут левым и правым индексами в leafNodes. поправьте меня, если я ошибаюсь, потому что именно так я читаю код opencv - person iedoc; 24.02.2016
comment
@iedoc О, пожалуйста! О node.left и node.right: Да, это правда. Однако они имеют смысл только тогда, когда у вас есть этапы с древовидной структурой. Если у вас есть пни (деревья высотой 1, в которых сбой этапа приводит к сбою каскада), как в случае с каскадами по умолчанию, предоставляемыми OpenCV, такими как lbpcascade_frontalface.xml, то они действительно бессмысленны. Этапы с древовидной структурой более сложны, поскольку дают сценам второй шанс искупить свою вину. - person Iwillnotexist Idonotexist; 24.02.2016
comment
ах, я вижу, поэтому я думаю, что реализация opencv lbp не использует более одного узла в каждом слабом классификаторе, поэтому имеет смысл, почему я продолжаю видеть, как люди говорят, что первые два значения на самом деле не имеют значения в реализации lbp. я думаю, что был неправ, когда сказал, что первые два значения были индексами в листовых узлах, тогда они действительно являются индексами в узлах (если бы lbp использовал больше узлов для ветвления)? - person iedoc; 24.02.2016
comment
@iedoc Честно говоря, я и сам не слишком уверен. Я никогда не видел, чтобы они использовались на практике; Я предполагаю, что -1 означает отсутствие дочернего элемента, а i>=0 означает наличие дочернего элемента i в этой ветке. - person Iwillnotexist Idonotexist; 25.02.2016
comment
Спасибо всем за объяснение этого материала. Как-то связанно: что в основном обнаруживает detectMultiscale? Насколько я понимаю, он вычисляет несколько масштабов (скажем, 11) в зависимости от параметров scaleFactor и min/max size(). На каждой из этих шкал он скользит, например. Окно детектора размером 24x24 пикселя над исходным изображением. Это правильно ? Итак, для изображения 424x424 нам нужно протестировать (424-24)x(424-24) подизображения, и это на всех 11 масштабах. Это правильно? Есть ли какие-нибудь хитрости, как снизить вычислительные требования, например, увеличить шаг или что-то другое? - person Chris; 19.12.2019
comment
Я также задал отдельный вопрос по этой теме здесь. Поскольку это немного отличается, пожалуйста, ответьте здесь: stackoverflow.com/questions/59404386 - person Chris; 19.12.2019