1 0 hai quan điểm về cách tô này, đó là dùng Don điem lân cận hay tám điem lân cận đoi với aiem đang xét được tô bằng màu trắng.
Đoạn chương trình sau minh hoạ
cài đặt thuật toán tô màu dựa theo đường biên sử dụng phương pháp tô 4 điểm lân cận.
(a) (b)
Hình 3.2. Bốn điểm lân cận (a) và 8 điểm lân cận (b)
3 - CÁC THU AT TOÁN TÔ MÀU
> Cài đặt minh hoạ thuật toán tô màu dựa theo đường biên
v o i d B o u n d a r y F i l l (int X, int y, int FillColor,
int B o u n d a r y C o 1o r ) { int C u r r e n t C o l o r; C u r r e n t C o l o r = getpixel(x, y) ; i f ( ( C u r r e n t C o l o r ! = B o u n d a r y C o l o r ) & & C u r r e n t C o l o r ! = F i l l C o l o r ) ) { p u t p i x e l (X, y ,F i l l C o l o r ) ;
BoundaryFill(x-l, y, FillColor, BoundaryColor) ;
BoundaryFill(X, y+1, FillColor, BoundaryColor) ;
BoundaryFill(x+1, y, FillColor, BoundaryColor) ;
BoundaryFill(X,y -l, FillColor, BoundaryColor) ;
}
} / / B o u n d a r y F i l l
Nhận xét: 、
- Thuật toán này có thể sẽ không hoạt động chính xác khi có một số điểm nằm trong vùng tô có màu là màu cần tô của vùng (FillColor). Đề khắc phục điều này, trước khi tô màu cần phải đảm bảo ràng toàn bộ các điểm thuộc về vùng tô cớ màu khác màu tô.
- Nhận xét ràng, trong cài đặt thuật toán ở trên, việc gọi thực hiện đệ quy thuật toán cho bốn điểm lân cận cùa điềm hiện hành không quan tâm tới một trong bốn điềm đó đã được xét ở bước trước hay chưa. Ví dụ: Khi xét bốn điểm lân cận của điểm hiện hành (x, y), thì khi gọi thực hiện đệ quy với điểm hiện hành là một trong bốn điểm lân cận trên, (x, y) vẫn được xem là điểm lân cận của chúng và lại được gọi thực hiện lại. Có thể đưa ra một cải tiến nhò để khắc phục điểm này, bằng cách mỗi lần xét điểm hiện hành (x, y), trong đó sẽ gọi bốn thủ tục riêng để tô các điểm lân cận và tránh gọi lại việc xét điểm (x, y).
ĐỒ HOẠ MÂYTÌNH
v o i d B o u n d a r y F i l l E n h a n c e d (int X, int y,
int F _ C o l o r , int B_Color)
{ ~ ~ int C u r r e n t C o l o r; C u r r e n t C o l o r = getpixel(x, y) ; i f ( ( C u r r e n t C o l o r ! = B _ C o l o r )& & C u r r e n t C o l o r ! = F _ C o l o r ) ) { _ p u t p i x e l (X, y , F _ C o l o r ); F i l l L e f t (x - 1 , y , F__c0 1 0 r, B _ C o l o r) ; F i l l T o p (x, y + 1 , F _ C o l〇r, B _ C o l o r ) ; F i l l R i g h t (x + 1 , y, F _ C o l o r , B__Color) ; F i l l B o t t o m (x, y — 1 , F _ C o l o r , B _ C o l o r) ; } " } / / B o u n d a r y F i l l E n h a n c e d
v o i a F i l l L e f t (int X, int Y , int F Color,
int B Color)
{ _
int C u r r e n t C o l o r;
C u r r e n t C o l o r = getpixel(x, y) ;
if ((CurrentColor! = B_Color) ỈL&CurrentColor!
= F C o l o r ) ) { _ putpixel(x, y , F C o l o r) ; F i l l L e f t(X — 1 , y, F Color, B _ C o l o r ) ; F i l l T o p (x, y + 1 , F _ C o l o r , B _ C o l o r) ; F i l l B o t t o m (x, y - 1 , F Color, B _ C o l o r) ; } ~ ~ } / / F i l l L e f t v o i d F i l l T o p ( i n t X, int y, int F _ C o l o r , int B Color)
3 - CÁC THU AT TOÁN TÔ MẢU BI int C u r r e n t C o l o r; C u r r e n t C o l o r = getpixel(x, y) ; i f ( ( C u r r e n t C o l o r ! = B _ C o l o r ) & & C u r r e n t C o l o r ! = F _ C o l o r ) ) { 一 p u t p i x e l( X , y , F C o l o r) ; F i l l L e f t (x - 1 , y, F Color, B C o l o r) ; F i l l T o p (x, y + 1 , F Color, B _ C o l o r) ; F i l l R i g h t (x + 1 , y, F Color, B _ C o l o r ) ; } } // F i l l T o p
v o i d F i l l R i g h t (int X, int Y • int F Color,
int B Color) { 一 int C u r r e n t C o l o r; C u r r e n t C o l o r = getpixel(x, y) ; i f ( ( C u r r e n t C o l o r ! = B C o l o r )& & C u r r e n t C o l o r ! F C o l o r ) ) p u t p i x e l( X , y,F Color);
FillTop { x t y 十 1 ,F_Col〇r*, B_Color);
F i l l R i g h t (x + 1 , y, F Color, B _ C o l o r );
F i l l B o t t o m (x, y — 1 , F_Color, B Color);
/ / F i l l R i g h t
v o i d F i l l B o t t o m ( i n t X , int y, int F Color,
int B Color)
int C u r r e n t C o l o r;
I^Ị Đ ồ HOẠ MÁY TÍNH i f ( ( C u r r e n t C o l o r ! = B _ C o l o r ) & & C u r r e n t C o l o r ! = F _ C o l o r ) ) { p u t p i x e l( X , y,F _ C o l o r) ; F i l l L e f t (x - 1 , y, F _ C o l o r , B__Color); F i l l R i g h t (x + 1 , y, F_Color, B _ C o l o r) ; F i l l B o t t o m (x, y - 1 , F_Color, B _ C o l〇r ) ; } } / / F i l l B o t t o m
Thuật toán này có tính đệ quy, do đó khi cài đặt thường gây lồi tràn bộ nhớ khi vùng tô khá lớn, do đó nếu cai tien thì sẽ tiến hành loang dần và lần lượt tô từng đoạn giao theo dòng quét ngang thay vì tô theo bốn điềm lân cận. Như vậy, chỉ cần lưu lại thông tin cùa điểm bắt đầu mồi đoạn giao của dòng quét ngang thay vì phải lưu toàn bộ các điểm lân cận chưa được tô cùa điểm hiện hành. Ta cho các dòng quét loang từ điềm bắt đầu theo hướng lên biên trên, sau khi đã tô xong, các dòng quét còn lại theo hướng xuống biên dưới sẽ được tô. ửng với mỗi dòng quét ngang, ta sẽ loang và tìm pixel trái nhất (có hoành độ nhò nhất) để lưu lại. Trong hình 3.3 dưới đây, đoạn giao đầu tiên chứa điềm bắt đầu (tô màu trắng) sẽ được tô trước. Sau đó các vị trí 1,2 ứng với các đoạn giao của các dòng quét kế tiếp sẽ được lưu lại (hình 3.3a). Bước tiếp theo, điềm ứng với vị trí 2 sẽ dược lấy ra và tiến hành tô màu bàng cách loang từ điềm này ra theo chiều ngang, sau đó pixel ứng vị trí 3 của dòng quét kế tiếp sẽ được lưu lại (hình 3.30). Sau khi dòng quét ứng với điểm 3 đã được xử lý tương tự như trên xong, stack lưu các vị trí của các điểm nhạt giống1* cho các dòng quét kế tiếp như trong hình 3.3c. Hình 3.3d minh hoạ khi thuật toán đã tô được toàn bộ một phần vùng phía trên bên phải của vùng tô. Khi pixel ứng với vị trí 5 được xử lý xong, phần còn lại phía trên bên trái sẽ được tô. Sau đó pixel ứng với vị trí 4 sẽ được xử lý, các dòng quét phía dưới sẽ được tô tiếp theo.
ĩ i - CÁC THU AT TOÁN TÔ MÀU 丄 (b) • • • • • • : • • • • • • • • 春 聲 ••••春 6 5 4 丄 (c) 5 4 1 (d)
Hình 3.3. Thuật toán tô màu theo dòng quét cải tiên
> Cài đăt minh hoạ thuât toán tô mâu Flood Fill
/* Chu ý: Thuạt to a n sư d ụ n g ky thuạt đẹ quy, ao đó m i e n lớn có thể g â y tràn s tack và tr e o m á y */ # i n c lude < c s t d l i b > # i n c lude <io s t r e a m > u s i n g n a m e s p a c e s t d; # i n c l u d e < g r a p h i c s .h> #include cconio.h>
v o i d F l o o dFill (int X , int y, int
if ( g e t p i x e l( X , y ) in color) p u t p i x e l( x , y, n e w c o l o r) ; F l o o d F i l l(X - 1 , y, in color, F l o o d F i l l ( x + 1 , V, in color, FloodFill(x, y — 1 , in color, FloodFill(x, y + 1 , in color, in color, int n e w color) new_color) new_color) new_color) n e w color) 5-OHMTINH
口 ĐÔ HOẠ MÁY TÍNH
ìnt m a i n u n t argc, char *argv [])
{
int gr_drive = DETECT, gr m o d e;
initgraph(&gr drive, &gr mode,
c i rcle(getmaxx() / 2, getmaxy() / 2, 40);
FloodFill(getmaxx() / 2, getmaxy() / 2, 0, 4);
circle (getmaxx () / 2 + 20, getmaxyO / 2 + 20, 4 0 );
FloodFill(getmaxx() / 2 + 2 0 , g e tmaxy() / 2 + 2 0 , 1 , 7 ) ; g e t c h O ; closegraph( ) ; system ( " P AUSE1' ) ; return E X I T S Ư C C E S S;
3 - CÁC THUAT TOÁN TÔ MÀU
3.2. THUẬT TOÁN TÒ MÀU THEO DÒNG QUÉT
Giả sử vùng tô được cho bởi một đa giác N đỉnh: Pi(Xj, Ỵi), i = 0, . . . , n - 1 . Đa giác này có thể là đa giác lồi, đa giác lõm và có thể là cả đa giác tự cắt.
Hình 3.5 minh hoạ ý tưởng chính của thuật toán. Với mồi dòng quét, ta xác định phần giao của đa giác và dòng quét, rồi tô màu các pixel thuộc đoạn giao đó. Để xác định các đoạn giao, ta tiến hành việc tim giao điểm của dòng quét với các cạnh của đa giác, sau đó các giao điềm này sẽ được sắp theo thứ tự tăng dần của hoành độ giao điểm. Các đoạn giao chính là các đoạn thẳng được giơi hạn bởi từng cặp giao điểm một, ví dụ như (0 ,1 ),(2 , 3),...
Hình 3.5. Thuật toán scan - line với một dòng quét nào đó
Ta cỏ thể tóm bắt các bước chính của thuật toán như sau:
- Tìm yt0p, ybottom lần lượt là giá trị lớn nhất, nhò nhất của tập các tung độ của các đỉnh cùa đa giác đã cho:
y 10p = m a x { y i , (Xj, y i ) e P } ; ybottom = m i n { y i , (Xj, y i ) e P } .
ffl ĐỒ HOẠ MÁY TÍNH
+ Tìm tất cả các hoành độ giao điềm của dòng quét y = k với các cạnh cùa đa giác.
+ sẳp xếp các hoành độ giao điểm theo thử tự tăng dần: x〇, Xị, …
+ Tô màu các đoạn thẳng trên đường thẳng y = k lần lượt được giới hạn bởi các cặp (x〇, X|), (x2, x3)v.., (x2k, x2k+1).
Nếu chỉ dừng ở mức này và chuyền sang cài đặt, sẽ gặp một số vấn đề sau:
- Nhận xét ràng, ứng với mỗi dòng quét, không phải lúc nào tất cả các cạnh của đa giac cũng tham gia cắt dòng quét. Do đó, đề cai thiện tốc độ cần phải có một cách nào đó để hạn chế được số cạnh cần tìm giao điểm ứng với mồi dòng quét.
- Viẹc tìm giao điềm của cạnh đa giac VƠI moi dòng quét sẽ gặp các phép toán phức tạp như nhan, chia,... trên số thực neu ta dùng cách giai hệ phương trình tìm giao diem. Đieu này sẽ làm giam tốc độ thuật toán khi phải lặp đi lặp lại nhiều lần thao tác này khi dòng quet quét qua đa giác.
- Nếu số giao điểm tìm được giữa các cạnh đa giac và dòng quét là lẻ thì việc nhỏm từng cặp giao điểm kế tiếp nhau để hình thành các đoạn tô cỏ thể sẽ không chính xác. Điều này chỉ xảy ra khi dòng quét đi ngang qua các đỉnh của đa giac. Nếu tính số giao aiem tại đỉnh dòng quét đi ngang qua là hai thì có thể sẽ cho kết quả tô không chính xác như trong trường hợp của hình 3.6.
y = k2
y = ki
Hinh 3.6. Dòng quét y - k1 đi ngang qua đỉnh có thé sẽ cho kết quả tô không chính xác so với dòng quét y = k2
rỹ- CÁC THUAT TOÁN TÔ MÀU m Ngoài ra, việc tìm giao điểm của dòng quét với các cạnh nằm ngang là một trường hợp đặc biệt cần phải có cách xừ lý thích hợp.
Để giải quyết các vấn đề trên, cần phải xây dựng một cấu trúc dữ liệu và thuật toán thích hợp đối với chúng.