OpenCV+Python图像处理基础(二)

文章目录

      • 1 opencv安装
      • 2 图像基本操作
      • 3 阈值与平滑(模糊)处理
      • 4 腐蚀和膨胀
      • 5 图像梯度处理
      • 6 边缘检测
      • 7 图像金字塔、轮廓检测与模板匹配
        • 7.1 图像金字塔
        • 7.2 轮廓与边缘检测
        • 7.3 模板匹配

本文知识点参考唐宇迪老师视频教程

1 opencv安装

(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)

2 图像基本操作

(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

3 阈值与平滑(模糊)处理

(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)
# 膨胀恰好相反

OpenCV+Python图像处理基础(二)_第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文件夹用来存放图片

4 腐蚀和膨胀

(1)这两者操作操作通常是对二值图像进行操作

(2)腐蚀:线条变细,毛刺消失(迭代次数为1),卷积核和迭代次数增加腐蚀强度增加。腐蚀和膨胀图分别如下:

OpenCV+Python图像处理基础(二)_第2张图片
OpenCV+Python图像处理基础(二)_第3张图片

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)

5 图像梯度处理

(1)图像梯度处理针对灰度图像

(2)边界位置产生梯度,数值相差大,梯度大,相当于边缘检测,图像梯度处理后边缘为白色

(3)sobel算子

  • 距离远近来决定所占权重,距离近比重大为2,远为1。
  • 左右像素差异值就当成水平梯度,上下像素差异值就当成竖直梯度。
  • sobel的核一般是3*3

OpenCV+Python图像处理基础(二)_第4张图片

  • 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")

(4)Scharr算子
相比Sobel算子,Scharr算子数值变大,对边缘的差异更加敏感,读出更多细节。
OpenCV+Python图像处理基础(二)_第5张图片

(5)Laplacian算子

  • 推到过程中用到二阶导,对变化更敏感。
  • 当前点和周围点,若出现浮动,则遇到边界点。对噪音点也很敏感不好,单独使用效果不是特别好,需要和其他算子配合使用。

OpenCV+Python图像处理基础(二)_第6张图片

(6)三种算子(sobel, scharr, laplacian三种算子一次比较)

OpenCV+Python图像处理基础(二)_第7张图片

# 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)

6 边缘检测

canny算法

(1) 使用高斯滤波器,平滑图像滤除噪声(高斯滤波器归一化处理)

  • 噪音点也发生梯度变化,使用高斯滤波器。

(2)计算图像中每个像素点的梯度强度和方向,梯度方向和边界方向是垂直的关系。

  • 有原始图和梯度图,原始图全是像素值,梯度图全是梯度值(梯度值有方向),非极大值抑制就是在梯度图上做的

(3)应用非0极大值(NMS)抑制,来消除边缘检测带来的杂散响应

  • 判断当前点是不是极大值,用它的梯度与当前临近的点比较,但梯度方向上不一定正好有值,可以把梯度的角度往0和90度方向近的靠,和近的比较。

OpenCV+Python图像处理基础(二)_第8张图片
OpenCV+Python图像处理基础(二)_第9张图片

比较当前两个点和周围点梯度大小,如果最大则保存下来

(4)应用双阈值检测来确定真实的和潜在的边缘

OpenCV+Python图像处理基础(二)_第10张图片

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)

7 图像金字塔、轮廓检测与模板匹配

7.1 图像金字塔

高斯金字塔

下采样,图像长宽缩小一半

OpenCV+Python图像处理基础(二)_第11张图片

上采样,图像长宽放大一半

OpenCV+Python图像处理基础(二)_第12张图片

可以对彩色图像直接进行采样。

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)))  # 这是第一层的结果

OpenCV+Python图像处理基础(二)_第13张图片

7.2 轮廓与边缘检测

轮廓是连在一起,边缘是零零散散的,轮廓检测针对二值图像来做。

轮廓显示: NONE输出所有点,SIMPLE只保留他们的终点。

OpenCV+Python图像处理基础(二)_第14张图片

   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。

轮廓近似

  • 曲线近似为直线。

  • 原理:

OpenCV+Python图像处理基础(二)_第15张图片

上图,AB曲线之间,连接成一条直线,在曲线上找一点C使得到AB直线的距离d最远,若d小于设定阈值T,则AB曲线可以用AB直线表示,如果d大于设定阈值,则需要将AB继续分段,如下图。就像一直做二分,把曲线分成尽可能少的线段。

OpenCV+Python图像处理基础(二)_第16张图片
OpenCV+Python图像处理基础(二)_第17张图片

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)

OpenCV+Python图像处理基础(二)_第18张图片

OpenCV+Python图像处理基础(二)_第19张图片
OpenCV+Python图像处理基础(二)_第20张图片

7.3 模板匹配

拿到模板窗口,从左到右,从上到下进行匹配,匹配程度计算,逐个比较对应位置像素点的差异(比如做减法,再平方等等)一个一个匹配,会返回每个窗口匹配返回值。如果内容越像,平方项越小,相关系数越大。

src = cv2.imread("pic/0.jpg", 0),直接读入灰度图

OpenCV+Python图像处理基础(二)_第21张图片

OpenCV+Python图像处理基础(二)_第22张图片

找到最小值点的坐标,然后根据模板的x,y长度来找到人脸。

OpenCV+Python图像处理基础(二)_第23张图片

尽量用归一化的来,得到的结果更加可靠一些。

返回的res 中min_loc代表每一个滑动窗口左上角坐标的值,min_val代表损失

不同算法,注意使用的是min还是max

对比6种方法

[外链图片转存失败(img-ofjkhZr2-1564965877934)(assets/OpenCV+Python图像处理基础(二)_第24张图片
做人脸检测:找最合适的多个位置(找多个人脸)

多模板匹配,自己选择一个合适的范围值,设定一个阈值。返回每一个窗口结果值,结

OpenCV+Python图像处理基础(二)_第25张图片

(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)]```

你可能感兴趣的:(OpenCV系列)