OpenCV

简介

计算机图像处理库,有丰富的接口,C++、python等

也支持深度学习

OpenCV-Python是一个Python绑定库,旨在解决计算机视觉问题,

图像处理简介

数字图像:

计算机采用0~255的灰度表示图像的亮度,0代表最黑

 

图像的分类:

二值图像:

图像只有01两种,0代表黑,1代表白

灰度图

0-255的灰度

彩色图

每个像素由红(red)、绿(green)、蓝(blue)共同表示,也是0-255。

安装

安装OpenCV之前需要先安装numpy, matplotlib。

创建Python虚拟环境cv, 在cv中安装即可。

先安装OpenCV-Python, 由于一些经典的算法被申请了版权,新版本有很大的限制,所以选用3.4.3以下的版本

pip install opencv-python==3.4.2.17

现在可以测试下是否安装成功,运行以下代码无报错则说明安装成功。

import cv2
# 读一个图片并进行显示(图片路径需自己指定)
lena=cv2.imread("1.jpg")
cv2.imshow("image",lena)
cv2.waitKey(0)

如果我们要利用SIFT和SURF等进行特征提取时,还需要安装:

pip install opencv-contrib-python==3.4.2.17

模块

  • core模块实现了最核心的数据结构及其基本运算,如绘图函数、数组操作相关函数等。

  • highgui模块实现了视频与图像的读取、显示、存储等接口。

  • imgproc模块实现了图像处理的基础方法,包括图像滤波、图像的几何变换、平滑、阈值分割、形态学处理、边缘检测、目标检测、运动分析和对象跟踪等。

对于图像处理其他更高层次的方向及应用,OpenCV也有相关的模块实现

  • features2d模块用于提取图像特征以及特征匹配,nonfree模块实现了一些专利算法,如sift特征。

  • objdetect模块实现了一些目标检测的功能,经典的基于Haar、LBP特征的人脸检测,基于HOG的行人、汽车等目标检测,分类器使用Cascade Classification(级联分类)和Latent SVM等。

  • stitching模块实现了图像拼接功能。

  • FLANN模块(Fast Library for Approximate Nearest Neighbors),包含快速近似最近邻搜索FLANN 和聚类Clustering算法。

  • ml模块机器学习模块(SVM,决策树,Boosting等等)。

  • photo模块包含图像修复和图像去噪两部分。

  • video模块针对视频处理,如背景分离,前景检测、对象跟踪等。

  • calib3d模块即Calibration(校准)3D,这个模块主要是相机校准和三维重建相关的内容。包含了基本的多视角几何算法,单个立体摄像头标定,物体姿态估计,立体相似性算法,3D信息的重建等等。

  • G-API模块包含超高效的图像处理pipeline引擎

1、OpenCV基础操作

1.1 读取图像

  1. API

cv.imread()

参数:

  • 要读取的图像

  • 读取方式的标志

    • cv.IMREAD*COLOR:以彩色模式加载图像,任何图像的透明度都将被忽略。这是默认参数。

    • cv.IMREAD*GRAYSCALE:以灰度模式加载图像

    • cv.IMREAD_UNCHANGED:包括alpha通道的加载图像模式。

      可以使用1、0或者-1来替代上面三个标志

  • 参考代码

    import numpy as np
    import cv2 as cv
    # 以灰度图的形式读取图像
    img = cv.imread('messi5.jpg',0)

注意:如果加载的路径有错误,不会报错,会返回一个None值

1.2显示图像

1 . API

cv.imshow()

参数:

  • 显示图像的窗口名称,以字符串类型表示

  • 要加载的图像

注意:在调用显示图像的API后,要调用cv.waitKey()给图像绘制留下时间,否则窗口会出现无响应情况,并且图像无法显示出来

另外我们也可使用matplotlib对图像进行展示。

  1. 参考代码

    # opencv中显示
    cv.imshow('image',img)
    cv.waitKey(0)
    # matplotlib中展示
    plt.imshow(img[:,:,::-1])

1.3 保存图像

  1. API

    cv.imwrite()

    参数:

    • 文件名,要保存在哪里

    • 要保存的图像

  2. 参考代码

    cv.imwrite('messigray.png',img)

1.4 绘制直线

cv.line(img,start,end,color,thickness)

