Адаптация заполнения линии сканирования для обнаружения отдельных объектов

Я пытаюсь обнаружить в Python смежные области достаточно близких цветов. Я самостоятельно наткнулся на алгоритм рекурсивной заливки с 8 путями (заканчивающийся, когда евклидово расстояние между найденным и желаемым цветами RGB превышает пороговое значение), который отлично работает в небольших масштабах, но вызывает переполнение стека на 2-мегапиксельном изображении.

Переполнение стека и Википедия указывают на заполнение строки сканирования в качестве ответа, но каждое объяснение, которое я нашел, либо на С++, либо на заполнение многоугольника известными вершинами. Может ли кто-нибудь указать мне хорошее объяснение псевдокода для ситуации, аналогичной рекурсивной заливке?

Я упираюсь в стену при исследовании сегментации изображений из-за отсутствия формальной математики (я учусь в старшей школе). Если есть простое объяснение K-средних или что-то в этом роде, это тоже было бы здорово. OpenCV выглядел многообещающе, но, похоже, все, что я получаю, это сглаженное по цвету изображение; все, о чем я забочусь, это список пикселей в объекте в точках x, y.


person closeparen    schedule 29.08.2012    source источник


Ответы (1)


Идея заливки сканлайном заключается в следующем.

  1. вам дана начальная точка (seed) (x, y)
  2. идите влево до тех пор, пока пиксель (x-1, y) не будет заполнен или вы не дойдете до x = 0
  3. x, которого вы достигли, будет началом строки сканирования; оставьте два флага «искать пещеры наверху» и «искать пещеры внизу», оба инициализированы в true
  4. this is the begin of scanline loop. Check the pixel above (x, y-1) if it's to be filled and now considering the look-above flag and the pixel there are 4 cases:
    • if "look above" is true and the pixel is to be filled then you found a "new cavern", store (x, y-1) in the "to-do-list" and mark "look above" as false
    • если «смотреть выше» ложно и пиксель НЕ должен быть заполнен, то текущая пещера заполнена, и вам нужно искать другую, поэтому просто отметьте «смотреть_выше» как истину.
    • в других случаях (смотрите выше верно и пиксель не должен быть заполнен или взгляд выше ложно и пиксель должен быть заполнен) вы просто ничего не делаете
  5. Повторите те же рассуждения с флагом «смотреть ниже» и цветом пикселя (x, y+1).
  6. закрасьте пиксель (x, y) и перейдите к (x+1, y), повторяя с (5), если только пиксель, к которому вы перемещаетесь, не будет окрашен.
  7. если есть что-то в «списке дел», выберите это и вернитесь к (1), используя координаты, которые вы нашли в списке дел, как (x, y)

Это версия для заливки с 4 связями. Для 8-связной заливки вам также необходимо проверить наличие каверн в точках (x-1, y-1) и (x-1, y+1) при запуске строки сканирования, а также вам необходимо проверить наличие каверн (x+1, y -1) и (x+1, y+1) в конце строки развертки (если соответствующие флаги истинны).

При перемещении по строке сканирования вы хотите добавить зеленые точки на картинке в свой список дел:

пример обработки

Обратите внимание, что количество семян не будет «минимальным» (например, первые два «вышеуказанных семени» в примере окажутся в одной и той же пещере, и поэтому действительно нужен только один из них). Тем не менее объем стека, необходимый для их хранения, обычно будет намного меньше, чем объем, необходимый для попиксельного рекурсивного подхода.

Другой возможный подход к ограничению объема необходимой памяти — использование алгоритма рисования границы:

  1. Вы помещаете начальное семя (x, y) в список current_active и рисуете его
  2. Инициализировать список next_active, чтобы он был пустым
  3. для каждого пикселя в списке current_active проверьте наличие соседей, которые нужно закрасить: когда вы найдете один, закрасьте его и добавьте в список next_active
  4. когда вы закончите, установите список current_active в список next_active и повторите с 2, если список не был пустым

Вы можете увидеть пример двух алгоритмов в действии в в этом видео.

person 6502    schedule 29.08.2012
comment
Вау! Спасибо за информацию. Scanline, наконец, теперь имеет смысл. На самом деле меня больше заинтриговал пограничный алгоритм, который кажется итеративным эквивалентом того, что я делал. Почему Python легко обрабатывает список из 30 000+ элементов, но терпит неудачу, когда я пытаюсь использовать глубину рекурсии выше 10 000? - person closeparen; 30.08.2012
comment
На самом деле я пошел с вашим передовым подходом, потому что он казался наиболее близким к первоначальной идее и был достаточно быстрым. Тем не менее интересно, почему это намного быстрее, чем рекурсивный подход... - person closeparen; 30.08.2012
comment
@jacobbaer: CPython не поддерживает неограниченную рекурсию, потому что он полагается на стек C в интерпретаторе, а ограничения на стек C иногда довольно низкие. Вы можете увеличить лимит python с помощью sys.setrecursionlimit, но установив его слишком большим, вы просто сломаете сам python вместо того, чтобы получить исключение. Также обратите внимание, что Python не поддерживает оптимизацию хвостовых вызовов и что чистое представление функционального программирования на самом деле не поддерживается. Рекурсия, конечно, хороша, но глубокая рекурсия (например, использование рекурсии для обхода связанного списка) — плохая идея. - person 6502; 30.08.2012
comment
@ 6502 Как вы проверяете, подали ли вы уже строку, чтобы не проверять пиксель вверх или вниз более одного раза или проверять пиксели более одного неизбежно. - person ; 31.03.2013
comment
При заполнении строки сканирования вы смотрите вверх (т.е. y-1), и каждый раз, когда вы видите новую апертуру, вы помещаете семя, но только одно, пока не увидите, что потолок снова закрыт. Предположим, глядя выше, вы видите 1100001111100011111011111, вам нужно всего лишь поместить семя в третью, двенадцатую и двадцатую позиции. Конечно, возможно, что размещение семени не требуется, потому что несколько отверстий могут быть частью одной и той же пещеры, но вы не можете знать об этом без глобального поиска. - person 6502; 31.03.2013
comment
@ 6502 Tnx, это означает, что некоторые пиксели будут проверяться более одного раза, верно? - person ; 01.04.2013