现在说的机器视觉(Machine Vision)一般指计算机视觉(Computer Vision),简单来说就是研究如何使机器看懂东西。就是指用摄影机和电脑代替人眼对目标进行识别、跟踪和测量等机器视觉,并进一步做图形处理,使电脑处理成为更合适人眼观察或传送给仪器检测的图像。
在上面的讨论中,计算机视觉(computer vision)和机器视(machine vision)两个术语是不加以区分的,在很多文献中也是如此。但其实这两个术语既有区别又有联系。计算机视觉是采用图像处理、模式识别、人工智能技术相结合的手段,着重于一副或多副图像的计算机分析。图像可以有单个或者多个传感器获取,也可以是单个传感器在不同时刻获取的图像序列。分析是对目标物体的识别,确定目标物体的位置和姿态,对三维景物进行符号描述和解释。在计算机视觉研究中,经常使用几何模型、复杂的知识表达,采用基于模型的匹配和搜索技术,搜索的策略常使用在自底向上、自顶向下、分层和启发式控制策略。机器视觉则偏重于计算机视觉技术工程化,能够自动获取和分析特定图像,以控制相应的行为。具体地说,计算机视觉为机器视觉提供图像和景物分析的理论以及算法基础,机器视觉为计算机视觉的实现提供传感器模型、系统构造和实现手段。因此可以认为,一个机器视觉系统就是一个能自动获取一副或多幅目标物体图像,对所获取图像的各种特征量进行处理、分析和测量,并对测量结果做出定性分析和定量解释,从而得到有关目标物体的某种认识并做出相应决策的系统。计算机视觉系统的功能包括:物体定位、特征测量、缺陷判断、目标识别、技术、运动跟踪。
为什么使用Python:
学习目标:
进入虚拟环境,执行命令:
pip3 install opencv-python opencv-contrib-python jupyter matplotlib -i https://pypi.douban.com/simple
如果安装不了需要手动下载:https://www.lfd.uci.edu/~gohlke/pythonlibs 检查安装(进入venv环境):
% ipython
In [1]: import cv2
In [2]: cv2.__version__
Out[2]: '4.6.0'
In [3]: exit()
如果出现 Out[2]: '4.6.0'
则说明安装且导入成功。
jupyter
在Jupyter中新建文件“图像和视频的加载和显示”并写入代码块:
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]
开始运行代码即可看到弹出的窗口。
# 导入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的图像显示方法:
cv2.imshow('doge', doge)
cv2.waitKey(0)
cv2.destroyAllWindows()
可以将其其为一个函数来显示图片
def cv_show(name, img):
cv2.imshow(name, img)
cv2.waitKey(0)
cv2.destroyAllWindows()
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()
保存后文件目录如下:
# 打开视频文件
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(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()
若设备没有摄像头或打开摄像头失败,程序会直接退出而非报错。这时需要检测是否正确打开摄像头:
if vc.isOpened():
# 读取视频的一帧
open, frame = vc.read()
else:
open = False
# 打开视频
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⌋
必须向下取整计算
# 30fps
key = cv2.waitKey(1000 // 30)
OpenCV可以将打开的视频或摄像头中的每一个画面保存到新的视频流中。 * 注:这里的(1920, 1080)以 MacBook Pro 16 英寸 M1 2021 的FaceTime高清摄像头为准。
# 摄像头录制视频
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()
OpenCV允许我们对窗口上的鼠标动作做出响应
事件名 | 事件值 | 操作 |
---|---|---|
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 | 鼠标滚轮左右滚动 |
事件名 | 事件值 | 操作 |
---|---|---|
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()
[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 # 鼠标移动 组合 鼠标移动
类似于可拖动的数值进度条
# 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()