Заливка
Заливка (від англ. flood fill чи англ. seed fill) — це алгоритм, що визначає область, «поєднану» з певним елементом у багатомірному масиві (як правило, це двовимірний масив точок растрового зображення). Алгоритм застосовується у програмах для редагування графіки для визначення області, яку треба заповнити певним кольором.
Заливка зображень — часто потрібна на практиці функція, суть якої — заповнити деяку область зображення, обмежену контуром, що заданий певним кольором.
Алгоритм
Алгоритм має три параметри на вхід: стартовий елемент (вузол), замінюваний колір і колір заливки. Відшукуються всі елементи масиву, що зв'язані з початковим шляхом змінюємого кольору, і перефарбовуються у колір заливки. Варіантів реалізації достатньо багато, але всі вони так чи інакше використовують чергу або стек. Ось псевдокод простого рекурсивного алгоритму, в якому зв'язність у двовимірному масиві визначається у 4 напрямах:
Заливка (вузол, замінюваний колір, колір заливки): 1. Якщо замінюваний колір дорівнює кольору заливки, то повернутись. 2. Якщо колір вузла не дорівнює замінюваному кольору, то повернутись. 3. Встановити колір вузла у колір заливки. 4. Виконати Заливку (крок на захід від вузла, замінюваний колір, колір заливки). Виконати Заливку (крок на схід від вузла, замінюваний колір, колір заливки). Виконати Заливку (крок на північ від вузла, замінюваний колір, колір заливки). Виконати Заливку (крок на південь від вузла, замінюваний колір, колір заливки). 5. Повернутися.
Хоча цей алгоритм і простий для розуміння, але він практично не застосовується у випадках обмеженості рекурсії (наприклад, у аплетах Java)
Інші способи реалізації
Наступний псевдокод показує реалізацію, засновану на застосуванні черги в явному вигляді. Це схоже на рекурсивне рішення, за винятком того, що замість рекурсивних викликів, він штовхає вузли в стек:
Заливка (вузол, замінюваний колір, колір заливки): 1. Якщо замінюваний колір дорівнює кольору заливки, то повернутись. 2. Присвоїти Q порожню чергу. 3. Додати вузол у кінець Q. 4. До тих пір, поки Q не порожній: 5. Присвоїти n перший елемент Q 6. Видалити перший елемент Q . 7. Якщо колір n дорівнює замінюваному кольору : 8. Встановити колір n до кольору заливки і відмітити "n" як елемент,що обробляється. 9. Додати західний вузол до кінця Q , якщо західний елемент ще не був оброблений. 10. Додати східний вузол до кінця Q , якщо східний елемент ще не був оброблений. 11. Додати північний вузол до кінця Q , якщо північний елемент ще не був оброблений. 12. Додати південний вузол до кінця Q , якщо південний елемент ще не був оброблений. 13.Повернутися.
Для того, щоб використовувати прапор "оброблено", усі пікселі доведеться ініціалізувати як необроблені перед запитом цього алгоритму. Найбільш практичні методики оптимізують використання стека або черги, вводячи цикл за «західним» і «східним» напрямками:
Заливка (вузол, замінюваний колір, колір заливки): 1. Присвоїти Q порожню чергу. 2. Якщо колір вузла не дорівнює замінюваному кольору, повернутись. 3. Додати вузол у Q . 4. Для кожного елемента від N до Q : 5. Присвоїти w і e дорівнюють N . 6. Переміщайте w на захід, поки колір вузла на захід від w більше не відповідає замінюваному кольору . 7. Переміщайте е на схід, поки колір вузла на схід від е більше не відповідає замінюваному кольору . 8. Для кожного вузла n між w і e : 9. Встановіть колір n як колір заливки 10. Якщо колір вузла на північ від n є замінюваним кольором , додати цей вузол у Q . 11. Якщо колір вузла на південь від n є замінюваним кольором , додати цей вузол у Q . 12. Продовжувати цикл доки Q не закінчиться. 13. Повернутись.
Якщо прописати в алгоритмі використання окремого масиву для зберігання форми області, це дозволить узагальнити його на випадок «нечіткого» заповнення, коли елементи можуть дещо відрізнятися від початково заданих. Використання цього додаткового масиву як альфа-каналу дозволяє створити плавний перехід на кордонах між залитою і не залитою областями.
Метод заливки для фіксованого об'єму пам'яті
Метод заливки, що реалізується в обмеженому об'ємі пам'яті (англ. Fixed memory method), або, як його ще називають, — «метод правої руки».
Опис методу
Є метод, що практично не використовує додаткової пам'яті для зв'язуючих по 4 напрямках (див. Околиця фон Неймана) областей. Цим же методом можна шукати вихід з лабіринту. Уявіть собі маляра, який фарбує область так, щоб не опинитися затиснутим в кутку. Початкові кордони — це чотири пікселя, які маляр розглядає, щоб вибрати одну з можливих дій. Основні можливі стани:
- Всі 4 граничних пікселя пофарбовані.
- Три граничних пікселя пофарбовані.
- Два граничних пікселя пофарбовані.
- Один граничний піксель зафарбований.
- Жоден з граничних пікселів не зафарбований.
При продовженні кордону використовується метод правої руки. Маляр обходить область, тримаючи праву руку на «стіні» (кордоні області) і просувається, не відриваючи руки.
У випадку № 1 маляр фарбує піксель, на якому стояв, і закінчує (алгоритм завершений).
У випадку № 2 існує шлях з області назовні. Маляр фарбує піксель, на якому стояв, і рухається цим шляхом.
У випадку № 3 два граничних пікселя створюють смугу, яка, якщо ми пофарбуємо поточний піксель, може відрізати нас від усього, що знаходиться по її іншу сторону. Потрібно закріпити «стрілку», що вказує, де ми зараз і куди дивимося, щоб, якщо ми колись повернемося на цей піксель, ми могли її побачити. Якщо така стрілка вже намальована, ми її зберігаємо і рухаємося далі за правилом правої руки.
Стрілка на першому двохпіксельному кордоні фіксує, де маляр почав роботу і куди звідти пішов. Якщо маляр зустрічає ту ж мітку, рухаючись в тому ж напрямку, то він розуміє, що фарбувати цей квадрат, рухаючись у цьому напрямку, безпечно: пікселі на іншій стороні по якомусь поки не відомому шляху будуть доступні для фарбування в майбутньому. Мітка знімається, і її можна поставити десь ще.
Якщо ж маляр зустрічає стрілку, що вказує не туди, куди він йде, значить, він пройшов якимось шляхом, що повернув його до мітки. Цю петлю треба виключити. Мітка забирається, а маляр рухається у вказаному нею напрямі, керуючись правилом лівої руки. Так він йде, поки не потрапить на перетин (з не менш ніж трьома пікселями відкритого кордону). Не відриваючи лівої руки, маляр шукає простий прохід (що складається з двох граничних пікселів). Знайшовши його, він фарбує цей піксель, петля розривається, і можна продовжувати алгоритм.
У випадку № 4 ми повинні перевірити протилежні зв'язані по 8 напрямам кути — пофарбовані вони чи ні. Якщо хоча б один з цих двох кутів пофарбований, виходить перетин багатьох шляхів, які ми зафарбувати не зможемо. Якщо обидва порожні, можемо пофарбувати поточний піксель і слідувати далі за правилом правої руки.
Алгоритм дає виграш в пам'яті за рахунок програшу за часом. Для областей простої форми він дуже ефективний, але в більш складних випадках багато часу витрачається на те, щоб відстежити межі області і переконатися, що все можна зафарбувати.
Перша комерційна реалізація цього алгоритму з'явилася в 1981 році на системі Vicom Image Processing, випущеної Vicom Systems, Inc. Також в цій системі був присутній і класичний рекурсивний алгоритм.
Алгоритм (англійською)
Це реалізація псевдокоду, який виконує алгоритм заливки, з фіксованим використанням пам'яті.
Змінні :
cur, mark, and mark2 each hold either pixel coordinates or a null value NOTE: when mark is set to null, do not erase its previous coordinate value. Keep those coordinates available to be recalled if necessary. cur-dir, mark-dir, and mark2-dir each hold a direction (left, right, up, or down) backtrack and findloop each hold boolean values count is an integer
(Примітка: усі напрямки (front, back, left, right) рахуються відносно поточного напрямку)
set cur to starting pixel set cur-dir to default direction clear mark and mark2 (set values to null) set backtrack and findloop to false while front-pixel is empty move forward end while jump to START MAIN LOOP: move forward if right-pixel is empty if backtrack is true and findloop is false and either front-pixel or left-pixel is empty set findloop to true end if turn right PAINT: move forward end if START: set count to number of non-diagonally adjacent pixels filled (front/back/left/right ONLY) if count is not 4 do turn right while front-pixel is empty do turn left while front-pixel is filled end if switch count case 1 if backtrack is true set findloop to true else if findloop is true if mark is null restore mark end if else if front-left-pixel and back-left-pixel are both empty clear mark fill cur jump to PAINT end if end case case 2 if back-pixel is filled if front-left-pixel is not filled clear mark fill cur jump to PAINT end if else if mark is not set set mark to cur set mark-dir to cur-dir clear mark2 set findloop and backtrack to false else if mark2 is not set if cur is at mark if cur-dir is the same as mark-dir clear mark turn around fill cur jump to PAINT else set backtrack to true set findloop to false set cur-dir to mark-dir end if else if findloop is true set mark2 to cur set mark2-dir to cur-dir end if else if cur is at mark set cur to mark2 set cur-dir to mark2-dir clear mark and mark2 set backtrack to false turn around fill cur jump to PAINT else if cur at mark2 set mark to cur set cur-dir and mark-dir to mark2-dir clear mark2 end end if end if end case case 3 clear mark fill cur jump to PAINT end case case 4 fill cur done end case end switch end MAIN LOOP
Метод сканування рядків
Алгоритм можна прискорити, заливаючи відразу лініями. Замість вкладання в стек координат кожного з можливих майбутніх пікселів розглядаються сусідні рядки (ті, що вище і ті, що нижче), і в них визначаються суміжні сегменти, які при наступному проході можна залити; координати (початок чи кінець) втискуються в стек. У більшості випадків рядковий алгоритм на порядок швидше попіксельного алгоритму. Його перевага в тому, що кожен піксель перевіряється тільки один раз.
У векторній графіці
Програма Inkscape версії 0.46 надає інструмент букетної заливки, що виглядає як звичайна растрова операція і в дійсності її застосовує: зображення вимальовується, застосовується заливка вибраної області, і її результат перетворюється назад у векторний вигляд. При цьому використовується концепція граничних умов.
Поведінка при великих розмірах
Основна методика використовується для управління заливкою і може бути орієнтована на данні або на процес. Підхід, що орієнтується на данні може використовувати або чергу, або стек щоб зберігати шлях пікселів, які потребують перевірки. Підхід, що орієнтується на процес має використовувати стек.
Алгоритм заливки у чотирьох напрямах використовує суміжну техніку і чергу як місце для зберігання пікселів та ромбовидно заповнюється.
Ефективність: для заливки одного пікселя перевіряються чотири (вісім, якщо рух іде у восьми напрямках)
Алгоритм заливки у чотирьох напрямах використовує суміжну техніку і стек як місце для зберігання пікселів з лінійною заливкою. Цей алгоритм може бути добре видно у старих 8-бітних іграх, що були створені за допомогою Graphic Adventure Creator.
Ефективність: для заливки одного пікселя перевіряються чотири (вісім, якщо рух іде у восьми напрямках)
Посилання
- Алгоритми заливки зображень, популярно і з відео
- Алгоритми заливки багатокутників
- Алгоритм заливки замкнутого контуру
- Didactical Javascript implementation of scanline polygon fill, Гільєрме Поло.
- C program to implement floodfill algorithm(4-connected boundary)
- Sample implementations for recursive and non-recursive, classic and scanline flood fill, by Lode Vandevenne.
- C implementation of Flood/Seed Fill Algorithm from Graphics Gems; BSD(ish) license, Пол Хекберт.
- Flash flood fill implementation, Емануеле Феронато.
- QuickFill: An efficient flood fill algorithm., Джон Р.Шоу.