运用python实现Cohen-Sutherland直线段裁剪算法


本篇为原创,仅仅作为学习参考之用,转载请说明。

一.题目描述:

在二维观察中,需要在观察坐标系下根据窗口大小对二维图形进行裁剪(clipping),只将位于窗口内的图形变换到视区输出。

直线段裁剪是二维图形裁剪的基础,裁剪的实质是判断直线段是否与窗口相交,如相交则进一步确定直线段上位于窗口内的部分。

那么怎么进行裁剪,首先要定义一下窗口内及窗口外的编码,每段直线的端点都被赋予一组四位二进制代码,

称为区域编码(region code,RC),用来标识直线段端点相对于窗口边界及其延长线的位置。如图

 

为了保证窗口内及窗口边界上直线段端点的编码为零,定义规则如下:

C0:若端点的x<wxl,则C0=1,否则C0=0。

C1:若端点的x>wxr,则C1=1,否则C1=0。

C2:若端点的y<wyb,则C2=1,否则C2=0。

C3:若端点的y>wyt,则C3=1,否则C3=0。

 

二.算法思路:

1.将直线起点p1和终点p2进行编码得到code1和code2.。

2.若两端点编码值均为0,说明直线段在窗口内,保留。

3.若两端点编码值code1&code2!=0,则说明直线段位于窗外的同一侧,或左方、或右方、或上方、或下方,抛弃。

4.若code1&code2==0,则直线段必然与窗口边界或窗口边界的延长线相交,此时该进行求交点操作。

任取编码值不为0的其中一点p1(该点必然在窗口外),然后用p1的编码值与窗口之外左右下上的编码值进行按位与&运算,

如果结果不为0,则直线段与窗口该侧的边界有交点,根据直线起点和终点求出交点。

将交点进行编码,把编码值赋给p1,则此时p1位于窗口边界上,即交点成为直线段起点,相当于舍去原来的p0与交点的那个线段,形成一个新的线段p1p2,

再次重复以上步骤,若皆在窗口中,则保留;否则继续循环,直到直线段全部位于窗口内为止。

 

 

三.注意事项

如果想绘制出图形的话要自己学习python的图形库哦,这里我用的是OpenCV库。首先建立画布,画出矩形窗口区域,然后才开始裁剪算法的,

这里裁剪的实质是判断直线的哪些部分在窗口内部,然后绘制出它们,窗口之外的部分就忽略。

 

四.代码

  1 import numpy as np
  2 import cv2
  3 
  4 LEFT = 1
  5 RIGHT = 2
  6 BOTTOM = 4
  7 TOP = 8
  8 
  9 xl = 300
 10 xr = 500
 11 yb = 150
 12 yt = 350  # 窗口的边界值
 13 
 14 # Create a black image
 15 img = np.zeros((500, 800, 3), np.uint8)  # 相当于建立画布
 16 
 17 
 18 # 编码
 19 def encode(x, y):
 20     c = 0
 21     if x < xl:
 22         c = c | LEFT
 23     if x > xr:
 24         c = c | RIGHT
 25     if y < yb:
 26         c = c | BOTTOM
 27     if y > yt:
 28         c = c | TOP
 29     return c
 30 
 31 
 32 # cohen-sutherland 算法
 33 def CohenSutherland(x1, y1, x2, y2):
 34     code1 = encode(x1, y1)
 35     code2 = encode(x2, y2)
 36     outcode = code1  # outcode是总在窗口外的那个端点
 37     x, y = 0, 0
 38     area = False  # 设置一个是否满足条件的区分标志
 39     while True:
 40         if (code2 | code1) == 0:
 41             area = True
 42             break
 43         if (code1 & code2) != 0:  # 简弃之
 44             break
 45         if code1 == 0:  # 开始求交点
 46             outcode = code2
 47         if (LEFT & outcode) != 0:  # 与窗口左边界相交
 48             x = xl
 49             y = y1 + (y2 - y1) * (xl - x1) / (x2 - x1)
 50         elif (RIGHT & outcode) != 0:
 51             x = xr
 52             y = y1 + (y2 - y1) * (xr - x1) / (x2 - x1)
 53         elif (BOTTOM & outcode) != 0:
 54             y = yb
 55             x = x1 + (x2 - x1) * (yb - y1) / (y2 - y1)
 56         elif (TOP & outcode) != 0:
 57             y = yt
 58             x = x1 + (x2 - x1) * (yt - y1) / (y2 - y1)
 59         x = int(x)  # 转换为整型
 60         y = int(y)
 61         if outcode == code1:
 62             # print('hhh')  # 测试用
 63             x1 = x
 64             y1 = y
 65             code1 = encode(x, y)
 66         else:
 67             # print('eee')
 68             x2 = x
 69             y2 = y
 70             code2 = encode(x, y)
 71     if area == True:  # 若满足条件即可划线
 72         cv2.line(img, (x1, y1), (x2, y2), (0, 255, 255))  # 这里传递的点的坐标必须是整型,否则出错
 73     return
 74 
 75 
 76 # 做测试用的  不用理会
 77 def test():
 78     print('test test test!')
 79     c = encode(23, 46)
 80     print(c)
 81 
 82 
 83 def main():   # 主函数
 84     # 绘制矩形窗口与直线   这里是显示裁剪之前的样子  要测试记得把下面的裁剪部分注释掉
 85     cv2.rectangle(img, (300, 150), (500, 350), 255)
 86     # cv2.line(img, (0, 0), (260, 260), (0, 255, 255))
 87     # cv2.line(img, (400, 50), (200, 400), (0, 255, 255))
 88     # cv2.line(img, (350, 100), (450, 400), (0, 255, 255))
 89     # cv2.line(img, (150, 250), (650, 250), (0, 255, 255))
 90     # cv2.line(img, (400, 75), (400, 425), (0, 255, 255))
 91     # cv2.line(img, (350, 300), (450, 200), (0, 255, 255))
 92 
 93     # 窗口裁剪直线        这里是显示裁剪之后的样子
 94     CohenSutherland(0, 0, 260, 260)  # 传递直线起点和终点坐标
 95     CohenSutherland(400, 50, 200, 400)
 96     CohenSutherland(350, 100, 450, 400)
 97     CohenSutherland(150, 250, 650, 250)
 98     CohenSutherland(400, 75, 400, 425)
 99     CohenSutherland(350, 300, 450, 200)
100     # test()
101 
102     # 窗口显示图形
103     cv2.imwrite('out1.jpg', img)  # 生成一张图片
104     cv2.namedWindow('image', cv2.WINDOW_NORMAL)  # 直接显示窗口
105     cv2.resizeWindow('image', 800, 500)  # 定义frame的大小
106     cv2.imshow('image', img)  # 显示图像
107     k = cv2.waitKey(0)  # 等待键盘输入,为毫秒级  0表示一直等待
108     if k == 27:  # 键盘上Esc键的键值  按下就会退出   设置好条件更加方便
109         cv2.destroyAllWindows()
110 
111 
112 # print('this message is from main function')
113 
114 
115 if __name__ == '__main__':
116     main()
117     # print('now __name__ is %s' % __name__)
View Code

 

五.运行结果

裁剪前:

 

 

裁剪后:

 

 五.思考与收获

学会把思路理清后,自己能够写出基本框架的程序,一些不懂的知识就去及时百度,以后要系统学习python的库,感觉很好用,

在学习的过程中,也要加强做笔记的能力,多写博客吧。

 

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM