1.1~2.5 初等概念及OpenCV的使用
1.1 机器视觉介绍
现在说的机器视觉(Machine Vision)一般指计算机视觉(Computer Vision),简单来说就是研究如何使机器看懂东西。就是指用摄影机和电脑代替人眼对目标进行识别、跟踪和测量等机器视觉,并进一步做图形处理,使电脑处理成为更合适人眼观察或传送给仪器检测的图像。
在上面的讨论中,计算机视觉(computer vision)和机器视(machine vision)两个术语是不加以区分的,在很多文献中也是如此。但其实这两个术语既有区别又有联系。计算机视觉是采用图像处理、模式识别、人工智能技术相结合的手段,着重于一副或多副图像的计算机分析。图像可以有单个或者多个传感器获取,也可以是单个传感器在不同时刻获取的图像序列。分析是对目标物体的识别,确定目标物体的位置和姿态,对三维景物进行符号描述和解释。在计算机视觉研究中,经常使用几何模型、复杂的知识表达,采用基于模型的匹配和搜索技术,搜索的策略常使用在自底向上、自顶向下、分层和启发式控制策略。机器视觉则偏重于计算机视觉技术工程化,能够自动获取和分析特定图像,以控制相应的行为。具体地说,计算机视觉为机器视觉提供图像和景物分析的理论以及算法基础,机器视觉为计算机视觉的实现提供传感器模型、系统构造和实现手段。因此可以认为,一个机器视觉系统就是一个能自动获取一副或多幅目标物体图像,对所获取图像的各种特征量进行处理、分析和测量,并对测量结果做出定性分析和定量解释,从而得到有关目标物体的某种认识并做出相应决策的系统。计算机视觉系统的功能包括:物体定位、特征测量、缺陷判断、目标识别、技术、运动跟踪。
计算机视觉的应用
- 物体识别:人脸识别、测量检测;
- 识别图像中的文字(OCR);
- 图像拼接、修复、背景替换;
1.2 OpenCV介绍
- Gray Bradsky 于 1999 年开发,2000 年发布
- C ,Python,Java,JS,C#,Ch,Ruby,Go
- 跨平台(Windows,Linux,Mac …)
为什么使用Python:
- Python语言简单,开发速度快
- 底层使用C/C ,速度有保障
- 有完整的生态链
学习目标:
- 了解OpenCV的运行机制
- 可以使用OpenCV处理一些图像常见问题
- 学会物体识别,文字识别等问题的处理思路
1.3 安装OpenCV环境
进入虚拟环境,执行命令:
代码语言:javascript复制pip3 install opencv-python opencv-contrib-python jupyter matplotlib -i https://pypi.douban.com/simple
如果安装不了需要手动下载:https://www.lfd.uci.edu/~gohlke/pythonlibs 检查安装(进入venv环境):
代码语言:javascript复制% ipython
In [1]: import cv2
In [2]: cv2.__version__
Out[2]: '4.6.0'
In [3]: exit()
如果出现 Out[2]: '4.6.0'
则说明安装且导入成功。
1.4 创建和显示窗口
- namedWindow():创建命名窗口
- imshow():显示窗口
- destoryAllwindows():销毁窗口
- resizeWindow():改变窗口大小
- waitKey():等待用户输入 使用 Jupyter 进行编写 ipynb 文件:
jupyter
在Jupyter中新建文件“图像和视频的加载和显示”并写入代码块:
代码语言:javascript复制In[1]:
# opencv导包为cv2
import cv2
In[2]:
# 创建窗口
# cv2.WINDOW_AUTOSIZE不允许修改窗口大小
# cv2.namedWindow('window', cv2.WINDOW_AUTOSIZE)
cv2.namedWindow('window', cv2.WINDOW_NORMAL)
# 更改窗口大小
cv2.resizeWindow('window', 800, 600)
# 展示名字为window的窗口
cv2.imshow('window',0)
# 等待按下键盘
# 返回按下键的ASCII码
# 0表示接收任意按键,如果给其他整数,表示等待按键的时间,单位ms
# 可以利用waitKey来销毁窗口,不用每次都重启python
key = cv2.waitKey(0)
# key是int型,最少都是16位,但是ASCII是8位
if key & 0xFF == ord('q'):
print('准备销毁窗口')
cv2.destroyAllWindows()
In[3]:
# 怎么计算Q的ASCII码
# ord('q')函数是python中计算ASCII值的函数
从 In[1]
开始运行代码即可看到弹出的窗口。
1.5 显示图像
- imread(path, flag):使用imread可以读取图片,默认读取的是彩色图片,比如:
# 导入opencv包
import cv2
import numpy as np
from matplotlib import pyplot as plt
# 读取图片
doge = cv2.imread('./doge.jpg')
# doge输出numpy的ndarray dtype=uint8 cat.max()=2^8-1
doge.max()
plt.imshow(doge)
其中,使用 cv2.imread('./doge.jpg')
方法读取图片显示如下:
使用 plt.imshow(doge)
方法读取图片显示如下:
其中狗的颜色发生了改变,这是因为OpenCV读取图片的颜色通道按照BGR(蓝绿红)排列的,一般图片通道都是按照RGB来排列的。为了正常显示猫的图片,要使用OpenCV的图像显示方法:
代码语言:javascript复制cv2.imshow('doge', doge)
cv2.waitKey(0)
cv2.destroyAllWindows()
可以将其其为一个函数来显示图片
代码语言:javascript复制def cv_show(name, img):
cv2.imshow(name, img)
cv2.waitKey(0)
cv2.destroyAllWindows()
2.1 保存图片
- imwrite(path, img):使用imwrite保存图片
import cv2
cv2.namedWindow('img', cv2.WINDOW_NORMAL)
cv2.resizeWindow('img', 320, 240)
img = cv2.imread("./doge.jpg")
# 利用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()
保存后文件目录如下:
2.2 读取摄像头和视频数据
视频采集
- 视频是由图片组成的,视频的每一帧就是一副图片,一般30帧,表示一秒显示30张图片。
- cv2.VideoCapture可以捕获摄像头,用数字来表示不同的设备,比如:0、1。
- 如果是视频文件,可以直接指定路径即可。
# 打开视频文件
vc = cv2.VideoCapture('./1.mp4')
# 打开摄像头
vc = cv2.VideoCapture(0)
读取摄像头
代码语言:javascript复制# 打开摄像头
import cv2
cv2.namedWindow('video', cv2.WINDOW_NORMAL)
cv2.resizeWindow('video', 640, 480)
cap = cv2.VideoCapture(0)
# 循环读取摄像头的每一帧
while True:
# 读取一帧数据,返回标记,True表示读到了数据反之亦然,和这一帧的数据
ret, frame = cap.read()
# 根据ret做出判断
if not ret:
# 没读到数据
break
# 读到了数据就显示
cv2.imshow('video', frame)
key = cv2.waitKey(10)
if key & 0xFF == ord('q'):
break
# 释放资源
cap.release()
cv2.destroyAllWindows()
若设备没有摄像头或打开摄像头失败,程序会直接退出而非报错。这时需要检测是否正确打开摄像头:
代码语言:javascript复制if vc.isOpened():
# 读取视频的一帧
open, frame = vc.read()
else:
open = False
读取视频
代码语言:javascript复制# 打开视频
import cv2
cv2.namedWindow('mp4', cv2.WINDOW_NORMAL)
cv2.resizeWindow('mp4', 640, 480)
cap = cv2.VideoCapture('./1.mp4')
# 循环读取摄像头的每一帧
while True:
# 读取一帧数据,返回标记,True表示读到了数据反之亦然,和这一帧的数据
ret, frame = cap.read()
# 根据ret做出判断
if not ret:
# 没读到数据
break
# 读到了数据就显示
cv2.imshow('video', frame)
key = cv2.waitKey(10)
if key & 0xFF == ord('q'):
break
# 释放资源
cap.release()
cv2.destroyAllWindows()
视频被加速的原因:cv2.waitKey(10)
使得视频每一帧的间隔缩短至了10毫秒。
考虑问题:假如一个视频为30FPS那么每张图之间的间隔为多少毫秒?
⌊100030⌋=⌊1003⌋lfloor frac{1000}{30} rfloor = lfloor frac{100}{3} rfloor ⌊301000⌋=⌊3100⌋
必须向下取整计算
代码语言:javascript复制# 30fps
key = cv2.waitKey(1000 // 30)
2.3 录制视频
- VideoWriter:参数一为输出文件、参数二为多媒体文件的格式(VideoWriter_fourcc对象),参数三位帧率,参数四为分辨率。
- write:编码写入缓存。
- release:缓存内容写入磁盘,并释放资源。
OpenCV可以将打开的视频或摄像头中的每一个画面保存到新的视频流中。 * 注:这里的(1920, 1080)以 MacBook Pro 16 英寸 M1 2021 的FaceTime高清摄像头为准。
代码语言:javascript复制# 摄像头录制视频
cap = cv2.VideoCapture(0)
# *mp4v就是解包操作 等同于 'm', 'p', '4', 'v'
# avi 格式为 XVID
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
# (1920, 1080)表示摄像头拍视频的分辨率,大小错了就不行了
vw = cv2.VideoWriter('output.mp4', fourcc, 20, (1920, 1080))
# vw = cv2.VideoWriter('output.mp4', cv2.VideoWriter_fourcc(*'mp4v'), 20, (1920, 1080))
while cap.isOpened():
ret, frame = cap.read()
if not ret:
print('摄像头未开启')
break
vw.write(frame)
cv2.imshow('frame', frame)
# 这里的1毫秒决定了根本帧率,需要使用 1000//30
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# 释放资源
cap.release()
vw.release()
cv2.destroyAllWindows()
2.4 控制鼠标
OpenCV允许我们对窗口上的鼠标动作做出响应
- setMouseCallback(winname, callback, userdata)
- winname:窗口名字
- callback:回调函数名
- userdata:传递给回调函数的参数列表
- callback(event, x, y, flags, userdata)
- 回调函数必须包含这五个参数
- event:鼠标事件(左键、右键、滚动、移动等操作事件)
- x,y:鼠标的位置坐标
- 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键 |
# opencv控制鼠标
import cv2
import numpy as np
# 回调函数参数必须为5个
# event事件,xy坐标,flags第二组合键,userdata自定义数据
def mouse_callback(event, x, y, flags, userdata):
print(event, x, y, flags, userdata)
# 创建窗口
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)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# 销毁窗口
cv2.destroyAllWindows()
代码语言:javascript复制[Output]:
0 376 174 0 123 # 鼠标移动 组合 鼠标移动
0 422 185 0 123
0 514 201 0 123
0 566 210 0 123
1 524 258 1 123 # 左键按下 组合 左键按下
4 524 258 1 123 # 左键释放 组合 左键按下
0 528 258 0 123
0 544 259 0 123
0 567 259 0 123
0 625 262 0 123 # 鼠标移动 组合 鼠标移动
2.5 TrackBar用法
TrackBar控件
类似于可拖动的数值进度条
- createTrackbar(trackbarname, winname, value, count, onChange)
- 创建Trackbar控件
- trackbarname:控件名字
- winname:窗口名字
- value:trackbar的默认值
- count:trackbar的最大值,最小为0
- onChange:回调函数
- getTrackbarPos(trackbarname, winname)
- 获取TrackjBar当前值
# 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)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cv2.destroyAllWindows()