绘图及交互
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))
red = (0,0,255)
for r in range(5, round(d/2),12):
cv2.circle(img, (centerX, centerY), r, red,3)
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))
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()
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)
pts=pts.reshape((-1,1,2))
cv2.polylines(img, [pts], True, (0,255,0),8)
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]]])
使用函数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("单击了中间键")
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()
|