绘图和交互是opencv中的GUI的重要知识。
一、绘图
用opencv绘制函数绘制图像时:
img参数都表示绘制图形的载体图形,有时也称画布、画板等。
color参数指绘制形状的颜色,使用的是BGR颜色模型,颜色通道顺序是BGR。
thickness参数表示线条的粗细,默认值是1,如果设置为-1,表示填充图形,就是绘制的图形是实心的。
lineType参数表示线条的类型。
shift参数表示精度,比如圆心坐标的精度,一般情况下这个参数不需要设置。
绘制直线
img = cv2.line(img, pt1, pt2, color[, thickness[, lineType]])
pt1,pt2分别是线段的起点和终点。注意:pt1,pt2都是用(x,y)的形式表示的,就是一个点的横纵坐标,但是这个坐标系和图形的行列标是正好相反的,就是:(x,y)--(列标,行标)
绘制矩形
img = cv2.rectangle(img, pt1, pt2, color[, thickness[, lineType]])
pt1,pt2分别是矩形的左上顶点和右下顶点。
绘制圆形
img = cv2.circle(img, center, radius, color[, thickness[, lineType]])
绘制椭圆
img = cv2.ellipse(img, center, axes, angle, startAngle, endAngle, color[, thickness[, lineType]])
center:椭圆的圆心坐标;axes:轴的长度;angle:是偏转的角度;startAngle:圆弧起始角的角度;endAngle:圆弧终结角的角度
绘制多边形
img = cv2.polylines(img, pts, isClosed, color[, thickness[, lineType[, shift]]])
pts:多边形的各个顶点;siClosed:是否封闭,当该参数=True时,将最后一个点和第一个点连接,让多边形闭合,否则,仅仅将各点依次连接起来构成一条曲线。
在图像上绘制文字
text = cv2.putText(img, text, org, fontFace, fontScale, color[, thickness[, lineType[, bottomLeftOrigin]]])
text:要绘制的字的内容;org:绘制的位置,以文字的左下角为起点;fontFace:字体类型;fontScale:字体大小;bottomLeftOrigin:控制文字方向,默认是False,当这个参数=True时,文字是垂直镜像的效果。
#例18.1 绘制各种形状
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = np.zeros((301,301,3), np.uint8)
#-----------绘制直线---------------------------#注意坐标点的表示,点用(列标,行标)表示
img1 = img.copy()
img1 = cv2.line(img1, (0,0), (300, 300), (255,0,0), 3)
img1 = cv2.line(img1, (0,100), (300,100), (0,255,0), 1)
img1 = cv2.line(img1, (100,0), (100,300), (0,0,255), 6)
#-----------绘制矩形------------------------------------
img2 = img.copy()
img2 = cv2.rectangle(img2, (50,50), (200,250), (0,0,255), -1)
#----------绘制圆形------------------------------------
img3 = img.copy()
for r in range(5, 150, 15): #绘制一组同心圆
img3 = cv2.circle(img3, (150,150), r, (255,255,255), 3)
img4 = img.copy()
for i in range(0, 100): #随机生成大小位置都随机的100个实心圆
x = np.random.randint(0,301)
y = np.random.randint(0,301)
r = np.random.randint(0, 20)
color = np.random.randint(0, 256, size=(3)).tolist()
img4 = cv2.circle(img4, (x,y), r, color, -1)
#----------随机绘制一组同心椭圆------------------------------------
img5 = img.copy()
for i in range(0,20):
angle = np.random.randint(0,361)
color = np.random.randint(0,256,size=(3)).tolist()
cv2.ellipse(img5, (150,150), (70,150), angle, 0, 360, color)
#----------绘制多边形------------------------------------
img6 = img.copy()
pts1 = np.array([[50,100],[100,220],[220,220],[240,80]]).reshape(-1, 1,2) #多边形的顶点坐标一定要是一个(顶点个数,1,2)的一个数组,而且这个数组的数据类型必须为np.int32的。
img6 = cv2.polylines(img6, [pts1], False, (0,255,0),2) #注意参数pts和控制是否闭合的第3个参数
pts2 = np.array([[40,90],[90,230],[230,230],[250,70]]).reshape(-1, 1,2)
img6 = cv2.polylines(img6, [pts2], True, (0,0,255),2)
#----------添加文字------------------------------------
img7 = img.copy()
img7 = cv2.putText(img7, 'OpenCV', (50,100), cv2.FONT_HERSHEY_SIMPLEX, 2, (0,255,0), 6) #参数依次是:画布、文字内容、文字的左下角坐标、字体、字的大小,字的颜色,字的粗细
img7 = cv2.putText(img7, 'OpenCV', (50,120), cv2.FONT_HERSHEY_SIMPLEX, 2, (0,0,255), 2)
img7 = cv2.putText(img7, 'OpenCV', (50,200), cv2.FONT_HERSHEY_SIMPLEX, 2, (0,0,255), 6, cv2.FONT_HERSHEY_SCRIPT_SIMPLEX, True) #镜像文字效果
#可视化:
plt.figure(figsize=(20,10))
plt.subplot(181), plt.imshow(img1[:,:,::-1])
plt.subplot(182), plt.imshow(img2[:,:,::-1])
plt.subplot(183), plt.imshow(img3[:,:,::-1])
plt.subplot(184), plt.imshow(img4[:,:,::-1])
plt.subplot(185), plt.imshow(img5[:,:,::-1])
plt.subplot(186), plt.imshow(img6[:,:,::-1])
plt.subplot(187), plt.imshow(img7[:,:,::-1])
plt.show()
二、鼠标交互
鼠标交互就是:opencv支持,在用opencv生成的桌面窗口里,用户在窗口上操作鼠标,也就是用户触发鼠标事件,这个窗口可以对用户的动作做出特定的响应。也就是通过鼠标的人机交互。比如,在窗口上,用户希望按一下鼠标左键就画一条直线,双击一下鼠标左键就出现一个圆,或者鼠标在窗口一移动就打印出鼠标的坐标等操作。
鼠标响应函数:def OnMouseAction(event, x, y, flags, param)
这个函数的函数名可以随便取,但是参数必须包含上面5个参数
event表示鼠标事件,比如鼠标移动、单击左键、右键等事件,比如,EVENT_MOUSEMOVE是鼠标移动事件,事件也可以用数字0表示,C++里面都是用数字表示。比如,EVENT_LBUTTONDOWN表示按下鼠标左键,就也可以用1表示。。。。。等等
x,y是发生鼠标事件的坐标点
flags是鼠标的组合按键,或者说是鼠标的组合操作,比如:EVENT_FLAG_LBUTTON按下左键,用1表示,EVENT_FLAG_CRTLKEY按下ctrl键和鼠标,也可以用8表示,,,等等。
param为函数ID,标识所响应的事件函数,相当于自定义一个OnMouseAction()函数的ID。或者说是用户传给cv2.setMouseCallback()的userdata参数,cv2.setMouseCallback()函数再把这个参数值传给OnMouseAction()函数的param参数。
实现鼠标交互的API是:cv2.setMouseCallback(winname, OnMouseAction, userdata)
这个函数也叫回调函数,就是给鼠标设置一个回调,意思就是一旦鼠标在窗口winname里做一个动作,比如单击左键,就要去执行OnMouseAction函数,而OnMouseAction函数就是定义鼠标事件对应的特定响应。所以这个API的参数OnMouseAction不是一个变量而是一个函数。
winname:是窗口的名字
OnMouseAction:是鼠标响应函数
userdata:是用户输入的外部参数。这个参数值会传给OnMouseAction()函数中的param参数。
小结:要实现人机通过鼠标交互:
第一,先要编写一个鼠标响应函数。这个函数的功能是:假设用户已经在显示器某个特定窗口触发了鼠标事件,这个函数可以返回一些参数和我们用户希望的结果。这个函数是我们自己定义的,比如def OnMouseAction()。
第二,编写回调函数。就是把第一步定义的函数和一个特定的窗口绑定,或者说是和某个特定的窗口建立联系,这样用户在这个特定窗口内触发鼠标事件时,这个窗口就能找到响应函数并执行。这样用户就能在电脑显示器屏幕上某个特定窗口里进行人机交互了。这个功能通过回调函数cv2.setMouseCallback()来实现。
##例18.2 熟悉鼠标交互函数的各个参数
#说明:
#本代码的功能是:生成一个新窗口,窗口展示一张黑图片,在这个窗口做任何的鼠标操作,就调用鼠标回调函数setMouseCallback(),执行这个函数就调用我们自定义的鼠标响应函数mouse_callback()
#然后打印出鼠标响应函数的各个参数,观察各个参数的含义
import cv2
import numpy as np
import matplotlib.pyplot as plt
def mouse_callback(event, x, y, flags, userdata): #定义鼠标响应函数,这个函数名可以随便取,但是5个位置参数不能随便写,它不是关键字参数,所以要知道每个参数的含义
print(event, x, y, flags, userdata) #把这个函数的参数打印出来观察一下这些参数的含义 A
if event ==2: #再执行一个功能,当按下鼠标右键就打印一串字符aaaa。按下鼠标右键的事件是EVENT_RBUTTONDOWN,数字表示是2
print('aaaaa')
cv2.namedWindow('mouse', cv2.WINDOW_NORMAL) #创建窗口
cv2.resizeWindow('mouse', 640, 360) #调整窗口大小
img = np.zeros((360, 640, 3), np.uint8) #生成一个全黑的图片,注意:这个图片的大小应该和窗口的大小一致,所以行列要交互位置!
cv2.setMouseCallback('mouse', mouse_callback, '123') #设置鼠标的回调函数,参数'123'是用户传入的用户数据,这个用户数据就会被传到mouse_callback的参数userdata里面。
while True: #一直循环去展示img这个图片
cv2.imshow('mouse', img) #展示img这个图片
key = cv2.waitKey(1) #图片显示1毫秒就可以退出了,我们接受退出返回的key
if key & 0xFF ==ord('q'): #如果key是q的ascii码就break
break
cv2.destroyAllWindows()
0 0 229 0 123 0 2 229 0 123 0 3 229 0 123 0 4 228 0 123 0 6 228 0 123 0 7 227 0 123 0 8 227 0 123 0 9 227 0 123 0 10 227 0 123 2 10 227 2 123 aaaaa 5 10 227 0 123 0 10 227 0 123 0 9 227 0 123 0 8 227 0 123 0 7 228 0 123 0 4 230 0 123
A:运行上面cell里面的代码,就会出现一个黑色的窗口,我们鼠标只要接触这个黑色窗口就开始打印鼠标事件。比如鼠标在窗口移动就触发鼠标移动事件,鼠标移动事件就被打印出来了,所以运行结果里面的第一个数字0就表示鼠标移动,假如我们还点击了鼠标左键(这里没操作是因为我们的waitkey设置的是1毫秒,就是每间隔1毫秒就监控一次鼠标,所以瞬间就会打印很多的鼠标信息,页面很快就被占满了,不方面显示)第一个数字就会是1,1表示按下鼠标左键,还会显示数字4,4表示左键释放。由于我们监控的时间极短,所以鼠标的任何动作事件都被打印出来了。
后面的第二个第三个数字表示鼠标的坐标;
第四个数字表示flags=0,表示没有组合事件;如果我们敲键盘上的shift键,这个数就会打印16,16表示按shift键;如果我们同时按shift键+鼠标左键,这个数就会是17,表示16+1,表示shift+左键。
第五个数字123就是我们用户传给回调函数的数据。
#例18.3 设计一个程序,对触发的鼠标事件进行判断
import cv2
import numpy as np
import matplotlib.pyplot as plt
#--------定义回调函数---------------------
def MouseAction(event, x, y, flags, param):
if event == cv2.EVENT_LBUTTONDBLCLK: #双击左键,注意:这个事件要放在单击左键的前面,否则没法执行这个代码块
px = np.random.randint(1,200)
py = np.random.randint(1,200)
color = np.random.randint(0,256, size=(2)).tolist()
cv2.rectangle(img, (x,y), (px,py), color, 2)
elif event == cv2.EVENT_LBUTTONDOWN:
print('您单击了鼠标左键')
elif event == cv2.EVENT_RBUTTONDOWN:
print('您单击了鼠标右键')
elif flags == cv2.EVENT_FLAG_LBUTTON: #注意:这里是赋值的是flags,因为是组合事件
print('您按住左键拖动了鼠标')
elif event == cv2.EVENT_MBUTTONDOWN:
print('您单击了鼠标中间键')
cv2.circle(img, (x,y), 100, 125)
#-------将回调函数和特定窗口绑定--------------
img = np.ones((600,600), np.uint8)*255
cv2.namedWindow('mouse action')
cv2.setMouseCallback('mouse action', MouseAction)
while True:
cv2.imshow('mouse action', img)
if cv2.waitKey(10)==97: #97是小写a的ascii码,27是esc的ascii码
break
cv2.destroyAllWindows()
您单击了鼠标中间键 您单击了鼠标左键 您按住左键拖动了鼠标 您按住左键拖动了鼠标 您按住左键拖动了鼠标
#例18.4 设计一个交互程序,运用waitKey函数来实现交互,即通过键盘和鼠标的组合控制显示不同的形状或文字
import cv2
import numpy as np
import matplotlib.pyplot as plt
thickness=-1
mode=1
d=400
def draw_circle(event, x, y, flags, param):
if event == cv2.EVENT_LBUTTONDOWN: #单击左键
a = np.random.randint(0, d-50)
r = np.random.randint(0, d/5)
angle = np.random.randint(0,361)
color = np.random.randint(0, 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, 'niuniu', (round(x),round(y)), 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 True:
cv2.imshow('image', img)
k=cv2.waitKey(1)&0xFF #用waitKey()函数来设计交互
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: #esc键
break
cv2.destroyAllWindows()
三、滚动条控件交互
滚动条TrackBar,是opencv中非常方便的交互工具,可以通过调节滚动条来设置、获取特定范围内的特定值。 滚动条也是依附特定的窗口而存在。
创建滚动条控件:cv2.createTrackbar(trackbarname, winname, value, count, function)
trackbarname:滚动条的名称
winname:滚动条所依附的窗口的名称
value:滚动条的最小值,也是滚动条的默认初始值
count:滚动条的最大值
function:滚动条改变后要实现的操作,就是滚动条函数要调用的函数。这个function是需要我们自己定义的一个函数,这个函数的功能是帮我们实现一些我们想要的操作。
获取滚动条的当前值:ret = getTrackbarPos(trackbarname, winname)
trackbarname:上面函数生成的滚动条的名称
winname:上面函数生成的滚动条所依附的窗口
#例18.5 通过滚动条控制三原色,模拟调色板效果
import cv2
import numpy as np
def changeColor(x):
r = cv2.getTrackbarPos('Rbar', 'colorchange')
g = cv2.getTrackbarPos('Gbar', 'colorchange')
b = cv2.getTrackbarPos('Bbar', 'colorchange')
img[:] = [b,g,r] #将Img的三个通道的值替换为bgr,[]表示替换原来的值
print(x)
img = np.zeros((500, 500, 3), np.uint8)
cv2.namedWindow('colorchange')
cv2.createTrackbar('Rbar', 'colorchange', 0, 255, changeColor)
cv2.createTrackbar('Gbar', 'colorchange', 0, 255, changeColor)
cv2.createTrackbar('Bbar', 'colorchange', 0, 255, changeColor)
while True:
cv2.imshow('colorchange', img)
k = cv2.waitKey(1)
if k==27: #esc键
break
cv2.destroyAllWindows()
#例18.6 用滚动条控制阈值处理参数
import cv2
ttype=0
value=0
def f_type(x):
ttype = cv2.getTrackbarPos('t_type', 'lena')
value = cv2.getTrackbarPos('t_value', 'lena')
ret, o = cv2.threshold(img, value, 255, ttype)
cv2.imshow('lena', o)
def f_value(x):
ttype = cv2.getTrackbarPos('t_type', 'lena')
value = cv2.getTrackbarPos('t_value', 'lena')
ret, o = cv2.threshold(img, value, 255, ttype)
cv2.imshow('lena', o)
img = cv2.imread(r'C:\Users\25584\Desktop\lena.bmp',0)
cv2.namedWindow('lena')
cv2.imshow('lena', img)
cv2.createTrackbar('t_type', 'lena', 0,4, f_type)
cv2.createTrackbar('t_value', 'lena', 0, 255, f_value)
if cv2.waitKey(0) == 27:
cv2.destroyAllWindows()
#例18.7 用滚动条作为开关使用
import cv2
import numpy as np
thickness=-1
def kaiguan(x):
pass
def draw(event, x, y, flags, param):
if event == cv2.EVENT_LBUTTONDBLCLK:
px = x
py = y
r = np.random.randint(1, 100)
color = np.random.randint(0, 256, size=(3)).tolist()
cv2.circle(img, (x,y), r, color, thickness)
img = np.ones((500, 500, 3), np.uint8)*255
cv2.namedWindow('hua ban')
cv2.setMouseCallback('hua ban', draw)
cv2.createTrackbar('bar', 'hua ban', 0, 1, kaiguan)
while True:
cv2.imshow('hua ban', img)
k = cv2.waitKey(1)
g = cv2.getTrackbarPos('bar', 'hua ban')
if g == 0:
thickness = -1
else:
thickness = 2
if k==27:
break
cv2.destroyAllWindows()