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

[问题描述]

在这里插入图片描述

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

示例:

在这里插入图片描述
为了使算法尽可能简单,我们做以下假设:

  1. 我们画直线从左往右画
  2. x1<x2 并且 y1<y2
  3. 直线的斜率在[0,1]之间,我们从左下往右上方向画线.

最原始方案:

看了上述描述,首先浮现在我们脑海的是最最原始直接的方法,如下所示:
在这里插入图片描述上述方法肯定可以正常工作,但是效率很慢.而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)
   }
}

改进除法和浮点运算操作:
上述算法还涉及浮点运算和浮点数的比较,运行的依旧较慢.实际上,我们可以进一步优化来消除浮点运算,我们注意到 m=(y2-y1)/(x2-x1)
此时我们可以通过在上式左右两侧同乘(x2-x1)来消除除法运算;
同时为了避免斜率增加量和0.5的比较操作,我们可以同时乘以2.
汇总后,得到如下公式:
slope_increment = m*(x2-x1)*2=(y2-y1)/(x2-x1) * (x2-x1) * 2 = 2(y2-y1)
此时伪代码优化后如下:

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 upper
def 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)

输出结果如下:

在这里插入图片描述




参考

附参考
链接一
链接二




关注公众号《AI算法之道》,获取更多AI算法资讯.
在这里插入图片描述

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

赵卓不凡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值