与另一个计算机视觉系列相对应,本系列主要探索OpenCV的具体操作。
学习资源:官网教程
https://docs.opencv.org/4.x/d6/d00/tutorial_py_root.htmlhttps://docs.opencv.org/4.x/d6/d00/tutorial_py_root.html
所有源码均上传至仓库:
https://github.com/xurongtang/OpenCV-pythonhttps://github.com/xurongtang/OpenCV-python
今天介绍前两章:Introduction to OpenCV 和 Gui Features in OpenCV
一、介绍
对应第一章
#简单介绍
基于OpenCV-python的官方文档进行opencv自学
https://docs.opencv.org/4.x/d6/d00/tutorial_py_root.html##其原文为
OpenCV introduces a new set of tutorials which will guide you through various functions available in OpenCV-Python. This guide is mainly focused on OpenCV 3.x version (although most of the tutorials will also work with OpenCV 2.x).
Prior knowledge of Python and Numpy is recommended as they won't be covered in this guide. Proficiency with Numpy is a must in order to write optimized code using OpenCV-Python.
This tutorial was originally started by Abid Rahman K. as part of the Google Summer of Code 2013 program under the guidance of Alexander Mordvintsev.
可见,即使OpenCV版本已经4.x,但官方给出的教程还是3.x,此教程还是以最新版4.10做学习。
##环境搭建
关键命令:
创建虚拟环境
conda create -n your_env_name python=3.10
配置国内源
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
将虚拟环境上传至Jupyter
python -m ipykernel install --user --name your_env_name --display-name "Your Env Display Name"
安装成功示例:
import cv2
print(cv2.__version__)
二、图像入门
对应:Gui Features in OpenCV——Getting Started with Images
加载、展示、保存
如何读入(cv2::imread)图像
如何展示(cv2::imshow)图像
如何写入(cv2::imwrite)图像
2.1 读入
import cv2
# 加载图像 主要参数文件路径
filepath = "demo.png"
# cv2支持读取多种类型的数据 如tif,jpg,png,jpeg,tiff等
img = cv2.imread(filepath)
第二个参数,图像的格式,“The second argument is optional and specifies the format in which we want the image”. 第二个参数常用的有:
IMREAD_COLOR loads the image in the BGR 8-bit format. This is the default that is used here. IMREAD_UNCHANGED loads the image as is (including the alpha channel if present)。IMREAD_GRAYSCALE loads the image as an intensity one
img_1 = cv2.imread(filepath,cv2.IMREAD_COLOR) # 彩色
img_2 = cv2.imread(filepath,cv2.IMREAD_UNCHANGED)
img_3 = cv2.imread(filepath,cv2.IMREAD_GRAYSCALE) # 灰白
print(type(img),type(img_1),type(img_2),type(img_3))
# <class 'numpy.ndarray'> <class 'numpy.ndarray'> <class 'numpy.ndarray'> <class 'numpy.ndarray'>
# 自然就可以进行索引切片之类的操作
# img.shape,img_3.shape
# ((144, 156, 3), (144, 156))
# img[:10,:10,0]
# array([[184, 182, 181, 181, 179, 179, 179, 179, 179, 179],
# [183, 182, 181, 181, 179, 179, 179, 179, 179, 179],
# [183, 182, 181, 181, 179, 179, 179, 179, 179, 179],
# [183, 182, 181, 181, 179, 179, 179, 179, 179, 179],
# [183, 182, 181, 181, 179, 179, 179, 179, 179, 179],
# [183, 182, 181, 181, 178, 179, 179, 179, 179, 179],
# [181, 180, 179, 179, 176, 178, 179, 179, 180, 181],
# [180, 179, 179, 179, 178, 180, 181, 181, 181, 181],
# [179, 179, 178, 179, 179, 180, 181, 181, 181, 181],
# [179, 179, 178, 179, 179, 182, 182, 182, 182, 182]], dtype=uint8)
2.2 展示
# 第一个参数代表展示的框的名称,第二个参数代表传入的矩阵(图像)
cv2.imshow("normal read",img)
# 单一的马上图像显示后就会消失,此次先引用后续维持,传入参数0表示维持窗口forever
k = cv2.waitKey(0)
# 尝试不同方式读入的图片(单通道的灰度图片)
cv2.imshow("gray read",img_3)
k = cv2.waitKey(0)
2.3 写入
# 写入
# 参数:路径,矩阵
# 正常保存
cv2.imwrite("same_save.png", img)
# 任意截取保存
cv2.imwrite("slice_save.png",img[20:80,20:80,:])
# 保留单通道
cv2.imwrite("single_channel.png",img_3)
三、视频入门
对应Getting_Started_with_Videos
3.1 从相机获取视频
# 从相机获得视频
# 关键点1 使用函数创建对象(object)
# 关键点2 传入的参数代表设备的第index个摄像头
# 关键点3 末尾记得释放(destory)
# 关键方法:cv2.VideoCapture,(object).read(),(object).release()
import numpy as np
import cv2
# 创建视频流获取对象
cap = cv2.VideoCapture(0)
# 判断数据流是否打开,此刻还没有抓取设备的相机视频流
if not cap.isOpened():
print("Cannot open camera")
exit()
else:
cap.open(0)
# 创建工作循环
while True:
# 抓取视频流
ret, frame = cap.read()
# 返回值ret为bool类型,代表是否成果抓取
# frame则为返回的视频流
# 未能成功抓取则break循环
if not ret:
print("Can't receive frame (stream end?). Exiting ...")
break
# 将视频转换为黑白视频(此处表示可以对视频色彩格式进行设置,直接imshow将得到原本的彩色视频)
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 展示
cv2.imshow('frame', frame)
# 桌面窗口的控制,在界面输入'q'则break循环
# cv.waitKey(1):在显示每一帧时等待1毫秒,若在此期间有键被按下,则返回该键的ASCII码;否则返回 -1。
# ord('q'):返回字符 'q' 的ASCII码,用于与按键的ASCII码进行比较
if cv2.waitKey(1) == ord('q'):
break
# When everything done, release the capture
cap.release()
cv2.destroyAllWindows()
可以通过.get()和.set()在显示前提前设置视频属性
cap_1 = cv2.VideoCapture(0)
print(cap_1.get(3)) # 0-18表示预设的参数,如3表示宽
cap_1.set(3,320)
while True:
_,temp = cap_1.read()
cv2.imshow("demo",temp)
if cv2.waitKey(1) == ord('q'):
break
print(cap_1.get(3))
cap_1.release()
cv2.destroyAllWindows()
3.2 从文件读取视频
# 支持多类型视频文件
cap_file = cv2.VideoCapture('demo.mp4')
不选择第index个摄像头,选择文件路径。
3.3 保存视频
基本的视频保存逻辑:首先实例化一个VideoWriter类的对象,第一个参数是输出路径,第二个参数是视频编码格式,第三个参数是视频帧数,第四个参数是视频尺寸。
使用该对象write方法对视频流进行保存
cap_save = cv2.VideoCapture(0)
# Define the codec and create VideoWriter object
fourcc = cv2.VideoWriter_fourcc(*'XVID')
out = cv2.VideoWriter('output.avi', fourcc, 20.0, (640, 480))
while cap_save.isOpened():
ret, frame = cap_save.read()
if not ret:
print("Can't receive frame (stream end?). Exiting ...")
break
# 图像翻转函数,第一个参数表示图像,第二个参数表示翻转标识,0为垂直翻转,1为水平翻转。
frame = cv2.flip(frame, 0)
# write the flipped frame
out.write(frame)
cv2.imshow('frame', frame)
if cv2.waitKey(1) == ord('q'):
break
# Release everything if job is finished
cap_save.release()
out.release()
cv2.destroyAllWindows()
四、绘图
首先创建背景图片
import numpy as np
import cv2
# 创建图像
# 如先前所述,图像是一个三维矩阵,那么创建三维矩阵也就创建了一个图像
img = np.zeros((512,512,3),np.uint8)
4.1 画线
# 对角的蓝色的5个像素的对角线
cv2.line(img,(0,0),(511,511),(255,0,0),5)
# 自定义绘制
cv2.line(img,(0,0),(235,500),(12,234,24),10)
cv2.imshow("demo",img)
cv2.waitKey(0)
# 其参数为,图像、线条端点1、线条端点2、颜色、像素大小,图像坐标系统为横着x竖着y。
# 该方法即对参数本身进行更改,同时也有返回值
ret = cv2.line(img,(0,0),(235,500),(12,234,24),10)
np.all(ret == img)
4.2 画矩形
# 示范1
cv2.rectangle(img,(384,0),(510,128),(0,255,0),3)
# 示范2
cv2.rectangle(img,(200,0),(261,89),(100,100,100),6)
# 参数为 图像、左上顶点坐标、右下顶点坐标、颜色、像素大小
cv2.imshow("rectangle",img)
cv2.waitKey(0)
4.3 画圆
# 参数:图像、圆心、半径、颜色、线条(像素)大小
# 示范1(线条大小设置为-1表示对整个圆进行填充)
cv2.circle(img,(447,63), 63, (0,0,255), -1)
# 示范2
cv2.circle(img,(261,89),40,(255,0,0),2)
cv2.imshow("circle",img)
cv2.waitKey(0)
4.4 画椭圆
# 参数:图像、椭圆中心点、(长轴长,短轴长)、旋转角度、起始(绘制)角度、截至(绘制)角度、大小
# 提示:关于理解起始绘制角度、截至绘制角度和旋转角度需要时刻注意的是牢记图像坐标轴规则。
# 示范1
cv2.ellipse(img,(256,256),(100,50),0,0,180,255,-1)
# 示范2
cv2.ellipse(img,(256,300),(80,40),0,0,360,(0,255,0),2)
# 示范3
cv2.ellipse(img,(100,300),(30,10),45,0,270,(0,0,255),4)
cv2.imshow("ellipse",img)
cv2.waitKey(0)
4.5 画多边形(有闭合检查)
# 参数:图像、各个多边形的点、是否闭合、颜色、线条大小
# 示范1
point_ls = np.array([[10,55],[20,80],[70,70],[50,60]],np.int32)
print(point_ls.shape)
ps = point_ls.reshape((-1,1,2))
print(ps.shape)
cv2.polylines(img,[ps],True,(255,0,255),3)
cv2.imshow("polylines",img)
cv2.waitKey(0)
4.6 画文本框
font = cv2.FONT_HERSHEY_SIMPLEX
# 参数:图像、文本内容、文本框的左下顶点、字体类型、字体大小、线条颜色、线条粗细、线条样式
cv2.putText(img,'OpenCV',(10,500), font, 4,(255,255,255),2,cv2.LINE_AA)
cv2.imshow("polylines",img)
cv2.waitKey(0)
五、鼠标事件
鼠标事件汇总
# 查看鼠标事件
import cv2
import numpy as np
event_ls = [i for i in dir(cv2) if 'EVENT' in i]
event_ls
结果:
['EVENT_FLAG_ALTKEY', 'EVENT_FLAG_CTRLKEY', 'EVENT_FLAG_LBUTTON', 'EVENT_FLAG_MBUTTON', 'EVENT_FLAG_RBUTTON', 'EVENT_FLAG_SHIFTKEY', 'EVENT_LBUTTONDBLCLK', 'EVENT_LBUTTONDOWN', 'EVENT_LBUTTONUP', 'EVENT_MBUTTONDBLCLK', 'EVENT_MBUTTONDOWN', 'EVENT_MBUTTONUP', 'EVENT_MOUSEHWHEEL', 'EVENT_MOUSEMOVE', 'EVENT_MOUSEWHEEL', 'EVENT_RBUTTONDBLCLK', 'EVENT_RBUTTONDOWN', 'EVENT_RBUTTONUP']EVENT_FLAG_ALTKEY: 表示Alt键被按下的状态,通常用于判断同时按下了Alt键的组合操作。
EVENT_FLAG_CTRLKEY: 表示Ctrl键被按下的状态,通常用于判断同时按下了Ctrl键的组合操作。
EVENT_FLAG_LBUTTON: 表示左鼠标按钮的状态,通常用于检测左键相关的操作。
EVENT_FLAG_MBUTTON: 表示中间鼠标按钮(滚轮)的状态,通常用于检测中键相关的操作。
EVENT_FLAG_RBUTTON: 表示右鼠标按钮的状态,通常用于检测右键相关的操作。
EVENT_FLAG_SHIFTKEY: 表示Shift键被按下的状态,通常用于判断同时按下了Shift键的组合操作。
EVENT_LBUTTONDBLCLK: 表示左鼠标按钮的双击事件。
EVENT_LBUTTONDOWN: 表示左鼠标按钮被按下的事件。
EVENT_LBUTTONUP: 表示左鼠标按钮被释放的事件。
EVENT_MBUTTONDBLCLK: 表示中鼠标按钮的双击事件。
EVENT_MBUTTONDOWN: 表示中鼠标按钮被按下的事件。
EVENT_MBUTTONUP: 表示中鼠标按钮被释放的事件。
5.1 示例1 (双击画填充圆,单击画圆圈)
# mouse callback function(定于鼠标的回调函数)
# 函数传入的是:事件类型、(事件发生的)坐标等
# 事件类型名称解析:EVENT_LBUTTONDBLCLK可理解为:event、L(左)、Button(按键)、DB(两次)、CLK(click)
def draw_circle(event,x,y,flags,param):
if event == cv2.EVENT_LBUTTONDBLCLK:
# 参数列表:图像、圆心、半径、颜色、线条大小(或是否填充)
cv2.circle(img,(x,y),100,(255,0,0),-1)
# 可自定义类型:单击绘制无填充的圆
if event == cv2.EVENT_LBUTTONDOWN:
cv2.circle(img,(x,y),50,(0,255,-1),2)
# Create a black image, a window and bind the function to window
img = np.zeros((512,512,3), np.uint8)
cv2.namedWindow('image') # 相当于新建一个名为‘image’的窗口
# 将窗口于事件进行绑定。这其中定义窗口使用的是“窗口名”进行对应。
cv2.setMouseCallback('image',draw_circle)
while(1):
# 将img展示到(已经定义好的)‘image’窗口中
cv2.imshow('image',img)
if cv2.waitKey(20) & 0xFF == 27:
break
cv2.destroyAllWindows()
5.2 进阶示例(不同模式切换)
drawing = False # true if mouse is pressed
mode = True # if True, draw rectangle. Press 'm' to toggle to curve
ix,iy = -1,-1
# mouse callback function
# 当你的图像窗口展示出来之后,每一个单位时间你的鼠标都在向窗口发送EVENT
def draw_circle(event,x,y,flags,param):
global ix,iy,drawing,mode
# 如果左键按下(还未松开),则开始绘制
if event == cv2.EVENT_LBUTTONDOWN:
drawing = True
ix,iy = x,y
# 鼠标移动
elif event == cv2.EVENT_MOUSEMOVE:
if drawing == True:
if mode == True:
# 绘制左键按下时的坐标和(移动的)当前坐标为顶点所构成的矩形
cv2.rectangle(img,(ix,iy),(x,y),(0,255,0),1)
else:
cv2.circle(img,(x,y),5,(0,0,255),-1)
# 鼠标抬起,结束绘制,
elif event == cv2.EVENT_LBUTTONUP:
drawing = False
if mode == True:
cv2.rectangle(img,(ix,iy),(x,y),(0,255,0),-1)
else:
cv2.circle(img,(x,y),5,(0,0,255),-1)
# 测试
img = np.zeros((512,512,3), np.uint8)
cv2.namedWindow('image')
cv2.setMouseCallback('image',draw_circle)
while(1):
cv2.imshow('image',img)
k = cv2.waitKey(1) & 0xFF
if k == ord('m'):
mode = not mode
elif k == 27:
break
cv2.destroyAllWindows()
六、滑动条处理
创建一个简单的应用来展示你指定的颜色,滑动轨迹条对应的就能该表颜色,默认情况下初始颜色是黑色。
cv2.createTrackbar(),第一个参数是滑动条名字,第二个是想要附着的窗口的名字,第三个是默认取值,第四个是最大值,第五个是当滑动条数值改变时会触发的回调函数(回调函数总是有个滑动条位置的默认参数)。
另一个重要的应用是使用有个0-1的trackbar来作为有个按钮或者开关,默认情况下opencv中没有按钮功能,因此可以使用0-1的trackbar来实现类似的功能。
###
# 主要方法:cv2.createTrackbar与cv2.getTrackbarPos
###
import numpy as np
import cv2
# 定义回调函数
def nothing(x):
pass
# Create a black image, a window
# 创建背景图片
img = np.zeros((300,512,3), np.uint8)
# 创建窗口
cv2.namedWindow('image')
# create trackbars for color change
# 创建滑动条
cv2.createTrackbar('R','image',0,255,nothing)
cv2.createTrackbar('G','image',0,255,nothing)
cv2.createTrackbar('B','image',0,255,nothing)
# create switch for ON/OFF functionality
switch = '0 : OFF \n1 : ON'
cv2.createTrackbar(switch, 'image',0,1,nothing)
while(1):
cv2.imshow('image',img)
k = cv2.waitKey(1) & 0xFF
if k == 27:
break
# get current positions of four trackbars
r = cv2.getTrackbarPos('R','image')
g = cv2.getTrackbarPos('G','image')
b = cv2.getTrackbarPos('B','image')
s = cv2.getTrackbarPos(switch,'image')
# r,g,b均为单个int类型
if s == 0:
img[:] = 0
else:
img[:] = [b,g,r]
cv2.destroyAllWindows()
上述代码配合注释解释性较强,若还有不懂之处,可github下载源码自行运行。
以上。
共勉。