一、 算法原理簡介:
轉自pheye
算法原理的詳細描述及部分實現可參考:
http://www.cs.helsinki.fi/group/goa/mallinnus/lines/bresenh.html
Fig. 1
假設以(x, y)為繪制起點,一般情況下的直觀想法是先求m = dy /dx(即x每增加1, y的增量),然后逐步遞增x, 設新的點為x1 = x + j, 則y1 = round(y + j * m)。可以看到,這個過程涉及大量的浮點運算,效率上是比較低的(特別是在嵌入式應用中,DSP可以一周期內完成2次乘法,一次浮點卻要上百個周期)。
下面,我們來看一下Bresenham算法,如Fig. 1,(x, y +ε)的下一個點為(x, y + ε + m),這里ε為累加誤差。可以看出,當ε+m < 0.5時,繪制(x + 1, y)點,否則繪制(x + 1, y + 1)點。每次繪制后,ε將更新為新值:
ε = ε + m ,如果(ε + m) <0.5 (或表示為2*(ε + m) < 1)
ε = ε + m – 1, 其他情況
將上述公式都乘以dx, 並將ε*dx用新符號ξ表示,可得
ξ = ξ + dy, 如果2*(ξ + dy) < dx
ξ = ξ + dy – dx, 其他情況
可以看到,此時運算已經全變為整數了。以下為算法的偽代碼:
ξ ← 0, y ← y1
For x ← x1 to x2 do
Plot Point at (x, y)
If (2(ξ + dy) < dx)
ξ ←ξ + dy
Else
y ← y + 1,ξ ←ξ + dy – dx
End If
End For
二、 算法的注意點:
Fig. 2
在實際應用中,我們會發現,當dy > dx或出現Fig.2 右圖情況時時,便得不到想要的結果,這是由於我們只考慮dx > dy, 且x, y的增量均為正的情況所致。經過分析,需要考慮8種不同的情況,如Fig. 3所示:
(Fig. 3)
當然,如果直接在算法中對8種情況分別枚舉, 那重復代碼便會顯得十分臃腫,因此在設計算法時必須充分考慮上述各種情況的共性,后面將給出考慮了所有情況的實現代碼。
三、 算法的實現
以下代碼的測試是利用Opencv 2.0進行的,根據需要,只要稍微修改代碼便能適應不同環境
1 void DrawLine( int x1, int y1, int x2, int y2,uint color) 2 { 3 int dx = x2 - x1; 4 int dy = y2 - y1; 5 int ux = ((dx > 0) << 1) - 1;//x的增量方向,取或-1 6 int uy = ((dy > 0) << 1) - 1;//y的增量方向,取或-1 7 int x = x1, y = y1, eps;//eps為累加誤差 8 9 eps = 0;dx = abs(dx); dy = abs(dy); 10 if (dx > dy) 11 { 12 for (x = x1; x != x2+ux; x += ux) 13 { 14 SetPixel(img, x, y); 15 eps += dy; 16 if ((eps << 1) >= dx) 17 { 18 y += uy; eps -= dx; 19 } 20 } 21 } 22 else 23 { 24 for (y = y1; y != y2+uy; y += uy) 25 { 26 drawPixel(x, y,color); 27 eps += dx; 28 if ((eps << 1) >= dy) 29 { 30 x += ux; eps -= dy; 31 } 32 } 33 } 34 }
四、 測試結果
五、 進一步可能的改進
設(x1, y1)為起點,(x2, y2)為終點,取中點(x1 + x2)/2, (y1 +y2)/2,然后從兩個端點同時向中點生長,這種並行運算可以提高一定的效率。