参数:

  • img:要绘制直线的图像

  • Start,end: 直线的起点和终点

  • color: 线条的颜色

  • Thickness: 线条宽度

1.5 绘制圆形

cv.circle(img,centerpoint, r, color, thickness)

参数:

  • img:要绘制圆形的图像

  • Centerpoint, r: 圆心和半径

  • color: 线条的颜色

  • Thickness: 线条宽度,为-1时生成闭合图案并填充颜色

1.6 绘制矩形

cv.rectangle(img,leftupper,rightdown,color,thickness)

参数:

  • img:要绘制矩形的图像

  • Leftupper, rightdown: 矩形的左上角和右下角坐标

  • color: 线条的颜色

  • Thickness: 线条宽度

1.7 向图像中添加文字

cv.putText(img,text,station, font, fontsize,color,thickness,cv.LINE_AA)

参数:

  • img: 图像

  • text:要写入的文本数据

  • station:文本的放置位置

  • font:字体

  • Fontsize :字体大小

 

1.8 获取并修改图像中的像素点

我们可以通过行和列的坐标值获取该像素点的像素值。对于BGR图像,它返回一个蓝,绿,红值的数组。对于灰度图像,仅返回相应的强度值。使用相同的方法对像素值进行修改。

import numpy as np
import cv2 as cv
img = cv.imread('messi5.jpg')
# 获取某个像素点的值
px = img[100,100]
# 仅获取蓝色通道的强度值
blue = img[100,100,0]
# 修改某个位置的像素值
img[100,100] = [255,255,255]

1.9 获取图像的属性

图像属性包括行数,列数和通道数,图像数据类型,像素数等。

img.shape

img.size

img.dtype

1.10 图像通道的拆分与合并

有时需要在B,G,R通道图像上单独工作。在这种情况下,需要将BGR图像分割为单个通道。或者在其他情况下,可能需要将这些单独的通道合并到BGR图像。你可以通过以下方式完成。

# 通道拆分
b,g,r = cv.split(img)
# 通道合并
img = cv.merge((b,g,r))

1.11 色彩空间的改变

OpenCV中有150多种颜色空间转换方法。最广泛使用的转换方法有两种,BGR↔Gray和BGR↔HSV。

API:

cv.cvtColor(input_image,flag)

参数:

  • input_image: 进行颜色空间转换的图像

  • flag: 转换类型

    • cv.COLOR_BGR2GRAY : BGR↔Gray

    • cv.COLOR_BGR2HSV: BGR→HSV

2、OpenCV图像处理

2.1图像的几何变换

2.1.1 图像缩放

缩放是对图像的大小进行调整,即使图像放大或缩小。

  1. API

    cv2.resize(src,dsize,fx=0,fy=0,interpolation=cv2.INTER_LINEAR)

    参数:

    • src : 输入图像

    • dsize: 绝对尺寸,直接指定调整后图像的大小

    • fx,fy: 相对尺寸,将dsize设置为None,然后将fx和fy设置为比例因子即可

    • interpolation:插值方法,

  2. 示例

  3. import cv2 as cv
    # 1. 读取图片
    img1 = cv.imread("./image/dog.jpeg")
    # 2.图像缩放
    # 2.1 绝对尺寸
    rows,cols = img1.shape[:2]
    res = cv.resize(img1,(2*cols,2*rows),interpolation=cv.INTER_CUBIC)

    # 2.2 相对尺寸
    res1 = cv.resize(img1,None,fx=0.5,fy=0.5)

    # 3 图像显示
    # 3.1 使用opencv显示图像(不推荐)
    cv.imshow("orignal",img1)
    cv.imshow("enlarge",res)
    cv.imshow("shrink)",res1)
    cv.waitKey(0)

    # 3.2 使用matplotlib显示图像
    fig,axes=plt.subplots(nrows=1,ncols=3,figsize=(10,8),dpi=100)
    axes[0].imshow(res[:,:,::-1])
    axes[0].set_title("绝对尺度(放大)")
    axes[1].imshow(img1[:,:,::-1])
    axes[1].set_title("原图")
    axes[2].imshow(res1[:,:,::-1])
    axes[2].set_title("相对尺度(缩小)")
    plt.show()

2.1.2 图像平移

图像平移将图像按照指定方向和距离,移动到相应的位置。

1API

cv.warpAffine(img,M,dsize)

