
OpenCV是一个(开源)发行的跨平台计算机视觉库,可以运行在Linux、Windows和MacOS操作系统上。它轻量级而且高效——由一系列C函数和少量C++类构成,同时提供了Python、Ruby、MATLAB等语言的接口,实现了图像处理和计算机视觉方面的很多通用算法。
在学习过程中遇到问题最好的办法就是查询opencv官方文档。
图像的读取、显示与写入分别对应三个函数,()、()、()。
1.1.读取图像语法:(filename[,flags])---image
参数:
filename---文件路径(相对路径和绝对路径),路径中不要带有中文。
flags---可选标志,用于指定读取图像的样式,常见的有_UNCHANGED(-1)、_GRAYSCALE(0)、_COLOR(1)。默认为1。
注意:opencv读取彩色图像的格式是BGR,而大多数视觉库使用的是RGB,因此当将OpenCV与其他工具包一起使用时,当从一个库切换到另一个库时,不要忘记交换蓝色和红色通道。
语法:(window_name,image)---None
参数:
window_name---显示图像的窗口的名字。
image---显示图像的变量名。
注意:该函数一般和()、()、()一起使用。()函数是键盘绑定函数,等待键击任意键或指定键继续程序。()用于销毁全部窗口(从内存中清除)、()销毁指定窗口(从内存中清除)。
importcv2读取图像image_unchanged=("image\\",_UNCHANGED)image_grayscale=("image\\",_GRAYSCALE)image_color=("image\\",_COLOR)永远暂停程序直到键击任意键(0)定义鼠标回调的‘动作’函数defshowPixelValue(event,x,y,flags,param):判断鼠标是否移动ifevent==_MOUSEMOVE:转换色彩空间值ycb=(([[bgr]]),_BGR2YCrCb)[0][0]lab=(([[bgr]]),_BGR2Lab)[0][0]hsv=(([[bgr]]),_BGR2HSV)[0][0]在结果区域内显示鼠标位置不同色彩空间的值(placeholder,"BGR{}".format(bgr),(20,70),_HERSHEY_COMPLEX,.9,(255,255,255),1,_AA)(placeholder,"HSV{}".format(hsv),(20,140),_HERSHEY_COMPLEX,.9,(255,255,255),1,_AA)(placeholder,"YCrCb{}".format(ycb),(20,210),_HERSHEY_COMPLEX,.9,(255,255,255),1,_AA)(placeholder,"LAB{}".format(lab),(20,280),_HERSHEY_COMPLEX,.9,(255,255,255),1,_AA)显示合并后的图像('PRESSPforPrevious,NforNextImage',combinedResult)if__name__=='__main__':显示转换尺寸后的图像(在‘PRESS’显示框加载)('PRESSPforPrevious,NforNextImage',img)创建一个鼠标回调,加载回调‘动作’函数,并且该动作在‘PRESS’显示框内实现('PRESSPforPrevious,NforNextImage',showPixelValue)i=0while1:k=(1)0xFF键击‘p’切换到上一张图片elifk==ord('p'):i-=1img=(files[i%len(files)])img=(img,(400,400))('PRESSPforPrevious,NforNextImage',img)指定目标图像的宽高图像失真cat_distortion=(cat,(200,300))('catdistortion',cat_distortion)(0)()图像不失真cat_down_1=(cat,None,fx=0.75,fy=0.75)cat_up_1=(cat,None,fx=1.2,fy=1.2)('cat_down-Nodistortion1',cat_down_1)('cat_up-Nodistortion1',cat_up_1)指定图像缩放方法cat_scale0=(cat,(150,150),interpolation=_AREA)cat_scale1=(cat,(450,450),interpolation=_CUBIC)cat_scale2=(cat,(450,450),interpolation=_LINEAR)cat_scale3=(cat,(150,150),interpolation=_NEAREST)('cat_scale0',cat_scale0)('cat_scale1',cat_scale1)('cat_scale2',cat_scale2)('cat_scale3',cat_scale3)(0)()运行结果(部分)如下:

