【2】图像视频的加载和显示
文章目录
- 【2】图像视频的加载和显示
- 一、代码在哪写
- 二、创建和显示窗口
- (一)导入OpenCV的包`cv2`
- (二)创建窗口
- (三)更改窗口大小 & 显示窗口
- (四)等待用户输入
- 补充:ord()函数来返回ASCII值
- (五)销毁窗口
- (※)完整代码
- 二、加载显示图片
- (一)cv2.imread()返回值
- (二)用matplotlib显示图片
- (三)用OpenCV显示图片
- (1)封装函数
- (2)封装外部.py文件
- (四)保存图片
- 三、视频采集和录制
- (一)视频采集
- (二)视频录制
- 四、控制鼠标
- 五、TrackBar控件
【2】图像视频的加载和显示
一、代码在哪写
注:上一节中已经创建好了虚拟环境。
打开Anaconda Prompt,切换到我们已经创建好的opencv
虚拟环境下。
我们使用jupyter notebook来写代码。(输入jupyter notebook
)
之后,就弹出一个控制台页面。
弹出的这些东西就不太对了。
为什么会弹出这一堆东西,原因在于:你在哪个目录下执行的jupyter notebook
命令,它就会以哪个目录作为根目录。
可见,此时这一堆东西都是我的C:\Users\11202
目录下的文件,这些文件肯定都是不能动的,我们也不应该以这里作为我们写代码的目录。
也就是说,上述是一个错误的演示。
正确的应该怎么做呢?如下。
如果是继续刚才的命令往下写,那么需要按两下ctrl+C
把刚才打开的jupyter notebook先退出。
如果是关掉,又新打开的Anaconda Prompt,则不要忘了使用activate opencv先跳转到需要的虚拟环境下。
然后我们使用cd
命令,打开到我们想要作为根目录的路径(我的是D:\ANACONDA\dirs\test2409
),然后再使用命令jupyter notebook
。
注意:从C盘跳转到D盘时,不要写成cd D:
,而是直接输入D:
。之后,跳转到D盘后,再使用cd
去打开对应文件夹。
这时,再跳转到的控制页面就是我们想要的了。之后我们可以在里面新建一个notebook,就可以写代码了。
二、创建和显示窗口
namedWindow()
创建命名窗口imshow()
显示窗口destroyAllwindows()
摧毁窗口resizeWindow()
改变窗口大小waitKey()
等待用户输入
我们依次试一下其效果。
(一)导入OpenCV的包cv2
注意:OpenCV名字虽然叫OpenCV,但是在导包的时候,导入的包叫作cv2
。(这是个历史遗留问题,很早的时候它就叫cv,之后进行了重构,于是又叫cv2了,并沿用至今)
运行没有报错,说明正确导入此包。说明我们之前的环境配置、包的安装等操作都是顺利的。
(二)创建窗口
注意:光标放在函数上,按shift+tab
可以显示对该函数用法的说明(按一下是简洁版说明,按两下是详细版说明)。这个功能也是使用jupyter notebook所带来的一个好处。
注意:如果发现按
shift+tab
无效,则首先检查一下是否执行过import cv2
代码,因为有时候你刚打开这个文件直接就去shift+tab
,发现显示不出说明。实际上是因为没有导入cv2的包,导致它不认识这个函数,自然也就不会给出说明。
这个函数的用法是,括号的内的第一个参数,是窗口名字
(注意,应当是一个字符串)。后面的参数是窗口标记
(可以从函数用法说明中阅读到具体介绍)。
可见,不同的窗口标记,可以使窗口以不同的风格进行显示;以及如果不注明窗口标记,则会有一个默认值。
注意:如果想要设置flag
这一参数,仅仅输入对应标记名是不够的,还要在前面加上cv2.
,如cv2.WINDOW_AUTOSIZE
。(注意标记名不要写错)
运行后,的确会出现一个窗口,说明代码没问题,但是是未响应状态,这是因为我们写的不是一个规范的写法。
注意:我们把
import cv2
与创建窗口代码
写在两个不同的代码框中,此时,务必要保证先运行了import cv2
之后,再去运行创建窗口代码
,否则会报错(报错原因是找不到cv2这个包)。或者你就把所有代码都写在同一个框里。
(三)更改窗口大小 & 显示窗口
cv2.resizeWindow()
的第一个参数为要修改大小的窗口名
,后两个参数为窗口大小尺寸
。
cv2.imshow()
的第一个参数为要显示的窗口名
,第二个参数为要显示的图片
。此处我们没有什么要显示的图片,因此第二个参数设置为0。
此外,需要注意的是,对于我们创建窗口时设置的窗口标记
,若设置的是cv2.WINDOW_AUTOSIZE
(根据内容自动调节窗口尺寸),那么你修改窗口大小是没有效果的。
注意,注释单行代码的快捷键:
ctrl+/
。
(四)等待用户输入
cv2.waitKey()
的作用是,等待接收用户按键,并返回该按键对应的ASCII码值。
其中参数设置为0,表示一直等待、接收任意按键。如果设置其他的整数,表示等待按键的时间(单位是毫秒)。比如设置为cv2.waitKey(5000)
,就表示它会等你5秒,在5秒之内你按键才有用,过了5秒就不等你了。
注意,此时观察左侧,是In [*]
,是星号说明这段代码正在运行中,还没有运行结束。
实际上,cv2.waitKey(0)
的作用就是等待用户按一个键。当然,焦点要在窗口上时才可以。
我按了一个键盘上的q键,它就会捕捉到并输出一个113
。113
就是q的ASCII码值。(注意是小写q,不是大写Q)
补充:ord()函数来返回ASCII值
(五)销毁窗口
我们可以配合cv2.waitKey(0)
,先接收到用户按键的ASCII码值,然后就能根据用户按的是什么键,判断是否要销毁窗口了。(常用的比如q键、ESC键)。
按下q键后,窗口直接被销毁(关闭),而不会再有窗口未响应的问题。
但是由于程序只接收一次按键,如果你按的不是q键,它还是会有窗口未响应的问题。
注意,如果对于
key == 'q'
为什么有问题有疑问,则需要补一补python基础。
(※)完整代码
# ------------------------- 创建和显示窗口 -------------------------
# opencv名字叫opencv,但是导包的时候叫做cv2
import cv2
# 创建窗口
# 注意,cv2.WINDOW_AUTOSIZE则后续修改窗口大小无效
# cv2.namedWindow('myWindow01', cv2.WINDOW_AUTOSIZE)
cv2.namedWindow('myWindow01', cv2.WINDOW_NORMAL)
# 更改窗口大小
cv2.resizeWindow('myWindow01', 800, 600)
# 显示指定名字的窗口
cv2.imshow('myWindow01', 0)
# 等待按键
# 若用户按键为q键,则销毁窗口
key = cv2.waitKey(0) # 先用变量key接收
# 注意,这样写更易读,而不要写key == 113,即使你知道q是113
# 注意,不要写成key == 'q'
if key == ord('q'):
print('准备销毁窗口')
cv2.destroyAllWindows()
# ------------------------- 创建和显示窗口 -------------------------
二、加载显示图片
imread(path, flag)
使用imread可以读取图片,默认读取的是彩色图片。
path是图片的路径(绝对路径、相对路径都可以)。
flag是以什么方式读取这个图片(比如读出来是黑白的)。
(一)cv2.imread()返回值
# 导入opencv包
import cv2
# 读取图片
cat = cv2.imread('./1.png')
# numpy的ndarray(多维数组)
cat
输出如下:
array([[[ 1, 12, 90],
[ 2, 12, 91],
[ 1, 13, 91],
...,
[ 3, 10, 61],
[ 4, 9, 61],
[ 4, 9, 65]],
[[ 3, 13, 91],
[ 3, 13, 92],
[ 2, 14, 92],
...,
[ 2, 11, 60],
[ 2, 10, 60],
[ 2, 9, 63]],
[[ 2, 12, 91],
[ 2, 13, 91],
[ 2, 14, 92],
...,
[ 3, 10, 60],
[ 3, 10, 60],
[ 3, 10, 62]],
...,
[[ 4, 31, 111],
[ 4, 32, 112],
[ 4, 31, 113],
...,
[119, 190, 208],
[119, 190, 208],
[119, 190, 208]],
[[ 5, 31, 111],
[ 5, 31, 111],
[ 5, 31, 112],
...,
[119, 190, 208],
[119, 190, 208],
[119, 190, 208]],
[[ 6, 32, 111],
[ 6, 31, 111],
[ 6, 31, 111],
...,
[118, 189, 207],
[119, 190, 208],
[119, 190, 208]]], dtype=uint8)
(二)用matplotlib显示图片
import matplotlib.pyplot as plt
plt.imshow(cat)
我的原图是:
输出结果如下:
发现这个猫的样子没变,但是颜色不太对。这是因为OpenCV读取的图片颜色通道是按照BGR(蓝绿红)排列的,一般的图片通道都是按照RGB来排列的。
为了正常显示图片,我们要使用OpenCV的图像显示方法。换句话说,用OpenCV读进来的图片一般不要用别的方式进行展示,比如matplotlib。
(三)用OpenCV显示图片
cv2.imshow('cat', cat)
# 按键以销毁窗口,避免每次都有窗口未响应的问题
key = cv2.waitKey(0)
if key == ord('q'):
print('准备销毁窗口')
cv2.destroyAllWindows()
这样就会弹出一个窗口,并正常显示该图片,我们可以按下q键以正常关闭窗口。
(1)封装函数
此外,我们可以把显示图片的方法封装成一个函数,方便我们显示图片:
# 把展示图片的代码封装成函数
def cv_show(name, img):
cv2.imshow(name, img)
key = cv2.waitKey(0)
if key == ord('q'):
cv2.destroyAllWindows()
之后想显示图片的时候,直接调用函数即可:
# 导入opencv包
import cv2
# 读取图片
cat = cv2.imread('./1.png')
cv_show('cat', cat)
(2)封装外部.py文件
我们可以在当前目录下创建一个.py
文件,把封装的函数代码放进去。
注意,如果这样放代码,那么在调用的时候会报错。因为,在utils.py
这个文件中,我们调用了cv2
,但是并没有导入cv2
,就会报错。
需要在函数中调用cv2之前,先import cv2
。
这样就可以正常运行了。
注意:如果还是有问题,可以使用%run utils.py
执行一下外部文件(这是在jupyter中执行外部文件的方法),看看外部文件本身是否运行不了。如果在执行外部文件时报错IndentationError: unindent does not match any outer indentation level
,则是代码缩进问题,一般是tab和空格混用导致的缩进问题,这个问题仅凭肉眼是不容易看出来的(新手容易踩的坑)。(如果使用的是notepad++,可以通过View--Show Symbol--Show All Characters
来检查这一问题)
(四)保存图片
imwrite(path, img)
:使用imwrite保存图片。
import cv2
cv2.namedWindow('img', cv2.WINDOW_NORMAL)
cv2.resizeWindow('img', 320, 240)
img = cv2.imread('./1.png')
# 利用while循环优化退出逻辑
while True:
cv2.imshow('img', img)
key = cv2.waitKey(0)
if(key & 0xFF == ord('q')):
break
elif(key & 0xFF == ord('s')):
cv2.imwrite('./123.png', img)
else:
print(key)
cv2.destroyAllWindows()
三、视频采集和录制
(一)视频采集
- 视频是由图片组成的,视频的每一帧就是一幅图片,一般是30帧,表示一秒显示30张图片。
cv2.VideoCapture
可以捕获摄像头,用数字来表示不同的设备,比如0、1。- 如果是视频文件,可以直接指定路径即可。
# 打开视频文件
vc = cv2.VideoCapture('./1.mp4')
# 打开摄像头
vc = cv2.VideoCapture(0)
打开摄像头
示例:
# 打开摄像头
import cv2
cv2.namedWindow('video', cv2.WINDOW_NORMAL)
cv2.resizeWindow('video', 640, 480)
# 如果打开失败(比如没有摄像头),不会报错
# cap = cv2.VideoCapture(1) # 我只有摄像头0,没有摄像头1
cap = cv2.VideoCapture(0)
# 循环读取摄像头的每一帧
# while True:
while cap.isOpened():
# 读一帧数据,返回标记和这一帧数据,标记为True表示读到了数据,False表示没读到数据
ret, frame = cap.read()
# 可以根据ret做个判断
if not ret:
# 没读到数据,直接退出
break
# 显示数据
cv2.imshow('video', frame)
key = cv2.waitKey(1)
# 注意,此处就不要再写0了,因为写0表示无限等待,也就是显示一帧数据然后一直等待用户按键
# 写个1,表示等1毫秒,若等不到按键就继续处理了
if key == ord('q'):
break
# 别忘了释放资源
cap.release()
cv2.destroyAllWindows()
打开视频文件
示例:
# 打开摄像头
import cv2
cv2.namedWindow('video', cv2.WINDOW_NORMAL)
cv2.resizeWindow('video', 640, 480)
# 打开视频,传入视频路径即可
cap = cv2.VideoCapture('./1.mp4')
# 循环读取摄像头的每一帧
# while True:
while cap.isOpened():
# 读一帧数据,返回标记和这一帧数据,标记为True表示读到了数据,False表示没读到数据
ret, frame = cap.read()
# 可以根据ret做个判断
if not ret:
# 没读到数据,直接退出
break
# 显示数据
cv2.imshow('video', frame)
key = cv2.waitKey(1)
# 注意,此处就不要再写0了,因为写0表示无限等待,也就是显示一帧数据然后一直等待用户按键
# 写个1,表示等1毫秒,若等不到按键就继续处理了
# key = cv2.waitKey(1000 // 30) # 让视频以30帧播放
if key == ord('q'):
break
# 别忘了释放资源
cap.release()
cv2.destroyAllWindows()
注意,上述代码,和刚才“打开摄像头”的代码只有一行不同,即第8行的cap = cv2.VideoCapture('./1.mp4')
,其他地方都相同。
运行此代码时,发现其播放的视频好像加速了一样,这是因为,第24行代码的key = cv2.waitKey(1)
造成的效果是每等待1毫秒显示一帧数据,所以播放的比较快。
假如我们想让视频是30帧,那么每张图片要间隔多少毫秒?
答:理论上来说,是1000 / 30
ms,但此处传递的参数必须是整数,所以我们代码写成1000 // 30
。(Python语法,//
表示向下取整)
另外,我们此处处理的是视频中每一帧的图片,所以没有声音。
(二)视频录制
VideoWriter
:参数1为输出文件,参数2为多媒体文件格式(VideoWriter_fourcc
),参数3为帧率,参数4为分辨率;write
编码并写入缓存;release
缓存内容写入磁盘,并释放资源。
import cv2
cap = cv2.VideoCapture(0)
# *mp4v 就是解包操作,等同于 'm','p','4','v'
# fourcc = cv2.VideoWriter_fourcc(*'mp4v')
# avi格式的视频
fourcc = cv2.VideoWriter_fourcc(*'XVID')
# (640, 480)表示摄像头拍视频的分辨率,这个大小搞错了也不行
# vw = cv2.VideoWriter('output.mp4', fourcc, 20, (640, 480))
# 如果前面写的是avi,这里要改成avi格式
vw = cv2.VideoWriter('output.avi', fourcc, 20, (640, 480))
while cap.isOpened():
ret, frame = cap.read()
if not ret:
print('can not receive frame, Exiting...')
break
vw.write(frame)
cv2.imshow('frame', frame)
if cv2.waitKey(1) == ord('q'):
break
cap.release()
vw.release()
cv2.destroyAllWindows()
四、控制鼠标
OpenCV允许我们对窗口上的鼠标动作做出响应。
setMouseCallback(winname, callback, userdata)
:winname是窗口名字,callback是回调函数,userdata是给回调函数的参数。callback(event, x, y, flags, userdata)
:回调函数必须包含这5个参数。event是事件(鼠标移动、左键、右键),xy是点鼠标的坐标点,flags主要用于组合键,userdata就是上面的setMouseCallback的userdata。
鼠标事件:
EVENT_MOUSEMOVE 0 鼠标移动
EVENT_LBUTTONDOWN 1 按下鼠标左键
EVENT_RBUTTONDOWN 2 按下鼠标右键
EVENT_MBUTTONDOWN 3 按下鼠标中键
EVENT_LBUTTONUP 4 左键释放
EVENT_RBUTTONUP 5 右键释放
EVENT_MBUTTONUP 6 中键释放
EVENT_LBUTTONDBLCLK 7 左键双击
EVENT_RBUTTONDBLCLK 8 右键双击
EVENT_MBUTTONDBLCLK 9 中键双击
EVENT_MOUSEWHEEL 10 鼠标滚轮上下滚动
EVENT_MOUSEHWHEEL 11 鼠标左右滚动
flags:
EVENT_FLAG_LBUTTON 1 按下左键
EVENT_FLAG_RBUTTON 2 按下右键
EVENT_FLAG_MBUTTON 4 按下中键
EVENT_FLAG_CTRLKEY 8 按下ctrl键
EVENT_FLAG_SHIFTKEY 16 按下shift键
EVENT_FLAG_ALTKEY 32 按下alt键
示例:
import cv2
import numpy as np
# 函数名可以随便取,但是参数必须是5个(参数名也可以随便取)
# event表示鼠标事件
# x, y是发生鼠标事件的坐标
# flags是鼠标的组合操作
def mouse_callback(event, x, y, flags, userdata):
print(event, x, y, flags, userdata)
# 按下鼠标右键退出
if event == 2:
cv2.destroyAllWindows()
# 创建窗口
cv2.namedWindow('mouse', cv2.WINDOW_NORMAL)
cv2.resizeWindow('mouse', 640, 360)
# 设置鼠标的回调函数
cv2.setMouseCallback('mouse', mouse_callback, '123')
# 生成全黑的图片
img = np.zeros((360, 640, 3), np.uint8)
while True:
cv2.imshow('mouse', img)
key = cv2.waitKey(1)
if key == ord('q'):
break
cv2.destroyAllWindows()
五、TrackBar控件
createTrackbar(trackbarname, winname, value, count, onChange)
:创建TrackBar控件,value为trackbar的默认值,count为bar的最大值,最小为0。getTrackbarPos(trackbarname, winname)
:获取TrackBar当前值。
示例:
import cv2
import numpy as np
# 创建窗口
cv2.namedWindow('trackbar', cv2.WINDOW_NORMAL)
cv2.resizeWindow('trackbar', 640, 480)
# 定义回调函数
def callback(value):
print(value)
# 创建trackbar
cv2.createTrackbar('R', 'trackbar', 0, 255, callback)
cv2.createTrackbar('G', 'trackbar', 0, 255, callback)
cv2.createTrackbar('B', 'trackbar', 0, 255, callback)
# 创建一个背景图片
img = np.zeros((480, 640, 3), np.uint8)
while True:
# 获取当前trackbar的值
r = cv2.getTrackbarPos('R', 'trackbar')
g = cv2.getTrackbarPos('G', 'trackbar')
b = cv2.getTrackbarPos('B', 'trackbar')
# 改变背景图颜色
img[:] = [b, g, r]
cv2.imshow('trackbar', img)
key = cv2.waitKey(1)
if key & 0xFF == ord('q'):
break
cv2.destroyAllWindows()