目录

0 前言

1.opencv入门

2 图像处理基础

3.图像运算

3.1图像加法

3.2 按位逻辑运算

3.3 位平面分解

4.色彩空间类型转换

4.1颜色提取

5. 几何变换

5.1 基础概念

5.2函数讲解

5.3代码演示

6. 阈值处理

6.1 基础概念

6.2 函数讲解

6.3代码演示

6.3.1 阈值处理函数演示

6.3.2 自适应阈值处理函数演示

7. 平滑滤波处理

7.1 基础概念

7.2函数讲解

7.2.1 均值滤波

7.2.2 方框滤波

7.2.3 高斯滤波

7.2.4 中值滤波

7.2.5 双边滤波

7.2.6 2D卷积

7.3函数演示

7.3.1 均值滤波

7.3.2 方框滤波

7.3.3 高斯滤波

7.3.4 中值滤波

7.3.5 边缘滤波

7.3.6 2D卷积

8.形态学操作

8.1基础概念

8.2函数介绍

8.3 函数演示

9. 图像梯度

9.1 基础概念

9.2 函数介绍

9.2.1 Sobel算子

9.2.2 Scharr算子

9.2.3 Laplacian(拉普拉斯)算子

9.3 代码演示

9.3.1 Sobel算子 (各算子使用方法大同小异,这里只演示一种)

10.Canny边缘检测

10.1 基础概念

10.2 代码演示

11.图像金字塔

11.1 基础概念

11.2 函数介绍

11.3代码演示

12 图像轮廓

12.1 基础概念

12.2 函数介绍

12.2.1 轮廓的寻找与绘制

12.2.2 矩特征

12.2.3 多边形拟合

12.2.4  凸包

12.2.5 利用形状场景算法比较图像(没太懂距离的含义和作用)

12.2.5 轮廓特征值

12.3 代码演示

12.3.1 轮廓的寻找与绘制

12.3.2 矩特征

12.3.3 多边形拟合

12.3.4 利用形状场景算法比较图像

12.3.5 轮廓特征值

13 直方图处理

13.1 基础概念

13.2 函数讲解

13.2.1 绘制直方图

13.2.2 直方图均衡化

13.2.3 pyplot模块介绍

13.3 代码演示

13.3.1 直方图绘制

13.3.2 均衡化

14 傅里叶变换

14.1 概念介绍

14.2 函数讲解

14.2.1 numpy实现傅里叶变换

14.2.2 opencv实现傅里叶变换

14.3 代码演示

15.模板匹配

15.1 概念介绍

15.2 函数介绍

15.3 代码演示

15.3.2 多模板匹配

16 霍夫变换

16.1 概念介绍

16.2 函数介绍

16.3 代码演示

第17章 图像的分割与提取

17.1 基础概念

17.2 函数介绍

17.3 代码演示

17.3.1 形态学操作

17.3.2 distanceTransform函数

17.3.3 获取未知区域,标注,分水岭图像分割

17.3.4 交互式前景处理函数

18 视频处理

18.1 基础概念

18.2 函数讲解

18.3 代码示例

18.3.1 捕获摄像头文件

18.3.2 保存视频文件

19 绘图及交互

19.1 基础概念

19.2 函数介绍

19.2.1 绘制图形函数

19.2.2 鼠标交互函数

19.2.3 创建滚动条

19.3 代码示例

19.3.1 基础函数使用

19.3.2 鼠标事件

19.3.3 滚动条

20 K近邻算法

20.1 基础概念

20.3 代码演示

20.3.1 自定义最近邻算法

20.3.2 调用opencv内部函数实现最近邻算法

21 支持向量机

21.1 基础概念

21.2 函数讲解

21.3 代码演示

22 K均值聚类算法

22.1 基本概念

22.2 函数介绍

22.3 代码演示

23 人脸识别

23.1 基本概念

23.2 函数介绍

23.3 函数演示

23.3.1 人脸检测

23.3.2 人脸识别


0 前言

终于终于,把这本书翻完了。虽然结尾的地方讲的马虎,我也看的马虎,但怎么也算是有始有终了,不知道这本书的内容以后是否能用得到,也不知道opencv的知识以后的学习功能能否用得到,艺多不压身嘛。接下来就是一边看论文,一边看一看嵌入式的东西或者深度学习的知识。

还是要有始有终嘛,书一共23章,到2022.9.26号之前到15章,还差9章,一天补两章的话,还有可能在这周之前做完。

可能这篇文章要gg了,刚通知我不做这个方向了,希望我还有时间看书。

在手术导航技术中需要进行一定的图像处理,因此便有了这一支线任务。查了一些信息说蝴蝶书是opencv的较为全面的入门书籍。但是蝴蝶书中是使用c++介绍的,我基本没有c++功底,简单尝试之后便选择放弃。现在我参考的书籍是李立宗老师写的《openCV轻松入门-面向python》。我目前的想法是先上手,到需要进一步提升算法性能时在回头学习c++写的蝴蝶书。

我是用anaconda搭建的编程环境,特别容易上手,opencv库的安装也很简单。

1.opencv入门

在本小节,会对opencv的最基本的函数进行介绍。

retval = cv2.imread(filename, flags)

cv2.imread为读取图片函数,返回值retval存放图像内容,filename为图片路径,flags为读取方式。需要注意的是一定要注意读取路径正确与否,如果路径错误那么retval将不是图片内容,并且程序也不会报错,此时retval的值为None。

补充:绝对路径与相对路径。

'C:/Users/Administrator/Desktop/1.jpg'这是绝对路径,需要注意的是路径中用的是'/',如果使用'\'需要在路径前加r,进行转义。r'C:\Users\Administrator\Desktop\1.jpg'

.\imge\1.jpg'这是相对路径。 

   cv2.namedWindow(window)

   cv2.namedWindow为新建窗口函数,无返回值,其中window为新建窗口的名称,可以在后续过程中进行调用,在该窗口显示图像。

   cv2.imshow(window, img)

   cv2.imshow为显示函数,无返回值,将img存放的内容显示在window命名的窗口中,如果没有名为window的窗口就新建一个名为window的窗口在显示图片。

   key = cv2.waitKey(val)

   cv2.waitKey函数为等待按键函数,返回值为按下按键的ascll值.val为等待时间,单位是ms,默认为0表示无限等待。仅在opencv画面上按下按键有效。

   cv2.destroyAllWindows()

   cv2.destroyAllWindows销毁所有窗口函数,cv2.destroyWindows()销毁指定窗口,输入参数为窗口名称。

   cv2.imwrite(filename, img)

   cv2.imwrite为保存图像函数,返回值表示保存是否成功,filename为保存路径,img为图像内容。

#示例
import cv2
img = cv2.imread(r'C:\Users\Administrator\Desktop\opencv-python\11.jpg',0)
cv2.namedWindow("window1")#创建窗口,关掉窗口会导致内核挂掉
cv2.imshow("window1", img)#显示图像
key = cv2.waitKey(0)#等待按键,按下后执行,并获取按键的ascll值.参数值为等待时间,单位是ms,默认为0表示无限等待
print(key)
if key == ord('a'):#通过ord函数将a转化为ascll值,a表示字符cv2.imshow("windowA", img)
else:cv2.imshow("window", img)
cv2.waitKey(1000)
cv2.destroyAllWindows()#销毁所有窗口,cv2.destroyWindows()销毁指定窗口,输入参数为窗口名称
cv2.imwrite("1.jpg", img)#保存图像,返回值为真假

2 图像处理基础

图像就相当于矩阵,对图像的处理就是对矩阵的处理。该部分的操作包括图片像素的读取与修改,像素块的移动,通道的拆分。涉及到的函数如下:

val = img.item(a, b)

item为矩阵元素读取函数,返回值val为对应位置矩阵元素的数值,(a,b)为元素坐标。

img.itemset((a,  b), val)

itemset为矩阵元素数值修改函数,无返回值,(a,b)为元素坐标。

b, g, r =cv2.split(img)

cv2.split为拆分函数,将三通道图像拆分为b、g、r三种。

img = cv2.merge([b, g, r])

cv2.merge为合并函数,将b、g、r合并为三通道图像。

