【OpenCV中文手册学习-4】基于OpenCV-Python的鼠标绘图
2021/5/8 20:27:14
本文主要是介绍【OpenCV中文手册学习-4】基于OpenCV-Python的鼠标绘图,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
文章目录
- 1. 前言
- 2. 程序分析
- 2.1 轨迹栏
- 2.1.1 函数原型
- 2.1.2 创建窗口
- 2.1.3 添加轨迹栏
- 2.2 画布
- 2.3 鼠标监听事件
- 2.4 主循环
- 2.5 绘图逻辑
- 3. 程序功能演示
- 4. 程序完整代码
1. 前言
本文给出一个鼠标绘图的综合程序。程序由两部分组成:一是参数设置,二是画板显示。参数设置包括画笔颜色、画笔大小和绘制图形。绘制图像可以在直线、矩形、圆形、曲线中进行选择。画布默认是黑色的,初始画笔默认是白色的,绘制图形默认为直线。画笔大小默认为1px,最大画笔大小为5px。如果画笔大小设置为0,代表以填充方式绘制图形。
2. 程序分析
2.1 轨迹栏
2.1.1 函数原型
createTrackbar(trackbarName,windowName,value,count,onChange)
- trackbarName : 轨迹栏名称
- windowName : 轨迹栏所在窗口名称
- value : 轨迹栏初始值
- count : 轨迹栏取值个数,轨迹栏取值从0开始逐1递增
- onChange : 轨迹栏值发生改变时的回调函数
2.1.2 创建窗口
轨迹栏必须存在一个窗口中,所以要先创建一个窗口才能添加轨迹栏
cv.namedWindow('image')
2.1.3 添加轨迹栏
# 轨迹栏发生变化时的回调函数 def nothing(x): pass # 创建颜色变化的轨迹栏 cv.createTrackbar('R','image',255,255,nothing) cv.createTrackbar('G','image',255,255,nothing) cv.createTrackbar('B','image',255,255,nothing) # 根据names生成选项 def get_choice_str(names): values = np.arange(0,len(names)) choices = dict(zip(names,values)) choice_str = [f'{key}:{value}' for (key,value) in choices.items()] choice_str = '\n'.join(choice_str) return choice_str # 创建绘制方式选择轨迹栏 shape_choice_str = get_choice_str(['line',"rectangle","circle","curve"]) cv.createTrackbar(shape_choice_str,'image',0,3,nothing) # 创建填充方式选择轨迹栏 size_choice_str = "pen size(0 is solid)" cv.createTrackbar(size_choice_str,'image',1,5,nothing)
2.2 画布
# 创建黑色画布 img = np.zeros((300,512,3), np.uint8)
创建画布后不用程序员手动添加到窗口中,当调用 imshow 函数时,画布会自动添加到窗口中。
2.3 鼠标监听事件
添加鼠标监听事件步骤
- 定义鼠标事件回调函数(即发生鼠标事件后调用的函数)
- 将第一步定义的函数绑定一个窗口
# 鼠标回调函数 def mouse_call(event,x,y,flags,param): # 鼠标左键按下 if event == cv.EVENT_LBUTTONDOWN: # do something1... pass # 鼠标移动 elif event == cv.EVENT_MOUSEMOVE: # do something2... pass # 鼠标左键抬起 elif event == cv.EVENT_LBUTTONUP: # do something3... pass # 监听鼠标事件 # image 为窗口名称 cv.setMouseCallback('image',mouse_call)
2.4 主循环
- 以全局变量的形式保存绘图参数
- 与轨迹栏有关的绘图参数包括:画笔颜色、画笔大小、绘图模式
- 与画布有关的绘图参数包括:画布(图像)、直线或矩形或圆的绘制起始点坐标、直线或矩形或圆上一次绘图的终点(防止线框模式重复绘图);
- 每1ms重新显示一次画布;
- 每1ms获取一次轨迹栏中的值对轨迹栏有关绘图参数进行更新;
- 如果用户按下 Esc 键将退出主循环;
- 画布中图形的绘制是在鼠标监听线程中完成的,主循环只负责画布的显示
while True: cv.imshow('image',img) k = cv.waitKey(1) if k == 27: break r = cv.getTrackbarPos('R','image') g = cv.getTrackbarPos('G','image') b = cv.getTrackbarPos('B','image') mode = cv.getTrackbarPos(shape_choice_str,'image') pen_size = cv.getTrackbarPos(size_choice_str,'image') cv.destroyAllWindows()
2.5 绘图逻辑
- 绘制直线:鼠标左键按下时记录直线起点
(ix,iy)
,在鼠标移动时获取当前鼠标坐标(x,y)
作为直线终点,然后利用line
函数绘制直线; - 绘制矩形:鼠标左键按下时记录矩形左上角坐标
(ix,iy)
,在鼠标移动时获取当前鼠标坐标(x,y)
作为矩形右下角坐标,然后利用rectangle
函数绘制矩形; - 绘制圆形:鼠标左键按下时记录矩形圆上一点坐标
(ix,iy)
,在鼠标移动时获取当前鼠标坐标(x,y)
作为圆心坐标,然后利用circle
函数绘制圆形; - 绘制曲线:在鼠标移动时获取当前鼠标坐标
(x,y)
作为圆心坐标,绘制以画笔大小pen_size
为半径的小圆,这些小圆连起来就是曲线。
按照上面的绘图逻辑,在以填充模式绘制图形时没有问题,但是若以线框方式绘制图形时,会在鼠标移动过程中画出很多重复图形,如下图所示。
为了解决上述问题,本程序在鼠标事件回调函数中记录了上一次绘制终点(px,py)
这个变量。对于非填充模式的绘制,在绘制前,先用背景色将上一次绘制图形进行一次覆盖。以直线为例给出下列代码。
bg_color = (0,0,0) if pen_size >= 1: cv.line(img,(ix,iy),(px,py),bg_color,pen_size) cv.line(img,(ix,iy),(x,y),color,pen_size)
绘图逻辑完整代码
# 全局变量 drawing = False # 如果按下鼠标,则为真 mode = 3 # 默认为曲线模式 pen_size = 0 # 默认为实心 ix,iy = 0,0 # 直线或矩形的起始点坐标/圆的圆心 px,py = 0,0 # 直线、矩形或圆上一次绘图的终点(防止线框模式重复绘图) img = None # 图像 b,g,r = 255,255,255 # 画笔初始颜色 # 绘制图像 def draw(mode,color,pen_size,ix,iy,x,y,px,py): global img bg_color = (0,0,0) if mode == 0: if pen_size >= 1: cv.line(img,(ix,iy),(px,py),bg_color,pen_size) cv.line(img,(ix,iy),(x,y),color,pen_size) elif mode == 1: if pen_size >= 1: cv.rectangle(img,(ix,iy),(px,py),bg_color,pen_size) cv.rectangle(img,(ix,iy),(x,y),color,pen_size) elif mode == 2: if pen_size >= 1: radius = int(((px-ix)**2 + (py-iy)**2)**0.5) cv.circle(img,(px,py),radius,bg_color,pen_size) radius = int(((x-ix)**2 + (y-iy)**2)**0.5) cv.circle(img,(x,y),radius,color,pen_size) elif mode == 3: cv.circle(img,(x,y),pen_size,color,pen_size) # 鼠标回调函数 def mouse_call(event,x,y,flags,param): global ix,iy,px,py,drawing,mode,pen_size,img,b,g,r if event == cv.EVENT_LBUTTONDOWN: drawing = True ix,iy = x,y elif event == cv.EVENT_MOUSEMOVE: if drawing == True: brush = pen_size if pen_size <= 0: brush = -1 draw(mode,(b,g,r),brush,ix,iy,x,y,px,py) elif event == cv.EVENT_LBUTTONUP: drawing = False brush = pen_size if pen_size <= 0: brush = -1 draw(mode,(b,g,r),brush,ix,iy,x,y,px,py) px,py = x,y
3. 程序功能演示
4. 程序完整代码
import cv2 as cv import numpy as np drawing = False # 如果按下鼠标,则为真 mode = 3 # 默认为曲线模式 pen_size = 0 # 默认为实心 ix,iy = 0,0 # 直线或矩形的起始点坐标/圆的圆心 px,py = 0,0 # 直线、矩形或圆上一次绘图的终点(防止线框模式重复绘图) img = None # 图像 b,g,r = 255,255,255 # 画笔初始颜色 # 根据names生成选项 def get_choice_str(names): values = np.arange(0,len(names)) choices = dict(zip(names,values)) choice_str = [f'{key}:{value}' for (key,value) in choices.items()] choice_str = '\n'.join(choice_str) return choice_str # 轨迹栏发生变化时的回调函数 def nothing(x): pass # 绘制图像 def draw(mode,color,pen_size,ix,iy,x,y,px,py): global img bg_color = (0,0,0) if mode == 0: if pen_size >= 1: cv.line(img,(ix,iy),(px,py),bg_color,pen_size) cv.line(img,(ix,iy),(x,y),color,pen_size) elif mode == 1: if pen_size >= 1: cv.rectangle(img,(ix,iy),(px,py),bg_color,pen_size) cv.rectangle(img,(ix,iy),(x,y),color,pen_size) elif mode == 2: if pen_size >= 1: radius = int(((px-ix)**2 + (py-iy)**2)**0.5) cv.circle(img,(px,py),radius,bg_color,pen_size) radius = int(((x-ix)**2 + (y-iy)**2)**0.5) cv.circle(img,(x,y),radius,color,pen_size) elif mode == 3: cv.circle(img,(x,y),pen_size,color,pen_size) # 鼠标回调函数 def mouse_call(event,x,y,flags,param): global ix,iy,px,py,drawing,mode,pen_size,img,b,g,r if event == cv.EVENT_LBUTTONDOWN: drawing = True ix,iy = x,y elif event == cv.EVENT_MOUSEMOVE: if drawing == True: brush = pen_size if pen_size <= 0: brush = -1 draw(mode,(b,g,r),brush,ix,iy,x,y,px,py) elif event == cv.EVENT_LBUTTONUP: drawing = False brush = pen_size if pen_size <= 0: brush = -1 draw(mode,(b,g,r),brush,ix,iy,x,y,px,py) px,py = x,y # 绘图程序 def test(): global mode,pen_size,img,b,g,r # 创建一个窗口 cv.namedWindow('image') # 创建颜色变化的轨迹栏 cv.createTrackbar('R','image',255,255,nothing) cv.createTrackbar('G','image',255,255,nothing) cv.createTrackbar('B','image',255,255,nothing) # 创建绘制方式选择轨迹栏 shape_choice_str = get_choice_str(['line',"rectangle","circle","curve"]) cv.createTrackbar(shape_choice_str,'image',0,3,nothing) # 创建填充方式选择轨迹栏 size_choice_str = "pen size(0 is solid)" cv.createTrackbar(size_choice_str,'image',1,5,nothing) # 创建黑色画布 img = np.zeros((300,512,3), np.uint8) # 监听鼠标事件 cv.setMouseCallback('image',mouse_call) while True: cv.imshow('image',img) k = cv.waitKey(1) if k == 27: break r = cv.getTrackbarPos('R','image') g = cv.getTrackbarPos('G','image') b = cv.getTrackbarPos('B','image') mode = cv.getTrackbarPos(shape_choice_str,'image') pen_size = cv.getTrackbarPos(size_choice_str,'image') cv.destroyAllWindows() if __name__ == "__main__": test()
这篇关于【OpenCV中文手册学习-4】基于OpenCV-Python的鼠标绘图的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-21Python编程基础教程
- 2024-11-20Python编程基础与实践
- 2024-11-20Python编程基础与高级应用
- 2024-11-19Python 基础编程教程
- 2024-11-19Python基础入门教程
- 2024-11-17在FastAPI项目中添加一个生产级别的数据库——本地环境搭建指南
- 2024-11-16`PyMuPDF4LLM`:提取PDF数据的神器
- 2024-11-16四种数据科学Web界面框架快速对比:Rio、Reflex、Streamlit和Plotly Dash
- 2024-11-14获取参数学习:Python编程入门教程
- 2024-11-14Python编程基础入门