多邊形區域填充演算法 掃瞄線種子填充演算法

2021-08-31 08:54:29 字數 3620 閱讀 2042

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!

1.3掃瞄線種子填充演算法

1.1和1.2節介紹的兩種種子填充演算法的優點是非常簡單,缺點是使用了遞迴演算法,這不但需要大量棧空間來儲存相鄰的點,而且效率不高。為了減少演算法中的遞迴呼叫,節省棧空間的使用,人們提出了很多改進演算法,其中一種就是掃瞄線種子填充演算法。掃瞄線種子填充演算法不再採用遞迴的方式處理「4-聯通」和「8-聯通」的相鄰點,而是通過沿水平掃瞄線填充畫素段,一段一段地來處理「4-聯通」和「8-聯通」的相鄰點。這樣演算法處理過程中就只需要將每個水平畫素段的起始點位置壓入乙個特殊的棧,而不需要象遞迴演算法那樣將當前位置周圍尚未處理的所有相鄰點都壓入堆疊,從而可以節省堆疊空間。應該說,掃瞄線填充演算法只是一種避免遞迴,提高效率的思想,前面提到的注入填充演算法和邊界填充演算法都可以改進成掃瞄線填充演算法,下面介紹的就是結合了邊界填充演算法的掃瞄線種子填充演算法。

掃瞄線種子填充演算法的基本過程如下:當給定種子點(x, y)時,首先分別向左和向右兩個方向填充種子點所在掃瞄線上的位於給定區域的乙個區段,同時記下這個區段的範圍[xleft, xright],然後確定與這一區段相連通的上、下兩條掃瞄線上位於給定區域內的區段,並依次儲存下來。反覆這個過程,直到填充結束。

掃瞄線種子填充演算法可由下列四個步驟實現:

(1) 初始化乙個空的棧用於存放種子點,將種子點(x, y)入棧;

(2) 判斷棧是否為空,如果棧為空則結束演算法,否則取出棧頂元素作為當前掃瞄線的種子點(x, y),y是當前的掃瞄線;

(3) 從種子點(x, y)出發,沿當前掃瞄線向左、右兩個方向填充,直到邊界。分別標記區段的左、右端點座標為xleft和xright;

(4) 分別檢查與當前掃瞄線相鄰的y - 1和y + 1兩條掃瞄線在區間[xleft, xright]中的畫素,從xleft開始向xright方向搜尋,若存在非邊界且未填充的畫素點,則找出這些相鄰的畫素點中最右邊的乙個,並將其作為種子點壓入棧中,然後返回第(2)步;

這個演算法中最關鍵的是第(4)步,就是從當前掃瞄線的上一條掃瞄線和下一條掃瞄線中尋找新的種子點。這裡比較難理解的一點就是為什麼只是檢查新掃瞄線上區間[xleft, xright]中的畫素?如果新掃瞄線的實際範圍比這個區間大(而且不連續)怎麼處理?我查了很多計算機圖形學的書籍和**,好像都沒有對此做過特殊說明,這使得很多人在學習這門課程時對此有揮之不去的疑惑。本著「毀人」不倦的思想,本文就羅嗦解釋一下,希望能解除大家的疑惑。

如果新掃瞄線上實際點的區間比當前掃瞄線的[xleft, xright]區間大,而且是連續的情況下,演算法的第(3)步就處理了這種情況。如圖(4)所示:

圖(4) 新掃瞄線區間增大且連續的情況

假設當前處理的掃瞄線是黃色點所在的第7行,則經過第3步處理後可以得到乙個區間[6,10]。然後第4步操作,從相鄰的第6行和第8行兩條掃瞄線的第6列開始向右搜尋,確定紅色的兩個點分別是第6行和第8行的種子點,於是按照順序將(6, 10)和(8, 10)兩個種子點入棧。接下來的迴圈會處理(8, 10)這個種子點,根據演算法第3步說明,會從(8, 10)開始向左和向右填充,由於中間沒有邊界點,因此填充會直到遇到邊界為止,所以儘管第8行實際區域比第7行的區間[6,10]大,但是仍然得到了正確的填充。

如果新掃瞄線上實際點的區間比當前掃瞄線的[xleft, xright]區間大,而且中間有邊界點的情況,演算法又是怎麼處理呢?演算法描述中雖然沒有明確對這種情況的處理方法,但是第4步確定上、下相鄰掃瞄線的種子點的方法,以及靠右取點的原則,實際上暗含了從相鄰掃瞄線繞過障礙點的方法。下面以圖(5)為例說明:

圖(5) 新掃瞄線區間增大且不連續的情況