import cv2
import numpy as np
img = np.random.randint(0, 255, (32, 32, 3), dtype = np.uint8)
cv2.imshow('before', img)
print(img.item(0, 0, 0))
for i in range(32):for j in range(32):for k in range(3):img.itemset((i, j, k), 255)
cv2.imshow('after', img)
cv2.waitKey(0)#在python的gui界面才有效
cv2.destroyAllWindows()
import cv2
import numpy as np
img = cv2.imread('11.jpg')
cv2.imshow('before', img)
#b, g, r = img[:, :, 0], img[:, :, 1], img[:, :, 2]
b, g, r =cv2.split(img)
cv2.imshow('b', b)
cv2.imshow('g', g)
cv2.imshow('r', r)
img = cv2.merge([b, g, r])
cv2.imshow('after', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

3.图像运算

图像运算主要指图像加法和位运算两种计算方法,在下面还会介绍两种运算用到的函数以及应用场合。

3.1图像加法

在第二节我们了解到对图像操作相当于对矩阵操作,对像素块操作相当于对矩阵的值操作。同理,那图像的加法运算也就是矩阵的加法运算。我们用A和B来表示两张图象,很容易可以想到图像加法的表示方法是A+B,实际就是这个样子去进行图像加法运算,只不过opencv也给出了cv2.add()这个函数来计算图像加法。因此有以下两种方法来表示矩阵相加。

C = A+B
C = cv2.add(A, B)

其中C表示输出函数,那两种表示方式的区别在哪里呢?在灰度空间里,像素块的数值越大,亮度越高,但是一般采用一个字节也就是8位二进制数来表示一个像素,最大值也就是255,两个像素值相加可能会出现溢出的情况。而两种图像求和的区别也就体现在对溢出的处理上。

以两个像素块值为205和80为例,使用”+“得到的数值为30,相当于溢出后对255取余;使用cv2.add()得到的结果是255,也就是溢出后将为最大值。

当然有加法就会有加权加法,函数如下。

输出图像 =cv2.addWeighted(图像1, 权重1, 图像2, 权重2, 亮度调节) 其中亮度调节参数不能省略
import cv2
import numpy as npimg = np.random.randint(0, 127, (16, 16), dtype = np.uint8)
cv2.imshow('img0', img)
a = np.random.randint(0, 127, (16, 16), dtype = np.uint8)
img = img + a
cv2.imshow('img1', img)
img = cv2.add(img, a)
cv2.imshow('img2', img)
cv2.waitKey()
cv2.destroyAllWindows()

3.2 按位逻辑运算

色素块由8位2进制数组成,也就可以将色素的值进行位逻辑运算,能够进行的运算有与、或、非、异或,函数如下。

    cv2.bitwise_add() 掩模处理,通过仅有0,255两种像素的掩模图相,对原图像进行筛选。cv2.bitwise_or()cv2.bitwise_xor()cv2.bitwise_not() 对图像进行打码

其中与运算可以做到使图像部分显示,部分不显示的功能。若像素块值为127,用二进制表示也就是0011 1111,如果与1111 1111进行与运算,结果为0011 1111,不变;而与0000 0000进行与运算,结果为0000 0000,颜色变为黑色。因此将需要显示的色块与1111 1111求与,将不需要显示的色块与0000 0000求与,便可以得到期望的结果。

import cv2
import numpy as npimg = cv2.imread('imge/1.jpg', 0)
shape = img.shape
img0 = np.zeros(shape, dtype = np.uint8)
img0[400:600, 700:1100] = 255
#print(shape)
img1 = cv2.bitwise_and(img, img0)cv2.imshow('img', img)
cv2.imshow('img0', img0)
cv2.imshow('img1', img1)cv2.waitKey()
cv2.destroyAllWindows()

异或运算可以用来做图像的加密与解密。异或的运算特点体现为一个二进制数A与二进制数B异或操作得到的结果C,在与B异或计算得到的结果是A,类似于通过密码B将A编码为C,还可以通过B将C解码为A。异或运算的法则放在下面了。

import cv2
import numpyimg0= cv2.imread('imge/tomato.jpg', 0)
img1= cv2.imread('imge/1.jpg', 0)
m, n= img0.shape
img2 = img1[0:m, 0:n]
img3 = cv2.bitwise_xor(img0, img2)
img4 = cv2.bitwise_xor(img3, img2)cv2.imshow('img0', img0)
cv2.imshow('img1', img1)
cv2.imshow('img2', img2)
cv2.imshow('img3', img3)
cv2.imshow('img4', img4)cv2.waitKey()
cv2.destroyAllWindows()

3.3 位平面分解

每一个像素块由8位二进制数组成,如A像素块是0101 0101,B像素块是1101 1110,那我将对应位置的二进制数组成一个平面,如A的最高位0和B的最高位1,一张图像里同一位置的像素块组成一个像素平面,其中高位的像素平面蕴含的信息最多。

求像素平面的方法就是使用0000 0001、0000 0010、0000 0100等算子通过与运算提取。

import cv2
import numpy as npimg= cv2.imread('imge/tomato.jpg', 0)
cv2.imshow('img', img)
print('img shape:', img.shape)
m, n= img.shape
a= np.ones((m, n, 8), dtype= np.uint8)
for i in range(8):a[:, :, i]= 2**imask= cv2.bitwise_and(img, a[:, :, i]) z = mask[:, :]>0mask[z]= 255cv2.imshow(str(i), mask)
cv2.waitKey()
cv2.destroyAllWindows()

4.色彩空间类型转换

图像的色彩有不同的表达方式,被称为色彩空间,如RGB、GRAY、XYZ、HSV等。需要学会不同色彩口空间之间的转化。
    cv2.cvtColor(原图像, 转换方式)
    转换方式如:cv2.COLOR_BGR2GRAY,cv2.COLOR_BGR2GRAY,cv2.COLOR_BGR2RGB

在HSV色彩空间中,h表示色调,s表示饱和度,v表示亮度。
    dst= cv2.inRange(src, 下界, 上界)
            判断src的像素值是否在上界与下界之间,满足的话,对应位置返回255,否则返回0。

4.1颜色提取

提取颜色的操作在RGB色彩空间难以实现,RGB的色彩空间与人眼看到的色彩空间不同,应该在HSV空间。将BGR转化到HSV,通过HSV确定需要颜色的位置,然后显示。在HSV中通过色调指定颜色很直观。方法如下:

import cv2
import numpy as npimg= cv2.imread('imge/OpenCv.png')
cv2.imshow('img',img)
hsv= cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
cv2.imshow('hsv',hsv)#默认按BGR显示min_blue = np.array([110, 50, 50])
max_blue = np.array([130, 255, 255])mask = cv2.inRange(hsv, min_blue, max_blue)blue = cv2.bitwise_and(img, img, mask = mask)cv2.imshow('blue',blue)cv2.waitKey()
cv2.destroyAllWindows()

5. 几何变换

5.1 基础概念

图形的几何变换主要包括缩放对称、仿射(平移旋转)、透视,三者之间的变换自由度越来越大。除以上三者之外,还有一个重映射函数。

5.2函数讲解

缩放函数:dst = cv2.resize(src, dsize, fx, fy, interpolation)

dsize:指的是缩放变换后的尺寸,如480*320。需要注意的是src.shape返回的是src的(行,列),而在dsize中为(列,行),两者顺序相反。

fx, fy:指的是缩放的比例,仅在dsize = None的时候才生效,同时fx是指列的缩放,fy是指行的缩放。

对称函数:dst = cv2.filp(src,  flipcode)

flipcode:指的是对称类型的编码,0沿X对称,正数沿Y对称,负数沿原点对称。需要注意的是在图像中左上为原点,右为X正方向,下为Y正方向。

仿射函数:dst = cv2.warpAffine(src, M, dsize)

M:指的是2*3的变换矩阵,前两列与旋转缩放有关,后一列与平移有关。在这里就需要有一定的矩阵知识了,有矩阵可以更加直观的理解,不过没有也没关系,opencv提供了计算矩阵的函数。

仿射旋转缩放变换矩阵计算函数:retval = cv2.getRotationMatrix2D(center, angle, scale)

center:旋转中心点

angle:旋转角度

scale:缩放大小

不过这个函数只能得到旋转、缩放的矩阵,没有添加平移,所有还有一个计算函数。

仿射变换矩阵计算函数:retval = cv2.getAffineTransform(src, dst)

scr:变换前的三个点的坐标,如[[1, 2], [3, 4], [5, 6]]

dst:变换后的三个点的坐标

这个函数的变换自由度更高,不过不太直观。

透视变换函数:dst = cv2.dst = cv2.warpPerspective(src, M, dsize)

函数参数形式与仿射变换类似,区别在于变换矩阵M是3*3的,有更多的变换可能性。我理解的是仿射是我们从不同视角可以观察到的图像的样子,而透视就是图像可能改变成的样子。同理M矩阵也是可以通过函数计算的。

透视变换矩阵计算函数:retval = cv2.getPerspectiveTransform(scr, dst)

整体样子和得到仿射变换旋转矩阵的函数类似,区别在于scr和dst中都含有4个坐标。

重映射函数:dts = cv2.remap(src,  map1,  map2, interpolation)

map1, map2 :表示映射的列号和行号,值为浮点数,为矩阵形式,大小与图像相同
                规则:将map1和map2中的点组成坐标对,将原图像中与坐标对对应的像素值取出,放在输出结果的取坐标值的位置。如map1的(0,0)位置是10,map2的(0,0)位置是15,则去到原图中找到(10,15)的像素值为127,得到输出图像的(0,0)位置像素值为127.

5.3代码演示

缩放和对称

import cv2
import numpyimg = cv2.imread('imge/tomato.jpg')
#print(img)
cv2.imshow('img0', img)img = cv2.resize(img, img.shape[:2])
cv2.imshow('img1', img)img = cv2.resize(img, None, fx = 0.7, fy = 0.5)#在size = None的前提下,才可以用fx和fy
cv2.imshow('img2', img)img = cv2.resize(img, None, fx = 1, fy = 1,interpolation=cv2.INTER_NEAREST)#在size = None的前提下,才可以用fx和fy
cv2.imshow('img3', img)img = cv2.flip(img, 0)
cv2.imshow('img4', img)img = cv2.flip(img, 1)
cv2.imshow('img5', img)img = cv2.flip(img, -1)
cv2.imshow('img6', img)cv2.waitKey()
cv2.destroyAllWindows()

仿射和投影

import cv2
import numpy as npimg0 = cv2.imread('imge/tomato.jpg')
x, y, a = img0.shape
print('img0 shape',img0.shape[:2])
cv2.imshow('img0', img0)M = np.float32([[1, 0, 200],[0, 1, 100]])#先平移列,后平移行
img1 = cv2.warpAffine(img0, M, (y, x))
print('img1 shape',img1.shape[:2])
cv2.imshow('img1', img1)M = cv2.getRotationMatrix2D((0, 0), 45, 1)#统一缩放,不能单独缩放
print(M)
img2 = cv2.warpAffine(img0, M, (y, x))
print('img2 shape',img2.shape[:2])
cv2.imshow('img2', img2)M1 = np.float32([[0, 0],[x-1, 0],[0, y-1]])
M2 = np.float32([[50, 50],[x-100, 0],[0, y-200]])
M = cv2.getAffineTransform(M1, M2)
img3 = cv2.warpAffine(img0, M, (y, x))
print('img3 shape',img3.shape[:2])
cv2.imshow('img3', img3)M1 = np.float32([[0, 0],[x-1, 0],[0, y-1], [x-1, y-1]])
M2 = np.float32([[50, 50],[x-100, 0],[0, y-200], [x-100, y-50]])
M = cv2.getPerspectiveTransform(M1, M2)
img4 = cv2.warpPerspective(img0, M, (y, x))
print('img4 shape',img4.shape[:2])
cv2.imshow('img4', img4)cv2.waitKey()
cv2.destroyAllWindows()

重映射

import cv2
import numpy as npimg0 = cv2.imread('imge/tomato.jpg')
x, y, a= img0.shape#x为行号,y为列号
cv2.imshow('img0', img0)mapx = np.zeros((x, y), dtype = np.float32 )
mapy = np.zeros((x, y), dtype = np.float32 )for i in range(x):for j in range(y):mapx[i, j] = jmapy[i, j] = i
img1 = cv2.remap(img0, mapx, mapy, cv2.INTER_LINEAR)#mapx存列号,mapy存行号
cv2.imshow('img1', img1)#y轴对称
for i in range(x):for j in range(y):mapx[i, j] = y-1-jmapy[i, j] = i
img2 = cv2.remap(img0, mapx, mapy, cv2.INTER_LINEAR)#mapx存列号,mapy存行号
cv2.imshow('img2', img2)#x轴对称
for i in range(x):for j in range(y):mapx[i, j] = jmapy[i, j] = x-1-i
img3 = cv2.remap(img0, mapx, mapy, cv2.INTER_LINEAR)#mapx存列号,mapy存行号
cv2.imshow('img3', img3)#x,y轴对称
for i in range(x):for j in range(y):mapx[i, j] = y-1-jmapy[i, j] = x-1-i
img4 = cv2.remap(img0, mapx, mapy, cv2.INTER_LINEAR)#mapx存列号,mapy存行号
cv2.imshow('img4', img4)#缩放
for i in range(x):for j in range(y):if(0.25*x<i<0.75*x and 0.25*y<j<0.75*y):#选择视窗mapx[i, j] = 2*(j-0.25*y)#由线性变换可以计算mapy[i, j] = 2*(i-0.25*x) else:mapx[i, j] = 0
img5 = cv2.remap(img0, mapx, mapy, cv2.INTER_LINEAR)#mapx存列号,mapy存行号
cv2.imshow('img5', img5)cv2.waitKey()
cv2.destroyAllWindows()

6. 阈值处理

6.1 基础概念

阈值处理指的就是将图像中高于或低于一定值的像素块像素值改变的操作。可以在一定程度上实现背景与前景的分离,难点在于分割阈值的确定。

6.2 函数讲解

在阈值处理中用到的函数为cv2.threshold()和cv2.adaptiveThreshold()函数。

阈值处理函数:retval, dst = cv2.threshold(src, thresh, maxval, type)

retval:代表返回的阈值

thresh:代表设定的阈值

maxval:在超过阈值后,可能将像素点改变为的值

type:处理方法。有二值化处理(cv2.THRESH_BINARY)、反二值化处理(cv2.THRESH_BINARY_INV)、截断阈值化处理(THRESH_TRUNC)、超阈值零处理(THRESH_TOZERO_INV)、低阈值零处理(THRESH_TOZERO),在处理方法后面还可以加上Otsu方法(cv2.THRESH_OTSU),选取最佳阈值。

二值化处理、反二值化处理是一对,从字面上也很容易理解,将图像按阈值分为0和最大值两值,最大值由maxval设定。二值就是低于阈值为0,反二值就是高于阈值为0。

截断阈值化处理、超阈值零处理、低阈值零处理三者有一定的类似之处。它们输出不是二值图像。在截断阈值化处理中,将大于阈值的值变为maxval,其他不变;其他两个分别将大于阈值赋0,一个将小于阈值赋0。

Otsu方法:通过整体方差与局部方差计算最佳阈值的方法,需要注意的是此时的阈值需要设定为0。
        自适应阈值处理函数:dst = cv2.adaptiveThreshold(src, maxValue, adaptiveMethod, thresholdType,blockSize,C)
                src必须是8位单通道图像
                maxvalue最大值
                adaptiveMethod自适应方法
                    自适应方法包括:
                    1.cv2.ADAPTIVE_THRESH_MEAN_C,相同权重计算领域均值
                    2.cv2.ADAPTIVE_THRESH_GAUSSIAN_C,通过高斯运算得到权重
                thresholdType阈值处理方法,仅有二值处理和反二值处理
                blcckSize块大小,常见为3、5、7
                C为常量

6.3代码演示

6.3.1 阈值处理函数演示

import cv2
import numpy as npimg0 = cv2.imread('imge/tomato.jpg',0)
cv2.imshow('img0', img0)retval, img1 = cv2.threshold(img0, 127, 255, cv2.THRESH_BINARY)#二值处理
cv2.imshow('img1', img1)
#print(retval)retval, img2 = cv2.threshold(img0, 127, 255, cv2.THRESH_BINARY_INV)#反二值处理
cv2.imshow('img2', img2)
#print(retval)retval, img3 = cv2.threshold(img0, 255, 255, cv2.THRESH_TRUNC)#截断处理
cv2.imshow('img3', img3)
#print(img3)retval, img4 = cv2.threshold(img0, 127, 255, cv2.THRESH_TOZERO_INV)#超阈值0处理
cv2.imshow('img4', img4)retval, img5 = cv2.threshold(img0, 127, 255, cv2.THRESH_TOZERO)#低阈值0处理
cv2.imshow('img5', img5)retval, img6 = cv2.threshold(img0, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)#彩色图像会报错
cv2.imshow('img6 ', img6 )
print(retval)cv2.waitKey()
cv2.destroyAllWindows()

6.3.2 自适应阈值处理函数演示

import cv2
import numpy as npimg0 = cv2.imread('imge/tomato.jpg',0)
cv2.imshow('img0', img0)retval, img1 = cv2.threshold(img0, 127, 255, cv2.THRESH_BINARY)#二值处理
cv2.imshow('img1', img1)
#print(retval)retval, img2 = cv2.threshold(img0, 127, 255, cv2.THRESH_BINARY_INV)#反二值处理
cv2.imshow('img2', img2)
#print(retval)img3 = cv2.adaptiveThreshold(img0, 127, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 7, 0)#反二值处理
cv2.imshow('img3', img3)img4 = cv2.adaptiveThreshold(img0, 127, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 3, 0)#反二值处理
cv2.imshow('img4', img4)cv2.waitKey()
cv2.destroyAllWindows()

7. 平滑滤波处理

7.1 基础概念

在尽量保留图像原有信息的前提下,过滤掉噪音,叫做平滑处理。即把与周围像素点差异较大的像素点调整为周围像素点近似值的过程。

7.2函数讲解

7.2.1 均值滤波

用周围N*N个像素点的均值来代替原有像素点,运算过程相当于图像区域与全1/(N*N)的矩阵做点乘效果类似。

均值滤波函数:dst = cv2.blur(scr, ksize, anchor, borderType)
                    ksize为滤波区域的大小,如(5,5)
                    anchor锚点,计算点位于核中心点的位置,默认为(-1,-1),代表核中心
                    borderType边界样式

7.2.2 方框滤波

在均值滤波中,默认使用归一化操作,在方框滤波中是否归一化是可选的。

dst = cv2.boxFilter(src, ddepth, ksize, anchor, normalize, borderType)
                ddepth指图像深度,默认为-1,与原图相同
                normalize指滤波类型,1取均值,0不取均值,默认取均值

7.2.3 高斯滤波

原理类似于均值滤波,不同点在于原理中心点的位置权重降低。
            dst = cv2.GaussianBlur(src, ksize, sigmaX, sigmaY, borderType)
                sigmaX和sigmaY为X方向和Y方向的标准差,用来计算权重的变化程度,其中0表示由程序自行设定。

7.2.4 中值滤波

用中值来代表周围像素值,中值滤波不会模糊,但是由于需要排序等操作,计算量较大

dst = cv2.medianBlur(src, ksize)
                ksize是一个数

7.2.5 双边滤波

考虑空间信息与色彩信息的滤波方式,保护边缘信息。在计算中,若颜色差值较大,也会改变权重,从而保护边缘信息,防止边缘被破坏。滤波效果一般,优点在于边缘保护
        dst = cv2.bilateralFilter(src, d, sigamorColor, sigamorSpace, borderType)
                d空间距离参数,越大计算越慢,小于0从sigamorSpace中计算,一般选5,噪声大选9
                sigamorColor滤波时的颜色选区范围,越大参与计算的像素点越多
                sigamorSpace越大,参与运算的像素点越多

7.2.6 2D卷积

前文中的卷积核kernal均为自动生成,灵活性较差
        dst = cv2.filter2D(src, ddepth, kernal, anchor, delta, borderType)
                kernal单层自定义卷积核,多通道图像需要不同卷积核时,需要分开分别卷积
                delta修正量,结果之后加

7.3函数演示

7.3.1 均值滤波

import cv2
import numpy as npimg0 = cv2.imread('./imge/tomato.jpg')
cv2.imshow('img0',img0)img1 = cv2.blur(img0, (5,5))
cv2.imshow('img1',img1)img2 = cv2.blur(img0, (3,3))
cv2.imshow('img2',img2)cv2.waitKey()
cv2.destroyAllWindows()

7.3.2 方框滤波

import cv2
import numpy as npimg0 = cv2.imread('./imge/tomato.jpg')
cv2.imshow('img0',img0)img1 = cv2.boxFilter(img0, -1, (5,5))
cv2.imshow('img1',img1)img2 = cv2.boxFilter(img0, -1, (3,3), normalize = 0)
cv2.imshow('img2',img2)cv2.waitKey()
cv2.destroyAllWindows()

7.3.3 高斯滤波

import cv2
import numpy as npimg0 = cv2.imread('./imge/tomato.jpg')
cv2.imshow('img0',img0)img1 = cv2.GaussianBlur(img0, (5,5), 0, 0)
cv2.imshow('img1',img1)img2 = cv2.boxFilter(img0, -1, (5,5), normalize = 1)
cv2.imshow('img2',img2)cv2.waitKey()
cv2.destroyAllWindows()

7.3.4 中值滤波

import cv2
import numpy as npimg0 = cv2.imread('./imge/tomato.jpg')
cv2.imshow('img0',img0)img1 = cv2.medianBlur(img0, 5)
cv2.imshow('img1',img1)img2 = cv2.blur(img0, (3,3))
cv2.imshow('img2',img2)cv2.waitKey()
cv2.destroyAllWindows()

7.3.5 边缘滤波

import cv2
import numpy as npimg0 = cv2.imread('./imge/tomato.jpg')
cv2.imshow('img0',img0)img1 = cv2.medianBlur(img0, 5)
cv2.imshow('img1',img1)img2 = cv2.bilateralFilter(img0, -1, 16, 16)
cv2.imshow('img2',img2)cv2.waitKey()
cv2.destroyAllWindows()

7.3.6 2D卷积

import cv2
import numpy as npimg0 = cv2.imread('./imge/tomato.jpg')
cv2.imshow('img0',img0)kernal = np.ones((5,5), dtype = np.float32)/25
img1 = cv2.filter2D(img0, -1, kernal)
cv2.imshow('img1',img1)img2 = cv2.blur(img0, (5,5))
cv2.imshow('img2',img2)cv2.waitKey()
cv2.destroyAllWindows()

8.形态学操作

8.1基础概念

形态学操作包括腐蚀、膨胀,以及在两者基础上衍生出的开运算、闭运算、形态学梯度运算、顶帽运算、黑帽运算等。

腐蚀是指通过结构元扫描的方法减少前景的范围,可以消除噪音,并将粘结的前景分隔开。而膨胀是指通过结构元扫描的方法扩张前景的范围,可以消除前景内噪点的,将不同的前景相连。

开运算是先腐蚀、后膨胀,从而将不同前景分开,并能保留一定原图的特征,能够消除噪音;闭运算则是先膨胀后腐蚀,可以将前景相连。形态学梯度运算形态学梯度运算则是,膨胀-腐蚀从而得到图像的边缘。

顶帽操作是原图-开运算,得到开运算消除的噪音;黑帽操作是闭运算-原图,得到小孔和比原图更暗的边缘。

8.2函数介绍

腐蚀操作函数:dst = cv2.erode(src, kernal, anchor, iterations, borderType, borderValue)
                kernal腐蚀结构元,可以系统生成(cv2.getStructingElement())也可以自定义

iterations:扫描次数

膨胀操作函数:dst = cv2.dilate(src, kernal, anchor, iterations, borderType, borderValue)

通用形态学函数:dst = cv2.morphologyEx( src, op, kernal,  anchor, iterations, borderType, borderValue)

op:操作方法开运算(cv2.MORPH_OPEN)、闭运算(cv2.MORPH_CLOSE)、形态学梯度运算(cv2.MORPH_GRADIENT)、顶帽运算(cv2.MORPH_TOPHAT)、黑帽运算(cv2.MORPH_BLACKHAT)

核函数:retval = cv2.getStructingElement(shape, ksize, anchor)
                  形态学操作需要结构元,可以自定义也可以通过函数cv2.getStructingElement()构造。
                   shape可以是矩形、对角矩形、椭圆

8.3 函数演示

import cv2
import numpy as npimg0 = cv2.imread('./imge/tomato.jpg')
cv2.imshow('img0',img0)kernal = np.ones((19,19), dtype = np.uint8)
img1 = cv2.erode(img0, kernal)#腐蚀运算
cv2.imshow('img1',img1)img2 = cv2.dilate(img1, kernal)#膨胀运算
cv2.imshow('img2',img2)img3 = cv2.morphologyEx(img0, cv2.MORPH_OPEN, kernal)#开运算
cv2.imshow('img3',img3)img4 = cv2.morphologyEx(img0, cv2.MORPH_CLOSE, kernal)#闭运算
cv2.imshow('img4',img4)img5 = cv2.morphologyEx(img0, cv2.MORPH_GRADIENT, kernal)#梯度运算
cv2.imshow('img5',img5)img6 = cv2.morphologyEx(img0, cv2.MORPH_TOPHAT, kernal)#顶帽运算
cv2.imshow('img6',img6)img7 = cv2.morphologyEx(img0, cv2.MORPH_BLACKHAT, kernal)#黑帽运算
cv2.imshow('img7',img7)cv2.waitKey()
cv2.destroyAllWindows()

9. 图像梯度

9.1 基础概念

图像梯度用来描述图像变化的速度,用于图像的边缘检测,使用像素值的差来代替求导。下面使用的梯度求解算子包括Sobel算子、Scharr算子、Laplacian算子。

9.2 函数介绍

9.2.1 Sobel算子

Sobel算子是离散的微分算子,结合了高斯平滑和微分求导运算,利用局部差分寻找边缘,该算子的缺点是核较小时,精度较低。
    滤波器也被称为掩模、核、模板、窗口、算子等。一般在信号领域称为滤波器,数学领域称为核。通过滤波器计算的目标结果是原图的像素的加权和,基于线性核的滤波称为卷积。
    Sobel算子求梯度函数:dst = cv2.Sobel(src, ddepth, dx, dy, ksize, scale, delta, borderType)
        ddepth:图像深度
        dx:x方向的求导阶数
        dy:y方向的求导阶数
        ksize:-1时使用Scharr函数
        scale:缩放因子
    关于ddepth运算中设计到求差,而在某些图像中不能显示负值的像素值,会导致信息的丢失,因此在运算时需要改变数据类型,其中u为无符号,结果变成有符号就可以。
    dst = cv2.convertScaleAbs(src, alpha, beta)
        将参数转化为绝对值

9.2.2 Scharr算子

对Socar算子进行改进,两者卷积核不同。
     求梯度函数:dst = cv2.Sobel(src, ddepth, dx, dy, ksize, scale, delta, borderType)
        注意:dx、dy不能都为1

9.2.3 Laplacian(拉普拉斯)算子

二阶导数算子,同时对两个方向求导,不需要指定fx,fy
    dst = cv2.Laplacian(src, ddepth,  ksize, scale, delta, borderType)

9.3 代码演示

9.3.1 Sobel算子 (各算子使用方法大同小异,这里只演示一种)

import cv2
import numpy as np
img = cv2.imread('./imge/tomato.jpg')img_x = cv2.Sobel(img, cv2.CV_64F, 1, 0)
img_y = cv2.Sobel(img, cv2.CV_64F, 0, 1)
img_x = cv2.convertScaleAbs(img_x)
img_y = cv2.convertScaleAbs(img_y)img_g = cv2.addWeighted(img_x, 0.5, img_y, 0.5, 0)cv2.imshow('img', img)
cv2.imshow('img_x', img_x)
cv2.imshow('img_y', img_y)
cv2.imshow('img_g', img_g)cv2.waitKey()
cv2.destroyAllWindows()

10.Canny边缘检测

10.1 基础概念

Canny边缘检测的步骤:
    1.去噪,使用5*5的高斯核进行滤波
    2.计算梯度的幅值与方向,计算梯度,并将计算的x,y方向的梯度合称为具有方向的梯度
    3.非极大值抑制,寻找相同方向上的梯度最大值
    4.双阈值确定边缘,确定两个阈值maxval和minval,大于maxval为强边缘,即一定为边缘;maxval和minval之间为虚边缘,若与强边缘连接,则保留,不连接则处理为弱边缘;小于minval为弱边缘,删除。
    边缘检测函数:dst = cv2.Canny( src, threshold1, threshold2 ,[apertureSize, L2gradient])
        threshold1与threshold2:上文提到的minval和maxval
        apertureSize:Sobal卷积核的尺寸
        L2gradient:范数的求解方法

10.2 代码演示

import cv2
import numpy as np
img = cv2.imread('./imge/tomato.jpg')img_canny1 = cv2.Canny(img, 192, 224)
img_canny2 = cv2.Canny(img, 128, 200)
img_canny3 = cv2.Canny(img, 64, 128)cv2.imshow('img', img)
cv2.imshow('img_canny1', img_canny1)
cv2.imshow('img_canny2', img_canny2)
cv2.imshow('img_canny3', img_canny3)cv2.waitKey()
cv2.destroyAllWindows()

11.图像金字塔

11.1 基础概念

由一张图像的不同分辨率子图构成的图像集合叫做图像金字塔。涉及到的操作包括向下滤波和向上滤波。其中最底层像素最高,向下取样精度变低,层数加一。
    向下取样具体方法有两种,一种是直接同时删除偶数行、偶数列(或者奇数行、奇数列),另一种是先进行滤波操作在删除。向上取样是该过程的反过程。

11.2 函数介绍

向下取样函数:dst =cv2.pyfDown( src, [dstsize, borderType])

向上取样函数:dst =cv2.pyfUp( src, [dstsize, borderType])

11.3代码演示

import cv2
import numpy as npimg = cv2.imread('imge/tomato.jpg')img1 = cv2.pyrDown(img)
img2 = cv2.pyrDown(img1)img3 = cv2.pyrUp(img2)
img4 = cv2.pyrUp(img3)cv2.imshow('img', img)
cv2.imshow('img1', img1)
cv2.imshow('img2', img2)
cv2.imshow('img3', img3)
cv2.imshow('img4', img4)
print(img.shape,'\n', img1.shape,'\n',img2.shape,'\n',img3.shape,'\n',img4.shape)cv2.waitKey()
cv2.destroyAllWindows()

12 图像轮廓

12.1 基础概念

这一章的内容多的离谱,概念多,函数多。首先明确一点,前文中Canny进行边缘检测得到的图形不等于轮廓。因为边缘可能并不连续。有了轮廓才能更方便的进行接下来的图像分割等操作。

12.2 函数介绍

12.2.1 轮廓的寻找与绘制

轮廓寻找函数:contours, hierarchy = cv2.findCountours(image, mode,method)
        contours:返回轮廓,在大列表中嵌套np.ndarray形式,每个np.ndarray为轨迹点的组合
        hierarchy:图像的拓扑信息(轮廓层次),每个轮廓对应一长度为4的列表,第一个数是前一轮廓的序号,第二个数是后一轮廓的序号,第三个数是父轮廓的序号,第四个数是子轮廓的序号。(A在B内,A为父轮廓,B为子轮廓)
      (参数)image:8位单通道二值图像
        mode:轮廓检索模式,,包括只检测外边缘(cv2.RETR_EXTERNAL),检测轮廓不建立等级关系(cv2.RETR_LIST),只建立单层父子关系(cv2.RETR_CCOMP),建立一个等级树结构的轮廓(cv2.RETR_TREE)
        method:轮廓近似模式,如何表达轮廓。存储所有轮廓,相邻点的像素值之差不超过1(cv2.CHAIN_APPROX_NONE),压缩水平、垂直、对角元素,只保留终点坐标,就是直线相连(cv2.CHAIN_APPROX_SIMPLE),使用近似算法(cv2.CHAIN_APPROX_TC89_L1、cv2.CHAIN_APPROX_TC89_KCOS)
        image = cv2.drawControus(image, controus, controurIdx, color, [thicknsee, lineType, hierarchy, maxLeval, offset])
        image:会在image上直接绘制轮廓图像,会改变输入的image
        contourIdx:需要绘制的轮廓编号,-1表示全部绘制
        maxLeval:绘制轮廓深度,-1表示完全填充
        offset:偏移参数

12.2.2 矩特征

矩特征反映了一个轮廓的大小、位置、形状、尺寸等信息。
    矩特征求解函数:retval = cv2.moments( array, [binaryImage])
    retval为矩特征,包含空间距、中心距、归一化中心距
            其中中心距具有平移不变性
            归一化中心距具有缩放不变性
    计算轮廓面积函数:retval = cv2.contourArea(contour, [oriented])
    计算轮廓周长函数:retval = cv2.arcLength(curve, closed)
            closed为布尔型常量,表示轮廓是否封闭
    计算hu矩函数:hu = cv2.HuMoments( m )
            m:为cv2.moment()函数的返回值
    模板匹配函数:retval = cv2.matchShapes(contour1, coutour2, method, parameter)
            method:比较方法(cv2.CONTOURS_MATCH_I1、cv2.CONTOURS_MATCH_I2、cv2.CONTOURS_MATCH_I1)

12.2.3 多边形拟合

有时可能并不需要实际轮廓,仅通过多边形拟合结果便可以满足需求。
    求包围矩形的坐标:retval = cv2.boundingRect( array )
         返回值是由x, y, w, h四个数值组成的数组
    求包围最小矩形的坐标:retval = cv2.minAreaRect(array)
        retval中的数据是矩形中心,宽度、高度和旋转角度,不能直接在cv2.drawcontours()中使用。
    将cv2.minAreaRect()的结果转化为cv2.drawcontours()可用:points = cv2.boxPoints(box)
        box为cv2.minAreaRect()结果的数据类型
    求包围圆:(x, y),radius = cv2.minEnclosingCircle(array)
        记得把数据取整
    求最优拟合椭圆:retval = cv2.fitEllipse( points )
        retval是RotateRect数据类型,有椭圆外接矩形的各种数据
    最优拟合直线:retval = cv2.fitLine(points, distType, param, reps, aeps)
        distType:距离类型,拟合直线与输入点的偏差
        param:距离参数,输入0自动选择最优
        reps:径向精度,通常为0.01
        aeps:角度精度,通常为0.01
    三角形拟合:retval, triangle = cv2.minEnclosingTriangle( points )
        retval:最小外包三角形的面积
        triangle:三角形三个顶点的位置
    逼近多边形:approxCurve = cv2.approxPolyDP( curve, epsilon, colsed)

12.2.4  凸包

有的时候不仅不需要图形的轮廓,甚至逼近多边形都不需要,需要的是凸包。凸包指的是任意凸包的每一处都是凸的,每两点的连线都在凸包中。物体实际边缘与凸包之间的区域成为凸缺陷。
    求凸包函数:hull = cv2.convexHull( points, [ clockwise, returnsPoints ])
        clockwise:布尔型,True顺时针,Flase逆时针
        returnsPoints:布尔型,True返回坐标,Flase返回索引
    求凸缺陷函数:convexityDefects = cv2.convexityDefects(contour, convexhull)
        求凸缺陷时,hall的returnsPoints需要为False
        convexityDefectss返回值是起点、终点、轮廓上距凸点最远的点在轮廓数组中的索引。其中起点、终点把轮廓分段了。
    判断是否为凸包函数:treval = cv2.isContourConvex(point)
    测试点到轮廓的距离:retval = cv2.pointPolygonTest(contour, point, measureDist)
        measureDist:为True返回距离,为Flase返回范围判断,外-1,上0,内1

12.2.5 利用形状场景算法比较图像(没太懂距离的含义和作用)

用矩比较是一种常见的图形比较方法,在opencv3中有专门的shape模块进行图形比较。
    创建距离算子:retval = cv2.createShapeContextDistanceExtractor([nAngularBins, nRadialBins, innerRadius, outerRadius, interations, comparer, transformei])
        retval返回结果,结果通过函数cv2.ShapeContextDistanceExtractor.computeDistance()计算距离。

计算距离函数:retval = cv2.ShapeContextDistanceExtractor.computeDistance(contour1, contour2)

12.2.5 轮廓特征值

轮廓的属性特性以及所包围对象的特性对于描述图像具有重要意义。
    1.宽高比
    2.面积与轮廓之比
    3.等效直径
    4.方向
    5.获取非0元素的位置,np.nonzero()返回一个元组将x、y坐标分别组成矩阵,cv2.findNonZero()返回一个列表,每个坐标已经组成一个两元素列表
    6.cv2.minMaxLoc()返回最大值、最小值以及其坐标
    7.cv2.mean()计算颜色均值

12.3 代码演示

12.3.1 轮廓的寻找与绘制

import cv2
import numpy as npimg0 = cv2.imread('imge/OIP.jpg')
img1 = cv2.cvtColor(img0, cv2.COLOR_BGR2GRAY)binary1 = cv2.GaussianBlur(img1, (13,13), 0, 0)
binary2 = cv2.Canny(binary1, 0, 128)
ret, binary3 = cv2.threshold(binary2, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
#print(ret)contours, hierarchy = cv2.findContours(binary3, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
binary3 = cv2.drawContours(binary3, contours, -1, (255, 255, 255), 5)cv2.imshow('img0', img0)
cv2.imshow('img1', img1)
cv2.imshow('binary1', binary1)
cv2.imshow('binary2', binary2)
cv2.imshow('binary3', binary3)print(len(contours),'\n',hierarchy.shape)cv2.waitKey()
cv2.destroyAllWindows()

12.3.2 矩特征

import cv2
import numpy as npimg0 = cv2.imread('imge/111.jpg')
img1 = cv2.GaussianBlur(img0, (15,15), 0, 0)#高斯滤波
img2 = cv2.morphologyEx(img0, cv2.MORPH_GRADIENT, (3, 3))#形态学梯度
img3 = cv2.Canny(img0, 127, 255)#Canny边缘检测,结果为灰度图
ret, img4 = cv2.threshold(img3, 0, 255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)#转化为灰度图contours, hierarchy = cv2.findContours(img4, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)#求轮廓
img5 = np.zeros(img4.shape, dtype = np.uint8)
img5 = cv2.drawContours(img5, contours, -1, 255, 3)
for i in range(len(contours)):moment = cv2.moments(contours[i])area = cv2.contourArea(contours[i])long = cv2.arcLength(contours[i], True)hu = cv2.HuMoments( moment )print('矩特征为\n', moment, '\n',moment['m00'],'\n面积为', area)print( '\n长度为', long, '\nHu矩为', hu.flatten())#flatten返回一个一维数组print(cv2.matchShapes(contours[0], contours[1], cv2.CONTOURS_MATCH_I1, 0))
print(cv2.matchShapes(contours[2], contours[3], cv2.CONTOURS_MATCH_I1, 0))
print(type(img3),img3.shape,type(img3[0,0]))
print(type(img4),img4.shape,type(img4[0,0]))
print(type(contours),len(contours))cv2.imshow('img0', img0)
cv2.imshow('img1', img1)
cv2.imshow('img2', img2)
cv2.imshow('img3', img3)
cv2.imshow('img4', img4)
cv2.imshow('img5', img5)cv2.waitKey()
cv2.destroyAllWindows()

12.3.3 多边形拟合

import cv2
import numpy as npimg0 = cv2.imread('imge/111.jpg', 0)
img0 = cv2.Canny(img0, 128, 196)
ret, img0 = cv2.threshold(img0, 127, 255, cv2.THRESH_BINARY)contours, hierarchy = cv2.findContours(img0, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
img1 = np.zeros(img0.shape, dtype = np.uint8)
img1 = cv2.drawContours(img1, contours, -1, 255, 3)x, y, w, h = cv2.boundingRect(contours[0])
brcnt = np.array([[x, y], [x+w, y], [x+w, y+h], [x, y+h]])retval = cv2.minAreaRect(contours[1])
point = cv2.boxPoints(retval)
point = np.int0(point)#取整后才能使用(x, y) , radius = cv2.minEnclosingCircle(contours[2])
center = (int(x), int(y))
radius = int(radius)retval = cv2.fitEllipse(contours[3])[vx, vy, x, y] = cv2.fitLine(contours[0], cv2.DIST_L2, 0, 0.01, 0.01)
lefty = int((-x*vy/vx) + y)
righty = int((img1.shape[1] - x) * vy / vx + y)retval1, trangle = cv2.minEnclosingTriangle(contours[1])
trangle = np.int0(trangle)retval2 = cv2.approxPolyDP(contours[2], 0.001 * cv2.arcLength(contours[2], True), True)cv2.drawContours(img1, [brcnt], -1, 255, 3)#坐标放在列表里
cv2.drawContours(img1, [point], -1, 255, 3)#坐标放在列表里
cv2.circle(img1, center, radius, 255, 3)
cv2.ellipse(img1, retval, 255, 3)
cv2.line(img1, (img1.shape[1] - 1, righty ), (0, lefty), 128, 3)
for i in range(0, 3):cv2.line(img1, tuple(trangle[i][0]), tuple(trangle[(i+1) % 3][0]), 255, 3)
cv2.drawContours(img1, [retval2], -1, 128, 3)#坐标放在列表里cv2.imshow('img0', img0)
cv2.imshow('img1', img1)cv2.waitKey()
cv2.destroyAllWindows()

12.3.4 利用形状场景算法比较图像

import cv2
import numpy as npimg0 = cv2.imread('imge/circle.jpg',0)kernal = np.ones((5,5), dtype = np.uint8)
img0 = cv2.erode(img0, kernal)
img0 = cv2.GaussianBlur(img0, (13,13), 0, 0)
img0 = cv2.Canny(img0, 64,128)
retval, img0 = cv2.threshold(img0, 128, 255, cv2.THRESH_BINARY)img1 = cv2.pyrUp(img0)contour0, hierarchy = cv2.findContours(img0, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contour1, hierarchy = cv2.findContours(img1, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)sd = cv2.createShapeContextDistanceExtractor()#构建距离运算算子
d1 = sd.computeDistance(contour0[0], contour1[0])#输入参数不是元组,是numpy.ndarray
print(d1, type(contour0[0]))sd1 = cv2.createHausdorffDistanceExtractor()
d2 = sd1.computeDistance(contour0[0], contour1[0])#输入参数不是元组,是numpy.ndarray
print(d2)img0 = cv2.drawContours(img0, contour0, -1, 255, 5)
img1 = cv2.drawContours(img1, contour1, -1, 255, 5)cv2.imshow('img0', img0)
cv2.imshow('img1', img1)cv2.waitKey()
cv2.destroyAllWindows()

12.3.5 轮廓特征值

import cv2
import numpy as npimg = cv2.imread('imge/circle.jpg', 0)
img = cv2.Canny(img, 128, 255)contours, hi = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
x, y, w, h = cv2.boundingRect(contours[0])(xx, yy), (mx, my), angle = cv2.fitEllipse(contours[0])retval0 = cv2.contourArea(contours[0])#计算面积
retval1 = cv2.arcLength(contours[0], True)#计算周长cv2.imshow('img', img)
cv2.rectangle(img, (x,y), (x+w,y+h), 255, 3)
print(w/h)
print(retval0 ,retval1 , retval0/retval1, w/2)
print(np.sqrt(4*retval0/ np.pi))
print(angle)cv2.waitKey()
cv2.destroyAllWindows()

13 直方图处理

13.1 基础概念

直方图是图像处理的一个非常重要的手段,可以在图像内部灰度级的角度对图形进行描述,包含十分重要的信息,可以增强图像显示。
    直方图的横轴是像素出现次数,纵轴是像数值的大小。通过归一化处理,可以将横轴调整为出现频率(百分数)。
    opencv在直方图中提出了三个参数。
    1.DIMS:收集参数的数量,一般情况下只有灰度值,该参数为1
    2.RANGE:灰度范围,一般为[0,255]
    3.BINS:子集的数量,将[0,255]可以分成不同的份数

13.2 函数讲解

13.2.1 绘制直方图

matplotlib.pyplot.hist( X, BINS)
       X:数据源必须是一维的,二维图像可以使用ravel()展成一维
       BINS:灰度级的分组情况
       hist =cv2.calcHist(images, channels, mask, histSize, ranges, accumlate)
       hist:返回值,为各个级别像素点的个数
       通道编号:指定通道编号,需要用[]括起来,灰度[0],彩色[0]、[1]、[2]
       mask:掩模图像,统计整个图像时为none。掩模图像是指全255和0的图像,通过按位与运算可以将为1的部位显示,将为0部位隐藏。
       histSize:BINS值,,需要用[]括起来
       ranges:像素范围
       accumlate:直方图可能是多幅图像的累加,为false为单独,ture为累加

13.2.2 直方图均衡化

均衡的直方图使图像色彩更均衡、外观更清晰,也是图像更便于处理。
    dst = cv2.equalizeHist(src)

13.2.3 pyplot模块介绍

该模块提供了一种类似于MATLAB绘图方式的框架,可以使用其中函数绘制图像。
    matplotlib.pyplot.subplot(nrows, ncols, index)
    该函数的功能是添加子图,nrows为行号,ncols为列号,index为序号。
    matplotlib.pyplot.imshow(X, cmap)
    该函数为绘图函数,X为各种图像信息,cmap为色彩空间,默认为rgb色彩空间。

13.3 代码演示

13.3.1 直方图绘制

import matplotlib.pyplot as plt#需要先安装pillow库,换了个内核就好使了
import cv2
import numpy as npimg = cv2.imread('imge/tomato.jpg', 0)
#plt.hist(img.ravel(), 16)mask = np.zeros(img.shape,  np.uint8)mask[100:500, 200:400] = 255
print(img.shape)
hist0 = cv2.calcHist(img, [0],  None, [32], [0, 255])#使用掩模失败
#hist1 = cv2.calcHist(img, [0],  None, [32], [0, 255])
#hist2 = cv2.calcHist(img, [0],  None, [32], [0, 255])
#plt.plot(hist0, color = 'r')
#plt.plot(hist1, color = 'g')
#plt.plot(hist2, color = 'b')cv2.imshow('img', img)cv2.waitKey()
cv2.destroyAllWindows()

13.3.2 均衡化

import cv2
import numpy as np
import matplotlib.pyplot as pltimg = cv2.imread('imge/tomato.jpg', 0)
img1 = cv2.equalizeHist(img)hist0 = cv2.calcHist(img, [0], None, [16], [0, 255])
hist1 = cv2.calcHist(img1, [0], None, [16], [0, 255])#plt.plot(hist0, color = 'g')
#plt.plot(hist1, color = 'r')
cv2.imshow('img', img)
cv2.imshow('img1', img1)cv2.waitKey()
cv2.destroyAllWindows()

13.3.3 plot函数

import cv2
import numpy as np
import matplotlib.pyplot as pltimg = cv2.imread('imge/tomato.jpg', 0)
img1 = cv2.equalizeHist(img)hist0 = cv2.calcHist(img, [0], None, [16], [0, 255])
hist1 = cv2.calcHist(img1, [0], None, [16], [0, 255])plt.subplot(221)
plt.plot(hist0, color = 'g')
plt.subplot(222)
plt.plot(hist1, color = 'r')plt.subplot(223)
plt.imshow(img, cmap= plt.cm.gray_r)
plt.axis('off')plt.subplot(224)
plt.imshow(img, cmap= plt.cm.gray)
#cv2.imshow('img', img)
#cv2.imshow('img1', img1)#cv2.waitKey()
#cv2.destroyAllWindows()

14 傅里叶变换

14.1 概念介绍

在我这个专栏里有对傅里叶变换的介绍,这里不再赘述。

14.2 函数讲解

14.2.1 numpy实现傅里叶变换

retval = numpy.fft.fft2(图像)
    retval = numpy.fft.fftshift(原始频谱)#将零频率分量移动至图像中心
    新像素值 = 20*np.log(np.abs(频谱值))#将复数转至[0, 255]

14.2.2 opencv实现傅里叶变换

傅里叶变换函数:返回结果 = cv2.dft(原始图像, 转换标符)(我没成功,找不到虚数部分)
        输入图像需要是np.float32的格式
        转换标符通常为“cv2.DFT_COMPEXT_OUTPUT”,输出复数阵列

14.3 代码演示

import cv2
import numpy as np
import matplotlib.pyplot as pltimg = cv2.imread('imge/tomato.jpg', 0)ff = np.fft.fft2(img)
ff = np.fft.fftshift(ff)
fshift = 20*np.log(np.abs(ff))img1 = np.fft.ifftshift(ff)
img1 = np.fft.ifft2(img1)
img1 = np.abs(img1)plt.subplot(1,3,1)
plt.imshow(img)
plt.title('before')plt.subplot(1,3,2)
plt.imshow(fshift)
plt.title('after')plt.subplot(1,3,3)
plt.imshow(img1)
plt.title('before')print(type(ff), len(ff))
import cv2
import numpy as np
import matplotlib.pyplot as pltimg = cv2.imread('imge/tomato.jpg', 0)ff = np.fft.fft2(img)
ff = np.fft.fftshift(ff)
fshift = 20*np.log(np.abs(ff))x, y = ff.shape
ff[int(x/2)-5:int(x/2)+5, int(y/2)-5:int(y/2)+5 ] = 0img1 = np.fft.ifftshift(ff)
img1 = np.fft.ifft2(img1)
img1 = np.abs(img1)plt.subplot(1,3,1)
plt.imshow(img)
plt.title('before')plt.subplot(1,3,2)
plt.imshow(fshift)
plt.title('after')plt.subplot(1,3,3)
plt.imshow(img1)
plt.title('after')print(type(ff), len(ff))
import cv2
import numpy as np
import matplotlib.pyplot as pltimg = cv2.imread('imge/tomato.jpg', 0)#img = np.float32(img)
#f = cv2.dft(img, cv2.DFT_COMPLEX_OUTPUT)#通过cv2得到的频谱找不到虚部
f1 = np.fft.fft2(img)
f1 = np.fft.fftshift(f1)
mask = np.zeros(f1.shape, dtype = np.uint8)
x, y = f1.shape
mask[int(x/2)-200:int(x/2)+200, int(y/2)-200:int(y/2)+200] = 1f1 = f1 * maskifshift = np.fft.ifftshift(f1)
iff = np.fft.ifft2(ifshift)
iff = np.abs(iff)plt.imshow(iff)

15.模板匹配

15.1 概念介绍

模板匹配的功能是实现在A图像中,找到与B图像最相似的部分。没有很难理解的概念,自己写也能实现,但是这边还是用opencv的函数。

15.2 函数介绍

模板匹配函数:result = cv2.matchTemplate(image, temp1, method [ mask])
                result:返回值,是一个矩阵,不同位置遍历时得到的数值,尺寸与image、temp1的尺寸有关。
                image:原始图像,即A图像
                temp1:模板图像
                method:匹配方法,具体的匹配方法不做介绍了,不过需要知道不同的方法得到的数值可能是相反的。

查找最值函数:minVal, maxVal, minLoc, maxLoc = cv2.minMaxLoc(src)

功能:该函数配合模板匹配函数,便可以确定image图片中与temp1最相似的初始位置。

绘制矩形函数:Img = cv2.rectangle(img, pt1, pt2, color, [thickness])

功能:绘制矩形。将最相似的位置圈出,便评价匹配结果

pt1, pt2:矩形的对角顶点

寻找特定值在矩阵位置的函数: result = np.where( mal 条件)

功能:寻找mal矩阵中满足条件的数值的位置。
                result:矩阵位置行角标组成数组、列角标组成数组.

打包函数:result = zip(可迭代对象)
                功能:将可迭代对象打包成元组

15.3 代码演示

15.3.1 单次模板匹配

import cv2
import numpy as np
import matplotlib.pyplot as plotimg = cv2.imread('OIP-C.jpg')#导入A图像
cv2.imshow("img", img)test = img[100:150, 100:150]#截取需要匹配部分
cv2.imshow("test", test)result = cv2.matchTemplate(img, test, 0)#使用0的方法进行匹配minVal, maxVal, minLoc, maxLoc = cv2.minMaxLoc(result)#寻找最值img = cv2.rectangle(img, minLoc, [minLoc[0] + test.shape[0], minLoc[1] + test.shape[1]], [255, 255, 255])#在A图像中标出匹配部分的位置
cv2.imshow("img1", img)#获取矩阵尺寸
print(result.shape[::], result.shape[::-1])
cv2.waitKey()
cv2.destroyAllWindows()

15.3.2 多模板匹配

import cv2
import numpy as np
import matplotlib.pyplot as plotimg = cv2.imread('OIP-C.jpg')
cv2.imshow("img", img)test = img[100:200, 100:150]
cv2.imshow("test", test)img1 = np.zeros([2*img.shape[0], 2*img.shape[1], 3], dtype = np.uint8)
img1[0:img.shape[0], 0:img.shape[1], :] = img
img1[0:img.shape[0], img.shape[1]:2*img.shape[1], :] = img
img1[img.shape[0]:2*img.shape[0], 0:img.shape[1], :] = img
img1[img.shape[0]:2*img.shape[0], img.shape[1]:2*img.shape[1], :] = img
cv2.imshow("img1", img1)#构件四合一的图片进行模板匹配result = cv2.matchTemplate(img1, test, 0)#minVal, maxVal, minLoc, maxLoc = cv2.minMaxLoc(result)threshold = 100#设置寻找阈值
a = np.where(result<threshold)#获取目标坐标print(a)
for i in zip(*a[::-1]):#将坐标打包img1 = cv2.rectangle(img1, i, [i[0] + test.shape[1], i[1] + test.shape[0]], [255, 255, 255])
cv2.imshow("img2", img1)#获取矩阵尺寸
print(result.shape[::], result.shape[::-1])
cv2.waitKey()
cv2.destroyAllWindows()

16 霍夫变换

16.1 概念介绍

霍夫变换的功能是在图像中寻找直线或者圆形的一种方法。
    原理:涉及到笛卡尔坐标系与霍尔坐标系的转换。

16.2 函数介绍

霍夫直线变换函数:lines = cv2.HoughLines(image, rho, theta, threshold)
                image:输入图像,需要是二值图像
                rho:像素精度,一般为1
                theta:角度精度,一般为pi/180,表示搜索所有可能
                threshold:阈值,该值越小识别到的直线越多。
                lines:返回值,(截距, 角度),np.ndarry格式
    概率霍夫直线变换函数:lines = cv2.HoughLinesP(image, rho, theta, threshold, min, max)
                min:接受直线的最小长度,默认值为0
                max:判断两点是否在一条直线上,超过该值认为不在,默认值为0。
    霍夫圆变换函数:circles = HoughCircles (image, method, dp, minDist, paraml, param2, min, max)
                dp:累计器分辨率

16.3 代码演示

import cv2
import numpy as np
import matplotlib.pyplot as plotimg = cv2.imread('OIP-C.jpg')
cv2.imshow("img", img)img1 = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv2.imshow("img1", img1)img2 = cv2.Canny(img1, 50, 150)
cv2.imshow("img2", img2)lines = cv2.HoughLines(img2, 1, 3.14/180, 50)#线性变换
lines1 = cv2.HoughLinesP(img2, 1, 3.14/180, 50, 10, 10)#改良线性变换
circles = cv2.HoughCircles(img2, cv2.HOUGH_GRADIENT, 1, 20, param1 = 50, param2 = 30, minRadius = 0,maxRadius =  0)#圆变换img3 = img.copy()
for line in lines:rho, thete = line[0]a = np.cos(thete)b = np.sin(thete)x0 = a * rhoy0 = b * rhox1 = int(x0 + 1000*(-b))y1 = int(y0 +1000*(a))x2 = int(x0 - 1000*(-b))y2 = int(y0 - 1000*(a))cv2.line(img3, (x1, y1), (x2, y2), (0, 0, 255), 5)
cv2.imshow("img3", img3)img4 = img.copy()
for line in lines1:x1, y1, x2, y2 = line[0]#优化了,直接输出点cv2.line(img4, (x1, y1), (x2, y2), (0, 0, 255), 5)print(circles)
img5 = img.copy()
for circle in circles[0, :]:print(circle)cv2.line(img5, (circle[0], circle[1]), circle[2], (0, 0, 255), 5)
cv2.imshow("img5", img5)cv2.waitKey()
cv2.destroyAllWindows()

第17章 图像的分割与提取

17.1 基础概念

这一章的内容不是很多,但是这一部分足足花了两天的时间才搞完。

这一章主要涉及到图像分割与处理的两种方法:分水岭法和交互式前景提取。在具体讲解两种算法之前,需要明白几个概念。图像,也就是整幅图中所有的像素点;前景,也就是目标区域的像素点,在灰度图及二值图像中,默认前景为白色,也就是像素值大的;背景,图像中除了目标区域以外的组成部分。由于我们不能明确的判断前景和背景,只能大概判断,于是就有了可能的前景,即所有前景像素加部分背景像素,可能的背景也就是这个意思。有了可能那就有可能既不是前景也不是背景的位置,被称为不明区域。

其中分水岭法是指通过类似水坝注水的原理进行分割。将像素值看为地面的高度,高度接近的位置看成一个山谷,也就是一各位部分;然后加水,相同的区域开始连接,最终将图像分成不同的部分。需要注意的问题有两个,刚开始就看成一个山谷的位置有多大,最终什么时候判断加水结束,因为一直加水的话会导致全部是一个山谷。

关于问题1,解决办法是通过一系列的操作确定一些区域是不同的山谷,这些区域都是前景靠中心的部分,但是不全,有遗漏。使用的办法就是通过腐蚀操作,或者使用distanceTransform函数找到前景的中心。

关于问题2,加水啥时候结束。需要用到上面提到不明区域的概念。其实分割核心的问题就是判断不明区域的归属。

交互式前景提取的操作较为简单,结果类似于PPT的前景提取,可以选择前景或者背景,便于计算机判断。

17.2 函数介绍

前面提到,可能需要遇到形态学操作中的腐蚀操作前面有介绍,这里不再赘述。

复习个操作:

A[条件] = a;

作用:将A矩阵中满足条件的幅值为a。

距离变换函数:dst = cv2.distanceTransform(src, distanceType, maskSize, [dstType])
                作用:反映了各个像素与背景(像素值为0,颜色为黑色的像素值的距离),通过距离值来确定是否为前景。距离越大,为前景的可能性越大,得到的是距离,跟阈值正相关,换算关系不明确。
                src:8位单通道二值函数
                distanceType:距离参数类型,不同计算距离的方法。

标注函数:retval, labels = cv2.connectedComponents( image )
               功能:把不同的区域赋不同的值,从0开始
               retval:标注数量
               labels:返回图像

图像分割函数:markers = cv2.watershed( image, markers)
               image:8位3通道图像
               markers:16位函数,通过标注函数产生的返回值调整下就可以(值应该需要连续的整数),也就是蓄水的种子。

交互式前景提取函数:mask, bgdModel, fgbModel = cv2.grabCut(img, mask, rect, BgdModel, fgbModel, iterCount, [mode])

作用:交互式前景提取,虽然参数多,但并不难。主要功能是判断未知区域是背景还是前景。
               img:8位3通道
               mask:掩码,8位单通道,用于确定前景、背景、不确定区域。
               rect:包含前景的区域,rect以外全是背景
               fgbModel,bgdModel:内部使用数组,(1,65),numpy.fioat64
               iterCount:迭代次数
               mode:迭代模式,cv2.GC_INIT_WITH_RECT(通过rect作为种子,rect以外全是背景,算法会在区域内找背景排除,如果前景在矩形外会找不到), cv2.GC_INIT_WITH_MASK(通过mask来判断)

17.3 代码演示

17.3.1 形态学操作

如果不同前景之间没有接触,直接原图-腐蚀就可以得到图像的边缘。

#前景背景无连接,使用形态学操作,获取图像边缘
import cv2
import numpy as np
import matplotlib.pyplot as plotimg = cv2.imread('OIP-C.jpg', 0)
retval, img = cv2.threshold(img, 128, 255, cv2.THRESH_BINARY)
cv2.imshow('img0', img)#开运算,去除噪音
kernal = np.ones((5,5), dtype = np.uint8)
img1 = cv2.morphologyEx( img, cv2.MORPH_OPEN, kernal)
cv2.imshow('img1', img1)#腐蚀操作
img2 = cv2.erode(img, kernal)
cv2.imshow('img2', img2)#原图-腐蚀得到边缘
img3 = img - img2
cv2.imshow('img3', img3)cv2.waitKey()
cv2.destroyAllWindows()

17.3.2 distanceTransform函数

前景之间有接触使用distanceTransform+二值化划分前景和背景。

#distanceTransform函数
import cv2
import numpy as np
import matplotlib.pyplot as plotimg = cv2.imread('circle.jpg', 0)
cv2.imshow('img0', img)retval, img1 = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
cv2.imshow('img1', img1)
img1 = cv2.distanceTransform(img1, cv2.DIST_L2, 5)#计算前景与背景之间的距离,输入图像为二值图像
print(img1.max())#最大值与确定前景阈值之间成正比例,通过最大值划分阈值
retval, img2 = cv2.threshold(img, 1*img1.max(), 255, 0)
cv2.imshow('img2', img2)cv2.waitKey()
cv2.destroyAllWindows()

17.3.3 获取未知区域,标注,分水岭图像分割

未知区域 = 图像 - 确认背景 - 确认前景,其中图像 - 确认背景通过腐蚀操作可以实现,确认前景通过distanceTransform实现。cv2.connectedComponents()和cv2.watershed()得到的结果均为由正整数表示区域的矩阵,不能cv2.imshow(),但是可以缩放后显示。缩放后的值就不能在当成marker了!!!

#获得未知区域
import cv2
import numpy as np
import matplotlib.pyplot as plotimg0 = cv2.imread('circle.jpg')
img = cv2.imread('circle.jpg', 0)
cv2.imshow('img0', img)#通过膨胀操作获得图像-确定背景
kernal = np.ones((5,5), dtype = np.uint8)
img1 = cv2.dilate(img, kernal)
cv2.imshow('img1',img1)#distanceTransform函数获得确定前景
retval, img2 = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
img2 = cv2.distanceTransform(img2, cv2.DIST_L2, 5)
print(img2.max())
retvale, img2 = cv2.threshold(img, 1.2 * img2.max(), 255, 0)
cv2.imshow('img2',img2)#做差,求未知区域
img3 = img1 - img2
cv2.imshow('img3',img3)
# print(img3.shape)#图像标注
img3 = np.uint8(img3)
retval, markers = cv2.connectedComponents(img2)
markers = markers + 1
markers[img3 == 255] = 0
# img4 = img4 * 255 / img4.max()#数值太小,不到255
# cv2.imshow('img4',img4)#进行图像分割
cv2.imshow('img0', img0)markers = cv2.watershed(img0, markers)
img0[markers == -1] = [0,255,0]
cv2.imshow('img0',img0)cv2.waitKey()
cv2.destroyAllWindows()

17.3.4 交互式前景处理函数

import cv2
import numpy as np
import matplotlib.pyplot as plotimg = cv2.imread('OIP-C.jpg')
cv2.imshow('img', img)
print(img.shape)
w,h,a = img.shapemask = np.zeros(img.shape[0:2], np.uint8)
bgdModel = np.zeros((1, 65), np.float64)
fgdModel = np.zeros((1, 65), np.float64)
rect = (int(w/4), int(h/4), int(3*w/4), int(3*h/4))
cv2.grabCut(img, mask, rect, bgdModel, fgdModel, 5, 0)mask2 = np.where((mask == 2)|(mask == 0), 0, 1)#把可能是背景的置0
img_a = img * mask2[:, :, np.newaxis]#将mask2中0位置的元素改为0
plot.imshow(img_a)cv2.waitKey()
cv2.destroyAllWindows()
import cv2
import numpy as np
import matplotlib.pyplot as plotimg = cv2.imread('OIP-C.jpg')
cv2.imshow('img', img)
print(img.shape)
w,h,a = img.shapemask = np.zeros(img.shape[0:2], np.uint8)
bgdModel = np.zeros((1, 65), np.float64)
fgdModel = np.zeros((1, 65), np.float64)
rect = (0 ,0 , w-10, h-10)
cv2.grabCut(img, mask, rect, bgdModel, fgdModel, 5, 0)img1 = cv2.imread('OIP-C 1.jpg', 0)#以灰度图读取
cv2.imshow('img1', img1)mask[ img1 == 0] = 0
mask[img1 == 255] = 1
cv2.grabCut(img, mask, rect, bgdModel, fgdModel, 5, cv2.GC_INIT_WITH_MASK)mask2 = np.where((mask == 2)|(mask == 0), 0, 1)#把可能是背景的置0
img_a = img * mask2[:, :, np.newaxis]#将mask2中0位置的元素改为0
plot.imshow(img_a)cv2.waitKey()
cv2.destroyAllWindows()

18 视频处理

18.1 基础概念

这一章的内容与opencv入门一样,内容就是摄像头数据的显示和视频数据的显示以及保存。

18.2 函数讲解

视频捕获函数:捕获对象 = cv2.VideoCapture('摄像头ID号'/'视频文件文件名')
                该函数的输入参数既可以是摄像头,也可以是视频文件,实现初始化功能。
       判断摄像头是否打开函数:retval = cv2.VideoCapture.isOpened()
       打开摄像头函数:retval = cv2.VideoCapture.open('摄像头ID号'/'视频文件文件名')
       帧捕获函数:retval, Image = cv2.VideoCapture.read()
               功能:读取当前帧,并指向视频下一帧可以看作是cv2.VideoCapture.grab()与cv2.VideoCapture.retrieve()的结合
               retval = cv2.VideoCapture.grab()
                       功能:指向下一帧,成功的话,返回Ture
               retval, Image = cv2.VideoCapture.retrieve()
                       功能:成功的话,retval为Ture,Image为图像信息
      释放摄像头函数:None = cv2.VideoCapture.release()
              如果有VideoCapture类的对象cap,可以使用cap.release()
      cv2.VideoCapture.get(参数)
        获取参数值
      cv2.VideoCapture.set(参数, 数值)
        设置参数值
      视频保存函数:retval = cv2.VideoWrite( filename, fourcc, fps, framesize, [Iscolor])

功能:可以实现图片序列保存为视频,视频参数的更改以及视频类型的更改。
              fourcc:视频解/编码格式,可以使用cv2.VideoWrite_fourcc()生成,如果是-1,可以在运行过程中的对话框中选择。   
              fourcc = cv2.VideoWrite_fourcc('', '', '', '')
              使用特定四字符组合生成编码格式,可以在http://www.fourcc.org查询
             fps:帧速率
             framesize:每一帧的长、宽
     None = cv2.VideoWrite.write(Image)
             写入下一帧
     None = cv2.VideoWrite.release()
              释放对象

18.3 代码示例

18.3.1 捕获摄像头文件

#捕获摄像头文件
import cv2
import numpycap = cv2.VideoCapture(0)#对象实例化
video = cv2.VideoCapture('Cancellation.mkv')
while(cap.isOpened()):retval, Image0 = cap.read()#得到当前帧存在Image0中,并指向下一帧retval, Image1 = video.read()cv2.imshow('摄像头', Image0)cv2.imshow('视频', Image1)c = cv2.waitKey(1)#按esc退出if c == 27:breakcap.release()#释放
video.release()
cv2.destroyAllWindows()

18.3.2 保存视频文件

#保存文件
import cv2
import numpycap = cv2.VideoCapture(0)fourcc = cv2.VideoWriter_fourcc('I', '4', '2', '0')
out = cv2.VideoWriter('out.avi', fourcc, 20, (640, 480))
while(cap.isOpened()):retval, Image0 = cap.read()out.write(Image0)Image1 = cv2.Canny(Image0, 100, 200)cv2.imshow('摄像头', Image1)c = cv2.waitKey(1)if c == 27:breakcap.release()
out.release()
cv2.destroyAllWindows()

19 绘图及交互

19.1 基础概念

opencv中也可以进行GUI(图形用户界面)的相关操作,其中包括绘图、鼠标交互以及滚动条交互。

19.2 函数介绍

19.2.1 绘制图形函数

绘制直线函数:img = cv2.line(img, pt1, pt2, color, [thickness, linetype])
                参数中,img原图像,thickness线条粗细,linetype线的类型
       绘制椭圆函数:img = cv2.ellipse(img, center, axes, angle, startAngle, endAngle, color, [thickness, linetype])
                axes:轴的长度
                angle:偏转角度
                startAngle, endAngle:起止角度
        绘制多边形函数:img = cv2.polylines(img, pts, isClosed, color,  [thickness, linetype])
                pts:点集,表示各个顶点,数据类型为numpy.int32
        绘制文字: img = cv2.putText(img, text, org, fontFace, fontScale, color, [thickness, linetype])
                org:文字位置的左下角坐标
                fontFace:字体
                fontScale:字号

在结尾加bottomLeftOrigin = True可以实现字体翻转

19.2.2 鼠标交互函数

自定义触发函数(类似回调函数):def OnMouseAction(event, x, y, flags, param):
                event:触发事件

事件如下:

EVENT_FLAG_LBUTTON 1         #左鍵拖曳  
EVENT_FLAG_RBUTTON 2         #右鍵拖曳  
EVENT_FLAG_MBUTTON 4         #中鍵拖曳  
EVENT_FLAG_CTRLKEY 8         #(8~15)按Ctrl不放事件  
EVENT_FLAG_SHIFTKEY 16       #(16~31)按Shift不放事件  
EVENT_FLAG_ALTKEY 32         #(32~39)按Alt不放事件

EVENT_MOUSEMOVE 0            #滑动
EVENT_LBUTTONDOWN 1          #左键点击
EVENT_RBUTTONDOWN 2          #右键点击
EVENT_MBUTTONDOWN 3          #中键点击
EVENT_LBUTTONUP 4            #左键放开
EVENT_RBUTTONUP 5            #右键放开
EVENT_MBUTTONUP 6            #中键放开
EVENT_LBUTTONDBLCLK 7        #左键双击
EVENT_RBUTTONDBLCLK 8        #右键双击
EVENT_MBUTTONDBLCLK 9        #中键双击

x,y :触发时,鼠标的位置
                flags:鼠标的拖曳事件,以及键盘鼠标联合事件
                param:函数ID
       函数与窗口绑定函数:cv2.setMouseCallback(winname, onMouse)
               winname:绑定窗口名
               onMouse:绑定函数

19.2.3 创建滚动条

创建滚动条函数:cv2.createTrackbar( trackbarname, winname, value, count, onChange)
                trackbarname:滚动条名称
                winname:依附窗口
                value:初始值
                count;进度条最大值
                onChange:回调函数,将滚动条改变后的操作写在这里
         获取滚动条数值:retval = getTrackbarPos(trackbarname, winname)

19.3 代码示例

19.3.1 基础函数使用

import cv2
import numpy as npimg = np.zeros([500, 500, 3], np.uint8)img = cv2.line(img, [20, 20], [450, 450], [255, 255, 255])#画直线
img = cv2.rectangle(img, [100, 100], [400, 400], [255, 255, 255])#画矩形
img = cv2.circle(img, [200, 200], 100, [255, 255, 255])#画圆#生成矩阵,np.array的参数是矩阵!!!
pts = np.array([[50, 60], [300, 20], [250, 170], [20, 500]], np.int32)
print(pts)
# pts = pts.reshape((-1, 1, 2))
#画多边形,线条参数改为-1,会报错;点坐标需要用[]括住,否则也会报错
img = cv2.polylines(img, [pts], True, [255, 255, 255], 8)img = cv2.putText(img, 'learn', [250, 250], cv2.FONT_HERSHEY_SIMPLEX, 3, [0, 255, 255], 8)
#参数顺序
img = cv2.putText(img, 'learn', [250, 300], cv2.FONT_HERSHEY_SIMPLEX, 3, [0, 255, 255], 8, bottomLeftOrigin = True)#画多个圆
for i in range(20):r = np.random.randint(0, high = 100)x = np.random.randint(0, high = 500)y = np.random.randint(0, high = 500)r = np.random.randint(0, high = 255)g = np.random.randint(0, high = 255)b = np.random.randint(0, high = 255)img = cv2.circle(img, [x, y], r, [b, g, r], -1)img = cv2.ellipse(img, [250, 250], [50, 100], 0, 90, 360, [0, 0, 255], -1)#画椭圆cv2.imshow('img', img)c = cv2.waitKey()
cv2.destroyAllWindows()

19.3.2 鼠标事件

import cv2
import numpy as npmode = 0#通过切换模式,改变鼠标交互的结果def OnMouseAction(event, x, y, flags, param):if event == cv2.EVENT_MBUTTONDBLCLK:print('中键双击')if event == cv2.EVENT_LBUTTONDBLCLK:print('左键双击')if event == cv2.EVENT_RBUTTONDBLCLK:print('右键双击')if event == cv2.EVENT_LBUTTONDOWN:print('左键单击')if event == cv2.EVENT_RBUTTONDOWN:print('右键单击')if mode == 0:#画图没有返回值cv2.ellipse(img, [x, y], [30, 60], 0, 90, 360, [0, 0, 255], -1)if mode == 1:x = np.random.randint(0, high = 500)y = np.random.randint(0, high = 500)r = np.random.randint(0, high = 255)g = np.random.randint(0, high = 255)b = np.random.randint(0, high = 255)cv2.circle(img, [x, y], r, [b, g, r], -1)if mode == 2:cv2.line(img, [20, 20], [450, 450], [255, 255, 255])img = np.zeros([500, 500, 3], np.uint8)cv2.namedWindow('img')
cv2.setMouseCallback('img', OnMouseAction)#字符用'a'表示,而不是a,通过按键切换交互模式
while(1):cv2.imshow('img', img)k = cv2.waitKey(1)&0xffif k == 27:breakelif k == ord('a'):mode = 1elif k == ord('s'):mode = 2cv2.destroyAllWindows()

19.3.3 滚动条

import cv2
import numpy as npdef changecolor(x):r = cv2.getTrackbarPos('r', 'img')g = cv2.getTrackbarPos('g', 'img')b = cv2.getTrackbarPos('b', 'img')img[:] = [r, g, b]
img = np.zeros([400, 400, 3], np.uint8)cv2.namedWindow('img')
cv2.createTrackbar( 'r', 'img', 0, 255, changecolor)
cv2.createTrackbar( 'g', 'img', 0, 255, changecolor)
cv2.createTrackbar( 'b', 'img', 0, 255, changecolor)while(1):cv2.imshow('img', img)#更新画面k = cv2.waitKey(1)&0xffif k == 27:breakcv2.waitKey()
cv2.destroyAllWindows()

20 K近邻算法

20.1 基础概念

原理:待测数据附近的K个有标签数据,有标签数据属于哪一类数量多,带判断数据就被分为哪类。

20.2 函数介绍

创建KNN算法:knn = cv2.ml.KNearest_create()

进行训练:knn.train(traindata, cv2.ml.ROW_SAMPLE, tbLabel)

traindata:训练集

cv2.ml.ROW_SAMPLE:训练集中特征值排列方式,按列放置

tbLabel:标签

获取结果:ret ,results, neighbours, dist = knn.findNearest(test, 5)

results:判断结果

neighbours:最近邻的位置

dist :与最近邻之间的距离

test:测试集

5:近邻个数

20.3 代码演示

20.3.1 自定义最近邻算法

#基本没有正确率
import cv2
import numpy as np
import math#数据初始化
s = 'image/'num = 100
row = 240
col = 240#加数据类型会报错
a = np.zeros([num, row, col])#读取图像
n = 0
for i in range(10):for j in range(1, 11):a[n, :, :] = cv2.imread(s+str(i)+'\\'+str(i)+'-'+str(j)+'.bmp', 0)n = n + 1#提取训练集特征值
feature = np.zeros([num, math.floor(row/5), math.floor(col/5)])#如果不能整除,需要调整
print(feature.shape)
for i in range(0, num):for j in range(0, row):for k in range(0, col):if a[i, j, k] == 255:feature[i, math.floor(j/5), math.floor(k/5)] += 1f = feature#提取测试集数据集
o_path = 'image/test/8.bmp'
o = cv2.imread(o_path, 0)f_o = np.zeros([math.floor(col/5), math.floor(row/5)])#计算除法会把int数据变成float数据
for i in range(col):for i in range(row):if o[i, j] == 255:f_o[math.floor(i/5), math.floor(j/5)] += 1#计算距离d = np.zeros(num)for i in range(num):d[i] = np.sum((f_o - f[i ,:, :])* (f_o - f[i ,:, :]))#获取最短距离及索引
d = d.tolist()
print(type(d))
temp = []
Inf = max(d)
k = 7
for i in range(k):temp.append(d.index(min(d)))d[d.index(min(d))] = Inf
print(temp)
#识别
end = np.zeros(10)
for i in range(k):end[temp[i]%10] += 1
end = end.tolist()
print(end)
print(max(end))
print('最有可能的数字是:', end.index(max(end)))img = cv2.imread(o_path)
cv2.imshow('img', img)
cv2.waitKey()
cv2.destroyAllWindows()

20.3.2 调用opencv内部函数实现最近邻算法

import cv2
import numpy as np
import matplotlib.pyplot as plot#数据格式很重要,.astype(np.float32)
rand1 = np.random.randint(0, 30, [20, 2]).astype(np.float32)
rand2 = np.random.randint(70, 100, [20, 2]).astype(np.float32)traindata = np.vstack([rand1, rand2])r1Label = np.zeros([20,1]).astype(np.float32)
r2Label = np.ones([20,1]).astype(np.float32)tbLabel = np.vstack([r1Label, r2Label])g = traindata[tbLabel.ravel() == 0]
plot.scatter(g[:, 0], g[:, 1], 80, 'g', 'o')b = traindata[tbLabel.ravel() == 1]
plot.scatter(b[:, 0], b[:, 1], 80, 'b', 's')test = np.random.randint(0, 100, (1, 2)).astype(np.float32)plot.scatter(test[:, 0], test[:, 1], 80, 'r', '*')knn = cv2.ml.KNearest_create()
knn.train(traindata, cv2.ml.ROW_SAMPLE, tbLabel)
ret ,results, neighbours, dist = knn.findNearest(test, 5)print(results, neighbours, dist)plot.show()

21 支持向量机

21.1 基础概念

支持向量机是一种基于关键点的算法。它的功能是找到一个分类器实现数据的分类,而分类器的寻找与离分裂器最近的点有关,这些点叫做支持向量。

21.2 函数讲解

构建向量机函数:svm = cv2.ml.SVM_create()
        训练函数:训练结果 = svm.train(训练数据, 排列格式, 标签)
                 数据格式:cv2.ml.ROW_SAMPLE,行排列;cv2.ml.COL_SAMPLE,列排列
        分类函数:(返回值, 返回结果) = svm.predict(测试数据)

21.3 代码演示

import cv2
import numpy as np
import matplotlib.pyplot as plot#数据格式很重要,.astype(np.float32)
rand1 = np.random.randint(0, 30, [20, 2]).astype(np.float32)
rand2 = np.random.randint(70, 100, [20, 2]).astype(np.float32)traindata = np.vstack([rand1, rand2])r1Label = np.zeros([20,1]).astype(np.float32)
r2Label = np.ones([20,1]).astype(np.float32)tbLabel = np.vstack([r1Label, r2Label])
tbLabel = np.array(tbLabel, dtype= 'int32')g = traindata[tbLabel.ravel() == 0]
plot.scatter(g[:, 0], g[:, 1], 80, 'g', 'o')b = traindata[tbLabel.ravel() == 1]
plot.scatter(b[:, 0], b[:, 1], 80, 'b', 's')test = np.random.randint(0, 100, (1, 2)).astype(np.float32)plot.scatter(test[:, 0], test[:, 1], 80, 'r', '*')SVM = cv2.ml.SVM_create()
results = SVM.train(traindata, cv2.ml.ROW_SAMPLE, tbLabel)
results, val = SVM.predict(test)print(results, val)plot.show()

22 K均值聚类算法

22.1 基本概念

K均值聚类的基本步骤:
                1.随机选取K个点作为聚类的中心点
                2.将其他数据点放到距离最近的类中
                3.计算各个类的平均值,将平均值作为下一次聚类的中点
                4.重复2、3,直至稳定

22.2 函数介绍

均值聚类函数:retval, bestLabels, centers = cv2.kmeans( data, K, bestLabels, criteria, attempts, flags)
                    data:数据类型为np.float32,同一特征放在同一列
                    K:聚类的个数
                    bestLabels:标签
                    criteria:终止条件,由type,max_iter,eps组成,type指终止条件,1.精度达到eps,停止;2.次数达到max_iter,停止;3.满足任一个停止
                   attempts:尝试次数
                   flags:选择中心点的方法
           返回值:
                  retval:距离值
                  bestLabels:标签
                  centers:中心点

22.3 代码演示

import cv2
import numpy as np
import math
import matplotlib.pyplot as plot#数据初始化
s = 'image/'num = 100
row = 240
col = 240#加数据类型会报错
a = np.zeros([num, row, col])#读取图像
n = 0
for i in range(10):for j in range(1, 11):a[n, :, :] = cv2.imread(s+str(i)+'\\'+str(i)+'-'+str(j)+'.bmp', 0)n = n + 1#提取训练集特征值
feature = np.zeros([num, math.floor(row/5), math.floor(col/5)])#如果不能整除,需要调整
print(feature.shape)
for i in range(0, num):for j in range(0, row):for k in range(0, col):if a[i, j, k] == 255:feature[i, math.floor(j/5), math.floor(k/5)] += 1f = np.array(feature, dtype= np.float32)retval, bestLabels, centers = cv2.kmeans(f, 10, None, (cv2.TERM_CRITERIA_EPS,10,0.000001), 100, cv2.KMEANS_RANDOM_CENTERS)a = []
b = []
for i in range(10):for j in range(10):a.append(int(bestLabels[10*i+j]))b.append(a)a = []print(b)

23 人脸识别

23.1 基本概念

连级分类器:将多个简单的分类器串联,可以提前筛选掉不符合要求的数据,减小计算。

opencv的人脸识别方法:LBPH人脸识别方法,EigenFishfaces方法,Fisherfaces方法。其中LBPH(局部二值模式直方图)人脸识别:考虑不同色素块之间的相对值,抗干扰能力较强;EigenFaces原理:PCA主成分分析,从而实现降维;Fisherfaces方法,线性判别分析,原理使同类尽量靠近,异类尽量远离。

23.2 函数介绍

创建人脸检测对象:faceCascade = cv2.CascadeClassifier('filename')
                filename为分类器的路径
       人脸检测函数:objects = cv2.CascadeClassifier.detectMultiScale( image, [scaleFactor, minNeighbors, flags, minSize, maxSize])
                image:待测图像,通常为灰度图
                scaleFactor:两次扫描中,窗口的缩放
                minNeighbors:构成检测目标的最小矩形的个数,越大越准确,但是可能忽略某些人脸。
                flags:通常被省略
                minSize、maxSize:目标最小、最大尺寸
                返回值:objects:目标对象的矩形框向量组,左上角x,y和宽度w,高度h

LBPH分类器实例化函数:retval = cv2.face.LBPHFaceRecognizer_create([radius, neighbors, grid_x, grid_y, threshold])
                radius:半径值,默认为1
                neighbors:邻域个数,默认为8邻域
                grid_x、grid_y:特征图像的水平、垂直分组个数
                threshold:预测阈值,大于该阈值,认为未识别到目标
       训练函数:None = cv2.face_FaceRecognizer.train(src, labels)
       判断函数:label, confidence = cv2.face_FaceRecognizer.predict(src)
               confidence:置信度,0为完全匹配,小于50可接受,大于80认为误差大

EigenFace分类器实例化函数:retval = cv2.face.EigenFaceRecognizer_create([num_components, threshold])
                num_components:主成分分析保留分量
       训练函数:None = cv2.face_FaceRecognizer.train(src, labels)
       判断函数:label, confidence = cv2.face_FaceRecognizer.predict(src)
               confidence:低于5000可靠

FisherFace分类器实例化函数:retval = cv2.face.FisherFaceRecognizer_create([num_components, threshold])
    训练函数:None = cv2.face_FaceRecognizer.train(src, labels)
    判断函数:label, confidence = cv2.face_FaceRecognizer.predict(src)
            confidence:低于5000可靠

23.3 函数演示

23.3.1 人脸检测

#人脸检测
import cv2
import numpy as np
import math
import matplotlib.pyplot as plot#图像的路径不能有中文
img = cv2.imread("IMG_2910.JPG")
img = cv2.resize(img, [1200, 800])faceCascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
faces = faceCascade.detectMultiScale(img, minNeighbors = 2)for (x,y,w,h) in faces:cv2.rectangle(img, (x, y), (x+w, y+h), (0,255,0), 2)
print(len(faces))
cv2.imshow('img', img)
cv2.waitKey()
cv2.destroyAllWindows()

23.3.2 人脸识别

#人脸识别
import cv2
import numpy as np
import math
import matplotlib.pyplot as plotimg = []img.append(cv2.imread('a1.png', 0))
img.append(cv2.imread('a2.png', 0))
img.append(cv2.imread('b1.png', 0))
img.append(cv2.imread('b2.png', 0))labels = np.array([0, 0, 1, 1])LBPH = cv2.face.LBPHFaceRecognizer_create()
LBPH.train(img, labels)
img1 = cv2.imread('b3.png', 0)
label, confidence = LBPH.predict(img1)print(label, confidence)EigenFaces = cv2.face.EigenFaceRecognizer_create()
EigenFaces.train(img, labels)
label, confidence = EigenFaces.predict(img1)print(label, confidence)Fisherfaces = cv2.face.FisherFaceRecognizer_create()
Fisherfaces.train(img, labels)
label, confidence = Fisherfaces.predict(img1)print(label, confidence)

完结撒花!!!

支线任务1 opencv学习(最后一次更新于2022.10.2,已完结)相关推荐

  1. CUDA版本与显卡驱动版本对照表(更新至2022.10.26 - CUDA11.8)

    更新2022-10-26-CUDA11.8 注:当前所有版本要求均为CUDA官方提供 如需转载,请注明出处. 更多优质内容,可点击原文链接进行阅读学习. CUDA 11.8 组件版本 Table 1. ...

  2. Ununtu 18.04 安装Carla 0.9.13 以及Carla ros bridge 超级避坑指南(更新于2022.10.20)

    Carla0.9.13 以及Carla ros bridge 超级避坑指南 Carla0.9.13 以及Carla ros bridge 超级避坑指南 站在巨人肩膀前进 显卡驱动问题 首先就是虚幻4的 ...

  3. OPENCV学习(更新)

    目录 OPENCV学习1 显示一张图片: 用==cv2.imread==来读取图片 ==定义函数cv_show来代替这三行代码(要显示图片每次都要输入这三行代码,非常麻烦)== ==上面是RGB,现在 ...

  4. OpenCV学习笔记(五十六)——InputArray和OutputArray的那些事core OpenCV学习笔记(五十七)——在同一窗口显示多幅图片 OpenCV学习笔记(五十八)——读《Mast

    OpenCV学习笔记(五十六)--InputArray和OutputArray的那些事core 看过OpenCV源代码的朋友,肯定都知道很多函数的接口都是InputArray或者OutputArray ...

  5. OpenCV学习笔记(五十一)——imge stitching图像拼接stitching OpenCV学习笔记(五十二)——号外:OpenCV 2.4.1 又出来了。。。。。 OpenCV学习笔记(五

    OpenCV学习笔记(五十一)--imge stitching图像拼接stitching stitching是OpenCV2.4.0一个新模块,功能是实现图像拼接,所有的相关函数都被封装在Stitch ...

  6. OpenCV学习笔记(四十一)——再看基础数据结构core OpenCV学习笔记(四十二)——Mat数据操作之普通青年、文艺青年、暴力青年 OpenCV学习笔记(四十三)——存取像素值操作汇总co

    OpenCV学习笔记(四十一)--再看基础数据结构core 记得我在OpenCV学习笔记(四)--新版本的数据结构core里面讲过新版本的数据结构了,可是我再看这部分的时候,我发现我当时实在是看得太马 ...

  7. OpenCV学习笔记(三十六)——Kalman滤波做运动目标跟踪 OpenCV学习笔记(三十七)——实用函数、系统函数、宏core OpenCV学习笔记(三十八)——显示当前FPS OpenC

    OpenCV学习笔记(三十六)--Kalman滤波做运动目标跟踪 kalman滤波大家都很熟悉,其基本思想就是先不考虑输入信号和观测噪声的影响,得到状态变量和输出信号的估计值,再用输出信号的估计误差加 ...

  8. OpenCV学习笔记(三十一)——让demo在他人电脑跑起来 OpenCV学习笔记(三十二)——制作静态库的demo,没有dll也能hold住 OpenCV学习笔记(三十三)——用haar特征训练自己

    OpenCV学习笔记(三十一)--让demo在他人电脑跑起来 这一节的内容感觉比较土鳖.这从来就是一个老生常谈的问题.学MFC的时候就知道这个事情了,那时候记得老师强调多次,如果写的demo想在人家那 ...

  9. OpenCV学习笔记(二十六)——小试SVM算法ml OpenCV学习笔记(二十七)——基于级联分类器的目标检测objdect OpenCV学习笔记(二十八)——光流法对运动目标跟踪Video Ope

    OpenCV学习笔记(二十六)--小试SVM算法ml 总感觉自己停留在码农的初级阶段,要想更上一层,就得静下心来,好好研究一下算法的东西.OpenCV作为一个计算机视觉的开源库,肯定不会只停留在数字图 ...

最新文章

  1. 机器学习特征工程之连续变量离散化:连续变量二值化(Binarizer)
  2. 使Mybatis开发变得更加轻松的增强工具 — Ourbatis
  3. 网易云信欢乐颂(送),领取“五美”送麻麻
  4. Eclipse配置工程自动执行ant实现热部署
  5. Jeecg-Boot前后端分离,针对敏感数据,加密传递方案
  6. mysql循环建表_mysql创建存储过程,批量建表分表00到99
  7. [数据结构与算法] 单链表的简单demo
  8. 数学特级老师:数学除了做习题,这份140G的资料一定要收藏!
  9. Python中几个有趣的函数
  10. python编程入门指南-Python入门学习指南
  11. grafana默认用户名密码_Grafana安装与配置
  12. [原创]Linux下网络性能测试Netperf工具介绍及安装
  13. 使用fileupload实现文件上传
  14. JAVA 计算小数位数
  15. 《死神》现队长、原队长和假面的对照
  16. MySQL 主从同步延迟的原因及解决办法
  17. 服务器文件夹怎么找回来,文件过期了怎么恢复(教你一招找回微信过期文件)...
  18. Android 配置文件锁设置
  19. heavy r.com index.php,AngularJS - Computation-Heavy Tasks
  20. 上海工程技术大学c语言试卷,上海工程技术大学2009_2010C语言试卷A.doc

热门文章

  1. 解除同居关系时共同财产的分割
  2. 风车网陈晓峰回忆录:我的两个月倒闭史
  3. Vue学习随笔+商城项目【上】
  4. 求二元函数最大值matlab,利用matlab, 二元函数求最大值
  5. CAD数据不通过ArcGIS导出为MDB
  6. Transformer课程 第8课NER案例代码笔记-IOB标记
  7. android 截取验证码的两种实现方式
  8. 学习python必备的软件
  9. 小程序开发笔记(二):微信小程序富文本编辑器editor的使用
  10. eel+python 开发html5跨平台桌面应用1