裁剪图像是为了从图像中删除不需要的对象或区域;也就是删除其他,保留想要的图像区域。
opencv中的图像裁剪是利用numpy数组切片的方法来实现的。首先,我们要知道图像就是一个数组,其由高、宽、通道数三个维度组成。仅对图像进行裁剪,就是意味着对宽和高两个维度进行切片操作。具体如下:
语法:image[start_row:_row,start_col:_col]---dst
参数:start_row、_row---图像的开始与结束的行坐标。
start_col:、_col---图像的开始与结束的列坐标。
注意:图像可视为坐标系,坐标原点为图像的左上角顶点,裁剪区域即是四条直线的围合区域。
importcv2cat=('image\\')print()旋转图像center=(width/3,height/10)rotate_matrix=(center,-15,scale=1.2)rotated_cat=(cat,rotate_matrix,dsize=(500,300))平移图像tx,ty=width/5,-height/5translation_matrix=([[1,0,tx],[0,1,ty]])translation_cat=(cat,translation_matrix,dsize=(500,300))('originalimage',cat)('translation_cat',translation_cat)(0)()运行结果如下:


图像翻转,也就是通常所说的镜像。主要包括垂直翻转、水平翻转、水平垂直翻转。
语法:(src,flipCode[,dst])---dst
参数:
src---源图像。
flipCode---翻转方式。flipCode==0,垂直翻转(沿x轴翻转);flipCode0,水平翻转(沿y轴翻转);flipCode0,水平垂直翻转(先沿X轴翻转,再沿Y轴翻转,等价于旋转180°)。
importcv2importnumpyasnpcat=('image\\')绘制一条长260像素的横向白色线girl_line=()(girl_line,(0,250),(260,250),(255,255,255),thickness=2)('girl_line',girl_line)()()运行结果如下:

语法:(image,center_coordinates,radius,color[,thickness[,lineType[,shift]]])---image
参数:
image---图像,通常是源图像的副本。
center_coordinates---圆形的圆心坐标,元组参数。
radius---圆形的半径。
color---直线的颜色,元组参数。
thickness---正值,直线的厚度;负值表示绘制一个填充圆。
lineType---圆边线类型。
shift---圆心和半径值中的小数位数。
注意:start_point与_point的值第一个值代表的是横轴值,第二个值代表的是竖轴值,可简单理解为x、y轴的值。
importcv2绘制一个填充圆girl_circle1=()(girl_circle1,(380,270),175,(255,255,255),thickness=-1)('girl_circle1',(girl_circle1,None,fx=0.6,fy=0.6))()()运行结果如下:

语法:(image,start_point,_point,color[,thickness[,lineType[,shift]]])---image
参数:
image---图像,通常是源图像的副本。
start_point---矩形的起始点,即其左上角,元组参数。
_point---矩形的终点,即其右下角,元组参数。
color---直线的颜色,元组参数。
thickness---正值,直线的厚度;负值表示绘制一个填充矩形。
lineType---矩形边线类型。
shift---坐标中的小数位数。
注意:start_point与_point的值第一个值代表的是横轴值,第二个值代表的是竖轴值,可简单理解为x、y轴的值。
importcv2绘制一个填充矩形girl_rec1=()(girl_rec1,(200,170),(570,400),(255,255,255),thickness=-2)('girl_rec1',(girl_rec1,None,fx=0.6,fy=0.6))()()运行结果如下:

语法:(image,center,axesLength,angle,startAngle,Angle,color[,thickness[,lineType[,shift]]])---image
参数:
image---图像,通常是源图像的副本。
centert---椭圆的圆心坐标,元组参数。
axesLength---椭圆长轴与短轴大小的一半,元组参数。
angle---椭圆的旋转角度(角度制)。
startAngle---椭圆弧的起始角度。
Angle---椭圆弧的结束角度。
color---直线的颜色,元组参数。
thickness---正值,直线的厚度;负值表示绘制一个填充矩形。
lineType---椭圆边线类型。
shift---坐标中的小数位数。
注意:①椭圆的角度均遵循顺逆时针规则,x轴正半轴为起始轴,顺时针为正,逆时针为负。②先绘制正常的椭圆,然后再根据参数angle以center为旋转中心旋转椭圆。
importcv2初始化参数top_left_corner,bottom_right_corner=[],[]键击‘q’退出程序,键击‘c’重新绘制矩形whilek!=113:("Window",image)k=(0)ifk==99:image=()("Window",image)()运行过程截图:

