本文知识点参考唐宇迪老师视频教程
(1) 下载Anaconda(官网)安装python3
(2) 安装完成后,进入终端,pip list
查看是否有opencv和opencv-contrib-python这两个包(最好是在3.4.2之前的包,3.4.2之后有些包申请了专利没有办法使用),如果这两个包没有,则使用:
- 法1:pip install 包名,来安装(如果不行,使用法2)
- 法2:https://www.lfd.uci.edu/~gohlke/pythonlibs/ 到该网站去找包对应的whl文件,下载该文件,终端进入该文件下载的目录,pip install 包.whl,安装该包
(3) 确认这两个包有后,执行pip install opencv==3.4.1.15*(对应自己包版本号)*,
进入Anaconda/Scripts执行opencv-contrib-python==3.4.1.15
(4) 安装完成后,在终端输入,python,import cv2, cv2.__version–(把后面的–替换成下划线),
如果正确则输出版本(3.4.1)
(1) cv2.waitkey(0) 按下键盘任意键关闭窗口
(2) opencv颜色BGR ,cv2.imshow(“img”,img)能正确显示图片。用plt显示cv2读取出来的的图像时,存在BGR和RGB问题,plt显示红色图片为蓝色。 需要转换一下, 解决方法如下:
b,g,r = cv2.split(img)
或者用下面的方法:b = img[:, :, 0] g = img[:, :, 1] r = img[:, :, 2]
img_rgb = cv2.merge([r,g,b])
(3)cv2.imread(“a.jpg”, cv2.IMREAD_GRAYSCALE) # 读取灰度图,默认读取彩色图;要变成灰度图可以使用cv2.cvtColor
(4)视频显示,如下代码:
ret, frame = capture.read("视频路径")
if ret == False:
break
# 或者 这种方式退出while循环
if frame is None:
break
if cv2.waitKey(10) & 0xFF == 27:
# 等待多少时间处理下一帧(每一帧之间的间隔时间),时间越长,视频播放越慢
break
# 或者
c = cv2.waitKey(10)
if c == 27: # 按Esc退出
break
(5)矩阵加法
- a1 = cv2.imread(“a.jpg”) # 读进来的是矩阵可直接进行相加a1 + 10
- b1 = cv2.imread(“b.jpg”)
- numpy相加 (a1 + b1)若相加溢出,读进来的图片的dtype是uint8,则除以256取余数 294/256 = 38
- cv2相加 cv2.add(a1, b1)这种相加245+90,溢出就为255
(6)边界填充,代码如下
import cv2
import matplotlib.pyplot as plt
src = cv2.imread("pic/0.jpg")
b, g, r = cv2.split(src)
src = cv2.merge((r, g, b)) # 因为用到了plt显示,所以用需要换成rgb模式。
top_size, bottom_size, left_size, right_size = (50, 50, 50, 50)
img1 = cv2.copyMakeBorder(src, top_size, bottom_size, left_size, right_size, borderType=cv2.BORDER_REPLICATE)
# 复制法,复制最边缘像素
img2 = cv2.copyMakeBorder(src, top_size, bottom_size, left_size, right_size, borderType=cv2.BORDER_REFLECT)
# 反射法,对感兴趣的图像中的像素在两边进行复制例如:fedcba|abcdefgh|hgfedcb
img3 = cv2.copyMakeBorder(src, top_size, bottom_size, left_size, right_size, borderType=cv2.BORDER_REFLECT_101)
# 反射法,也就是以最边缘像素为轴,对称,gfedcb|abcdefgh|gfedcba
img4 = cv2.copyMakeBorder(src, top_size, bottom_size, left_size, right_size, borderType=cv2.BORDER_WRAP)
# 外包装法cdefgh|abcdefgh|abcdefg
img5 = cv2.copyMakeBorder(src, top_size, bottom_size, left_size, right_size, borderType=cv2.BORDER_CONSTANT, value=(0,0,255))
# 常量法,常数值填充,默认填充为黑色。value=(0, 0, 255)这个是填充蓝色,按照RGB来的
fig = plt.figure()
ax1 = fig.add_subplot(231)
ax2 = fig.add_subplot(232)
ax3 = fig.add_subplot(233)
ax4 = fig.add_subplot(234)
ax5 = fig.add_subplot(235)
ax1.imshow(src)
ax2.imshow(img2)
ax3.imshow(img3)
ax4.imshow(img4)
ax5.imshow(img5)
plt.show()
(9)图像缩放
cv2.resize(img, (500, 400)) # 这个500代表的是宽,400代表高,img的shape为(400, 500)
cv2.resize(img, (0, 0), fx=2, fy=3) # 横轴(宽)放大2倍,纵轴(高)放大3倍
(10)两张图像混合(要保证两张图片大小相同)
cv2.addWeighted(img1, 0.4, img2, 0.6, 10) 第一站图像素占0.4,第二张图像素占0.6,整体亮度调亮10
(1)二值化、腐蚀和膨胀
src = cv2.imread("pic/ss_crop.jpg")
src_gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
ret, src_bi = cv2.threshold(src_gray, 45, 256, cv2.THRESH_BINARY)
# ret返回值一定不能少,ret返回的是阈值
kernel = np.ones((3, 3), dtype=np.uint8)
src_erode = cv2.erode(src_bi, kernel=kernel, iterations=1)
# 注意腐蚀是1的范围变小,所以是黑色腐蚀白色,在显示人脸时,脸通常是白色,所以会变小,头发眉毛会变粗。
src_dilate = cv2.dilate(src_bi, kernel=kernel, iterations=1)
# 膨胀恰好相反
(2)方框滤波
cv2.boxFilter(img, -1, (3, 3), normalize=True)
方框滤波和均值滤波类似。
当normalize为Ture时,就是均值滤波,当normalize为False时,就是所有元素相加,会发生越界,大于255就为255,normalize就是归一化,如果设置为True和均值滤波效果一样,相当于除以9。(3)矩阵的stack与hstack
src.shape为(495,500,3)
np.stack([src, src], axis=0).shape 为(2, 495, 500, 3)#axis为0,就是最外层拼接
np.hstack((src, src)).shape为(495, 1000,3)
4 人脸二值图阈值变化导致的二值图像不同
视频的合成?? 参考网址:https://blog.csdn.net/github_39611196/article/details/83242370
# 显示人脸图像变化,类似于视频
src = cv2.imread("pic/ss_crop.jpg")
src = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
for i in range(256):
ret, thresh_src1 = cv2.threshold(src, i, 255, cv2.THRESH_BINARY)
cv2.imshow("thresh_src1", thresh_src1)
cv2.waitKey(100)
cv2.waitKey(0)
补充:
1 typora mac版本有本地自动上传,win没有自动上传图床。所以无法获取url,只有本地目录。
2 在”文件-偏好设置-图片插入“里面勾选了复制到./assets文件,这样typora生成md文件时,会在md文件所在的目录下生成assets文件夹用来存放图片
(1)这两者操作操作通常是对二值图像进行操作
(2)腐蚀:线条变细,毛刺消失(迭代次数为1),卷积核和迭代次数增加腐蚀强度增加。腐蚀和膨胀图分别如下:
src = cv2.imread("pic/ss_crop.jpg")
src_gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
ret, src_bi = cv2.threshold(src_gray, 45, 256, cv2.THRESH_BINARY) # ret返回值一定不能少,ret返回的是阈值
kernel = np.ones((3, 3), dtype=np.uint8)
src_erode = cv2.erode(src_bi, kernel=kernel, iterations=1) # 注意腐蚀是1的范围变小,所以是黑色腐蚀白色,在显示人脸时,脸通常是白色,所以会变小,头发眉毛会变粗。
src_dilate = cv2.dilate(src_bi, kernel=kernel, iterations=1) # 膨胀恰好相反
(3)其他运算:
开运算:先腐蚀再膨胀
闭运算: 先膨胀再腐蚀
梯度运算: 膨胀-腐蚀
对圆而言,先膨胀后腐蚀,会胖一些
对圆而言,先腐蚀后膨胀,会瘦一些
- 顶帽:原始输入-开运算结果(剩下刺)顶帽操作,突出更明亮的区域
- 黑帽:闭运算结果-原始输入(剩下原始小轮廓)
ret, src_bi = cv2.threshold(src_gray, 127, 256, cv2.THRESH_BINARY)
kernel = np.ones((3, 3), np.uint8) # 可以变换大小试试看效果,比如(5, 5)
src_open = cv2.morphologyEx(src_bi, cv2.MORPH_CLOSE, kernel=kernel)
src_close = cv2.morphologyEx(src_bi, cv2.MORPH_OPEN, kernel=kernel)
src_gradient = cv2.morphologyEx(src_bi, cv2.MORPH_GRADIENT, kernel=kernel)
src_tophat = cv2.morphologyEx(src_bi, cv2.MORPH_TOPHAT, kernel=kernel)
src_blackhat = cv2.morphologyEx(src_bi, cv2.MORPH_BLACKHAT, kernel=kernel)
(1)图像梯度处理针对灰度图像
(2)边界位置产生梯度,数值相差大,梯度大,相当于边缘检测,图像梯度处理后边缘为白色
(3)sobel算子
- 距离远近来决定所占权重,距离近比重大为2,远为1。
- 左右像素差异值就当成水平梯度,上下像素差异值就当成竖直梯度。
- sobel的核一般是3*3
- ddepth输入图像和输出图像深度是一样的,dx和dy分别表示水平和竖直方向,ksize为Sobel算子的大小
- cv2.CV_64F 把位数变得更高了,能表示负数的形式。(CV_64FC1:64F代表每一个像素点元素占64位浮点数,通道数为1)
- x, y分开算,再合并在一起
src = cv2.imread("pic/s.jpg")
src_gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
cv2.imshow("src_gray", src_gray)
# src_sobel_x = cv2.Sobel(src_gray, -1, 1, 0, ksize=3) 这样在左减去右的时候,会出现边缘为负,边缘不显示
# 由于显示范围为(0,255),这样出现的负数会被截成0,所以需要cv2.CV_64F
src_sobel_x = cv2.Sobel(src_gray, cv2.CV_64F, 1, 0, ksize=3)
src_sobel_y = cv2.Sobel(src_gray, cv2.CV_64F, 0, 1, ksize=3)
# cv2.CV_64F加上这个后,数据表示范围变广,这样就会在返回的src_sobel中出现负数,而且负数的范围满足Float64
# src_sobel_2,该图片出现最小值为-974
src_sobel_x = cv2.convertScaleAbs(src_sobel_x)
src_sobel_y = cv2.convertScaleAbs(src_sobel_y) # 对矩阵取绝对值,最大值为255
# cv2.convertScaleAbs作用,先对元素取绝对值,再将其转回原来的uint8形式
src_sobel_xy = cv2.addWeighted(src_sobel_x, 0.5, src_sobel_y, 0.5, 0)
cv2.imshow("src_sobel_x", src_sobel_x)
cv2.imshow("src_sobel_y", src_sobel_y)
cv2.imshow("src_sobel_xy", src_sobel_xy)
cv2.waitKey(0)
# 另一种实现形式
gradX = cv2.Sobel(tophat, ddepth=cv2.CV_32F, dx=1, dy=0, ksize=-1) # ksize=-1相当于用3*3的
cv_show('gradX1', gradX)
gradX = np.absolute(gradX) # 这个是(0,3040)之间的浮点数
(minVal, maxVal) = (np.min(gradX), np.max(gradX))
gradX = (255 * ((gradX - minVal) / (maxVal - minVal))) # 这个是(0,255)之间的浮点数
gradX = gradX.astype("uint8")
(5)Laplacian算子
- 推到过程中用到二阶导,对变化更敏感。
- 当前点和周围点,若出现浮动,则遇到边界点。对噪音点也很敏感不好,单独使用效果不是特别好,需要和其他算子配合使用。
(6)三种算子(sobel, scharr, laplacian三种算子一次比较)
# scharr算子
src_scharr_x = cv2.Scharr(src_gray, cv2.CV_64F, 1, 0)
src_scharr_y = cv2.Scharr(src_gray, cv2.CV_64F, 0, 1)
src_scharr_x_1 = cv2.convertScaleAbs(src_scharr_x)
src_scharr_y_1 = cv2.convertScaleAbs(src_scharr_y)
src_scharr = cv2.addWeighted(src_scharr_x_1, 0.5, src_scharr_y_1, 0.5, 0)
# laplacian算子
src_Lap = cv2.Laplacian(src_gray, cv2.CV_64F)
src_Lap_1 = cv2.convertScaleAbs(src_Lap)
cv2.imshow("src_Lap_1", src_Lap_1)
cv2.waitKey(0)
canny算法
(1) 使用高斯滤波器,平滑图像滤除噪声(高斯滤波器归一化处理)
- 噪音点也发生梯度变化,使用高斯滤波器。
(2)计算图像中每个像素点的梯度强度和方向,梯度方向和边界方向是垂直的关系。
- 有原始图和梯度图,原始图全是像素值,梯度图全是梯度值(梯度值有方向),非极大值抑制就是在梯度图上做的
(3)应用非0极大值(NMS)抑制,来消除边缘检测带来的杂散响应
- 判断当前点是不是极大值,用它的梯度与当前临近的点比较,但梯度方向上不一定正好有值,可以把梯度的角度往0和90度方向近的靠,和近的比较。
比较当前两个点和周围点梯度大小,如果最大则保存下来
(4)应用双阈值检测来确定真实的和潜在的边缘
minval小,条件放松,检测的边界多(但不一定全是边界),反之大,标准高
maxval,越小,标准低,越大,标准高
(5)通过抑制孤立的弱边缘最终完成边缘检测
src = cv2.imread("pic/ss_crop.jpg")
src_gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
src_canny_1 = cv2.Canny(src, 100, 200) # 100为minval,值越小标准越低,检测线条越多,200为maxval,值越大标准越高,检测的线条越少
src_canny_2 = cv2.Canny(src, 50, 150)
src_canny = np.hstack((src_canny_1, src_canny_2))
cv2.imshow("src_canny", src_canny)
cv2.waitKey(0)
高斯金字塔
下采样,图像长宽缩小一半
上采样,图像长宽放大一半
可以对彩色图像直接进行采样。
src = cv2.imread("pic/ss_crop.jpg")
src_gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
src_down = cv2.pyrDown(src)
src_up = cv2.pyrUp(src)
src_up_down = cv2.pyrDown(cv2.pyrUp(src))
cv2.imshow("src", src) # (640, 540, 3)
cv2.imshow("src_up", src_up) # (1280, 1080, 3)
cv2.imshow("src_down", src_down) # (320, 270, 3)
cv2.imshow("src_up_down", src_up_down) # (640, 540, 3) 先上采样再下采样,得到的图像没有原始图像那么清楚。up和down都要损失值。
# 不能np.hstack合在一起显示,因为图片的大小不一样,矩阵大小不一样无法合并
拉普拉斯金字塔
src = cv2.imread("pic/ss_crop.jpg")
src_gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
cv2.imshow("1", src - cv2.pyrUp(cv2.pyrDown(src))) # 这是第一层的结果
轮廓是连在一起,边缘是零零散散的,轮廓检测针对二值图像来做。
轮廓显示: NONE输出所有点,SIMPLE只保留他们的终点。
src = cv2.imread("pic/s.jpg")
src_gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
ret, src_bi = cv2.threshold(src_gray, 127, 256, cv2.THRESH_BINARY)
binary, contours, hierarchy = cv2.findContours(src_bi, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
print(src_bi == binary) # 全是True,说明binary就是,src_bi
src1 = src.copy() # 需要复制一下原图,否则在原图上画轮廓的时候会改变原图
res = cv2.drawContours(src1, contours, -1, (255, 0, 0), 2)
# -1表示画出所有的轮廓,若-1换成是0,1,2..就是画出所有图像的边缘。也可以将contours换成contours[0],contours[1]来显示第几张图
cont = contours[0] # 0表示第0个特征,将具体的每一个轮廓
print(cv2.contourArea(cont)) # 计算面积
print(cv2.arcLength(cont, True)) # 计算周长,True表示闭合
cv2.imshow("src", src)
cv2.imshow("res", res)
draw = img.copy()复制出新的东西,draw=img是同一个东西,但是属于不同变量,不同于draw=img。
轮廓近似
曲线近似为直线。
原理:
上图,AB曲线之间,连接成一条直线,在曲线上找一点C使得到AB直线的距离d最远,若d小于设定阈值T,则AB曲线可以用AB直线表示,如果d大于设定阈值,则需要将AB继续分段,如下图。就像一直做二分,把曲线分成尽可能少的线段。
epsilon越小,轮廓变化越小。
src2 = src.copy()
res = cv2.drawContours(src1, contours, -1, (255, 0, 0), 2)
cont = contours[1] # 0表示第0个特征
x, y, w, h = cv2.boundingRect(cont)
res1 = cv2.rectangle(src2, (x, y), (x+w, y+h), (255, 0, 0), 2)
cv2.imshow("res1", res1)
拿到模板窗口,从左到右,从上到下进行匹配,匹配程度计算,逐个比较对应位置像素点的差异(比如做减法,再平方等等)一个一个匹配,会返回每个窗口匹配返回值。如果内容越像,平方项越小,相关系数越大。
src = cv2.imread("pic/0.jpg", 0),直接读入灰度图
找到最小值点的坐标,然后根据模板的x,y长度来找到人脸。
尽量用归一化的来,得到的结果更加可靠一些。
返回的res 中min_loc代表每一个滑动窗口左上角坐标的值,min_val代表损失
不同算法,注意使用的是min还是max
对比6种方法
[外链图片转存失败(img-ofjkhZr2-1564965877934)(assets/
做人脸检测:找最合适的多个位置(找多个人脸)
多模板匹配,自己选择一个合适的范围值,设定一个阈值。返回每一个窗口结果值,结
(1) np.where
res = np.where(A > 2, 1, 0) # A种大于2的为1,小于等于2的为0 # [0 0 1 1]
np.where在这里返回res中值大于0.8的所有坐标,如:
x = np.arange(9).reshape(3, 3) print(np.where(x > 5)) # 结果(先y坐标,再x坐标):(array([2, 2, 2]), array([0, 1, 2]))```
(2)zip,[::-1]
x = [1, 2, 3] y = [4, 5, 6] a = list(zip(x, y)) print(a) # [(1, 4), (2, 5), (3, 6)] print(a[::-1]) # [(3, 6), (2, 5), (1, 4)]```