绘图及交互
Opencv提供了方便的绘图功能,使用其中的绘图函数可以绘制直线、矩形、圆、椭圆等多种几何图形,还能在图像中的指定位置添加文字说明。
OpenCV提供了鼠标事件,使用户可以通过鼠标与图像交互。鼠标事件能够识别常用的鼠标操作,例如:针对不同按键的单击、双击,鼠标的滑动、拖曳等。
OpenCV还提供了滚动条用于实现交互功能。用户可以拖动滚动条在某一个范围内设置特定的值,并将该值应用于后续的图像处理中。而且,如果设置为二值形式,滚动条还可以作为开关选择器使用。
绘画基础
OpenCV提供了绘制直线的函数cv2.line()、绘制矩形的函数cv2.rectangle()、绘制圆的函数cv2.circle()、绘制椭圆的函数cv2.ellipse()、绘制多边形的函数cv2.polylines()、在图像内添加文字的函数cv2.putText()等多种绘图函数。
这些绘图函数有一些共有的参数,主要用于设置源图像、颜色、线条属性等。
共有参数简单的介绍:
- img:在其上面绘制图形的载体图像(绘图的容器载体,也称为画布、画板)。
- color:绘制形状的颜色。通常使用BGR模型表示颜色,例如,(0, 255, 0)表示绿色。对于灰度图像,只能传入灰度值。需要注意,颜色通道的顺序是BGR,而不是RGB。
- thickness:线条的粗细。默认值是1,如果设置为-1,表示填充图形(即绘制的图形是实心的)。
- lineType:线条的类型,默认是8连接类型。
- shift:数据精度。该参数用来控制数值(例如圆心坐标等)的精度,一般情况下不需要设置。
绘制直线
OpenCV提供了函数cv2.line()用来绘制直线(线段)。
该函数的语法格式为:
img = cv2.line( img, pt1, pt2, color[, thickness[, lineType ]])
- 参数img、color、thickness、lineType的含义如前面的说明所示。
- pt1表示线段的第1个点(起点)。
- pt2表示线段的第2个点(终点)。
使用cv2.line()函数在一个黑色背景图像内绘制三条线段。
import numpy as np import cv2 n = 300 img = np.zeros((n+1, n+1,3), np.uint8) img = cv2.line(img, (0,0), (n, n), (255,0,0),3) img = cv2.line(img, (0,100), (n,100), (0,255,0),1) img = cv2.line(img, (100,0), (100, n), (0,0,255),6) cv2.namedWindow("test") cv2.imshow("test", img) cv2.waitKey(0) cv2.destroyAllWindows()
绘制矩形
OpenCV提供了函数cv2.rectangle()用来绘制矩形。
该函数的语法格式为:
img = cv2.rectangle( img, pt1, pt2, color[, thickness[, lineType]] )
- 参数img、color、thickness、lineType的含义如前面的说明所示。
- pt1为矩形顶点。
- pt2为矩形中与pt1对角的顶点。
使用函数cv2.rectangle()在一个白色背景图像内绘制一个实心矩形。
import numpy as np import cv2 n = 300 img = np.ones((n, n,3), np.uint8)*255 img = cv2.rectangle(img, (50,50), (n-100, n-50), (0,0,255), -1) winname = 'test' cv2.namedWindow(winname) cv2.imshow(winname, img) cv2.waitKey(0) cv2.destroyAllWindows()
绘制圆形
OpenCV提供了函数cv2.circle()用来绘制圆。
该函数的语法格式为:
img = cv2.circle( img, center, radius, color[, thickness[, lineType]] )
- 参数img、color、thickness、lineType的含义如前面的说明所示。
- center为圆心。
- radius为半径。
使用函数cv2.circle()在一个白色背景图像内绘制一组同心圆。
import numpy as np import cv2 d = 400 img = np.ones((d, d,3), dtype="uint8")*255 (centerX, centerY) = (round(img.shape[1] / 2), round(img.shape[0] / 2)) # 将图像的中心作为圆心,实际值为d/2 red = (0,0,255) # 设置红色变量 for r in range(5, round(d/2),12): cv2.circle(img, (centerX, centerY), r, red,3) # circle(载体图像,圆心,半径,颜色,宽度) cv2.imshow("test", img) cv2.waitKey(0) cv2.destroyAllWindows()
绘制椭圆
OpenCV提供了函数cv2.ellipse()用来绘制椭圆。
该函数的语法格式为:
img=cv2.ellipse(img, center, axes, angle, startAngle, endAngle, color[, thickness[, lineType]])
- 参数img、color、thickness、lineType的含义如前面的说明所示。
- center为椭圆的圆心坐标。
- axes为轴的长度。有两个轴,所以是个元组
- angle为偏转的角度。
- startAngle为圆弧起始角的角度
- endAngle为圆弧终结角的角度
使用函数cv2.ellipse()在一个白色背景图像内随机绘制一组空心椭圆。
import numpy as np import cv2 d = 400 img = np.ones((d, d,3), dtype="uint8")*255 # 生成白色背景 center=(round(d/2), round(d/2)) # 注意数值类型,不可以使用语句center=(d/2, d/2) size=(100,200) # 轴的长度 for i in range(0,10): angle = np.random.randint(0,361) # 偏移角度 color = np.random.randint(0, high = 256, size = (3, )).tolist() # 生成随机颜色,3个[0,256)的随机数 thickness = np.random.randint(1,9) cv2.ellipse(img, center, size, angle, 0, 360, color, thickness) cv2.imshow("test", img) cv2.waitKey(0) cv2.destroyAllWindows()
绘制多边形
cv2.polylines()用来绘制多边形。
该函数的语法格式为:
img = cv2.polylines( img, pts, isClosed, color[, thickness[, lineType[, shift]]])
- 参数img、color、thickness、lineType和shift的含义如前面的说明所示。
- pts为多边形的各个顶点。
- isClosed为闭合标记,用来指示多边形是否是封闭的。若该值为True,则将最后一个点与第一个点连接,让多边形闭合;否则,仅仅将各个点依次连接起来,构成一条曲线。
在使用函数cv2.polylines()绘制多边形时,需要给出每个顶点的坐标。
这些点的坐标构建了一个大小等于“顶点个数12”的数组,这个数组的数据类型必须为numpy.int32。
使用函数cv2.polylines()在一个白色背景图像内绘制一个多边形。
import numpy as np import cv2 d = 400 img = np.ones((d, d,3), dtype="uint8")*255 # 生成白色背景 pts=np.array([[200,50], [300,200], [200,350], [100,200]], np.int32) # 生成各个顶点,注意数据类型为int32 pts=pts.reshape((-1,1,2)) # 第1个参数为-1,表明它未设置具体值,它所表示的维度值是通过其他参数值计算得到的 cv2.polylines(img, [pts], True, (0,255,0),8) # 调用函数cv2.polylines()完成多边形绘图。注意,第3个参数控制多边形是否封闭 cv2.imshow("test", img) cv2.waitKey(0) cv2.destroyAllWindows()
可以使用函数cv2.polylines()来绘制多条首尾相连的线段。只要把线段的各个点放在一个数组中,将这个数组传给函数cv2.polylines()的第2个参数pts,并且isClosed为False。
注意pts参数的维度
绘制文字
OpenCV提供了函数cv2.putText()用来在图形上绘制文字。
该函数的语法格式为:
img=cv2.putText(img, text, org, fontFace, fontScale, color[, thickness[, lineType[, bottomLeftOrigin]]])
- 参数img、color、thickness、lineType和shift的含义如前面的说明所示。
- text为要绘制的文本。
- org为绘制字体的位置,以文字的左下角为起点。
- fontFace表示字体类型
- fontScale表示字体大小。
- bottomLeftOrigin用于控制文字的方向。默认值为False,当设置为True时,文字是垂直镜像的效果。
使用函数cv2.putText()在一个白色背景图像内绘制文字。
import numpy as np import cv2 d = 400 img = np.ones((d, d,3), dtype="uint8")*255 # 生成白色背景 font=cv2.FONT_HERSHEY_SIMPLEX cv2.putText(img, 'OpenCV', (0,200), font, 3, (0,255,0),15) cv2.putText(img, 'OpenCV', (0,200), font, 3, (0,0,255),5) cv2.imshow("test", img) cv2.waitKey(0) cv2.destroyAllWindows()
import numpy as np import cv2 d = 400 img = np.ones((d, d,3), dtype="uint8")*255 # 生成白色背景 font=cv2.FONT_HERSHEY_SIMPLEX cv2.putText(img, 'OpenCV', (0,150), font, 3, (0,0,255),15) cv2.putText(img, 'OpenCV', (0,250), font, 3, (0,255,0),15, cv2.LINE_8, True) cv2.imshow("test", img) cv2.waitKey(0) cv2.destroyAllWindows()
参数bottomLeftOrigin的值被设置为True,实现绘制镜像文字效果
鼠标交互
用户单击鼠标,就画一个圆。通常的做法是,创建一个OnMouseAction()响应函数,将要实现的操作写在该响应函数内。
响应函数是按照固定的格式创建的,其格式为:
def OnMouseAction(event, x, y, flags, param):
- event表示触发了何种事件,具体事件如表所示。
- x, y代表触发鼠标事件时,鼠标在窗口中的坐标(x, y)
- flags代表鼠标的拖曳事件,以及键盘鼠标联合事件
- param为函数ID,标识所响应的事件函数,相当于自定义一个OnMouseAction()函数的ID
- OnMouseAction为响应函数的名称,该名称可以自定义。
定义响应函数以后,要将该函数与一个特定的窗口建立联系(绑定),让该窗口内的鼠标触发事件时,能够找到该响应函数并执行。
要将函数与窗口绑定,可以通过函数cv2.setMouseCallback()实现,其基本语法格式是:
cv2.setMouseCallback(winname, onMouse)
- winname为绑定的窗口名。
- onMouse为绑定的响应函数名。
例子: 设计一个程序,对触发的鼠标事件进行判断:
import cv2 import numpy as np def Demo(event, x, y, flags, param): if event == cv2.EVENT_LBUTTONDOWN: print("单击了鼠标左键") elif event==cv2.EVENT_RBUTTONDOWN : print("单击了鼠标右键") elif flags==cv2.EVENT_FLAG_LBUTTON: print("按住左键拖动了鼠标") elif event==cv2.EVENT_MBUTTONDOWN : print("单击了中间键") # 创建名称为Demo的响应(回调)函数OnMouseAction # 将响应函数Demo与窗口“test”建立连接(实现绑定) img = np.ones((300,300,3), np.uint8)*255 cv2.namedWindow('test') cv2.setMouseCallback('test', Demo) cv2.imshow('test', img) cv2.waitKey() cv2.destroyAllWindows()
按住鼠标左键拖动鼠标,会依次触发单击左键事件“cv2.EVENT_LBUTTONDOWN”和左键拖动事件“cv2.EVENT_FLAG_LBUTTON”
例子: 实现一个双击鼠标绘制矩形的简单程序。
import cv2 import numpy as np d = 400 def draw(event, x, y, flags, param): if event==cv2.EVENT_LBUTTONDBLCLK: p1x=x p1y=y p2x=np.random.randint(1, d-50) p2y=np.random.randint(1, d-50) color = np.random.randint(0, high = 256, size = (3, )).tolist() cv2.rectangle(img, (p1x, p1y), (p2x, p2y), color,2) img = np.ones((d, d,3), dtype="uint8")*255 cv2.namedWindow('test') cv2.setMouseCallback('test', draw) while(1): cv2.imshow('test', img) # 刷新界面 if cv2.waitKey(20)==27: break cv2.destroyAllWindows()
**例子: ** 通过键盘与鼠标的组合控制显示不同的形状或文字。
import cv2 import numpy as np thickness=-1 mode=1 d=400 def draw_circle(event, x, y, flags, param): if event==cv2.EVENT_LBUTTONDOWN: a=np.random.randint(1, d-50) r=np.random.randint(1, d/5) angle = np.random.randint(0,361) color = np.random.randint(0, high = 256, size = (3, )).tolist() if mode==1: cv2.rectangle(img, (x, y), (a, a), color, thickness) elif mode==2: cv2.circle(img, (x, y), r, color, thickness) elif mode==3: cv2.line(img, (a, a), (x, y), color,3) elif mode==4: cv2.ellipse(img, (x, y), (100,150), angle, 0, 360, color, thickness) elif mode==5: cv2.putText(img, 'OpenCV', (0, round(d/2)),cv2.FONT_HERSHEY_SIMPLEX, 2, color,5) img=np.ones((d, d,3), np.uint8)*255 cv2.namedWindow('image') cv2.setMouseCallback('image', draw_circle) while(1): cv2.imshow('image', img) k=cv2.waitKey(1)&0xff if k==ord('r'): mode=1 elif k==ord('c'): mode=2 elif k==ord('l'): mode=3 elif k==ord('e'): mode=4 elif k==ord('t'): mode=5 elif k==ord('f'): thickness=-1 elif k==ord('u'): thickness=3 elif k==27: break cv2.destroyAllWindows()
滚动条
滚动条(Trackbar)在OpenCV中是非常方便的交互工具,它依附于特定的窗口而存在。
通过调节滚动条能够设置、获取指定范围内的特定值。
在OpenCV中,函数cv2.createTrackbar()用来定义滚动条,其语法格式为:
cv2.createTrackbar(trackbarname, winname, value, count, onChange)
- trackbarname为滚动条的名称。
- winname为滚动条所依附窗口的名称。
- value为初始值,该值决定滚动条中滑块的位置。
- count为滚动条的最大值。通常情况下,其最小值是0。
- onChange为回调函数。一般情况下,将滚动条改变后要实现的操作写在回调函数内。
注意: 该回调函数需要有一个参数,接收当前滚动条的值
函数cv2.createTrackbar()用于生成一个滚动条。拖动滚动条,就可以设置滚动条的值,并让滚动条返回对应的值。滚动条的值可以通过函数cv2.getTrackbarPos()获取,其语法格式为:
retval=getTrackbarPos( trackbarname, winname )
- retval为返回值,获取函数cv2.createTrackbar()生成的滚动条的值。
- trackbarname为滚动条的名称
- winname为滚动条所依附的窗口的名称。
用滚动条实现调色板
在RGB颜色空间中,任何颜色都是由红(R)、绿(G)、蓝(B)三种颜色构成的,每一种颜色分量的区间是[0, 255]。
用函数cv2.createTrackbar()和函数cv2.getTrackbarPos()设计一个模拟调色板:在窗体中,有三个滚动条分别用来设置R、G、B的值,调色板会根据当前的R、G、B值实时显示其所对应的颜色。
设计一个滚动条交互程序,通过滚动条模拟调色板效果。
import cv2 import numpy as np def changeColor(x): r=cv2.getTrackbarPos('R', 'image') g=cv2.getTrackbarPos('G', 'image') b=cv2.getTrackbarPos('B', 'image') img[:]=[b, g, r] img=np.zeros((100,700,3), np.uint8) cv2.namedWindow('image') cv2.createTrackbar('R', 'image',0,255, changeColor) cv2.createTrackbar('G', 'image',0,255, changeColor) cv2.createTrackbar('B', 'image',0,255, changeColor) while(1): cv2.imshow('image', img) k=cv2.waitKey(1)&0xff if k==27: break cv2.destroyAllWindows()
用滚动条控制阈值处理参数
设计一个滚动条交互程序,通过滚动条控制函数cv2.threshold()中的阈值和模式。
回顾一下函数cv2.threshold()的语法格式:
retval, dst=cv2.threshold(src, thresh, maxval, type)
- src:源图像。
- thresh:阈值。
- maxval:当type参数的值为THRESH_BINARY或者THRESH_BINARY_INV时,需要设定的最大值。
- type:阈值处理的方式(类型)
- dst:处理结果
- retval:返回的阈值
import cv2 Type=0 # 阈值处理方式 Value=0 # 使用的阈值 def onType(a): Type= cv2.getTrackbarPos(tType, windowName) Value= cv2.getTrackbarPos(tValue, windowName) ret, dst = cv2.threshold(o, Value,255, Type) cv2.imshow(windowName, dst) # 在这里显示 def onValue(a): Type= cv2.getTrackbarPos(tType, windowName) Value= cv2.getTrackbarPos(tValue, windowName) ret, dst = cv2.threshold(o, Value, 255, Type) cv2.imshow(windowName, dst) # 在这里显示 o = cv2.imread("./img/hand1.png",0) windowName = "test" #窗体名 cv2.namedWindow(windowName) cv2.imshow(windowName, o) # 创建两个滚动条 tType = "Type" # 用来选取阈值处理方式的滚动条 tValue = "Value" # 用来选取阈值的滚动条 cv2.createTrackbar(tType, windowName, 0, 4, onType) cv2.createTrackbar(tValue, windowName,0, 255, onValue) if cv2.waitKey(0) == 27: cv2.destroyAllWindows()
用滚动条作为开关
滚动条只有两种值“0”和“1”,当滚动条的值为0时,代表False;当滚动条的值为1时,代表True。
设计一个滚动条交互程序,用滚动条控制绘制的矩形是实心的还是空心的。
import cv2 import numpy as np d=400 global thickness thickness=-1 def fill(x): pass def draw(event, x, y, flags, param): if event==cv2.EVENT_LBUTTONDBLCLK: p1x=x p1y=y p2x=np.random.randint(1, d-50) p2y=np.random.randint(1, d-50) color = np.random.randint(0, high = 256, size = (3, )).tolist() cv2.rectangle(img, (p1x, p1y), (p2x, p2y), color, thickness) img=np.ones((d, d,3), np.uint8)*255 cv2.namedWindow('image') cv2.setMouseCallback('image', draw) cv2.createTrackbar('R', 'image',0,1, fill) while(1): cv2.imshow('image', img) g=cv2.getTrackbarPos('R', 'image') if g==0: thickness=-1 else: thickness=2 k=cv2.waitKey(1)&0xff if k==27: break cv2.destroyAllWindows()