大数跨境
0
0

【深度好文】直线Bresenham算法实现

【深度好文】直线Bresenham算法实现 AI算法之道
2021-07-13
0
导读:本文就计算机图形学中画直线方法进行了研究,阐明了其原理,并给出了其优化策略和相关代码实现。

【问题描述】

在图像处理和计算机图形学中,我们经常需要在屏幕上画直线操作。考虑给定平面上两个点A(x1,y1)以及B(x2,y2),我们的任务是实现在屏幕上画通过AB两点的直线,并找出这条直线通过的所有中间点的坐标。值得注意的是这里每个像素的坐标均为整数。

【示例】

为了使算法尽可能简单,我们做以下假设

  1.  x1<x2 并且 y1<y2

  2.  直线的斜率在[0,1]之间,我们从左下往右上方向画线

【原始方案】

看了上述描述,首先浮现在我们脑海的是最原始简单直接的方法,如下所示:

// A naive way of drawing linevoid naiveDrawLine(x1, x2, y1, y2){   m = (y2 - y1)/(x2 - x1)   for (x  = x1; x <= x2; x++)    {          // Assuming that the round function finds      // closest integer to a given float.      y = round(mx + c);          print(x, y);    }}

   上述方案可以正常工作,但是效率很慢。而Bresenham算法则是为了避免float类型数据的乘法和加法操作,并且避免了每次在for循环里执行对(mx+c)的取整操作。

Bresenham算法

考虑到图像或者屏幕上像素的特殊性,(x,y)取值均为整数数据类型。基于此性质,我们可以每次沿着x轴增加1,接着我们来选择下一个y,此时我们有两种选择:要么y增加1要么y依旧维持不变。换句话说我们沿直线方向从点(xk,yk)移动一个像素,下一个点我们有两个选择即(xk+1,yk)或者(xk+1,yk+1)。该过程如下图所示


如上图所示,我们沿着直线方向从左下角往右上角移动,从红色点(xk,yk)移动一个像素,那么直线上下一个点的位置可能会有两个选择,如图中绿色点所示,即(xk+1,yk+1)或者(xk+1,yk).具体选择这两个点里的哪一个,需要对比直线在xk+1处的y值距离这两个整数点那个距离更小.此时的伪代码逻辑如下:

void drawLine(x1,x2,y1,y2){      x = x1    y = y1    print(x,y)    while(x<x2)    {      x = x + 1      If we should increment Y        y = y + 1      print(x,y)    }}


接着我们的问题转化为如何设计一个准则来判断每次x增加1,y的取值是否增加1。一个直观的判断准则如下:


  1.  将x'=x+1代入直线方程,计算此时y'

  2.  比较y'和y+0.5的大小,如果y'>y+0.5,则下一个点去(x+1,y+1);否则取(x+1,y)即可


接着我们使用间接方法来实现上述过程:


  1. 不妨假设直线方程为 y=mx+b,则每次x增加1,那么y将增加m

  2.  记变量 fraction 含义为从上一次y增加开始到现在的斜率的累计增加量,那么如果fraction>0.5,那么我们应该增加y

此时的伪代码逻辑如下:

void drawLine(x1,x2,y1,y2){     x = x1   y = y1   print(x,y)   fraction=0   m = (y2-y1)/(x2-x1)   while(x<x2)   {     x = x + 1     fraction += m     if(fraction > 0.5)          y = y + 1     fraction -= 1    }     print(x,y)   }}


进除法和浮点运算操作】

上述算法还涉及浮点运算和浮点数的比较,运行的依旧较慢.实际上,我们可以进一步优化来消除浮点运算,我们注意到 

此时我们可以通过在上式左右两侧同乘(x2-x1)来消除除法运算;

同时为了避免斜率增加量和0.5的比较操作,我们可以同时乘以2.

汇总后,得到如下公式:

此时伪代码优化后如下:

void drawLine(x1,x2,y1,y2){    x = x1  y = y1  print(x,y)  fraction=0  m = (y2-y1)*2  while(x<x2)  {    x = x + 1    fraction += m    if(fraction >  x2-x1)        y = y + 1    fraction -= 2*(x2-x1)   }    print(x,y)  }}

【完整的python代码实现

# Assumptions :# 1) Line is drawn from left to right.# 2) x1 < x2 and y1 < y2# 3) Slope of the line is between 0 and 1.# We draw a line from lower left to upperdef bresenham(x1, y1, x2, y2):    print("Input : A({},{}), B({},{})".format(x1,y1,x2,y2))    pt_list = []    x = x1    y = y1    pt_list.append((x,y))    fraction = 0    m = (y2-y1) * 2    diff_x = x2 - x1    while x < x2:        x = x + 1        fraction += m        if fraction > diff_x:            y = y+ 1            fraction = fraction - 2*diff_x        pt_list.append((x, y))    print("Output:",pt_list)    if __name__ == '__main__':    bresenham(x1=0, y1=0, x2=4, y2=4)    bresenham(x1=0, y1=0, x2=4, y2=2)

运行效果如








【声明】内容源于网络
0
0
AI算法之道
一个专注于深度学习、计算机视觉和自动驾驶感知算法的公众号,涵盖视觉CV、神经网络、模式识别等方面,包括相应的硬件和软件配置,以及开源项目等。
内容 573
粉丝 0
AI算法之道 一个专注于深度学习、计算机视觉和自动驾驶感知算法的公众号,涵盖视觉CV、神经网络、模式识别等方面,包括相应的硬件和软件配置,以及开源项目等。
总阅读23
粉丝0
内容573