一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Android - Android多邊形區域掃描線種子填充算法的示例

Android多邊形區域掃描線種子填充算法的示例

2022-02-16 16:23吹泡泡的小貓 Android

這篇文章主要介紹了Android多邊形區域掃描線種子填充算法的示例,具有很好的參考價值,希望對大家有所幫助,一起跟隨小編過來看看吧

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)所示:

Android多邊形區域掃描線種子填充算法的示例

圖(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)為例說明:

 Android多邊形區域掃描線種子填充算法的示例

圖(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行就有了兩個種子點,就可以被完整地填充了。

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
void SearchLineNewSeed(std::stack<Point>& stk, int xLeft, int xRight,
 int y, int new_color, int boundary_color)
 {
 int xt = xLeft;
 bool findNewSeed = false;
 while(xt <= xRight)
 {
 findNewSeed = false;
 
 while(IsPixelValid(xt, y, new_color, boundary_color) && (xt < xRight))
 {
 findNewSeed = true;
 xt++;
 }
 if(findNewSeed)
 {
 if(IsPixelValid(xt, y, new_color, boundary_color) && (xt == xRight))
 
 stk.push(Point(xt, y));
 else
 stk.push(Point(xt - 1, y));
 }
 
 /*向右跳過內部的無效點(處理區間右端有障礙點的情況)*/
 int xspan = SkipInvalidInLine(xt, y, xRight, new_color, boundary_color);
 xt += (xspan == 0) ? 1 : xspan;
 /*處理特殊情況,以退出while(x<=xright)循環*/
 }
}

FillLineRight()和FillLineLeft()兩個函數就是從種子點分別向右和向左填充顏色,直到遇到邊界點,同時返回填充的點的個數。這兩個函數返回填充點的個數是為了正確調整當前種子點所在的掃描線的區間[xLeft, xRight]。

SearchLineNewSeed()函數完成算法第4步所描述的操作,就是在新掃描線上尋找種子點,并將種子點入棧,新掃描線的區間是xLeft和xRight參數確定的:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
void SearchLineNewSeed(std::stack<Point>& stk, int xLeft, int xRight,
 int y, int new_color, int boundary_color)
 {
 int xt = xLeft;
 bool findNewSeed = false;
 while(xt <= xRight)
 {
 findNewSeed = false;
 
 while(IsPixelValid(xt, y, new_color, boundary_color) && (xt < xRight))
 {
 findNewSeed = true;
 xt++;
 }
 if(findNewSeed)
 {
 if(IsPixelValid(xt, y, new_color, boundary_color) && (xt == xRight))
 
 stk.push(Point(xt, y));
 else
 stk.push(Point(xt - 1, y));
 }
 
 /*向右跳過內部的無效點(處理區間右端有障礙點的情況)*/
 int xspan = SkipInvalidInLine(xt, y, xRight, new_color, boundary_color);
 xt += (xspan == 0) ? 1 : xspan;
 /*處理特殊情況,以退出while(x<=xright)循環*/
 }
}

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

算法的實現其實就在ScanLineSeedFill()和SearchLineNewSeed()兩個函數中,神秘的掃描線種子填充算法也并不復雜,對吧?至此,種子填充算法的幾種常見算法都已經介紹完畢,接下來將介紹兩種適合矢量圖形區域填充的填充算法,分別是掃描線算法和邊標志填充算法,注意適合矢量圖形的掃描線填充算法有時又被稱為“有序邊表法”,和掃描線種子填充算法是有區別的。

原文鏈接:https://blog.csdn.net/orbit/article/details/7343236

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 女医学护士一级毛片 | 性夜影院午夜看片 | 国产亚洲精品第一综合linode | 男女男在线精品网站免费观看 | 日韩欧美精品一区二区 | 亚洲精品视频在线免费 | 日本黄色网页 | 四虎影视e456fcom四虎影视 | 三级伦理在线播放 | a级在线看| 日韩高清在线观看 | 国产精品永久免费自在线观看 | 91色视| 国产一区二区视频在线播放 | 调教麻麻成贱m | 人人精品久久 | 午夜无码国产理论在线 | 亚洲免费在线观看 | 国产在线欧美精品 | 无遮18禁在线永久免费观看挡 | 好男人社区www影院在线观看 | 国产馆精品推荐在线观看 | 免费观看在线永久免费xx视频 | 日本wwxx护士 | 色老板在线播放 | 久久精品成人免费网站 | 国产农村一级特黄α真人毛片 | 波多野结衣一区免费作品 | 日韩精品免费一区二区三区 | 99精品久久精品一区二区小说 | 亚洲成人免费看 | 男生同性视频twink在线 | 国产91精品区 | 黑人巨荃大战乌克兰美女 | 女同志freelesvoices | 四虎精品永久在线网址 | 闺蜜调教我做她的脚奴 | 99在线精品日韩一区免费国产 | 99久久er这里只有精品17 | 精品区卡一卡2卡三免费 | 手机看片福利 |