参数:

  • img: 输入图像

  • M: 2*∗3移动矩阵

    对于(x,y)处的像素点,要把它移动到(x+t_x , y+t_yx+t**x,y+t**y)处时,M矩阵应如下设置:

    M=\begin{bmatrix} 1&0&t_x\ 0&1&t_y\ \end{bmatrix}M=⎣⎡1001txt**y⎦⎤

    注意:将MM设置为np.float32类型的Numpy数组。

  • dsize: 输出图像的大小

    注意:输出图像的大小,它应该是(宽度,高度)的形式。请记住,width=列数,height=行数。

  • 示例

需求是将图像的像素点移动(50,100)的距离:

import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt
# 1. 读取图像
img1 = cv.imread("./image/image2.jpg")

# 2. 图像平移
rows,cols = img1.shape[:2]
M = M = np.float32([[1,0,100],[0,1,50]])# 平移矩阵
dst = cv.warpAffine(img1,M,(cols,rows))

# 3. 图像显示
fig,axes=plt.subplots(nrows=1,ncols=2,figsize=(10,8),dpi=100)
axes[0].imshow(img1[:,:,::-1])
axes[0].set_title("原图")
axes[1].imshow(dst[:,:,::-1])
axes[1].set_title("平移后结果")
plt.show()

2.1.3 图像旋转

图像旋转是指图像按照某个位置转动一定角度的过程,旋转中图像仍保持这原始尺寸。图像旋转后图像的水平对称轴、垂直对称轴及中心坐标原点都可能会发生变换,因此需要对图像旋转中的坐标进行相应转换。

那图像是怎么进行旋转的呢?如下图所示:

假设图像逆时针旋转\thetaθ,则根据坐标转换可得旋转转换为:

其中:

带入上面的公式中,有:

假设在旋转的时候是以旋转中心为坐标原点的,旋转结束后还需要将坐标原点移到图像左上角,也就是还要进行一次变换。

 

在OpenCV中图像旋转首先根据旋转角度和旋转中心获取旋转矩阵,然后根据旋转矩阵进行变换,即可实现任意角度和任意中心的旋转效果。
  1. API

    cv2.getRotationMatrix2D(center, angle, scale)

    参数:

    • center:旋转中心

    • angle:旋转角度

    • scale:缩放比例

    返回:

    • M:旋转矩阵

      调用cv.warpAffine完成图像的旋转

  2. 示例

    import numpy as np
    import cv2 as cv
    import matplotlib.pyplot as plt
    # 1 读取图像
    img = cv.imread("./image/image2.jpg")

    # 2 图像旋转
    rows,cols = img.shape[:2]
    # 2.1 生成旋转矩阵
    M = cv.getRotationMatrix2D((cols/2,rows/2),90,1)
    # 2.2 进行旋转变换
    dst = cv.warpAffine(img,M,(cols,rows))

    # 3 图像展示
    fig,axes=plt.subplots(nrows=1,ncols=2,figsize=(10,8),dpi=100)
    axes[0].imshow(img1[:,:,::-1])
    axes[0].set_title("原图")
    axes[1].imshow(dst[:,:,::-1])
    axes[1].set_title("旋转后结果")
    plt.show()

     

2.1.4 仿射变换

图像的仿射变换涉及到图像的形状位置角度的变化,是深度学习预处理中常到的功能,仿射变换主要是对图像的缩放,旋转,翻转和平移等操作的组合。

那什么是图像的仿射变换,如下图所示,图1中的点1, 2 和 3 与图二中三个点一一映射, 仍然形成三角形, 但形状已经大大改变,通过这样两组三点(感兴趣点)求出仿射变换, 接下来我们就能把仿射变换应用到图像中所有的点中,就完成了图像的仿射变换。

 

需要注意的是,对于图像而言,宽度方向是x,高度方向是y,坐标的顺序和图像像素对应下标一致。所以原点的位置不是左下角而是右上角,y的方向也不是向上,而是向下。

在仿射变换中,原图中所有的平行线在结果图像中同样平行。为了创建这个矩阵我们需要从原图像中找到三个点以及他们在输出图像中的位置。然后cv2.getAffineTransform 会创建一个 2x3 的矩阵,最后这个矩阵会被传给函数 cv2.warpAffine。

示例

import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt
# 1 图像读取
img = cv.imread("./image/image2.jpg")