跟踪栏回调函数与鼠标回调函数类似。
①怎么定义跟踪栏回调函数?
语法:defTrackbarCallback(args)
参数:args---跟踪栏参数。args[0]可以检索跟踪栏条目位置,这是一个与用户发生交互的参数,其范围是(0,count]内的整数。
注意:定义回调函数时,函数名是可变的。
②怎么调用跟踪栏回调函数?
使用()函数来调用跟踪栏回调函数。
语法:(trackbarName,winname,value,count,onChange[,userdata])
参数:
trackbarName---创建的跟踪栏名称。
winname---显示窗口的名称。
value---滑块的默认位置。
count---滑块可滑到的最大位置。
onChange---回调函数名。
userdata---传递给回调函数的用户数据。它可用于处理跟踪栏事件,而无需使用全局变量。
③代码的具体实现。
importcv2创建回调函数,图像可以在[1,2]之间缩放defscaleImage(*args):scaleFactor=1+args[0]scaledImage=(image,None,fx=scaleFactor,fy=scaleFactor,interpolation=_LINEAR)(windowName,scaledImage)image=("image\\")(windowName,_AUTOSIZE)创建恒等核kernel1=([[0,0,0],[0,1,0],[0,0,0]])显示原始图像和经过恒等核过滤后的图像('originalimageontheleft,smoothimageontheright',combine)(0)('image\\smooth_',smooth_car)()运行结果如下:

importcv2importnumpyasnpcar=('image\\')使用filter2D()函数执行线性滤波操作smooth_car=(car,ddepth=-1,kernel=kernel1)combine=((car,smooth_car))创建锐化内核(归一化处理,保持亮度不变)kernel1=([[0,-1,0],[-1,3,0],[0,0,0]])显示原始图像和经过自定义内核锐化后的图像('originalimageontheleft,smoothimageontheright',combine)(0)('image\\sharp_',sharp_car)()运行结果如下:

通常,单一使用图像模糊或锐化是达不到预期效果的,此时我们可以选择使用双边滤波方法让二者共同作用在图像中。双边滤波本质上是对图像应用2D高斯模糊,且考虑了相邻像素强度的变化(用以降低边缘区域的高斯权重)。
让我们先来探讨下双边滤波的工作过程。假如我们正在过滤图像中靠近边缘的区域,普通的高斯模糊会根据实际权重模糊边缘,但是双边滤波器可以通过感知边缘(通过像素强度的差异)主动降低边缘区域的权重,从而减少高斯模糊对边缘的影响。因此,像素强度更均匀的区域更加模糊,边缘区域几乎无模糊。
图像中的过滤像素的最终值是由空间和强度权重决定,因此
相似且接近过滤像素的像素将产生不同程度的影响(根据高斯权重);
远离过滤像素的像素影响较小(由于高斯权重);
过滤像素附近的像素强度差异越大影响越小,即使很靠近过滤像素。
opencv中利用()函数实现双边过滤。
语法:(src,d,sigmaColor,sigmaSpace[,dst[,borderType]])---dst
参数:
src---源图像。
d---定义用于过滤的像素邻域的直径。
sigmaColor---颜色(像素)强度标准差,定义一维高斯分布,该分布指定允许的像素强度变化的程度。
sigmaSpace---x和y轴组成的空间的标准差。
borderType---制作图形边界。
importcv2importnumpyasnpcar=('image\\')bilateral_filter_car=(car,d=5,sigmaColor=40,sigmaSpace=50)combine=((car,bilateral_filter_car))需要对图像做预处理——转化为灰度图src=('image\\',_GRAYSCALE)retval,dst=(src,100,255,_BINARY)print(retval)combine=((src,dst))('originalimageontheleft,smoothimageontheright',combine)()()运行结果如下:


