OpenCV-Part1
OpenCV-Part1——基础操作
[TOC]
OpenCV
简介
- OpenCV是计算机视觉中经典的专用库。
- OpenCV-Python为OpenCV提供了Python接口,使得使用者在Python中能够调用C/C++,在保证易读性和运行效率的前提下,实现所需的功能。
- 所有OpenCV数组结构都与Numpy数组相互转换,所以要使用OpenCV-Python编写优化的代码,必须先明白Numpy。
- 本笔记以OpenCV4为主,会有个别版本上的差异,可能的话会提供解决方案。
安装
1 | pip install opencv-python |
GUI特性
图像入门
读取图像
- 鉴于兼容windows和linux,文件路径请用
/
分隔 - 文件路径中不得出现中文
- 可以选择图像加载方式:
- cv2.IMREAD_COLOR:默认加载彩色图像。任何图像的透明度都会被忽视。
- cv2.IMREAD_GRAYSCALE:以灰度模式加载图像。
- cv2.IMREAD_UNCHANGED:加载图像,包括alpha通道。
1 | img = cv2.imread("validation/origin/circle/4101035072410.jpg", cv2.IMREAD_COLOR) |
显示图像
- 图像窗口名称参数不建议使用中文
- 如果创建同名窗口两次,则第二张图片会替换前一张图片
1 | cv2.imshow("img", img) |
等待事件
- 如果想要暂停并显示图片,必须使用
cv2.waitKey(0)
,不能用input
替代 - 可以选择等待时间参数:
- 非零:单位为毫秒,期间如果按下键,则忽略剩余时间、直接运行
- 0:无限期地等待一次敲击键
1 | cv2.waitKey(0) |
- 返回参数为keyCode,通过此可以设置检测特定的按键
1 | while cv2.waitKey(1000) != ord("q"): |
销毁窗口
- cv2.destroyAllWindows():会销毁创建的所有窗口
- cv2.destroyWindow():销毁特定窗口
创建窗口
- 手动创建窗口,并指定窗口是否可以调整大小
- cv2.WINDOW_AUTOSIZE:默认,按照图像大小固定窗口大小
- cv2.WINDOW_NORMAL:可以自动调整大小,对于大分辨率图像的查看有帮助
1 | cv2.namedWindow('image', cv2.WINDOW_NORMAL) |
写入图像
- 同样路径不得出现中文,否则无输出
1 | cv2.imwrite("test.jpg", img) |
OpenCV与其他库
OpenCV与Matplotlib互转
- OpenCV加载的彩色图像处于BGR模式,但是Matplotlib以RGB模式显示。
1 | import cv2 |
PIL.Image转换成OpenCV
1 | import cv2 |
OpenCV转换成PIL.Image
1 | import cv2 |
视频入门
读取摄像头视频
- 需要创建一个 VideoCapture 对象。它的参数可以是设备索引或视频文件的名称。
cap.read()
:返回是否正确读取帧。可以通过检查此返回值来判断是否到达视频的结尾。cap.isOpened()
:返回是否初始化捕获。否则,使用cap.open()
打开它。- 当打开视频文件时,播放速度会不受限制,最好是从fps属性动态计算每帧之间相隔的时间,作为参数传入
cv2.waitKey()
- 使用视频捕获必须确保安装了正确的 ffmpeg 或 gstreamer 版本。
1 | import cv2 |
视频属性
属性方法
cap.get(propId)
:查看视频的属性。cap.set(propId,value)
:修改视频的属性。- cap画布的长宽上下限似乎和摄像头本身有关。
1 | cap = cv2.VideoCapture(0) |
propId对应表
propid | 属性 | 视频流属性 |
---|---|---|
0 | CV_CAP_PROP_POS_MSEC | 视频文件以毫秒为单位的当前位置或视频捕获时间戳(就是说你这个位置是视频当中的第几秒第几毫秒) |
1 | CV_CAP_PROP_POS_FRAMES | 基于索引的帧解码/捕获(就是说你现在的位置是视频的第几帧位置) |
2 | CV_CAP_PROP_POS_AVI_RATIO | 视频文件的相对位置:0 -电影,开始1 -电影结束(就是说输出0表示视频刚开始,输出1表示视频结束了) |
3 | CV_CAP_PROP_FRAME_WIDTH | 视频流画面的宽度 |
4 | CV_CAP_PROP_FRAME_HEIGHT | 视频流画面的长度 |
5 | CV_CAP_PROP_FPS | 视频流的帧频 |
6 | CV_CAP_PROP_FOURCC | 编辑器的四字符编码 |
7 | CV_CAP_PROP_FRAME_COUNT | 视屏文件的帧数 |
8 | CV_CAP_PROP_FORMAT | Format of the Mat objects returned by retrieve() |
9 | CV_CAP_PROP_MODE | Backend-specific value indicating the current capture mode |
10 | CV_CAP_PROP_BRIGHTNESS | Brightness of the image (only for cameras)摄像机图像的亮度(只有摄像头才可以) |
11 | CV_CAP_PROP_CONTRAST | Contrast of the image (only for cameras).摄像机图像的对比度(只有摄像头才可以) |
12 | CV_CAP_PROP_SATURATION | Saturation of the image (only for cameras)摄像机图像的饱和度(只有摄像头才可以) |
13 | CV_CAP_PROP_HUE | 图片的色调(只有摄像头才可以) |
14 | CV_CAP_PROP_GAIN | Gain of the image(只有摄像头才可以) |
15 | CV_CAP_PROP_EXPOSURE | Exposure 曝光(只有摄像头才可以) |
16 | CV_CAP_PROP_CONVERT_RGB | 布尔值指示是否应该转换为RGB图 |
17 | CV_CAP_PROP_WHITE_BALANCE_U | The U value of the whitebalance setting (note: only supported by DC1394 v 2.x backend currently) |
18 | CV_CAP_PROP_WHITE_BALANCE_V | The V value of the whitebalance setting (note: only supported by DC1394 v 2.x backend currently) |
19 | CV_CAP_PROP_RECTIFICATION | Rectification flag for stereo cameras (note: only supported by DC1394 v 2.x backend currently) |
20 | CV_CAP_PROP_ISO_SPEED | The ISO speed of the camera (note: only supported by DC1394 v 2.x backend currently) |
21 | CV_CAP_PROP_BUFFERSIZE | Amount of frames stored in internal buffer memory (note: only supported by DC1394 v 2.x backend currently) |
保存视频
- 需要创建一个 VideoWriter 对象。
1 | import cv2 |
绘图功能
- color:对于bgr颜色要传递元组,例如
(255,0,0)
,传递int则视为b值。 - thickness:线条厚度,默认为
1
;对于封闭图形,-1
位填充。 - lineType:线的类型,默认为为8连接线;对于曲线可以设置为抗锯齿线
cv2.LINE_AA
。
画线
1 | cv2.line(img, pt1=(0, 0), pt2=(511, 511), color=(255, 0, 0), thickness=5, lineType=cv2.LINE_AA) |
画矩形
- 两个点分别为左上角和右下角
1 | cv2.rectangle(img, pt1=(384, 0), pt2=(510, 128), color=(0, 255, 0), thickness=3) |
画圆
1 | cv2.circle(img, center=(447, 63), radius=63, color=(0, 0, 255), thickness=-1) |
画椭圆
- angle:按顺时针偏转的角度
- startAngle、endAngle:按顺时针计算区间。[0, 0]则无椭圆,[0, 360]则完整椭圆。
1 | cv2.ellipse(img, center=(256, 256), axes=(100, 50), angle=0, startAngle=0, endAngle=180, color=255, thickness=-1) |
画多边形
- 必须转换成int32
- 注意pts要用列表装饰
1 | pts = [[10, 5], [20, 30], [70, 20], [50, 10]] |
添加文本
1 | cv2.putText(img, text='OpenCV', org=(10, 350), fontFace=cv2.FONT_HERSHEY_SIMPLEX, |
鼠标作为画笔
可用事件与回调函数
- 列出所有可用事件
1 | import cv2 as cv |
setMouseCallback
:鼠标回调函数,参数是固定的,会自动与窗口绑定
1 | import numpy as np |
核心操作
图像基本操作
访问和修改像素值
对于 BGR 图像,它返回一个由蓝色、绿色和红色值组成的数组。对于灰度图像,只返回相应的灰度。
通常会使用numpy切片图像,不建议使用numpy索引来访问、修改单个像素值。
访问、修改单个像素值时,使用方法
item()
和itemset())
被认为更好,但是它们始终操作单通道。
1 | print(img[100, 100]) # 彩色图片返回 [蓝, 绿, 红] |
访问图像属性
img.shape
:图像的形状[行数(高height), 列数(宽width), [蓝, 绿, 红]]
- 如果图像是灰度的,则返回的元组仅包含行数和列数
1 | print(img.shape) |
img.size
:像素总数
1 | print(img.size) |
img.dtype
:图像数据类型- img.dtype在调试时非常重要,因为OpenCV-Python代码中的大量错误是由无效的数据类型引起的。
1 | print( img.dtype ) |
图像区域切片
- 注意,索引顺序是
[行数, 列数, 通道]
,即[高, 宽, [蓝, 绿, 红]]
1 | # 将球复制到图像中的另一个区域 |
拆分和合并图像通道
cv2.split(img)
:切分三个通道
1 | # split()比较耗时,可以考虑使用np切片 |
cv2.merge((b,g,r))
:合并三个通道- 可以将灰度图扩展成三通道
1 | img = cv2.merge((b,g,r)) |
填充边框
cv2.copyMakeBorder()
:padding经常使用。src
:输入图像top
,bottom
,left
,right
:边界宽度borderType
:定义要添加哪种边框的标志。它可以是以下类型:cv2.BORDER_CONSTANT
:添加恒定的彩色边框。该值应作为下一个参数给出。cv2.BORDER_REFLECT
或cv2.BORDER_REFLECT_101
或cv2.BORDER_DEFAULT
:边框将是边框元素的镜像cv2.BORDER_REPLICATE
:重复边界最后一个元素被复制cv2.BORDER_WRAP
:将图片复制平铺
value
:边框的颜色,如果边框类型为 cv.BORDER_CONSTANT
1 | img = cv2.imread("test.jpg", cv2.IMREAD_COLOR) |
图像加法
- 可以通过OpenCV函数
cv2.add()
或numpy操作res = img1 + img2
相加两个图像。 - OpenCV加法和Numpy加法之间有区别。OpenCV加法是饱和运算,而Numpy加法是模运算。
- 两个图像需要有相同的深度和类型,或者第二个图像可以只是一个标量值。
- 一般会使用OpenCV的方法,或直接使用图像融合。
1 | x = np.uint8([250]) |
图像反色
- 千万不要用循环!
- 需要在指定区域反色的话,就加入mask参数。比如,将黑色背景反转为白色背景。
- 因为两者范围都在0-255,所以直接做np减法。
1 | img = np.full(img.shape, (255, 255, 255), dtype=np.uint8) - img |
图像融合
cv2.addWeighted()
:对图像赋予不同的权重,并相加。- 通过动态更改两张的图像权重
0->1, 1->0
,可以完成一个图像到另一个图像之间的视觉过渡。 - 并不要求两者权重相加为1
1 | img1 = cv.imread('ml.png') |
按位运算
AND
、OR
、NOT
、XOR
:用于提取图像的任意部分、定义和处理非矩形ROI。当需要将A图像覆盖在B图像上时。如果将两个图像相加,则改变颜色。如果将两个图像混合,则获得透明效果。
如果是矩形区域,则可以像上一章一样使用ROI切片,重置区域数值。A图像不是矩形。因此,可以进行按位操作。、
mask需要为np.uint8类型,否则
.astype(np.uint8)
;mask需要通道数为1,否则mask = mask[:, :, 0]
前景图片大小不能超过背景,否则会报错
mask的含义是:仅mask中的白色区域会进行运算,其他部分全部为黑色
最后相加时,要确保相加部分是 0 + img_mask = img_mask;否则依然会出现融合效果
1 | # 加载两张图片 |
性能衡量和提升技术
使用OpenCV衡量性能
cv2.getTickCount
:函数返回从参考事件(如打开机器的那一刻)到调用此函数那一刻之间的时钟周期数。
cv2.getTickFrequency
:函数返回时钟周期的频率或每秒的时钟周期数。
1 | e1 = cv2.getTickCount() |
使用time.time()函数,然后取两次相差,与上述方法是等价的。
OpenCV中的默认优化
许多OpenCV函数都默认使用了SSE2、AVX等进行优化。可以使用 cv2.Useoptimized
检查是否启用和 cv2.Setuseoptimized
以启用/禁用优化。
性能优化技术
首先尝试以一种简单的方式实现算法。然后,分析、找到瓶颈,再进行优化。
使用下面的技巧来充分利用 Python 和 Numpy 的最大性能。
- Python标量操作比Numpy标量操作快。因此,对于包含一两个元素的运算,Python标量比Numpy数组好。当数组大小稍大时,Numpy会占优势。
- 通常,OpenCV函数比Numpy函数要快。因此,对于相同的操作,首选OpenCV功能。但是,可能会有例外,尤其是当Numpy处理视图而不是副本时。
- 尽量避免在Python中使用循环,尤其是双/三重循环等。
- 由于Numpy和OpenCV已针对向量运算进行了优化,因此将算法/代码向量化到最大程度。
- 利用缓存一致性。
- 除非需要,否则切勿创建数组的副本。尝试改用视图。数组复制是一项昂贵的操作。
执行了所有这些操作后,如果仍然很慢,或者不可避免地需要使用大循环,请使用Cython等其他库来使其更快。
其他资源
- Python优化技术:http://wiki.python.org/moin/PythonSpeed/PerformanceTips
- Scipy讲义- 高级Numpy:http://scipy-lectures.github.io/advanced/advanced_numpy/index.html#advanced-numpy
- IPython中的时序和性能分析:http://pynash.org/2013/03/06/timing-and-profiling/