# 2 仿射变换
rows,cols = img.shape[:2]
# 2.1 创建变换矩阵
pts1 = np.float32([[50,50],[200,50],[50,200]])
pts2 = np.float32([[100,100],[200,50],[100,250]])
M = cv.getAffineTransform(pts1,pts2)
# 2.2 完成仿射变换
dst = cv.warpAffine(img,M,(cols,rows))

# 3 图像显示
fig,axes=plt.subplots(nrows=1,ncols=2,figsize=(10,8),dpi=100)
axes[0].imshow(img[:,:,::-1])
axes[0].set_title("原图")
axes[1].imshow(dst[:,:,::-1])
axes[1].set_title("仿射后结果")
plt.show()

 

2.1.5 透射变换

透射变换是视角变化的结果,是指利用透视中心、像点、目标点三点共线的条件,按透视旋转定律使承影面(透视面)绕迹线(透视轴)旋转某一角度,破坏原有的投影光线束,仍能保持承影面上投影几何图形不变的变换。

 

  1. 示例

    import numpy as np
    import cv2 as cv
    import matplotlib.pyplot as plt
    # 1 读取图像
    img = cv.imread("./image/image2.jpg")
    # 2 透射变换
    rows,cols = img.shape[:2]
    # 2.1 创建变换矩阵
    pts1 = np.float32([[56,65],[368,52],[28,387],[389,390]])
    pts2 = np.float32([[100,145],[300,100],[80,290],[310,300]])

    T = cv.getPerspectiveTransform(pts1,pts2)
    # 2.2 进行变换
    dst = cv.warpPerspective(img,T,(cols,rows))

    # 3 图像显示
    fig,axes=plt.subplots(nrows=1,ncols=2,figsize=(10,8),dpi=100)
    axes[0].imshow(img[:,:,::-1])
    axes[0].set_title("原图")
    axes[1].imshow(dst[:,:,::-1])
    axes[1].set_title("透射后结果")
    plt.show()

2.2图像的形态学转换

2.2.1腐蚀和膨胀:

腐蚀:求局部最大值

膨胀:求局部最小值

2.2.2开闭运算:

开:先腐蚀后膨胀

闭:先膨胀后腐蚀

2.2.3礼帽和黑帽:

礼帽:原图像与开运算之差

黑帽:闭运算与原图像之差

2.3图像的平滑方法

2.3.1图像噪声

  • 椒盐噪声:图像中随机出现的白点或者黑点

  • 高斯噪声:噪声的概率密度分布是正态分布

2.3.2图像平滑

  • 均值滤波:算法简单,计算速度快,在去噪的同时去除了很多细节部分,将图像变得模糊

    cv.blur()

  • 高斯滤波: 去除高斯噪声

    cv.GaussianBlur()

  • 中值滤波: 去除椒盐噪声

    cv.medianBlur()

2.4直方图的方法

2.4.1灰度直方图:

  • 直方图是图像中像素强度分布的图形表达方式。

  • 它统计了每一个强度值所具有的像素个数。

  • 不同的图像的直方图可能是相同的

cv.calcHist(images,channels,mask,histSize,ranges [,hist [,accumulate]])

2.4.2掩膜

创建蒙版,透过mask进行传递,可获取感兴趣区域的直方图

2.4.3直方图均衡化:增强图像对比度的一种方法

cv.equalizeHist(): 输入是灰度图像,输出是直方图均衡图像

2.4.4自适应的直方图均衡

将整幅图像分成很多小块,然后再对每一个小块分别进行直方图均衡化,最后进行拼接

clahe = cv.createCLAHE(clipLimit, tileGridSize)

2.5边缘检测的方法

识别图像中亮度变化比较明显的点

2.5.1边缘检测的原理

  • 基于搜索:利用一阶导数的最大值获取边界sobel【效果最好 】

     

     

  • 基于零穿越:利用二阶导数为0获取边界laplacian

     

     

2.5.2Sobel算子(ok)

利用OpenCV进行sobel边缘检测的API是:

Sobel_x_or_y = cv2.Sobel(src, ddepth, dx, dy, dst, ksize, scale, delta, borderType)

