Адаптиране на запълването на линията на сканиране за откриване на отделни обекти

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

Stack Overflow и Wikipedia посочват запълването на сканирана линия като отговор, но всяко обяснение, което открих, е или в C++, или за запълване на многоъгълник с известни върхове. Може ли някой да ме насочи към добро обяснение на псевдокод за ситуация, аналогична на рекурсивното запълване на наводнение?

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


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


Отговори (1)


Идеята за запълване на сканиращата линия е следната

  1. дадена ви е началната точка (семе) (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
    • ако "look above" е невярно и пикселът НЕ трябва да се запълва, тогава текущата пещера е завършена и трябва да потърсите друга, така че просто маркирайте "look_above" като true
    • в други случаи (поглед отгоре е вярно и пикселът не трябва да се запълва или поглед отгоре е невярно и пикселът трябва да се запълва) просто не правите нищо
  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