Blob,译文是“(颜色)的一小片,斑点”。它是图像中拥有共同属性(例如灰度值)的连通域,上图中的Blob就是灰色的连通域。Blob检测就是为了识别并标记这些连通域。
就上图而言,使用Blob检测可以达到如下效果:

Blob检测是通过算子实现的。
8.1Blob检测是如何工作的?阈值化(Thresholding):通过对源图像进行阈值化,将源图像转换为若干二值图像。阈值从minThreshold开始,以thresholdStep为间隔递增,直到maxThreshold。
分组(Grouping):对每份二值图像提取连通域。
合并(Merging):计算每个连通域的中心,并合并距离小于minDistBetweenBlobs的连通域。
返回(Returning):计算并返回新合并的连通域的中心和半径。
8.2.按颜色、大小和形状筛选Blob按颜色:首先,需要明确需要颜色参数筛选Blob,即设置filterByColor=1(或True)。其次设置颜色倾向,blobColor=0以选择较暗的blob,blobColor=255以选择较浅的区域。
按大小:首先,需要明确需要大小参数筛选Blob,即设置filterByArea=1(或True)。其次设置minArea和maxArea的适当值。例如,设置minArea=50,maxArea=234,将滤除所有少于50和多于234个像素点的blob。
按形状:在opencv中形状由三个参数控制,圆度(Circularity)、凸度(Convexity)、惯性比(InertiaRatio)。圆度:Blob与圆的接近程度,其计算公式为。例如正六边形的圆度大于正方形的。圆的圆度为1,正方形的为0.785。首先设置filterByCircularity=1(或True),然后设置适当的minCircularity和maxCircularity。凸度:Blob面积/凸包面积。首先设置filterByConvexity=1(或True),然后设置适当的minConvexity和maxConvexity(两值在[0,1]范围)。惯性比:对于一个圆,该值是1,对于椭圆它是0和1之间,而对于线是0。首先设置filterByInertia=1(或True),然后设置适当的minInertiRatio和maxInertiRatio(两值在[0,1]范围)。
8.3.设置参数读取图像im=("image\\",_GRAYSCALE)改变阈值=10=200根据Circularity过滤==0.1根据Inertia过滤==0.01前的版本使用()创建检测器检测blobskeypoints=(im)_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS确保圆的大小对应于blob的大小im_with_keypoints=(im,keypoints,([]),(0,0,255),_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
语法:(src,ddepth,dx,dy[,dst[,ksize[,scale[,delta[,borderType]]]]])---dst
参数:
src---源图像。ddepth---输出图像的位深度。
dx---x方向的求导阶数。dy---y方向的求导阶数。
ksize---Sobel卷积核的尺寸,必须是奇数。importcv2image=('image\\',_GRAYSCALE)Sobel算子1,dx=1,dy=0sobel_x=(image_blur,_64F,dx=1,dy=0,ksize=3)Sobel算子3,dx=1,dy=1sobel_xy=(image_blur,_64F,dx=1,dy=1,ksize=3)('SobelX',(sobel_x,None,fx=0.6,fy=0.6))(0)('SobelY',(sobel_y,None,fx=0.6,fy=0.6))(0)('SobelXY',(sobel_xy,None,fx=0.6,fy=0.6))(0)()运行结果如下:

9.2Canny边缘检测关于Canny检测的原理,可参看EdgeDetectionUsingOpenCV|LearnOpenCV平滑图像image_blur=(image,(3,3),sigmaX=1,sigmaY=0)edges=(image_blur,75,200)('CannyEdgeDetection',edges)(0)()
运行结果如下:

轮廓检测可以检测物体的边界,并在图像中轻松定位它们。它可以被应用到图像前景提取、简单图像分割、检测和识别等工作中。
等高线:指具有相同颜色和强度的边界像素点连接而成的闭合线路,也就是轮廓。
在opencv中,我们可以使用()和()在图像中查找和绘制轮廓。在查找轮廓时,常用两种检测算法:_APPROX_NONE、_APPROX_SIMPLE。
读取图像并将其转换为灰度格式。这是由于轮廓检测是以阈值检测为基础的,而阈值又是针对灰度图而言的。
应用二值图像。利用阈值检测或Canny检测将灰度图转化为二值图,并将此二值图输入到查找轮廓算法中。
查找轮廓。使用()函数检测图像轮廓。
绘制轮廓。使用()函数绘制图像轮廓。
10.2.轮廓检测的实现首先我们先来了解两个函数,()和()。
()
语法:(image,mode,method[,contours[,hierarchy[,offset]]])---contours,hierarchy
参数:
image---二值图像。
mode---轮廓检索模式。包括_EXTERNAL、_LIST、_CCOMP、_TREE。示例中,我们将使用_TREE,这意味着将从二值图像中检索所有可能的轮廓。
method---轮廓近似方法。常用_APPROX_NONE和_APPROX_SIMPLE。_APPROX_NONE存储所有的等高点;_APPROX_SIMPLE压缩水平、垂直和对角线段,仅保留其端点。例如,举行轮廓使用4个角点进行编码。
hierarchy---层次结构。
offset---偏移。
返回:contours---轮廓线。hierarchy---层次结构。
()
语法:(image,contours,contourIdx,color[,thickness[,lineType[,hierarchy[,maxLevel[,offset]]]]])---image
参数:
image---要在其上绘制轮廓的图像。
contours---从()函数获得的轮廓线。
contoursIdx---等值线点的像素坐标列在获得的等值线中。使用此参数,可以指定此列表中的索引位置,以准确指示要绘制的轮廓点。提供负值将绘制所有等值线点。
color---要绘制的轮廓线的颜色。
thickness---要绘制的轮廓线的粗细,若为负数则填充轮廓线内部。
importcv2apple=('image\\')image=(apple,_BGR2GRAY)image_copy=()retval,dst=(image,100,255,_BINARY)contours,hierarchy=(dst,_TREE,_APPROX_NONE)(image=image_copy,contours=contours,contourIdx=-1,color=(0,255,0),thickness=2)('111',image_copy)(0)()运行结果如下:
importcv2apple=('image\\')image=(apple,_BGR2GRAY)image_copy=()retval,dst=(image,100,255,_BINARY)contours,hierarchy=(dst,_TREE,_APPROX_SIMPLE)(image=image_copy,contours=contours,contourIdx=-1,color=(0,255,0),thickness=2)('111',image_copy)(0)()运行结果如下:
可以看到_APPROX_NONE和_APPROX_SIMPLE的输出之间几乎没有差异。这是为什么呢?
这归功于()函数。尽管_APPROX_SIMPLE方法通常生成的点较少,但()函数会自动连接相邻的点,即使它们不在轮廓列表中,也会连接它们。
层次结构(hierarchy)表示等高线(轮廓线)之间的父子关系,在轮廓检测中轮廓检索模式mode对生成的轮廓层次结构产生影响。
为什么要引入层次结构概念呢?这是因为,我们既需要描绘图像中单个对象的轮廓,也需要描绘对象和其内部的轮廓。

通过上图中的几个形状和线条我们可以很好的了解层次结构,不同的数字显示了不同形状之间的层次结构。
根据等高线层次结构和父子关系,所有单独的数字(即1、2、3和4)都是单独的对象。
我们可以说3a是3的孩子。请注意,3a表示轮廓3的内部部分。
等高线1、2和4都是父形状,没有任何关联的子形状,因此它们的编号是任意的。换句话说,轮廓2可以标记为1,反之亦然。
()函数返回的hierarchy(层次结构)是一个三维数组,该数组中的每个层次结构包含了四个值,分别是Next,Previous,First_Child,Parent。
Next---表示当前轮廓的下一个处于同一层次结构级别的轮廓索引。上图中,等高线1与2的结构级别处于同一层次结构级别上,因此1Next---2;没有与等高线3处于同一层次结构级别的等高线,因此3Next----1。
Previous---表示当前轮廓的上一个处于同一层次结构级别的轮廓索引。
First_Child---表示当前轮廓的第一个子轮廓索引。上图中等高线3的子轮廓是3a,因此3First_Child---3a。
Parent---表示当前轮廓的父等高线的索引。
不同轮廓检索模式下的轮廓层次结构和轮廓线:
mode=_LIST
RETR_LIST模式不会在提取的轮廓之间创建任何父子关系,因此,对于检测到的所有等高线的First_Child与Parent的值都是-1。
importcv2image2=('image\\')image=(image2,_BGR2GRAY)retval,thresh2=(image,150,255,_BINARY)contours5,hierarchy5=(thresh2,_LIST,_APPROX_NONE)image_copy6=()(image_copy6,contours5,-1,(0,255,0),2,_AA)('RETR_LIST',image_copy6)print(f"RETR_LIST:\n{hierarchy5}")(0)('image\\contours_retr__',image_copy6)()'''RETR_LIST:[[[1-1-1-1][20-1-1][31-1-1][42-1-1][-13-1-1]]]'''生成图如下:

mode=_EXTERNAL
RETR_EXTERNAL模式仅检测父轮廓,并忽略任何子轮廓,因此所有的内部轮廓(如3a和4)都不会被绘制。
importcv2image2=('image\\')image=(image2,_BGR2GRAY)retval,thresh2=(image,150,255,_BINARY)contours5,hierarchy5=(thresh2,_EXTERNAL,_APPROX_NONE)image_copy6=()(image_copy6,contours5,-1,(0,255,0),2,_AA)('RETR_EXTERNAL',image_copy6)print(f"RETR_EXTERNAL:\n{hierarchy5}")(0)('image\\contours_retr_',image_copy6)()'''RETR_EXTERNAL:[[[1-1-1-1][20-1-1][-11-1-1]]]'''生成图如下:

mode=_CCOMP
RETR_CCOMP模式建立了两级层次结构,即轮廓线不是第一级层次结构就是第二级层次结构;若有嵌套的轮廓线,则2N+1(N是非负整数)是第一级层次结构。如下图所示,

importcv2image2=('image\\')image=(image2,_BGR2GRAY)retval,thresh2=(image,150,255,_BINARY)contours5,hierarchy5=(thresh2,_CCOMP,_APPROX_NONE)image_copy6=()(image_copy6,contours5,-1,(0,255,0),2,_AA)('RETR_CCOMP',image_copy6)print(f"RETR_CCOMP:\n{hierarchy5}")(0)('image\\contours_retr_',image_copy6)()'''RETR_CCOMP:[[[1-1-1-1][302-1][-1-1-11][41-1-1][-13-1-1]]]'''生成图如下:

mode=_TREE
RETR_TREE模式创建完整的层次结构,级别不限于两级,其视情况而定。

importcv2image2=('image\\')image=(image2,_BGR2GRAY)retval,thresh2=(image,150,255,_BINARY)contours5,hierarchy5=(thresh2,_TREE,_APPROX_NONE)image_copy6=()(image_copy6,contours5,-1,(0,255,0),2,_AA)('RETR_TREE',image_copy6)print(f"RETR_TREE:\n{hierarchy5}")(0)('image\\contours_retr_',image_copy6)()'''RETR_TREE:[[[3-11-1][-1-120][-1-1-11][40-1-1][-13-1-1]]]'''生成图如下:
不同轮廓检索方式的运行时间比较:
轮廓检索方式
运行时间(s)
_LIST
0.000382
_EXTERNAL
0.000554
_CCOMP
0.001845
_TREE
0.005594
_LIST和_EXTERNAL执行所需的时间最少,因为_LIST没有定义任何层次结构,_EXTERNAL只检索父轮廓
_CCOMP执行时间第次之,它检索所有轮廓并定义两级层次结构。
_TREE执行所需的时间最长,因为它会检索所有轮廓,并为每个父子关系定义独立的层次结构级别。
为帮助更多对人工智能感兴趣的小伙伴们能够有效的系统性的学习以及论文的研究,小编特意制作整理了一份人工智能学习资料给大家,整理了很久,非常全面。

每一个专栏都是大家非常关心,和非常有价值的话题,如果我的文章对你有所帮助,还请帮忙点赞、好评、转发一下,你的支持会激励我输出更高质量的文章,非常感谢!
