简介 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. 该模块带来了不同图像哈希算法的实现 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. 附加照片处理算法 基本操作 图像数据读取 1 2 import cv2 img = cv2.imread("test.jpg" ,cv.IMREAD_COLOR)
cv2.IMREAD_COLOR:彩色图像
cv2.IMREAD_GRAYSCALE:灰度图像
图像显示 1 2 3 4 5 def cv_show (name, img ): """图像显示""" cv2.imshow(name,img) cv2.waitKey(0 ) cv2.destroyAllWindows()
图像保存 1 cv2.imwrite("test.png" , img)
视频读取展示
cv2.VideoCapture可以捕获摄像头,用数字来控制不同的设备,例如0,1。
如果是视频文件,直接指定路径即可。
1 2 3 4 5 6 7 8 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()
截取部分图像数据 1 2 cat = img[0 :50 ,0 :200 ] cv_show("cat" ,cat)
颜色通道提取 1 2 3 4 5 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)
边界填充 1 2 3 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:常量法,常数值填充。
数值计算 1 2 3 4 5 6 img_1 = img + 10 print (img[:5 , :, 0 ])print (img_1[:5 , :, 0 ])print ((img + img_1)[:5 , :, 0 ]) print (cv2.add(img, img_1))
修改图片尺寸 1 2 3 4 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值变为一致,再融合
1 2 3 4 5 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的反转。
1 2 3 img_gray = cv2.imread("test.png" , cv2.IMREAD_GRAYSCALE) ret, dst = cv2.threshold(img_gray, 127 , 255 , cv2.THRESH_BINARY) cv_show("dst" , dst)
图像平滑 1 2 3 4 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 )
形态学-腐蚀操作 1 2 3 kernel = np.ones((5 ,5 ), np.uint8) erosion = cv2.erode(img, kernel, iterations=1 ) cv_show("erosion" , erosion)
形态学-膨胀操作 可以对腐蚀操作进行弥补
1 2 3 kernel = np.ones((5 ,5 ), np.uint8) img_dilate = cv2.dilate(erosion, kernel, iterations=1 ) cv_show("img_dilate" , img_dilate)
开运算与闭运算 1 2 3 4 5 6 7 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)
梯度运算 可以帮助寻找边界
1 2 3 4 5 6 7 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)
礼帽与黑帽 1 2 3 4 5 6 7 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算子的大小
1 2 3 4 5 6 7 8 9 10 sobelx = cv2.Sobel(img, cv2.CV_64F, 1 , 0 , ksize=3 ) 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" ) sobelxy = cv2.addWeighted(sobelx, 0.5 , sobely, 0.5 , 0 ) cv_show(sobelxy, "sobelxy" )
scharr算子 差异变得更大,变得更敏感
lapkacian算子 对一些变化更敏感,对一些噪音点敏感,需要融合其他方法一块使用,一般不单独使用
Canny边缘检测
使用高斯滤波器,以平滑图像,滤除噪声。
计算图像中每个像素点的梯度强度和方向。
应用非极大值(Non-Maximum Suppression)抑制,以消除边缘检测带来的杂散影响。
应用双阈值(Double-Threshold)检测来确定真实和潜在的边缘。
通过抑制孤立的弱边缘最终完成边缘检测。
1 2 3 4 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)
图像金字塔
高斯金字塔
向下采样法(缩小)
向上采样法(放大)
将图像在每个方向扩大为原来的两倍,新增的行和列以0填充。
使用先前同样的内核(乘以4)与放大后的图像卷积,获得近似值。
拉普拉斯金字塔
低通滤波
缩小尺寸
放大尺寸
图像相减
图像轮廓 cv2.findContours(img, mode, method)
mode: 轮廓检索模式
RETR_EXTERNAL:只检索最外边轮廓;
RETR_LIST:检索所有轮廓,并将其保存到一条链表中;
RETR_CCOMP:检索所有轮廓,并将他们组织为两层;顶层是各部分的外部边界,第二层是空洞的边界;
RETR_TREE:检索所有轮廓,并重构嵌套轮廓的整个层次;
method:
CHAIN_APPROX_NONE:以Freeman链码的方式输出轮廓,所有的其他方法输出多边形(顶点的序列)。
CHAIN_APPROX_SIMPLE:压缩水平的,垂直的和斜的部分,也就是函数只保留他们的终点部分。
为了更高的准确率,使用二值图像。
1 2 3 4 5 6 7 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) draw_img = img.copy() res = cv2.drawContours(draw_img, contours, -1 , (0 , 0 , 255 ), 2 )
轮廓特征 1 2 3 cnt = contours[0 ] cv2.contours(cnt) cv2.arcLength(cnt, True )
轮廓近似 1 2 3 4 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 )
边界矩形 1 2 x, y, w, h = cv2.boundingRect(cnt) img = cv2.rectangle(img, (x,y), (x+w,y+h), (0 ,255 ,0 ), 2 )
外接圆 1 2 3 4 (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大小,而模板是a b大小,则输出结果的矩阵是(A-a+1)*(B-b+1)。
TM_SQDIFF:计算平方不同,计算出来的值越小,越相关。
TM_CCORR:计算相关性,计算出来的值越大,越相关。
TM_CCOEFF:计算相关系数,计算出来的值越大,越相关。
TM_SQDIFF_NORMED:计算归一化平方不同,计算出来的值越接近0,越相关。
TM_CCORR_NORMED:计算归一化相关性,计算出来的值越接近1,越相关。
TM_CCOEFF_NORMED:计算归一化相关系数,计算出来的值越接近1,越相关。
1 2 3 4 5 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。
1 2 img = cv2.imread("img.jpg" ,0 ) hist = cv2.calcHist([img], [0 ], None , [256 ], [0 ,256 ])
直方图均衡化 1 2 equ = cv2.equalizeHist(img) plt.hist(equ.ravel(),256 )
自适应直方图均衡化 1 2 3 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)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import numpy as npimport cv2from matplotlib import pyplot as pltimg = 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()
信用卡识别卡号 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 from imutils import contoursimport numpy as npimport argparseimport imutilsimport cv2import myutilsap = 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) 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 = {}