简介
OpenCV(开源计算机视觉库)是一个开源库,包含数百种计算机视觉算法。它本质上是一个 C++ API,而不是基于 C 的 OpenCV 1.x API(自 OpenCV 2.4 发布以来,C API 已弃用,并且未使用“C”编译器进行测试)。 OpenCV 具有模块化结构,这意味着该软件包包含多个共享或静态库。
自动内存管理
OpenCV 自动处理所有内存。首先, std::vector 、cv::Mat以及函数和方法使用的其他数据结构具有析构函数,可以在需要时释放底层内存缓冲区。 这意味着析构函数并不总是像 Mat 那样释放缓冲区。他们考虑了可能的数据共享。析构函数递减与矩阵数据缓冲器关联的参考计数器。 当且仅当引用计数器达到零时,即没有其他结构引用同一缓冲区时,缓冲区才会被释放。类似地,当复制 Mat 实例时,并没有真正复制任何实际数据。 相反,引用计数器会增加以记住相同数据的另一个所有者。还有cv::Mat::clone方法可创建矩阵数据的完整副本。
输出数据的自动分配
大多数时候,OpenCV 会自动释放内存,并自动为输出函数参数分配内存。因此,如果函数具有一个或多个输入数组(cv::Mat实例)和一些输出数组, 则输出数组会自动分配或重新分配。输出数组的大小和类型由输入数组的大小和类型确定。如果需要,这些函数会采用额外的参数来帮助确定输出数组的属性。
多线程和可重输入性
当前的 OpenCV 实现是完全可重新输入的。也就是说,不同类实例的相同函数或相同方法可以从不同线程调用。此外,相同的 Mat 可以在不同的线程中使用, 因为引用计数操作使用特定于体系结构的原子指令。
安装
pip安装
pip install opencv-python==3.4.1.15 pip install opencv-contrib-python==3.4.1.15
注意:
3.4.2后版本部分算法被申请专利,建议安装3.4.1.15
主要模块
额外模块
core. 核心功能
定义基本数据结构的紧凑模块,包括密集多维数组 Mat 和所有其他模块使用的基本函数。
imgproc. 图像处理
图像处理模块,包括线性和非线性图像过滤、几何图像变换(调整大小、仿射和透视变形、基于通用表的重新映射)、颜色空间转换、直方图等。
imgcodecs. 图像文件的读写
videoio. 视频输入/输出
highgui. 高级图形用户界面
video. 视频分析
视频分析模块,包括运动估计、背景扣除和对象跟踪算法。
calib3d. 相机标定和3D重建
features2d. 2D特征框架
objdetect. 物体识别
dnn. 深度神经网络模块
ml. 机器学习
flann. 多维空间中的聚类和搜索
photo. 计算摄影
stitching. 图像拼接
gapi. 图形API
模块教程
alphamat. 阿尔法抠图
aruco. Aruco标记, 模块功能已迁移至objdetect模块
bgsegm. 改进的背景-前景分割方法
bioinspired. 受生物学启发的视觉模型和衍生工具
cannops. 上升后端的操作
ccalib. 用于3D重建的自定义校准模式
cudaarithm. 矩阵运算
cudabgsegm. 背景分割
cudacodec. 视频编码/解码
cudafeatures2d. 特征检测和描述
cudafilters. 图像过滤
cudaimgproc. 图像处理
cudalegacy. 旧版支持
cudaobjdetect. 物体识别
cudaoptflow. 光流
cudastereo. 立体对应
cudawarping. 图像变形
cudev. 设备层
cvv. 用于计算机视觉程序交互式可视化调试的GUI
datasets. 处理不同数据集的框架
dnn_objdetect. DNN用于物体识别
dnn_superres. DNN用于超分辨率
dpm. 基于可变形零件的模型
face. 人脸分析
freetype. 使用freetype/harfbuzz绘制UTF-8字符串
fuzzy. 基于模糊数学的图像处理
hdf. 分层数据格式输入/输出例程
hfs. 用于高效图像分割的分形特征选择
img_hash. 该模块带来了不同图像哈希算法的实现
intensity_transform. 该模块带来了强度变换算法的实现来调整图像对比度
julia. OpenCV的Julia绑定
line_descriptor. 从图像中提取线条的二进制描述符
mcc. Macbeth图标模块
optflow. 光流算法
ovis. OGRE 3D 可视化工具
phase_unwrapping. 相位展开API
plot. Mat数据的绘图函数
quality. 图像质量分析(IQA)API
rapid. 基于轮廓的3D对象跟踪
reg. 图像配准
rgbd. RGB深度处理
saliency. 显着性API
sfm. 运动结构
shape. 形状距离和匹配
stereo. 立体对应算法
structured_light. 结构光API
superres. 超分辨率
surface_matching. 表面匹配
text. 场景文本检测和识别
tracking. 追踪API
videostab. 视频稳定
viz. 3D展示台
wechat_qrcode. 微信二维码检测器,用于检测和解析二维码
xfeatures2d. 额外的2D特征框架
ximgproc. 扩展图像处理
xobjdetect. 扩展物体识别
xphoto. 附加照片处理算法
基本操作
图像数据读取
import cv2 # opencv读取的格式是BGR
img = cv2.imread("test.jpg",cv.IMREAD_COLOR)
- cv2.IMREAD_COLOR:彩色图像
- cv2.IMREAD_GRAYSCALE:灰度图像
图像显示
def cv_show(name, img):
"""图像显示"""
cv2.imshow(name,img)
cv2.waitKey(0) # 按任意键退出
cv2.destroyAllWindows()
图像保存
cv2.imwrite("test.png", img)
视频读取展示
- cv2.VideoCapture可以捕获摄像头,用数字来控制不同的设备,例如0,1。
- 如果是视频文件,直接指定路径即可。
vc = cv2.VideoCapture(0)
while 1:
ret, frame = vc.read()
cv2.imshow("frame", frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
vc.release()
cv2.destroyAllWindows()
截取部分图像数据
cat = img[0:50,0:200]
cv_show("cat",cat)
颜色通道提取
b, g, r = cv2.split(img)
hmerge = np.hstack((b, g, r))
cv_show("hmerge", hmerge)
img_new = cv2.merge((b, g, r))
cv_show("img_new", img_new)
边界填充
top_size, bottom_size, left_size, right_size = (50, 50, 50, 50)
replicate = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, borderType=cv2.BORDER_REPLICATE)
cv_show("img_new", replicate)
- BORDER_REPLICATE:复制法,也就是复制最边缘像素。
-
BORDER_REFLECT:反射法,对感兴趣的图像中的像素在两边进行复制。例如:fedcba abcdefgh hgfedcb -
BORDER_REFLECT_101:反射法,也就是以最边缘像素为轴,对称。例如:gfedcb abcdefgh gfedcba -
BORDER_WRAP:外包装法。例如:cdefgh abcdefgh abcdefg - BORDER_CONSTANT:常量法,常数值填充。
数值计算
img_1 = img + 10 # 各像素点数值加1
print(img[:5, :, 0])
print(img_1[:5, :, 0])
print((img + img_1)[:5, :, 0]) # 各像素点数值相加,除256取余
print(cv2.add(img, img_1)) # 各像素点数值相加,超过255取255
修改图片尺寸
img_2 = cv2.resize(img, img.shape[:2]) # 指定尺寸修改
img_3 = cv2.resize(img, (0, 0), fx=3, fy=3) # 同比例放大
cv_show("img_2", img_2)
cv_show("img_3", img_3)
图像融合
先将图片shape值变为一致,再融合
img = cv2.imread("test.png")
img1 = cv2.imread("test1.png")
img_2 = cv2.resize(img1, img.shape[:2]) # 指定尺寸修改
res = cv2.addWeighted(img, 0.4, img_2, 0.6, 0)
cv_show("res", res)
图像阈值
ret, dst = cv2.threshold(src, thresh, maxval, type)
- src:输入图,只能输入单通道图像,通常来说为灰度图。
- dst:输出图。
- thresh:阈值。
- maxval:当像素值超过了阈值(或者小于阈值,根据type来决定),所赋予的值
- type:二值化操作的类型,包含以下五种类型:
- cv2.THRESH_BINARY:超过阈值部分取maxval(最大值),否则取0。
- cv2.THRESH_BINARY_INV:THRESH_BINARY的反转。
- cv2.THRESH_TRUNC:大于阈值部分设为阈值,否则不变。
- cv2.THRESH_TOZERO:大于阈值部分不改变,否则设为0。
- cv2.THRESH_TOZERO_INV:THRESH_TOZERO的反转。
img_gray = cv2.imread("test.png", cv2.IMREAD_GRAYSCALE)
ret, dst = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY)
cv_show("dst", dst)
图像平滑
blur = cv2.blur(img, (3,3)) # 均值滤波,简单的平均卷积操作,需要设定核的大小
box = cv2.boxFilter(img,-1,(3,3), normalize=True) # 方框滤波,基本和均值一样,可以选择归一化
gaussian = cv2.GaussianBlur(img, (5,5), 1) # 高斯滤波,高斯模糊的卷积核里的数值是满足高斯分布的,相当于更重视中间的
median = cv2.medianBlur(img, 5) # 中值滤波,相当于用中值代替
形态学-腐蚀操作
kernel = np.ones((5,5), np.uint8)
erosion = cv2.erode(img, kernel, iterations=1)
cv_show("erosion", erosion)
形态学-膨胀操作
可以对腐蚀操作进行弥补
kernel = np.ones((5,5), np.uint8)
img_dilate = cv2.dilate(erosion, kernel, iterations=1)
cv_show("img_dilate", img_dilate)
开运算与闭运算
kernel = np.ones((5,5), np.uint8)
# 开运算:先腐蚀,再膨胀
img_open = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)
cv_show("open", img_open)
# 闭运算:先膨胀,在腐蚀
img_close = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)
cv_show("close", img_close)
梯度运算
可以帮助寻找边界
# 梯度运算 = 膨胀 - 腐蚀
img = cv2.imread("test.png")
kernel = np.ones((3, 3), np.uint8)
erosion = cv2.erode(img, kernel, iterations=1)
img_dilate = cv2.dilate(erosion, kernel, iterations=1)
gradient = cv2.morphologyEx(img_dilate, cv2.MORPH_GRADIENT, kernel)
cv_show("gradient", gradient)
礼帽与黑帽
kernel = np.ones((3, 3), np.uint8)
# 礼貌 = 原始输入 - 开运算结果
tophat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel)
# 黑帽 = 闭运算 - 原始输入
blackhat = cv2.morphologyEx(img, cv2.MORPH_BLACKHAT, kernel)
cv_show("tophat", tophat)
cv_show("blackhat", blackhat)
图像梯度-Sobel算子*
dst = cv2.Sobel(src, ddepth, dx, dy, ksize)
- ddepth:图像深度
- dx和dy:分别表示水平和竖直方向
- ksize:Sobel算子的大小
sobelx = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=3)
# 白到黑是正数,黑到白就是负数了,所有的负数会被截断成0,所以要绝对值。
sobelx = cv2.convertScaleAbs(sobelx)
cv_show(sobelx, "sobelx")
sobely = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=3)
sobely = cv2.convertScaleAbs(sobely)
cv_show(sobely, "sobely")
# 分别计算x和y,再求和,不建议直接计算。
sobelxy = cv2.addWeighted(sobelx, 0.5, sobely, 0.5, 0)
cv_show(sobelxy, "sobelxy")
scharr算子
差异变得更大,变得更敏感
lapkacian算子
对一些变化更敏感,对一些噪音点敏感,需要融合其他方法一块使用,一般不单独使用
Canny边缘检测
- 使用高斯滤波器,以平滑图像,滤除噪声。
- 计算图像中每个像素点的梯度强度和方向。
- 应用非极大值(Non-Maximum Suppression)抑制,以消除边缘检测带来的杂散影响。
- 应用双阈值(Double-Threshold)检测来确定真实和潜在的边缘。
- 通过抑制孤立的弱边缘最终完成边缘检测。
img_1 = cv2.Canny(img, 80, 150)
img_2 = cv2.Canny(img, 80, 100)
res = np.hstack((img_1, img_2))
cv_show("res", res)
图像金字塔
- 高斯金字塔
- 向下采样法(缩小)
- 将Gi与高斯内核卷积
- 将所有偶数行和列去除
- 向上采样法(放大)
- 将图像在每个方向扩大为原来的两倍,新增的行和列以0填充。
- 使用先前同样的内核(乘以4)与放大后的图像卷积,获得近似值。
- 向下采样法(缩小)
- 拉普拉斯金字塔
- 低通滤波
- 缩小尺寸
- 放大尺寸
- 图像相减
图像轮廓
cv2.findContours(img, mode, method)
mode: 轮廓检索模式
- RETR_EXTERNAL:只检索最外边轮廓;
- RETR_LIST:检索所有轮廓,并将其保存到一条链表中;
- RETR_CCOMP:检索所有轮廓,并将他们组织为两层;顶层是各部分的外部边界,第二层是空洞的边界;
- RETR_TREE:检索所有轮廓,并重构嵌套轮廓的整个层次;
method:
- CHAIN_APPROX_NONE:以Freeman链码的方式输出轮廓,所有的其他方法输出多边形(顶点的序列)。
- CHAIN_APPROX_SIMPLE:压缩水平的,垂直的和斜的部分,也就是函数只保留他们的终点部分。
为了更高的准确率,使用二值图像。
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
binary, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
# 传入绘制图像,轮廓,轮廓索引,颜色模式,线条厚度
# 注意需要copy,要不原图会变
draw_img = img.copy()
res = cv2.drawContours(draw_img, contours, -1, (0, 0, 255), 2)
轮廓特征
cnt = contours[0]
cv2.contours(cnt) # 面积
cv2.arcLength(cnt, True) # 周长,True表示闭合的
轮廓近似
epsilon = 0.1*cv2.arcLength(cnt, True)
approx = cv2.approxPolyDP(cnt, epsilon, True)
draw_img = img.copy()
res = cv2.drawContours(draw_img, [approx], -1, (0, 0, 255), 2)
边界矩形
x, y, w, h = cv2.boundingRect(cnt)
img = cv2.rectangle(img, (x,y), (x+w,y+h), (0,255,0), 2)
外接圆
(x,y),radius = cv2.minEnclosingCircle(cnt)
center = (int(x), int(y))
radius = int(radius)
img = cv2.circle(img, center, radius, (0,255,0),2)
模板匹配
模板匹配和卷积原理很像,模板在原图像上从原点开始滑动,计算模板与(图象被模板覆盖的地方)的差别程度,这个差别程度的计算方法在opencv里有6种,然后将每次计算的结果放入一个矩阵里,作为结果输出。假如原图形是AB大小,而模板是ab大小,则输出结果的矩阵是(A-a+1)*(B-b+1)。
- TM_SQDIFF:计算平方不同,计算出来的值越小,越相关。
- TM_CCORR:计算相关性,计算出来的值越大,越相关。
- TM_CCOEFF:计算相关系数,计算出来的值越大,越相关。
- TM_SQDIFF_NORMED:计算归一化平方不同,计算出来的值越接近0,越相关。
- TM_CCORR_NORMED:计算归一化相关性,计算出来的值越接近1,越相关。
- TM_CCOEFF_NORMED:计算归一化相关系数,计算出来的值越接近1,越相关。
img = cv2.imread("img.jpg",0) # 原始图像
template = cv2.imread("template.jpg",0) # 模板
h, w = template.shape[:2]
res = cv2.matchTemplate(img, template, cv2.TM_SQDIFF)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
直方图
cv2.calcHist(images, channels, mask, histSize, ranges)
- images:原图像的图像格式为uint8或float32.当传入函数时应用中括号“[]”括起来,例如“[ img ]”。
- channels:如果传入图像的灰度图,它的值就是“[ 0 ]”,如果是彩色图像,传入的参数可以是“[ 0 ]”、“[ 1 ]”、“[ 2 ]”,分别对应着 BGR。
- mask:掩模图像。统计整幅图像的直方图就把它设为None,但是如果你想统计图像的某一部分的直方图,你就只做一个掩膜图像并使用它。
- histSize:BIN 的数目。也应用中括号括起来。
- ranges:像素值范围常为 0 ~ 255。
img = cv2.imread("img.jpg",0)
hist = cv2.calcHist([img], [0], None, [256], [0,256])
直方图均衡化
equ = cv2.equalizeHist(img)
plt.hist(equ.ravel(),256)
自适应直方图均衡化
clach = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
res_clach = clach.apply(img)
res = np.hstack((img, res_clach))
傅里叶变换
我们生活在时间的世界中,早上7:00起来吃早饭,8:00去挤地铁,9:00开始上班。以时间为参照就是时域分析,但是在频域中一切都是静止的。
傅里叶变换作用
- 高频:变化剧烈的灰度变量,例如边界
- 低频:变化缓慢的灰度分量,例如一片大海
滤波
- 低通滤波器:只保留低频,会使得图像模糊
- 高通滤波器:只保留高频,会使得图像细节增强
opencv中主要就是cv.dft()和cv.idft()函数,输入图像需要先转换成np.float32格式。得到的结果中频率为0的部分会在左上角,通常要转换到中心位置,可以通过shift变换来实现。cv2.dft()返回的结果是双通道的(实部,虚部),通常还需要转换成图像格式才能显示(0,255)。
import numpy as np
import cv2
from matplotlib import pyplot as plt
img = cv2.imread("img.jpg",0)
img_float = np.float32(img)
dft = cv2.dft(img_float, flags = cv2.DFT_COMPLEX_OUTPUT)
dft_shift = np.fft.fftshift(dft)
# 得到灰度图能表示的形式
magnitude_spectrum = 20*np.log(cv2.magnitude(dft_shift[:,:,0],dft_shift[:,:,1]))
plt.subplot(121),plt.imshow(img, cmap = 'gray')
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(magnitude_spectrum, cmap = 'gray')
plt.title('Magnitude Spectrum'), plt.xticks([]), plt.yticks([])
plt.show()
信用卡识别卡号
from imutils import contours
import numpy as np
import argparse
import imutils
import cv2
import myutils
# 设置参数
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True, help="Path to input image")
ap.add_argument("-t", "--template", required=True, help="Path to template OCR_A image")
args = vars(ap.parse_args())
# 指定信用卡类型
FIRST_NUMBER = {
"3": "American Express",
"4": "Visa",
"5": "MasterCard",
"6": "Discover Card"
}
# 绘图展示
def cv_show(name, img):
cv2.imshow(name, img)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 读取一个模板图像
img = cv2.imread(args["template"])
cv_show("img", img)
# 灰度图
ref = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv_show("ref", ref)
# 二值图像
ref = cv2.threshold(ref, 10, 255, cv2.THRESH_BINARY_INV)[1]
cv_show("ref",ref)
# 计算轮廓
# cv2.findContours()函数接受的参数为二值图,即黑白的(不是灰度图),cv2.RETR_EXTERNAL只检测外轮廓,cv2.CHAIN_APPROX_SIMPLE只保留终点坐标
# 返回的list中每个元素都是图像中的一个轮廓。
ref_,refCnts,hierarchy = cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(ref, refCnts, -1, (0,255,0), 3)
cv_show("img", img)
print(np.array(refCnts).shape)
refCnts = myutils.sort_contours(refCnts, method="left-to-right")[0]
digits = {}
###