参数:

  • src:传入的图像

  • ddepth: 图像的深度

  • dx和dy: 指求导的阶数,0表示这个方向上没有求导,取值为0、1。

  • ksize: 是Sobel算子的大小,即卷积核的大小,必须为奇数1、3、5、7,默认为3。

    注意:如果ksize=-1,就演变成为3x3的Scharr算子。

  • scale:缩放导数的比例常数,默认情况为没有伸缩系数。

  • borderType:图像边界的模式,默认值为cv2.BORDER_DEFAULT。

Sobel函数求完导数后会有负值,还有会大于255的值。而原图像是uint8,即8位无符号数,所以Sobel建立的图像位数不够,会有截断。因此要使用16位有符号的数据类型,即cv2.CV_16S。处理完图像后,再使用cv2.convertScaleAbs()函数将其转回原来的uint8格式,否则图像无法显示。

Sobel算子是在两个方向计算的,最后还需要用cv2.addWeighted( )函数将其组合起来

Scale_abs = cv2.convertScaleAbs(x)  # 格式转换函数
result = cv2.addWeighted(src1, alpha, src2, beta) # 图像混合

示例:

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
# 1 读取图像
img = cv.imread('./image/horse.jpg',0)
# 2 计算Sobel卷积结果
x = cv.Sobel(img, cv.CV_16S, 1, 0)
y = cv.Sobel(img, cv.CV_16S, 0, 1)
# 3 将数据进行转换
Scale_absX = cv.convertScaleAbs(x)  # convert 转换 scale 缩放
Scale_absY = cv.convertScaleAbs(y)
# 4 结果合成
result = cv.addWeighted(Scale_absX, 0.5, Scale_absY, 0.5, 0)
# 5 图像显示
plt.figure(figsize=(10,8),dpi=100)
plt.subplot(121),plt.imshow(img,cmap=plt.cm.gray),plt.title('原图')
plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(result,cmap = plt.cm.gray),plt.title('Sobel滤波后结果')
plt.xticks([]), plt.yticks([])
plt.show()

 

 

 

2.5.3Laplacian算子

基于零穿越获取边界

cv.Laplacian()

2.5.4Canny算法(best)

流程:

  • 噪声去除:高斯滤波

  • 计算图像梯度:sobel算子,计算梯度大小和方向

  • 非极大值抑制:利用梯度方向像素来判断当前像素是否为边界点

  • 滞后阈值:设置两个阈值,确定最终的边界

2.6模板匹配和霍夫变换的应用

2.6.1模板匹配

原理:在给定的图片中查找和模板最相似的区域

API:利用cv.matchTemplate()进行模板匹配,然后

使用cv.minMaxLoc()搜索最匹配的位置。

2.6.2霍夫线检测

原理:将要检测的内容转换到霍夫空间中,利用累加器统计最优解,将检测结果表示处理

API:cv2.HoughLines()

注意:该方法输入是的二值化图像,在进行检测前要将图像进行二值化处理

2.6.3霍夫圆检测

方法:霍夫梯度法

API:cv.HoughCircles()

3、图像特征提取与描述

3.1图像的特征

特征:一般认为角点,斑点等是较好的图像特征

特征检测:找到图像中的特征

特征描述:对特征及其周围的区域进行描述

3.2Harris和Shi-Tomasi算法

  1. Harris算法

    思想:通过图像的局部的小窗口观察图像,角点的特征是窗口沿任意方向移动都会导致图像灰度的明显变化。

    API: cv.cornerHarris()

  2. Shi-Tomasi算法

    对Harris算法的改进,能够更好地检测角点

    API: cv2.goodFeatureToTrack()

 

3.3SIFT/SURF算法

SIFT原理:

  • 尺度空间极值检测:构建高斯金字塔,高斯差分金字塔,检测极值点。

  • 关键点定位:去除对比度较小和边缘对极值点的影响。

  • 关键点方向确定:利用梯度直方图确定关键点的方向。

  • 关键点描述:对关键点周围图像区域分块,计算块内的梯度直方图,生成具有特征向量,对关键点信息进行描述。

API:cv.xfeatures2d.SIFT_create()

SURF算法:

对SIFT算法的改进,在尺度空间极值检测,关键点方向确定,关键点描述方面都有改进,提高效率

3.4Fast算法角点检测

原理

3.5ORB算法

原理

特征点检测的实现

视频操作

案例:人脸识别

posted on 2023-02-15 15:05  黑逍逍  阅读(78)  评论(0编辑  收藏  举报