演算法第3步處理完第5行後,確定了區間[7, 9],相鄰的第4行雖然實際範圍比區間[7, 9]大,但是因為被(4, 6)這個邊界點阻礙,使得在確定種子點(4, 9)後向左填充只能填充右邊的第7列到第10列之間的區域,而左邊的第3列到第5列之間的區域沒有填充。雖然作為第5行的相鄰行,第一次對第4行的掃瞄根據靠右原則只確定了(4, 9)乙個種子點。但是對第3行處理完後,第4行的左邊部分作為第3行下邊的相鄰行,再次得到掃瞄的機會。第3行的區間是[3, 9],向左跨過了第6列這個障礙點,第2次掃瞄第4行的時候就從第3列開始,向右找,可以確定種子點(4, 5)。這樣第4行就有了兩個種子點,就可以被完整地填充了。

由此可見,對於有障礙點的行,通過相鄰邊的關係,可以跨越障礙點,通過多次掃瞄得到完整的填充,演算法已經隱含了對這種情況的處理。根據本節總結的四個步驟,掃瞄線種子填充演算法的實現如下:

263 void scanlineseedfill(int x,int y, int new_color, int boundary_color)

264

283 }

filllineright()和filllineleft()兩個函式就是從種子點分別向右和向左填充顏色,直到遇到邊界點,同時返回填充的點的個數。這兩個函式返回填充點的個數是為了正確調整當前種子點所在的掃瞄線的區間[xleft, xright]。searchlinenewseed()函式完成演算法第4步所描述的操作,就是在新掃瞄線上尋找種子點,並將種子點入棧,新掃瞄線的區間是xleft和xright引數確定的:

234 void searchlinenewseed(std::stack& stk,int xleft, int xright,

235                       int y, int new_color, int boundary_color)

236

248         if(findnewseed)

249        

255

256         /*向右跳過內部的無效點(處理區間右端有障礙點的情況)*/

257         int xspan = skipinvalidinline(xt, y, xright, new_color, boundary_color);

258         xt += (xspan ==0) ? 1 : xspan;

259         /*處理特殊情況,以退出while(x<=xright)迴圈*/

260     }

261 }

最外層的while迴圈是為了保證區間[xleft, xright]右端被障礙點分隔成多段的情況能夠得到正確處理,通過外層while迴圈,可以確保為每一段都找到乙個種子點(對於障礙點在區間左端的情況,請參考圖(5)所示例項的解釋,是隱含在演算法中完成的)。內層的while迴圈只是為了找到每一段最右端的乙個可填充點作為種子點。skipinvalidinline()函式的作用就是跳過區間內的障礙點,確定下乙個分隔段的開始位置。迴圈內的最後一行**有點奇怪,其實只是用了乙個小「詭計」,確保在遇到真正的邊界點時迴圈能夠正確退出。這不是乙個值得稱道的做法,實現此類軟體控制有更好的方法,本文這樣做的目的只是為了使**簡短一些,讓讀者把注意力集中在演算法處理邏輯上,而不是冗雜難懂的迴圈控制條件上。

演算法的實現其實就在scanlineseedfill()和searchlinenewseed()兩個函式中,神秘的掃瞄線種子填充演算法也並不複雜,對吧?至此,種子填充演算法的幾種常見演算法都已經介紹完畢,接下來將介紹兩種適合向量圖形區域填充的填充演算法,分別是掃瞄線演算法和邊標誌填充演算法,注意適合向量圖形的掃瞄線填充演算法有時又被稱為「有序邊表法」,和掃瞄線種子填充演算法是有區別的。

給我老師的人工智慧教程打call!

掃瞄線法填充多邊形

如下圖所示多邊形 直線y 1,2,3 8順序掃瞄多邊形,以直線y 3為例,它與多邊形的邊有4個交點,將這4個交點的x座標儲存下來,兩兩之間畫線,也就是 a,b c,d 之間畫線。對於每乙個y都是兩兩之間畫線。現在考慮直線y 2,它與多邊形有3個交點,很顯然是f,g之間畫線,那麼p1怎麼處理呢?可以觀...

掃瞄多邊形填充演算法

掃瞄多邊形填充演算法 在做手機地圖的過程中,由於j2me沒提供多邊形填充的api,只能自己實現了,以下是實現的思路,請批評指正 多邊形填充,就是把多邊形所佔據的柵格象素賦予指定的顏色值。要完成這個任務,乙個首要的問題就是求出多邊形所佔據的柵格象素,判斷乙個網格在多邊形內還是多邊形外,在多邊形內的象素...

掃瞄多邊形填充演算法

多邊形填充,就是把多邊形所佔據的柵格象素賦予指定的顏色值。要完成這個任務,乙個首要的問題就是求出多邊形所佔據的柵格象素,判斷乙個網格在多邊形內還是多邊形外,在多邊形內的象素,則賦予指定的顏色值,多邊形外的象素,則不賦予指定的顏色值,具體該如何判斷象素是否在多邊形內呢?這裡我們採用 掃瞄線多邊形填充演...