文章目录

  • OpenCV-Python计算机视觉入门
  • 开发环境
  • 一、入门基础
    • 1.演示
    • 2.图像的读取、显示和保存
  • 二、图像处理基础
    • 1.图像处理入门基础
    • 2.图像像素处理
    • 3.使用numpy访问像素
    • 4.获取像素属性
    • 5.感兴趣区域ROI
    • 6.通道的拆分和合并
  • 三、图像运算
    • 1.图像加法
    • 2.图像融合
  • 四、类型转换
    • 1.类型转换
  • 五、几何变换
    • 1.图像缩放
    • 2.图像旋转
  • 六、阈值分割
    • 1.基础理论
    • 2.threshold函数
  • 七、图像平滑处理
    • 1.均值滤波
    • 2.方框滤波
    • 3.高斯滤波
    • 4.中值滤波
  • 八、形态学处理
    • 1.图像腐蚀
    • 2.图像膨胀
    • 3.开运算
    • 4.闭运算
    • 5.梯度运算
    • 6.礼帽运算
    • 7.黑帽运算
  • 九、图像梯度
    • 1.sobel算子理论基础
    • 2.sobel算子及其函数使用
    • 3.scharr算子及其函数使用
    • 4.sobel算子和scharr算子的比较
    • 5.laplacian算子的使用
  • 十、边缘检测
    • 1.Canny边缘检测原理
    • 2.Canny函数及使用
  • 十一、图像金字塔
    • 1.基础理论
    • 2.pyrDown函数及使用
    • 3.pyrUp函数及使用
    • 4.取样可逆性研究
    • 5.拉普拉斯金字塔
  • 十二、图像轮廓
    • 1.图像轮廓
  • 十三、直方图
    • 1.直方图的概念
    • 2.绘制直方图
    • 3.使用OpenCV统计直方图
    • 4.绘制OpenCV统计直方图
    • 5.使用掩膜直方图
    • 6.掩膜原理及演示
    • 7.直方图均衡化原理
    • 8.直方图均衡化函数
    • 9.subplot函数的使用
    • 10.imshow函数的使用
    • 11.直方图均衡化对比
  • 十四、傅里叶变换
    • 1.傅里叶变换的理论基础
    • 2.numpy实现傅里叶变换
    • 3.numpy实现逆傅里叶变换
    • 4.高通滤波演示
    • 5.OpenCV实现傅里叶变换
    • 6.OpenCV实现逆傅里叶变换
    • 7.低通滤波示例
  • 十五、其它
    • 1.PyCharm无法使用复制粘贴快捷键
    • 2.too many values to unpack

OpenCV-Python计算机视觉入门

开发环境

  • 开发工具:PyCharm 2020.1
  • Python 版本:3.7
  • OpenCV 版本:4.5.0
  • numpy 版本:1.19.4

OpenCV 官网:https://opencv.org/
OpenCV 中文网站:http://wiki.opencv.org.cn/index.php
Anacoda安装和使用:https://blog.csdn.net/u011424614/article/details/105579502
当前博文:https://blog.csdn.net/u011424614/article/details/112428533

一、入门基础

1.演示

  • 显示图像的例子
# 引入OpenCV
import cv2# 读取图片-与python文件相同目录
img = cv2.imread("image.png")
# 显示窗口命名
cv2.namedWindow("demo")
# 显示加载的图像
cv2.imshow("demo", img)
# 窗口保持显示(回车键可关闭窗口)
cv2.waitKey(0)
# 销毁所有窗口
cv2.destroyAllWindows()

2.图像的读取、显示和保存

1)读取图像

  • retval = cv2.imread(文件名 [,显示控制参数])

显示控制参数:

  • http://www.emgu.com/wiki/files/3.2.0/document/html/880eb761-0ba1-c412-13db-93d6f11aea3b.htm
  1. cv2.IMREAD_UNCHANGED:不改变,原始图像
  2. cv2.IMREAD_GRAYSCALE:灰度
  3. cv2.IMREAD_COLOR:彩色

2)显示图像

  • Nonc = cv2.imshow(窗口名, 图像变量名)
  • retval = cv2.waitKey([delay])

delay 说明:

delay > 0 :等待 delay 毫秒

delay < 0 :等待键盘单击

delay = 0 :无限等待

  • cv2.destroyAllWindows()

销毁所有窗口,清除内存的缓存

3)保存图像

  • retval = cv2.imwrite(文件保存位置和文件名, 图像变量名)
# 导入 OpenCV
import cv2# 读取图片-与python文件相同目录
img = cv2.imread("image.png", cv2.IMREAD_GRAYSCALE)
# 显示窗口命名
cv2.namedWindow("demo")
# 显示加载的图像
cv2.imshow("demo", img)
# 窗口保持显示(回车键可关闭窗口)
cv2.waitKey(0)
# 销毁所有窗口
cv2.destroyAllWindows()
# 保存图像
cv2.imwrite("D:\\demo.png", img)

二、图像处理基础

1.图像处理入门基础

1)图像是由像素构成的

  • 像素可以是方格或是点;相同面积下,像素越多,图像越清晰

2)图像分类

  • 二值图像:图像只有两个值,任何一个点非黑即白

  • 灰度图像:图像有黑白灰三种颜色,并分为 256 个颜色,0 为黑色,255 为白色,1 - 254 为不同程度的灰色

  • RGB 图像:

    1. 彩色图像,三原色 R [red 红色]、G [green 绿色]、B[blue 蓝色] ;

    2. 每个颜色都有 0 - 255 的数值,表示颜色的深浅;通过三种颜色,不同比例的混合,生成新的颜色

    3. OpenCV 中的通道顺序为:B G R,如某个像素用 OpenCV 读取为 (122, 87, 23) ,即 B = 122,G = 87,R = 23

  • RGB 图像 转 灰度图像:

    1. 目的是将彩色图原来任何一个像素都是3个值,转成灰度图变为任何一个像素都是1个值,方便图像处理
    2. 从彩色图转为灰度图后,原来RGB的数值,依旧保留在灰度图中
  • 灰度图像 转 二值图像

  • 二值图像数字化表示:

  • 灰度图数字化表示:

  • RGB 图像数字化表示:

  • RGB 图像构成的数字化表示:

2.图像像素处理

1)读取像素

  • 返回值 = 图像(位置参数)
  1. 灰度图像,返回灰度值;

    • 例如:p = img[88, 123] 表示 88 行、123 列的值
  2. BGR 图像,返回 B,G,R 的值;例如

    • 例如:blue = img[45, 123, 0] 表示 45行、123列和第 0 个通道(蓝色 Blue)的值;0,1,2 通道分别对应 B,G,R 三个通道
    • 例如:bgr = img[45, 123] 表示同时读取 45 行、123 列的 BGR 三个通道的值
    • 例如:bgra = img[45, 123] 示同时读取 45 行、123 列的 BGRA 四个通道的值,其中 A 表示图像的 alpha 通道

2)修改像素值

  • 像素 = 新值
  1. 灰度图像

    • 例如:img[11, 22] = 233 表示给图像 11 行、22 列的像素进行赋值 233
  2. BGR 图像
    • img[11, 22, 0] = 233 表示给 11 行、22 列和第 0 通道的像素进行赋值 233
    • img[11, 22] = [233, 233, 233]表示同时给 B,G,R 三个通道的 11行和22列进行赋值 233
# 导入 OpenCV
import cv2# 读取图片-与python文件相同目录, 灰度图
img = cv2.imread("image.png", cv2.IMREAD_GRAYSCALE)
# 读取 11行 - 22列 的值
print(img[11, 22])
# 修改 11行 - 22列 的值
img[11, 22] = 233
print(img[11, 22])print("========================================")
# 读取图片-与python文件相同目录, 原图-彩色图
img = cv2.imread("image.png", cv2.IMREAD_UNCHANGED)
# 读取 11行 - 22列 的值
print(img[11, 22])
# 修改 11行 - 22列 的 BGRA 值
img[11, 22] = [233, 233, 233, 233]
print(img[11, 22])print("========================================")
# 读取图片-与python文件相同目录, 原图-彩色图
img = cv2.imread("image.png", cv2.IMREAD_UNCHANGED)
# 读取 第 0 个通道 - 11行 - 22列 的值
print(img[11, 22, 0])
# 修改 第 0 个通道 - 11行 - 22列 的 BGRA 值
img[11, 22, 0] = 233
print(img[11, 22])print("========================================")
# 读取图片-与python文件相同目录, 原图-彩色图
img = cv2.imread("image.png", cv2.IMREAD_UNCHANGED)
# 显示原图
cv2.imshow("original", img)
# 修改 11行至16行 - 22列至28列 的 BGRA 值
img[11:16, 22:28] = [233, 233, 233, 233]
# 显示修改图
cv2.imshow("result", img)
cv2.waitKey(0)
cv2.destroyAllWindows()

3.使用numpy访问像素

1)读取像素

  • 返回值 = 图像.item(位置参数)
  1. 灰度图像,返回灰度值

    例如:img.item(11, 22) 表示读取 11 行、22 列的值

  2. BGR 图像,返回值 B,G,R 的值

    例如:blue = img.item(11, 22, 0) 表示读取第 0 个通道,11行、22 列的值

2)修改显示

  • 图像名.itemset(位置, 新值)
  1. 灰度图像

    例如:img.itemset((11, 22), 233) 表示给 11 行、22 列赋值为 233

  2. BRG 图像、

    例如:img.itemset((11, 22, 0), 233) 表示给第 0 个通道、11 行、22 列赋值为 233

# 导入 OpenCV
import cv2
# 导入 numpy
import numpy as np# 读取图片-与python文件相同目录, 灰度图
img = cv2.imread("image.png", cv2.IMREAD_GRAYSCALE)
# 读取 11行 - 22列 的值
print(img.item(11, 22))
# 修改 11行 - 22列 的值
img.itemset((11, 22), 233)
print(img.item(11, 22))print("========================================")
# 读取图片-与python文件相同目录, 原图-彩色图
img = cv2.imread("image.png", cv2.IMREAD_UNCHANGED)
# 读取 第 0 个通道 - 11行 - 22列 的值
print(img.item(11, 22, 0))
# 修改 第 0 个通道 - 11行 - 22列 的值
img.itemset((11, 22, 0), 233)
print(img.item(11, 22, 0))

4.获取像素属性

1)形状:行、列、通道数

  • shape 可以获取图像的形状,返回包含行数,列数,通道数的元组
  1. 灰度图像,返回行数,列数

    例如:img.shape 获取图像形状的元组,(600, 200) 表示图像有 600 行、200 列

  2. 彩色图像,返回行数,列数,通道数

    例如:img.shape 获取图像形状的元组和通道数,(600, 200, 3) 表示图像有 600 行、200 列、有 3 个通道

**2)像素数目 **

  • size 可以获取图像的像素数目
  1. 灰度图像,返回行数 * 列数

    例如:img.size 获取像素数目

  2. 彩色图像,返回行数 * 列数 * 通道数

3)图像的数据类型

  • dtype 可以获取每一个图像点的数据类型

    例如:img.dtype 返回 uint8 数据类型

# 导入 OpenCV
import cv2# 读取图片-与python文件相同目录, 原图
img = cv2.imread("image.png", cv2.IMREAD_UNCHANGED)
# 形状
print(img.shape)
# 像素数数目
print(img.size)
# 像素点的数据类型
print(img.dtype)print("========================================")
# 读取图片-与python文件相同目录, 灰度图
img = cv2.imread("image.png", cv2.IMREAD_GRAYSCALE)
# 形状
print(img.shape)
# 像素数数目
print(img.size)
# 像素点的数据类型
print(img.dtype)

5.感兴趣区域ROI

  • ROI (region of interest)感兴趣区域
  • 从被处理的图像,以方框、圆、椭圆、不规则多边形等方式勾勒出需要处理的区域
  • 可以通过各种算子(Operator)和函数来求得感兴趣区域 ROI,并进行图像的下一步处理

1)通过像素数位置,获取感兴趣的区域

  • 获取感兴趣区域
  • 将 ROI 图像显示在原图的左上角中
import cv2
import numpy as np# 读取图片-与python文件相同目录, 原图
img = cv2.imread("image.png", cv2.IMREAD_UNCHANGED)
# 显示图像
cv2.imshow("result-original", img)# 获取区域
roi = img[11:33, 22:44]
# 显示图像
cv2.imshow("result-roi", roi)# numpy 创建图像
roi = np.ones((22, 22, 3))
# 获取 ROI 图像
roi = img[11:33, 22:44]
# 显示图像
cv2.imshow("result-roi2", roi)# 将 ROI 图像显示在原图的左上角中
img[0:22, 0:22] = roi
# 显示图像
cv2.imshow("result", img)cv2.waitKey(0)
cv2.destroyAllWindows()

6.通道的拆分和合并

1)拆分通道

  • 将彩色图像分为B,G,R

    例如:b, g, r = cv2.split(img)

2)合并通道

  • 将B,G,R 通道合并为彩色图像

    例如:cv2.merge([b, g, r])

import cv2
import numpy as np# 读取图片-与python文件相同目录, 原图
img = cv2.imread("image.png", cv2.IMREAD_UNCHANGED)
# 拆分图像
b,g,r,a = cv2.split(img)
# 显示图像(可通过 shape 属性查看通道数)
cv2.imshow("B", b)
cv2.imshow("G", g)
cv2.imshow("R", r)
cv2.imshow("A", a)# 可获取单通道图像
b = cv2.split(img)[0]
g = cv2.split(img)[1]
r = cv2.split(img)[2]# numpy 创建零值通道
rows, cols, chn = img.shape
zero = np.zeros((rows, cols), dtype=img.dtype)# 合并绿色通道和零值通道
mg = cv2.merge([zero, g, zero])
cv2.imshow("merge-g", mg)# 合并通道
mbgr = cv2.merge([b, g, r])
cv2.imshow("merge-bgr", mbgr)
cv2.waitKey(0)
cv2.destroyAllWindows()

三、图像运算

1.图像加法

参与运算的图像大小、类型必须一致

1)取模加法

  • numpy 运算方式:结果 = 图像1 + 图像2

  • 像素值在十进制的情况下,最大值为 255,像素值相加后,有可能出现大于 255 的情况

    (1) 像素值 <= 255,直接使用结果

    (2) 像素值 > 255,需要结果对 255 取模,即【结果 % 255 = 最终结果】

2)饱和运算

  • opencv 运算方式:结果 = cv2.add(图像1, 图像2)

    (1) 像素值 <= 255,直接使用结果

    (2) 像素值 > 255,取最大值 255

import cv2
import numpy as np# 读取图片-与python文件相同目录, 原图
img = cv2.imread("image.png", cv2.IMREAD_UNCHANGED)
img2 = img
# numpy 加法(取模加法)
numpy_img = img + img2
# opencv 加法(饱和运算)
cv_img = cv2.add(img, img2)
# 显示图像
cv2.imshow("numpy_img", numpy_img)
cv2.imshow("cv_img", cv_img)cv2.waitKey(0)
cv2.destroyAllWindows()

2.图像融合

  • 将 2 张或 2 张以上的 图像融合为 1 张图像上

  • 融合的图像含有更多的信息,能够更方便人的观察和计算机处理

  • 例如:两张相同的图像,每张图片都在不同的位置出现模糊的情况,但是两张图像融合之后,还原出一张清晰的图像

  • 图像加法中,图像时按照 1:1 进行相加,而融合是需要调整系数后再相加

1)numpy 图像融合

  • 结果图像 = 图像1 * 系数1 + 图像2 + 系数2 + 亮度调节量

  • img = img1 * 0.3 + img2 * 0.7 + 18

2)opencv 图像融合

  • dst = cv2.addWeighted(img1, alpha, img2, beta, gamma)
  • 内部计算方式:dst = img1 * alpha + img2 * beta + gamma
  • 参数 gamma 不能省略,可传入 0 值
import cv2
import numpy as np# 读取图片-与python文件相同目录, 原图
img = cv2.imread("image.png", cv2.IMREAD_UNCHANGED)
img2 = cv2.imread("image2.png", cv2.IMREAD_UNCHANGED)
# numpy 融合
numpy_img = img * 0.3 + img2 * 0.7 + 10
# opencv 融合
cv_img = cv2.addWeighted(img, 0.3,img2, 0.7, 10)
# 显示图像
cv2.imshow("numpy_img", numpy_img)
cv2.imshow("cv_img", cv_img)cv2.waitKey(0)
cv2.destroyAllWindows()

四、类型转换

1.类型转换

  • 将图像有一种类型转换为另外一直类型

opencv 中提供了 200 多种不同类型直接的转换

  • 彩色图像转灰度图像:cv2.COLOR_BGR2GRAY
  • BGR 转 RGB:cv2.COLOR_BGR2RGB
  • 灰度转 BGR:cv2.COLOR_GRAY2BGR
import cv2# 读取图片-与python文件相同目录, 原图
img = cv2.imread("image2.png", cv2.IMREAD_UNCHANGED)
# 读取图片-与python文件相同目录, 灰度图
img2 = cv2.imread("image2.png", cv2.IMREAD_GRAYSCALE)
# 图像转换
b2g = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
b2r = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
g2b = cv2.cvtColor(img2, cv2.COLOR_GRAY2BGR)
# 输出形状信息
print(b2g.shape)
print(b2r.shape)
print(g2b.shape)
# 显示图像
cv2.imshow("b2g", b2g)
cv2.imshow("b2r", b2r)
cv2.imshow("g2b", g2b)cv2.waitKey(0)
cv2.destroyAllWindows()

五、几何变换

1.图像缩放

  • resize 函数

  • 语法格式:dst = cv2.resize(img, dsize) 参数为图像和缩放大小(列, 行)

    例如:dst = cv2.resize(img, (122, 122))

  • 语法格式:dst = cv2.resize(img, dsize, fx, fy) 参数 fx 表示水平缩放倍数,fy 表示垂直缩放倍数

    例如:dst = cv2.resize(img, None, fx=0.3, fy=0.6)

dsize 和 fx,xy 设置一个即可,dsize 优先

import cv2# 读取图片-与python文件相同目录, 原图
img = cv2.imread("image2.png", cv2.IMREAD_UNCHANGED)# 图像缩放(列,行)
rezie = cv2.resize(img, (100, 100))
# 图像缩放-倍数(行,列)
rezie2 = cv2.resize(img, None, fx=0.7, fy=0.7)
# 图像缩放-比例(列,行)
rows,cols = img.shape[:2]
rezie3 = cv2.resize(img, (round(cols * 0.8), round(rows * 0.8)))# 显示图像
cv2.imshow("rezie", rezie)
cv2.imshow("rezie2", rezie2)
cv2.imshow("rezie3", rezie2)
cv2.waitKey(0)
cv2.destroyAllWindows()

2.图像旋转

  • flip 函数

  • 语法:dst = cv2.flip(img, filipCode) 参数为图像 和翻转模式

  • 翻转模式:

    等于 0 表示以 X 轴进行对称翻转(上下翻转)

    大于 0 表示以 Y 轴进行对称轴翻转(左右翻转)

    小于 0 表示 在 X 轴和 Y 轴方向同时翻转(先上下,后左右)

import cv2# 读取图片-与python文件相同目录, 原图
img = cv2.imread("image2.png", cv2.IMREAD_UNCHANGED)# 上下翻转
x = cv2.flip(img, 0)
# 左右翻转
y = cv2.flip(img, 1)
# 上下、左右同时翻转
xy = cv2.flip(img, -1)# 显示图像
cv2.imshow("x", x)
cv2.imshow("y", y)
cv2.imshow("xy", xy)
cv2.waitKey(0)
cv2.destroyAllWindows()

六、阈值分割

1.基础理论

1)Value and Threshold Level 原始图像

  • 图像中有阈值、最大值和最小值

2)Threshold Binary 二进制阈值化

  • 将比阈值大的像素值处理为图像的最大值,将等于或小于阈值的像素值处理为最小值
  • 例如:图像中最大值为 255,最小值为 6,阈值为 99,经过二进制阈值化后,图像大于 99 的像素值处理为 255,等于或小于 99 的像数值处理为 6


3)Threshold Binary, Inverted 反二进制阈值化

  • 将比阈值大的像素值处理为图像的最小值,将等于或小于阈值的像素值处理为最大值
  • 例如:图像中最大值为 255,最小值为 6,阈值为 99,经过反二进制阈值化后,图像大于 99 的像素值处理为 6,等于或小于 99 的像数值处理为 255



4)Truncate 截断阈值化

  • 将大于阈值的像数值处理为阈值,而等于或小于阈值的像素值保持不变
  • 例如:设置阈值为 99,经过截断阈值化后,像素值大于 99 的像素值处理为 99,其它值保持不变



5)Threshold Zero,Inverted 反阈值化为 0

  • 将大于阈值的像数值处理为 0,而等于或小于阈值的像素值保持不变
  • 例如:设置阈值为 99,经过反阈值化为 0 后,图像大于 99 的像素值处理为 0,其它值保持不变



6)Threshold to Zero 阈值化为0

  • 将等于或小于阈值的像素值处理为 0,而大于阈值的像素值保持不变
  • 例如:设置阈值为 99,经过阈值化为 0 后,图像等于或小于 99 的像素值处理为 0,其它值保持不变



2.threshold函数

  • 语法:retval, dst = cv2.threshod(img, thresh, maxval, type)

    参数:retval=阈值,dst=处理结果,img=源图像,thresh=阈值,maxval=最大值,type=类型

type 类型 说明 简要
cv2.THRESH_BINARY 二进制阈值化 较亮的更亮,较暗的更暗
cv2.THRESH_BINARY_INV 反二进制阈值化 亮的变暗,暗的变亮
cv2.THRESH_TRUNC 截断阈值化 降低亮度
cv2.THRESH_TOZERO_INV 反阈值化为 0 亮的变黑
cv2.THRESH_TOZERO 阈值化为 0 暗的变黑
import cv2# 读取图片-与python文件相同目录, 原图
img = cv2.imread("image2.png", cv2.IMREAD_UNCHANGED)# 二进制二值化
th,binary = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
# 反二进制二值化
th,binary_inv = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV)
# 截断阈值化
th,trunc = cv2.threshold(img, 127, 255, cv2.THRESH_TRUNC)
# 反二值化为 0
th,tozero_inv = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO_INV)
# 二值化为 0
th,tozero = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO)# 显示图像
cv2.imshow("binary", binary)
cv2.imshow("binary_inv", binary_inv)
cv2.imshow("trunc", trunc)
cv2.imshow("tozero_inv", tozero_inv)
cv2.imshow("tozero", tozero)
cv2.waitKey(0)
cv2.destroyAllWindows()

七、图像平滑处理

1.均值滤波

  • 图像上,任意一点的像素值,都是周围 N*N 个像素值的均值

    例如:下图中,对红色点进行均值滤波处理,会由周围 5*5 的所有像数值相加(包括红点的像数值),然后除以25,最终得到红色点的均值

  • 周围的 N*N 个像素称为 核

    红点周围的 5*5 个点,每个点的像素值乘以 1/25,然后所有值相加,得到红点的均值

  • 均值滤波图像:对图像内的所有像素点,逐个采用核进行处理,得到结果图像

  • blur 函数

    语法:reval = cv2.blur(img, ksize)

    参数:img 表示图像;ksize 表示核大小,以元组表示 (宽度, 高度)

  • 效果:可将带雪花白点干扰的图像,处理为平滑无干扰的图像

import cv2# 读取图片-与python文件相同目录, 原图
img = cv2.imread("image2.png", cv2.IMREAD_UNCHANGED)# 均值滤波
bval = cv2.blur(img, (5, 5))# 显示图像
cv2.imshow("bval", bval)
cv2.waitKey(0)
cv2.destroyAllWindows()

2.方框滤波

  • boxFilter 函数

    语法:reval = cv2.boxFilter(img, depth, ksize, normalize)

    参数:reval 表示处理结果,img 表示图像,depth 表示目标图像的深度( -1 表示与原始图像一致),ksize 表示核大小,normalize 表示归一化属性

  • normalize = 1 表示与均值滤波的处理相同,normalize = 0 表示核内的像素值相加(255 白色),可以控制核大小,避免图像空白

import cv2# 读取图片-与python文件相同目录, 原图
img = cv2.imread("image2.png", cv2.IMREAD_UNCHANGED)# 方框滤波,不做归一化处理
bval0 = cv2.boxFilter(img, -1, (2, 2), normalize = 0)
# 方框滤波,归一化处理
bval1 = cv2.boxFilter(img, -1, (5, 5), normalize = 1)# 显示图像
cv2.imshow("bval0", bval0)
cv2.imshow("bval1", bval1)
cv2.waitKey(0)
cv2.destroyAllWindows()

3.高斯滤波

  • 让临近的像素具有更高的重要度,图像上某一点,对周围像素计算加权平均值,距离点较近的像素具有较大的权重值

    例如:对红点进行高斯滤波处理,在 3*3 的核中,距离红点最近的像素是上下左右的像素,所以权重值最大,其它像素较远,所以权重值会减少,最终红点值为核内所有的像素值乘以权重值之和

  • GaussianBlur 函数

    语法:reval = cv2.GaussianBlur(img, ksize, sigmaX)

    参数:img 表示图像,ksize 表示核大小,必须为奇数,sigmaX 表示X方向方差,控制权重【sigmaX=0 时,sigmaX = 0.3 * ((ksize-1) * 0.5 - 1) + 0.8】,Y方向方差默认与X方向方差一致

import cv2# 读取图片-与python文件相同目录, 原图
img = cv2.imread("image2.png", cv2.IMREAD_UNCHANGED)# 高斯滤波
reval = cv2.GaussianBlur(img, (5, 5), 0)# 显示图像
cv2.imshow("reval", reval)
cv2.waitKey(0)
cv2.destroyAllWindows()

4.中值滤波

  • 对核内的像素值进行排序,去排序中间位置的像数值作为中值滤波的像数值

  • medianBlur 函数

    语法: reval = cv2.medianBlur(img, ksize)

    参数:img 表示图像;ksize 表示核大小,必须是比 1 大的奇数

import cv2# 读取图片-与python文件相同目录, 原图
img = cv2.imread("image2.png", cv2.IMREAD_UNCHANGED)# 高斯滤波
reval = cv2.medianBlur(img, 5)# 显示图像
cv2.imshow("reval", reval)
cv2.waitKey(0)
cv2.destroyAllWindows()

八、形态学处理

  • 形态学处理一般只要针对的是 二值图像
  • 一般有两个输入对象,二值图像和卷积核

1.图像腐蚀

  • 图像中,以卷积核的中心点为中心,个像素扫描原始图像,如果卷积核覆盖的区域全都是 1(白色),则保留 1,如果卷积核覆盖的区域有 0(黑色),则将和中心点的像素设为 0

  • 效果:将粗线条处理为细线条,或者去除毛边

  • erode 函数

    语法:reval = cv2.erode(img, kernel, iterations)

    参数:img 表示图像;kernel 表示卷积核;iterations 表示迭代次数,默认 1 次腐蚀

import cv2
import numpy as np# 读取图片-与python文件相同目录, 原图
img = cv2.imread("image2.png", cv2.IMREAD_GRAYSCALE)# 创建 5*5 值为 1 的卷积核
kernel = np.ones((5, 5), np.uint8)
# 腐蚀处理
reval = cv2.erode(img, kernel)# 显示图像
cv2.imshow("reval", reval)
cv2.waitKey(0)
cv2.destroyAllWindows()

2.图像膨胀

  • 膨胀是腐蚀的逆操作
  • 一般的,可以先用腐蚀处理图像,去除噪点,再使用膨胀处理,使其恢复原来形状(以去除噪点)

  • 图像中,以卷积核的中心点为中心,个像素扫描原始图像,如果卷积核覆盖的区域有 1(白色),则设置为 1,如果卷积核覆盖的区域全都是 0(黑色),则将和中心点的像素保留为 0

  • dilate 函数

    语法:reval = cv2.dilate(img, kernel, iterations)

    参数:img 表示图像;kernel 表示卷积核;iterations 表示迭代次数

import cv2
import numpy as np# 读取图片-与python文件相同目录, 原图
img = cv2.imread("image3.png", cv2.IMREAD_UNCHANGED)# 创建 5*5 值为 1 的卷积核
kernel = np.ones((5, 5), np.uint8)
# 膨胀处理
reval = cv2.dilate(img, kernel)# 显示图像
cv2.imshow("reval", reval)
cv2.waitKey(0)
cv2.destroyAllWindows()

3.开运算

  • 处理过程:先腐蚀,后膨胀;即 开运算(image) = 膨胀(腐蚀(image))

  • morphologyEx 函数

    知识点:在形态学处理中,除了腐蚀和膨胀,其它的处理方法均为形态学扩展,统一使用 morphologyEx 函数实现

    语法:opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)

    参数:img 表示图像;cv2.MORPH_OPEN 表示开运算常量;kernel 表示卷积核

import cv2
import numpy as np# 读取图片-与python文件相同目录, 原图
img = cv2.imread("image3.png", cv2.IMREAD_UNCHANGED)# 创建 5*5 值为 1 的卷积核
kernel = np.ones((5, 5), np.uint8)
# 开运算处理
reval = cv2.morphologyEx(img, cv2.MORPH_OPEN,kernel)# 显示图像
cv2.imshow("reval", reval)
cv2.waitKey(0)
cv2.destroyAllWindows()

4.闭运算

  • 处理过程:先膨胀,后腐蚀;即 闭运算(image) = 腐蚀(膨胀(image))

  • morphologyEx 函数

    语法:opening = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)

    参数:img 表示图像;cv2.MORPH_CLOSE 表示闭运算常量;kernel 表示卷积核

import cv2
import numpy as np# 读取图片-与python文件相同目录, 原图
img = cv2.imread("image3.png", cv2.IMREAD_UNCHANGED)# 创建 5*5 值为 1 的卷积核
kernel = np.ones((5, 5), np.uint8)
# 闭运算处理
reval = cv2.morphologyEx(img, cv2.MORPH_CLOSE,kernel)# 显示图像
cv2.imshow("reval", reval)
cv2.waitKey(0)
cv2.destroyAllWindows()

5.梯度运算

  • 处理过程:第一步生成膨胀图像,第二步生成腐蚀图像,第三步膨胀图像的像素值减去腐蚀图像的像素值,最终得到轮廓图像,即 梯度(image) = 膨胀(image) - 腐蚀(image)
  • 像素值:0 表示黑色,1 表示白色;1 - 1 = 0 即白色减白色等于黑色

  • morphologyEx 函数

    语法:opening = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel)

    参数:img 表示图像;cv2.MORPH_GRADIENT 表示梯度运算常量;kernel 表示卷积核

import cv2
import numpy as np# 读取图片-与python文件相同目录, 原图
img = cv2.imread("image3.png", cv2.IMREAD_UNCHANGED)# 创建 5*5 值为 1 的卷积核
kernel = np.ones((5, 5), np.uint8)
# 梯度运算处理
reval = cv2.morphologyEx(img, cv2.MORPH_GRADIENT,kernel)# 显示图像
cv2.imshow("reval", reval)
cv2.waitKey(0)
cv2.destroyAllWindows()

6.礼帽运算

  • 先进行开运算,后使用原始图像减去开运算图像;即 礼帽(image) = image - 开运算(image)
  • 像素值:0 表示黑色,1 表示白色;1 - 1 = 0 表示白色减白色等于黑色;1 - 0 = 1 表示白色减黑色等于白色

  • morphologyEx 函数

    语法:opening = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel)

    参数:img 表示图像;cv2.MORPH_TOPHAT 表示礼帽运算常量;kernel 表示卷积核

import cv2
import numpy as np# 读取图片-与python文件相同目录, 原图
img = cv2.imread("image3.png", cv2.IMREAD_UNCHANGED)# 创建 5*5 值为 1 的卷积核
kernel = np.ones((5, 5), np.uint8)
# 礼帽运算处理
reval = cv2.morphologyEx(img, cv2.MORPH_TOPHAT,kernel)# 显示图像
cv2.imshow("reval", reval)
cv2.waitKey(0)
cv2.destroyAllWindows()

7.黑帽运算

  • 先进行闭运算,后使用闭运算图像减去原始图像;即 黑帽(image) = 闭运算(image) - image
  • 像素值:0 表示黑色,1 表示白色;1 - 1 = 0 表示白色减白色等于黑色;1 - 0 = 1 表示白色减黑色等于白色

  • morphologyEx 函数

    语法:opening = cv2.morphologyEx(img, cv2.MORPH_BLACKHAT, kernel)

    参数:img 表示图像;cv2.MORPH_BLACKHAT 表示黑帽运算常量;kernel 表示卷积核

import cv2
import numpy as np# 读取图片-与python文件相同目录, 原图
img = cv2.imread("image3.png", cv2.IMREAD_UNCHANGED)# 创建 5*5 值为 1 的卷积核
kernel = np.ones((5, 5), np.uint8)
# 黑帽运算处理
reval = cv2.morphologyEx(img, cv2.MORPH_BLACKHAT,kernel)# 显示图像
cv2.imshow("reval", reval)
cv2.waitKey(0)
cv2.destroyAllWindows()

九、图像梯度

  • 梯度、边界

1.sobel算子理论基础

  • X 轴方向的梯度,计算水平方向有没有边界,梯度值大说明存在边界,否则不存在

    例如:P5 点的 X 轴方向的梯度值,使用右边值减去左边值;由于 X 轴方向上,P4 和 P6 距离 P5 最近,对应权重最大为 2,其它为 1;如果两边的值差别比较大,P5 相应的会比较大,说明边界存在,否则边界不存在(比如值为 0)

  • Y 轴方向的梯度,计算垂直方向有没有边界,梯度值大说明存在边界,否则不存在

    例如:P5 点的 Y 轴方向的梯度值,使用下边值减去上边值;由于Y 轴方向上,P2 和 P8 距离 P5 最近,对应权重最大为 2,其它为 1;如果两边的值差别比较大,P5 相应的会比较大,说明边界存在,否则边界不存在(比如值为 0)

  • 最终,P5 像素点的梯度值是,水平方向梯度值的平方加上垂直方向的梯度值平方开根号;而为了方便计算,一般可以简化为,水平方向梯度值的绝对值加上垂直方向梯度值的绝对值


2.sobel算子及其函数使用

  • Sobel 函数

    语法:reval = cv2.Sobel(img, ddepth, dx, dy, [ksize])

    参数:img 表示图像;ddepth 表示处理结果图像的深度(设置为 -1 表示让结果与原始图像保持一致);dx 表示 X 轴方向(如果只计算 X 轴:dx=1,dy=0);dy 表示 Y 轴方向(如果只计算 Y 轴:dx=0,dy=1);ksize 表示核大小

  • 实际操作中,计算梯度值可能会出现负数,通常处理的图像是 np.uint8 类型,如果结果为该类型,所有的负数会自动截取为 0,发现信息丢失(例如:两个边界,却只获得一个边界),所以,在计算时需要使用 cv2.CV_64F ,取绝对值后,在转换为 np.uint8 (cv2.CV_8U) 类型

  • convertScaleAbs 函数

    语法:reval = cv2.convertScaleAbs(img, [, alpha[, beta]])

    参数:img 表示图像;alpha 表示系数;beta 表示修正值

    作用:将图像转为 256 色位图

    内部逻辑:结果图像 = 调整(原始图像 * alpha + beta)

  • 在 sobel 计算中,同时计算梯度值的方式有两种

  1. 第一种:dx=1,dy=1 ;即 reval = cv2.Sobel(img, ddepth, 1, 1)

  2. 第二种(推荐):分别计算 dx 和 dy,然后相加

    X 轴梯度值:dx = cv2.Sobel(img, ddepth, 1, 0)

    Y 轴梯度值:dy = cv2.Sobel(img, ddepth, 0, 1)

    最终梯度值:reval = dx + dy ;也可加入系数:reval = dx * 系数1 + dy * 系数2

  • addWeighted 函数

    语法:reval = cv2.addWeighted(img1, alpha, img2, beta, gamma)

    参数:img1 表示图像1;alpha 表示图像1的系数;img2 表示图像2;beta 表示图像2的系数;gamma 表示修正值

    效果:计算两幅图像的权重和

import cv2
import numpy as np# 读取图片-与python文件相同目录, 原图
img = cv2.imread("image4.png", cv2.IMREAD_UNCHANGED)# (第一种方式) 计算图像梯度值
sobelxy1 = cv2.Sobel(img, cv2.CV_64F, 1, 1)
sobelxy1 = cv2.convertScaleAbs(sobelxy1)# (第二种方式) 计算图像梯度值
# X轴梯度值
sobelx = cv2.Sobel(img, cv2.CV_64F, 1, 0)
# 将 cv2.CV_64F 转回 uint8
sobelx = cv2.convertScaleAbs(sobelx)
# Y轴梯度值
sobely = cv2.Sobel(img, cv2.CV_64F, 0, 1)
# 将 cv2.CV_64F 转回 uint8
sobely = cv2.convertScaleAbs(sobely)
# 图像梯度值
sobelxy2 = cv2.addWeighted(sobelx, 0.5, sobely, 0.5, 0)# 显示图像
cv2.imshow("original", img)
cv2.imshow("sobelxy1", sobelxy1)
cv2.imshow("sobelx", sobelx)
cv2.imshow("sobely", sobely)
cv2.imshow("sobelxy2", sobelxy2)
cv2.waitKey(0)
cv2.destroyAllWindows()

3.scharr算子及其函数使用

  • scharr 算子是 sobel 算子的升级版,主要是解决使用 3*3 的sobel 算子时,可能不太精确的问题;主要改进是卷积核的系数

  • Scharr 函数

    语法:reval = cv2.Scharr(img, ddpeth, dx, dy)

    参数:img 表示图像;ddepth 表示处理结果图像的深度(设置为 -1 表示让结果与原始图像保持一致;但是为了处理负数问题,需要将深度设置为 cv2.CV_64F ,然后在转换为 uint8 类型 ,详情查看 sobel 算子笔记);dx 表示 X 轴方向;dy 表示 Y 轴方向

    注意:dx >=0 && dy >=0 && dx+dy == 1 ,即 dx 和 dy 不能同时设置为 1

  • 两个方向的梯度计算方式

    dx = cv2.Scharr(img, ddpeth, 1, 0)

    dy = cv2.Scharr(img, ddpeth, 0, 1)

    dxy = dx + dy --> cv2.addWeighted(dx, alpha, dy, beta, gamma)

  • 如果将 Sobel 函数的 ksize 设置 -1 ,表示使用 Sobel 算子的升级版 Scharr 算子,即 ksize = -1 的 Sobel 算子等于 Scharr 算子

import cv2
import numpy as np# 读取图片-与python文件相同目录, 原图
img = cv2.imread("image4.png", cv2.IMREAD_UNCHANGED)# 计算图像梯度值
# X轴梯度值
scharrx = cv2.Scharr(img, cv2.CV_64F, 1, 0)
# 将 cv2.CV_64F 转回 uint8
scharrx = cv2.convertScaleAbs(scharrx)
# Y轴梯度值
scharry = cv2.Scharr(img, cv2.CV_64F, 0, 1)
# 将 cv2.CV_64F 转回 uint8
scharry = cv2.convertScaleAbs(scharry)
# 图像梯度值
sobelxy = cv2.addWeighted(scharrx, 0.5, scharry, 0.5, 0)# 显示图像
cv2.imshow("original", img)
cv2.imshow("sobelx", scharrx)
cv2.imshow("sobely", scharry)
cv2.imshow("sobelxy", sobelxy)
cv2.waitKey(0)
cv2.destroyAllWindows()

4.sobel算子和scharr算子的比较

  • sobel 算子和 scharr 算子的卷积核大小相同,所以计算复杂度一样
  • scharr 算子的精确度优于 sobel 算子,具体原因是临近像素的权重分配,最直观的体现是,scharr 算子可以找到更精细的边界,而 sobel 算子是无法检测到的

5.laplacian算子的使用

  • 拉普拉斯算子类似与二阶 sobel 导数,如下为公式和卷积核及权重


  • 例如:计算P5 像素值

  • sobel 算子、scharr 算子和 laplacian 算子对比

  • Laplacian 函数

    语法:reval = cv2.Laplacian(img, ddepth)

    参数:img 表示图像;ddepth 表示处理图像的深度(设置为 -1 表示让结果与原始图像保持一致;但是为了处理负数问题,需要将深度设置为 cv2.CV_64F ,然后在转换为 uint8 类型 ,详情查看 sobel 算子笔记)

import cv2
import numpy as np# 读取图片-与python文件相同目录, 原图
img = cv2.imread("image4.png", cv2.IMREAD_UNCHANGED)# 计算图像梯度值
reval = cv2.Laplacian(img, cv2.CV_64F)
# 将 cv2.CV_64F 转回 uint8
reval = cv2.convertScaleAbs(reval)# 显示图像
cv2.imshow("original", img)
cv2.imshow("reval", reval)
cv2.waitKey(0)
cv2.destroyAllWindows()

十、边缘检测

1.Canny边缘检测原理

  • Canny 边缘检测步骤:

    去噪 --> 梯度(大小和方向) --> 非极大值抑制 --> 滞后阈值

  • 第一步:去噪

    1. 由于边缘检测容易受到噪声的影响;因此在边缘检测前,需要先进行去噪处理
    2. 一般使用高斯滤波器进行去噪处理(平滑处理)
  • 第二步:梯度

    1. 对平滑后的图像进行 sobel 算子的计算,得到梯度值和方向
    2. 梯度方向:垂直、水平和两个对角线


  • 第三步:非极大值抑制

    1. 在获得梯度和方向后,遍历图像,去除不是边界的像素点

    2. 去除方法:逐个遍历像素点,判断当前像素点是否是周围像素点中具有相同方向梯度的最大值

      例如:图中黄色部分,第一列方向向上中,7 最大;第一列方向向上中,8 最大,以此类推,得到边界;其它值被抑制为 0

  • 第四步:滞后阈值

    1. 在计算梯度值时,获取到滞后阈值1为极小值(minVal)和滞后阈值2为极大值(maxVal)

      例如:梯度值 > maxVal 保留边界;梯度值 < minVal 剔除边界;minVal < 梯度值 < maxVal 如果点与边界相连,则保留,否则剔除,如 C 与 A 相连则保留,B 则剔除

2.Canny函数及使用

  • Canny 函数

    语法:reval = cv2.Canny(img, threshold1, threshold2)

    参数:reval 表示边界图像;img 表示图像;threshold1 表示阈值1;threshold2 表示阈值2;阈值越小,图像边界越多

import cv2
import numpy as np# 读取图片-与python文件相同目录, 原图
img = cv2.imread("image2.png", cv2.IMREAD_UNCHANGED)# Canny边缘检测
reval = cv2.Canny(img, 20, 50)# 显示图像
cv2.imshow("original", img)
cv2.imshow("reval", reval)
cv2.waitKey(0)
cv2.destroyAllWindows()

十一、图像金字塔

1.基础理论

  • 同一图像的不同分辨率的子图集合


  • 生成图像金字塔(高斯金字塔)有两种方式
  1. 向下采样
  • 像素减少,缩小图像

  • 实现步骤:

    (1)对图像进行高斯核卷积(高斯滤波中使用下图卷积核)

(2)删除所有的偶数行和列

  1. 向上采样
  • 像素增多,放大图像

  • 实现步骤:

    (1)在每个方向上扩大为原来的 2 倍,新增的行和列以 0 填充

    (2)使用与 向下采样 一样的卷积核乘以4,作为新增像素的的新值

  • 注意:向上采样、向下采样不是互逆操作,经过这两种采样操作后,无法恢复原有图像

2.pyrDown函数及使用

  • pyrDown 函数

    语法:reval = cv2.pyrDown(img)

    参数:reval 表示向下取样结果;img 表示图像

    效果:图像缩小为原图的 1/ 4

import cv2
import numpy as np# 读取图片-与python文件相同目录, 原图
img = cv2.imread("image2.png", cv2.IMREAD_UNCHANGED)# 向下取样
reval = cv2.pyrDown(img)
reval2 = cv2.pyrDown(reval)# 显示图像
cv2.imshow("original", img)
cv2.imshow("reval", reval)
cv2.imshow("reval2", reval2)
cv2.waitKey(0)
cv2.destroyAllWindows()

3.pyrUp函数及使用

  • pyrUp 函数

    语法:reval = cv2.pyrUp(img)

    参数:reval 表示向上取样结果;img 表示图像

    效果:图像放大为原图的 4 倍,清晰度降低

import cv2
import numpy as np# 读取图片-与python文件相同目录, 原图
img = cv2.imread("image.png", cv2.IMREAD_UNCHANGED)# 向上取样
reval = cv2.pyrUp(img)
reval2 = cv2.pyrUp(reval)# 显示图像
cv2.imshow("original", img)
cv2.imshow("reval", reval)
cv2.imshow("reval2", reval2)
cv2.waitKey(0)
cv2.destroyAllWindows()

4.取样可逆性研究

  • 一个图像先向下取样,后向上取样,图像恢复为原来大小,清晰度下降
  • 一个图像先向上取样,后向下取样,图像恢复为原来大小,清晰度下降

5.拉普拉斯金字塔

  • 在高斯金字塔的基础上构建

  • 拉普拉斯金字塔处理的图像可进行复原操作

  • 公式:Li = Gi - PyrUp( PyrDown( Gi ) )

    参数:Gi 表示原始图像;Li 表示拉普拉斯金字塔图像 ;其中 Gi 和 Li 中的 i 表示层数

import cv2
import numpy as np# 读取图片-与python文件相同目录, 原图
img = cv2.imread("image2.png", cv2.IMREAD_UNCHANGED)# 第 0 层拉普拉斯金字塔
imgd = cv2.pyrDown(img)
imgdu = cv2.pyrUp(imgd)
reval = img - imgdu# 第 1 层拉普拉斯金字塔
imgd1 = imgd
img1d = cv2.pyrDown(imgd1)
img1du = cv2.pyrUp(img1d)
reval1 = imgd1 - img1du;# 显示图像
cv2.imshow("original", img)
cv2.imshow("reval", reval)
cv2.imshow("reval1", reval1)
cv2.waitKey(0)
cv2.destroyAllWindows()

十二、图像轮廓

1.图像轮廓

  • 轮廓是什么?
  1. 边缘检测可以测出边缘,但是,边缘时不连续的
  2. 将边缘连接为一个整体,构成轮廓
  • 计算图像轮廓注意事项:
  1. 对象时二值图像(需要将彩色图像或灰度图像处理为二值图像),所以需要预先进行阈值分割或边缘检测处理
  2. 查询轮廓需要更改原始图像,因此,通常需要对原始图像进行拷贝操作
  3. 在 opencv 中,是从黑色背景中查找白色对象,因此,对象必须是白色,背景必须是黑色
  • findContours() 函数

    作用:查找图像轮廓

    语法:image, contours,hierarchy = cv2.findContours(img, mode, method)

    参数 说明
    image 修改后的图像(二值化图像)
    contours 图像轮廓
    hierarchy 图像的拓扑信息(轮廓层次)
    img 原始图像
    mode 轮廓检索模式(详情查看下方表格)
    method 轮廓的近似方法(详情查看下方表格)

    mode 检索轮廓模式:

    常量 说明
    cv2.RETR_EXTERNAL 只检测外轮廓
    cv2.ERTR_LIST 检测的轮廓不建立等级关系
    cv2.RETR_CCOMP 建立两个等级的轮廓,上面的一层为外边界,里面的一层为内孔的边界信息。如果内孔内部哎呦一个连通物体,这个物体的边界,也在顶层
    cv2.RETR_TREE 建立一个等级树结构的轮廓

    method 轮廓的近似方法:

    常量 说明
    cv2.CHAIN_APPROX_NONE 存储所有轮廓点,相邻的两个点的像素位置差不超过1,即 max(abs(x1-x2) , abs(y2-y1)) == 1
    cv2.CHAIN_APPROX_SIMPLE 压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标,例如一个矩形只需要4个点来保持轮廓信息
    cv2.CHAIN_APPROX_TC89_L1 使用 tehChinl chain 近似算法
    cv2.CHAIN_APPROX_TC89_KCOS 使用 teh-Chinl chain 近似算法
  • drawContours() 函数

    作用:将查找到的轮廓,绘制到图像上

    语法:reval = cv2.drawContours(img, contours, contourIdx, color [, thickness])

    参数 说明
    reval 绘制了轮廓的结果图像
    img 原始图像
    contours 需要绘制的边缘数组(轮廓)
    contourIdx 需要绘制阿边缘索引,如果全部绘制则为 -1
    color 绘制的颜色,为 BGR 格式的 Scalar
    thickness (可选)表示绘制的密度,即绘制轮廓时所使用的画笔粗细
import cv2# 读取图片-与python文件相同目录, 原图
img = cv2.imread("image4.png", cv2.IMREAD_UNCHANGED)# 彩色图像转为灰度图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 使用二进制阈值化处理,将灰度图像转为二值图像
ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# 查找轮廓
contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# 绘制轮廓
tmp = img.copy()
reval = cv2.drawContours(tmp, contours, -1, (0, 0, 255), 1)# 显示图像
cv2.imshow("original", img)
cv2.imshow("reval", reval)
cv2.waitKey(0)
cv2.destroyAllWindows()

十三、直方图

1.直方图的概念

  • 灰度直方图中,横坐标表示图像中各个像素点的 灰度级 ,纵坐标表示具有该灰度级的像素 个数 ,例如 8 位位图中,灰度级是 0 至 255 ,即 256 个灰度级

  • 归一化直方图中,横坐标表示图像中各个像素点的 灰度级 ,纵坐标表示出现这个灰度级的 概率

  • DIMS:使用参数的数量

    dims=1 :灰度直方图,仅考虑灰度的情况(其它的可能是亮度等)

  • BINS:参数子集的数量

    灰度级的数量,bins=256

    也可是整合像素值之后的数量,如 0-15 为一组,16-31 为一组,即 bins=16

  • RANGE:统计灰度值的范围,一般为8位位图 [0, 255]

2.绘制直方图

  • matplotlib.pyplot 提供了类似于 matlab 的绘图框架

import matplot.pyplot as plt

  • hist 函数

    功能:根据数据源和像素级绘制直方图

    语法:hist(数据源, 像素级}

    参数:数据源:图像,必须是一维数组;像素级:一般是 256,即 [0, 255]

  • ravel 函数

    功能:将二维图像数据转换为一维图像数据

    语法:reval = img.ravel()

import cv2
import matplotlib.pyplot as plt# 读取图片-与python文件相同目录, 原图
img = cv2.imread("image2.png", cv2.IMREAD_UNCHANGED)# 绘制直方图
plt.hist(img.ravel(), 256)
plt.show()

3.使用OpenCV统计直方图

  • calcHist 函数

    功能:计算默认横坐标为 [0, 255] 的灰度级,直接计算纵坐标灰度级的像素个数

    语法:reval = cv2.calcHist(imgs, channels, mask, histSize, ranges, accumulate)

    参数 说明
    reval 直方图,二维数组(256行 1 列)
    imgs 图像
    channels 指定通道;灰度图像 = [0],彩色图像=[0],[1],[2] 分别对应B,G,R
    mask 掩码图像(仅计算某一部分图像);统计整幅图像的直方图,设为None;统计图像某一部分的直方图时,需要掩码图像
    histSize BINS的数量,如:[265]
    ranges 像素值范围,如:[0, 255]
    accumulate 累计标识;默认值为 false,如果为 true,则可计算多幅图像的直方图
import cv2
import numpy as np# 读取图片-与python文件相同目录, 原图
img = cv2.imread("image2.png", cv2.IMREAD_UNCHANGED)# 统计直方图
reval = cv2.calcHist([img], [0], None, [256], [0, 255])print(type(reval)) # 类型
print(reval.size) # 大小
print(reval.shape) # 形状
print(reval) # 直方图

4.绘制OpenCV统计直方图

  • 绘制 0 至 5 ,sin 函数的直方图
import numpy as np
import matplotlib.pyplot as plt# 创建范围为 0 - 5(不包括5),步长为 0.1
x = np.arange(0, 5, 0.1)
y = np.sin(x)
plt.plot(x, y)
plt.show()
  • 指定值,绘制直方图(这里 X 轴数据可省略)
import numpy as np
import matplotlib.pyplot as plt# X 轴数据
x = [0, 1, 2, 3, 4, 5]
# Y 轴数据
y = [2, 10, 30, 25, 10, 20]# plt.plot(x, y) # 正常使用 X 轴和Y 轴的数据
# plt.plot(y) # 省略 X 轴数据
plt.plot(y, color='r') # 指定颜色
plt.show()
  • 绘制图像的直方图
import cv2
import numpy as np
import matplotlib.pyplot as plt# 读取图片-与python文件相同目录, 原图
img = cv2.imread("image2.png", cv2.IMREAD_UNCHANGED)# 统计直方图
revalB = cv2.calcHist([img], [0], None, [256], [0, 255])
revalG = cv2.calcHist([img], [1], None, [256], [0, 255])
revalR = cv2.calcHist([img], [2], None, [256], [0, 255])
# 绘制直方图
plt.plot(revalB, color='b')
plt.plot(revalG, color='g')
plt.plot(revalR, color='r')
plt.show()

5.使用掩膜直方图

  • 白色部分透明,黑色部分不透明

  • 生成掩膜图像

    1. 生成一个与原始图像相同大小的全黑图像
    2. 在全黑图像中,指定行和列,设置值为 255(白色)
import cv2
import numpy as np
import matplotlib.pyplot as plt# 读取图片-与python文件相同目录, 灰度图
img = cv2.imread("image2.png", cv2.IMREAD_GRAYSCALE)# 创建掩膜图像
# 值为 0 的全黑图像
mask = np.zeros(img.shape, np.uint8)
# 设置 200- 400 行,200 - 400 列为白色
mask[200:400, 200:400] = 255# 统计直方图
revalM = cv2.calcHist([img], [0], mask, [256], [0, 255])
reval = cv2.calcHist([img], [0], None, [256], [0, 255])
#绘制直方图
plt.plot(revalM)
plt.plot(reval)
plt.show()

6.掩膜原理及演示




  • bitwise_and 函数

    语法:reval = cv2.bitwise_and(img1, img2)

    参数:reval 表示结果图像;img1 表示原始图像;img2 表示掩膜图像

import cv2
import numpy as np# 读取图片-与python文件相同目录, 灰度图
img = cv2.imread("image2.png", cv2.IMREAD_GRAYSCALE)# 创建掩膜图像
# 值为 0 的全黑图像
mask = np.zeros(img.shape, np.uint8)
# 设置 200- 400 行,200 - 400 列为白色
mask[200:400, 200:400] = 255# 被掩膜后的图像
reval = cv2.bitwise_and(img, mask)# cv2.imshow("mask", mask)
cv2.imshow("reval", reval)
cv2.waitKey(0)
cv2.destroyAllWindows()

7.直方图均衡化原理


  • 基础理论

    前提:如果一副图像占有全部的灰度级,并且均匀分布

    结论:该图像具有高对比度和多变的灰度色调

    图像表现:图像细节丰富,质量更高

  • 算法

    1. 计算累计直方图
    2. 将累计直方图进行区间换算
    3. 在累计直方图中,概率相近的原始值,会被处理为相同的值
  • 下图中,为 3 位位图,灰度级位 1 - 7,首先进行统计直方图

  • 归一化处理中,图像像素为 7*7 = 49,所以,像素 0 的个数 9/49 = 0.18,以此类推

  • 累计直方图中,0 的值和归一化直方图一致,1 的值为归一化 0+1 的值,即 0.18+0.18=0.37(四舍五入),以此类推;最后一个为 1,所有百分比的和

  • 将百分比乘以灰度级,得到结果,即 0.18*7= 1(四舍五入);最终将原始图像的灰度级 0 转为 1,1 转为 3,以此类推;最终将 1 和 2 合并为 3,4 和 5 合并为 5

  • 通过合并像素级得到均衡化直方图

8.直方图均衡化函数

  • equalizeHist 函数

    语法:reval = cv2.equalizeHist(img)

    参数:reval 表示结果图像;img 表示原始图像

import cv2
import matplotlib.pyplot as plt# 读取图片-与python文件相同目录, 灰度图
img = cv2.imread("image2.png", cv2.IMREAD_GRAYSCALE)# 直方图均衡化
ravel = cv2.equalizeHist(img)
# 绘制直方图
plt.hist(img.ravel(), 256)
plt.show()
# plt.figure() # 创建新的图像
plt.hist(ravel.ravel(), 256)
plt.show()
  • 显示直方图均衡化的图像
import cv2
import matplotlib.pyplot as plt# 读取图片-与python文件相同目录, 灰度图
img = cv2.imread("image2.png", cv2.IMREAD_GRAYSCALE)# 直方图均衡化
reval = cv2.equalizeHist(img)cv2.imshow("img", img)
cv2.imshow("reval", reval)
cv2.waitKey(0)
cv2.destroyAllWindows()

9.subplot函数的使用

  • subplot 函数

    功能:同时显示多个窗口

    语法:plt.subplot(nrows, ncols, plot_nuber)

    参数:nrows 表示行数;ncols 表示列数;plot_number 表示窗口序号

    特例:每个参数小于 10 时,可省略逗号,如:plt.subplot(234)

import cv2
import matplotlib.pyplot as plt# 读取图片-与python文件相同目录, 灰度图
img = cv2.imread("image2.png", cv2.IMREAD_GRAYSCALE)# 直方图均衡化
reval = cv2.equalizeHist(img)# 1 行 2 列 序号 1
plt.subplot(121)
plt.hist(img.ravel(), 256)
# 1 行 2 列 序号 2
plt.subplot(122)
plt.hist(reval.ravel(), 256)
plt.show()

10.imshow函数的使用

  • matplot.pyplot.imshow 函数

    语法:plt.imshow(img, cmap=None)

    参数:img 表示绘制的图像;cmap 表示 colormap;颜色图谱,默认RGB(A)

    显示:灰度图像:cmap 默认通道为 RGB(A) ,需要使用参数 cmap=plt.cm.gray ;彩色图像:如果使用 opencv 读取图像,默认通道为 BGR,所以需要调整通道为 RGB(A)

import cv2
import matplotlib.pyplot as plt# 读取图片-与python文件相同目录
imgA = cv2.imread("image2.png", cv2.IMREAD_GRAYSCALE)
imgB = cv2.imread("image2.png", cv2.IMREAD_UNCHANGED)# BGR to RGB
new_imgB = cv2.cvtColor(imgB, cv2.COLOR_BGR2RGB)# 1 行 2 列 序号 1
plt.subplot(121)
plt.imshow(imgA, cmap=plt.cm.gray) # 灰度图
plt.axis('off') # 关闭坐标轴# 1 行 2 列 序号 2
plt.subplot(122)
plt.imshow(new_imgB)
plt.axis('off') # 关闭坐标轴plt.show()

11.直方图均衡化对比

  • 一个图像中,对比原始图像和直方图均衡化图像,以及对比原始图像直方图和均衡化直方图
import cv2
import matplotlib.pyplot as plt# 读取图片-与python文件相同目录
img = cv2.imread("image2.png", cv2.IMREAD_GRAYSCALE)# 直方图均衡化
equ = cv2.equalizeHist(img)# 2 行 2 列 序号 1
plt.subplot(221)
plt.imshow(img, cmap=plt.cm.gray)
plt.axis('off') # 关闭坐标轴# 2 行 2 列 序号 2
plt.subplot(222)
plt.imshow(equ, cmap=plt.cm.gray)
plt.axis('off') # 关闭坐标轴# 2 行 2 列 序号 3
plt.subplot(223)
plt.hist(img.ravel(), 256)# 2 行 2 列 序号 4
plt.subplot(224)
plt.hist(equ.ravel(), 256)plt.show()

十四、傅里叶变换

1.傅里叶变换的理论基础

**任何连续周期信号,可以由一组适当的正弦曲线组合而成。— 傅里叶 **

  • 时间:动态

  • 频率:静态

  • 相位:时间差,不是同时开始的一组余弦函数,在叠加时要体现开始的时间

  • 下图中,将复杂的时间角度正弦函数图像,转换为简洁的频率角度的图像,相互可逆

2.numpy实现傅里叶变换

  • numpy.fft.fft2 函数

    功能:实现傅里叶变换,返回一个复数数值(Complex ndarray)

  • numpy.fft.fftshift 函数

    功能:将零频率分量移到频谱中心;将傅里叶变换后的结果(频谱)中左上角的低频移动到中心点

  • np.log 函数

    功能:设置频谱的范围,如将傅里叶变换的结果映射到 [0, 255] 区间,使得频谱图像可以显示

    语法:20 * np.log( np.abs(fshift) )

import cv2
import numpy as np
import matplotlib.pyplot as plt# 读取图片-与python文件相同目录
img = cv2.imread("image2.png", cv2.IMREAD_GRAYSCALE)# 计算傅里叶变换
fnum = np.fft.fft2(img)
# 移动低频到中心点
fshift = np.fft.fftshift(fnum)
# 频谱映射区间
reval = 20 * np.log(np.abs(fshift))# 1 行 2 列 序号 1
plt.subplot(121)
plt.imshow(img, cmap=plt.cm.gray)
plt.title('original')
plt.axis('off') # 关闭坐标轴# 1 行 2 列 序号 2
plt.subplot(122)
plt.imshow(reval, cmap=plt.cm.gray)
plt.title('reval')
plt.axis('off') # 关闭坐标轴plt.show()
  • 注意:

    1. 傅里叶变换后得到低频和高频信息,针对低频和高频信息的处理,实现不同的目的
    2. 傅里叶变换过程是可逆的,即图像经过傅里叶变换后,能够恢复到原始图像
    3. 在频域对图像进行处理,会反映在逆变换图像上

3.numpy实现逆傅里叶变换

  • 完整过程:原始图像 --> 得到图像频谱 --> 处理最高频、最低频信息 --> 还原图像

  • 如果只保留低频信息,即为低通滤波,去除边界;如果只保留高频信息,即为高通滤波,保留边界,细节模糊

  • numpy.fft.ifft2 函数

    功能:实现逆傅里叶变换,返回一个复数数组(complex ndarray)

  • numpy.fft.ifftshift 函数

    功能:fftshift 函数的逆函数

  • np.abs(ifftshift)

    功能:设置值的范围,如 [0, 255]

import cv2
import numpy as np
import matplotlib.pyplot as plt# 读取图片-与python文件相同目录
img = cv2.imread("image2.png", cv2.IMREAD_GRAYSCALE)# 计算傅里叶变换
fnum = np.fft.fft2(img)
# 移动低频到中心点
fshift = np.fft.fftshift(fnum)
# 移动低频到左上角
ishift = np.fft.ifftshift(fshift)
# 逆傅里叶变换
img2 = np.fft.ifft2(ishift)
# 频谱映射区间 (绝对值)
reval = np.abs(img2)# 1 行 2 列 序号 1
plt.subplot(121)
plt.imshow(img, cmap=plt.cm.gray)
plt.title('original')
plt.axis('off') # 关闭坐标轴# 1 行 2 列 序号 2
plt.subplot(122)
plt.imshow(reval, cmap=plt.cm.gray)
plt.title('reval')
plt.axis('off') # 关闭坐标轴plt.show()

4.高通滤波演示

  • 低频对应图像内变化缓慢的灰度分量,颜色趋于一致

  • 高频对应图像内变化越来越快的灰度分量,由灰度的尖锐过渡造成

  • 滤波:接受(通过)或拒绝一定频率的分量

  • 低通滤波器:通过低频的滤波器;即衰减高频而通过低频,将模糊一幅图像

  • 高通滤波器:通过高频的滤波器;即衰减低频而通过高频,将增强尖锐的细节,导致图像对比度减低

  • 频域滤波

    1. 修改傅里叶变换的高频或低频信息,以达到特殊目的,然后再通过逆傅里叶变换返回到图像域
    2. 特殊目的:图像增强、图像去噪、边缘检测、特征提取、压缩、加密 等
import cv2
import numpy as np
import matplotlib.pyplot as plt# 读取图片-与python文件相同目录
img = cv2.imread("image2.png", cv2.IMREAD_GRAYSCALE)# 计算傅里叶变换
fnum = np.fft.fft2(img)
# 移动低频到中心点
fshift = np.fft.fftshift(fnum)# 高频滤波器:将低频信息设置为 0
rows,cols = img.shape # 形状
crows, ccols = int(rows/2),int(cols/2) # 中心点
fshift[crows-10:crows+10, ccols-10:ccols+10] = 0 # 中心点+10 的正方形范围的频率设置为 0# 移动低频到左上角
ishift = np.fft.ifftshift(fshift)
# 逆傅里叶变换
img2 = np.fft.ifft2(ishift)
# 频谱映射区间 (绝对值)
reval = np.abs(img2)# 1 行 2 列 序号 1
plt.subplot(121)
plt.imshow(img, cmap=plt.cm.gray)
plt.title('original')
plt.axis('off') # 关闭坐标轴# 1 行 2 列 序号 2
plt.subplot(122)
plt.imshow(reval, cmap=plt.cm.gray)
plt.title('reval')
plt.axis('off') # 关闭坐标轴plt.show()

5.OpenCV实现傅里叶变换

  • dft 函数

    语法:reval = cv2.dtf(img, flags)

    参数 说明
    reval 返回结果,双通道,第一个通道是实数部分,第二个通道是虚数部分
    img 图像,需要转换为 np.float32 格式
    flags 转换标识; flags=cv2.DFT_COMPLEX_OUTPUT,输出一个复数阵列(振幅、频率)
  • magnitude 函数

    功能:计算幅值

    语法:reval = cv2.magnitude(p1, p2)

    参数:p1 表示浮点型 X 坐标值,实数部分;p2 表示浮点型 Y 坐标值,虚数部分

import cv2
import numpy as np
import matplotlib.pyplot as plt# 读取图片-与python文件相同目录
img = cv2.imread("image2.png", cv2.IMREAD_GRAYSCALE)# 计算傅里叶变换
fnum = cv2.dft(np.float32(img), flags=cv2.DFT_COMPLEX_OUTPUT)
# 移动低频到中心点
fshift = np.fft.fftshift(fnum)
# 计算幅值 # 0 通道,1 通道
reval = 20 * np.log(cv2.magnitude(fshift[:,:,0], fshift[:,:,1])) # 1 行 2 列 序号 1
plt.subplot(121)
plt.imshow(img, cmap=plt.cm.gray)
plt.title('original')
plt.axis('off') # 关闭坐标轴# 1 行 2 列 序号 2
plt.subplot(122)
plt.imshow(reval, cmap=plt.cm.gray)
plt.title('reval')
plt.axis('off') # 关闭坐标轴plt.show()

6.OpenCV实现逆傅里叶变换

  • idft 函数:

    功能:逆傅里叶变换

    语法:reval = cv2.idft(idata)

    参数:reval 表示返回结果,取决于原始数据的类型和大小;idata 表示原始数据,实数或虚数均可

import cv2
import numpy as np
import matplotlib.pyplot as plt# 读取图片-与python文件相同目录
img = cv2.imread("image2.png", cv2.IMREAD_GRAYSCALE)# 计算傅里叶变换
fnum = cv2.dft(np.float32(img), flags=cv2.DFT_COMPLEX_OUTPUT)
# 移动低频到中心点
fshift = np.fft.fftshift(fnum)
# 移动低频到左上角
ishift = np.fft.ifftshift(fshift)
# 逆傅里叶变换
img2 = cv2.idft(ishift)
# 计算幅值 # 0 通道,1 通道
reval = cv2.magnitude(img2[:,:,0], fshift[:,:,1])# 1 行 2 列 序号 1
plt.subplot(121)
plt.imshow(img, cmap=plt.cm.gray)
plt.title('original')
plt.axis('off') # 关闭坐标轴# 1 行 2 列 序号 2
plt.subplot(122)
plt.imshow(reval, cmap=plt.cm.gray)
plt.title('reval')
plt.axis('off') # 关闭坐标轴plt.show()

7.低通滤波示例

  • 通过掩膜图像,构建低通滤波器图像

import cv2
import numpy as np
import matplotlib.pyplot as plt# 读取图片-与python文件相同目录
img = cv2.imread("image2.png", cv2.IMREAD_GRAYSCALE)# 计算傅里叶变换
fnum = cv2.dft(np.float32(img), flags=cv2.DFT_COMPLEX_OUTPUT)
# 移动低频到中心点
fshift = np.fft.fftshift(fnum)# 构建低通滤波图像
rows, cols = img.shape
crows, ccols = int(rows/2),int(cols/2) # 中心点
mask = np.zeros((rows, cols, 2), np.uint8)
mask[crows-10:crows+10, ccols-10:ccols+10] = 1
fshift = fshift * mask# 移动低频到左上角
ishift = np.fft.ifftshift(fshift)
# 逆傅里叶变换
img2 = cv2.idft(ishift)
# 计算幅值 # 0 通道,1 通道
reval = cv2.magnitude(img2[:,:,0], fshift[:,:,1])# 1 行 2 列 序号 1
plt.subplot(121)
plt.imshow(img, cmap=plt.cm.gray)
plt.title('original')
plt.axis('off') # 关闭坐标轴# 1 行 2 列 序号 2
plt.subplot(122)
plt.imshow(reval, cmap=plt.cm.gray)
plt.title('reval')
plt.axis('off') # 关闭坐标轴plt.show()

十五、其它

1.PyCharm无法使用复制粘贴快捷键

问题:PyCharm 编辑器中无法使用 Ctrl+C 和 Ctrl+V 进行复制和粘贴的快捷键操作

原因:安装和正在使用 vim 插件

解决方法:关闭 vim 插件

  1. File – Settings… – Plugins – Installed 选项卡 – 取消 Idea Vim 的勾选 – 点击 Apply 按钮

  2. 重启编辑器

2.too many values to unpack

  • 报错:ValueError: too many values to unpack (expected 3)
  • 场景:获取拆分图像时,接收返回值的参数数量不对,split 有 4 个返回值,分别是 BGRA

OpenCV-Python计算机视觉入门相关推荐

  1. Opencv - python快速入门

    Opencv - python 文章目录 Opencv - python 1. 图像的IO操作 1.1 读取图像 1.2显示图像 1.3 保存图像 1.4 总结 2 绘制几何图形 2.1 绘制直线 2 ...

  2. opencv python轻松入门_OpenCV轻松入门 面向Python

    章OPENCV入门1 1.1如何使用1 1.2图像处理基本操作3 1.2.1读取图像3 1.2.2显示图像5 1.2.3保存图像9 1.3OpenCV贡献库10 第2章图像处理基础11 2.1图像的基 ...

  3. 基于OpenCV的计算机视觉入门(3)图像特效

    灰度处理 import cv2 import numpy as np #方法一 实现灰度处理 img0 =cv2.imread('haha.png',0) img1=cv2.imread('haha. ...

  4. 基于OpenCV的计算机视觉入门(5)图像美化(下)

    灰度直方图均衡化 一是为什么要选用累积分布函数,二是为什么使用累积分布函数处理后像素值会均匀分布. 第一个问题.均衡化过程中,必须要保证两个条件: ①像素无论怎么映射,一定要保证原来的大小关系不变,较 ...

  5. 基于OpenCV的计算机视觉入门(1)图片操作入门

    图片读取和显示 import cv2 img=cv2.imread('clip.png',1) cv2.imshow('image',img) cv2.waitKey(0) 写入图片 import c ...

  6. 基于OpenCV的计算机视觉入门(2)图片几何变换入门

    图片缩放 # 1. 图片加载 2.图片信息 3.resize方法 4.检查结果 import cv2 img= cv2.imread('timg.jpeg',1) #1代表彩色 imgInfo =im ...

  7. 基于OpenCV的计算机视觉入门(4)线段和形状的绘制

    线段绘制 import cv2 import numpy as np newImageInfo =(500,500,3) dst=np.zeros(newImageInfo,np.uint8) #绘制 ...

  8. 基于OpenCV的计算机视觉入门(5)图像美化(上)

    彩色图片直方图 import cv2 import numpy as np def ImageHist(image,type):color = (255,255,255)windowName = 'G ...

  9. Opencv python基础入门(4)---sobel算子及边缘检测

    本文主要介绍sobel算子及边缘检测: 代码如下: #sobel,ddepth如果设置为-1的话,水平处理只能取出左边界,右边界为负值丢失了,所以需要先用64F扩展然后用convertScaleAbs ...

  10. Python+Opencv图像处理新手入门教程(二):颜色空间转换,图像大小调整,灰度直方图

    一步一步来吧 上一节:Python+Opencv图像处理新手入门教程(一):介绍,安装与起步 1.什么是图像 对于计算机而言,图像的本质是一个由像素点构成的矩阵. 例如我们用肉眼很容易分辨一辆汽车的后 ...

最新文章

  1. 10年工作经验老程序员推荐的7个开发类工具
  2. PYTHON1.day01
  3. 006_FastDFS文件上传
  4. hdu 1511(dp)
  5. Rails 开发小贴士积累
  6. 前端图片canvas,file,blob,DataURL等格式转换
  7. Java单例模式:为什么我强烈推荐你用枚举来实现单例模式
  8. access没有使用 对象的必要权限_厨房有没有必要安燃气报警器 使用燃气要注意哪些安全性措施...
  9. 数据倾斜是什么以及造成的原因?
  10. 发现一个bug如何定位是前端还是后台问题?
  11. CentOS7 搭建samba服务
  12. 表妹即将去读研,我送了她11个建议
  13. 热烈祝贺排名进入前1000
  14. opencv快速下载
  15. 中国石油大学-《现代应用文写作》第三阶段在线作业
  16. oracle11g本地安装,windows64位机oracle11g+pl/sql安装教程图解
  17. Ken Thompson 在餐巾纸上设计的 UTF-8
  18. Wiki开源软件介绍
  19. 第四篇:UE4视角切换节点,Possess和Set View Target With Blend的区别
  20. 年、月、日格式正则表达式

热门文章

  1. python爬虫之Scrapy框架原理及操作实例详解、股票数据Scrapy爬虫
  2. 微信接口调用返回码一览表
  3. cloud探索 - ASW基础知识
  4. 高逼格UI-ASD(Android Support Design)
  5. android沉浸式导航栏与键盘的冲突
  6. 看电气图的一般步骤和方法
  7. Palisade DecisionTools Suite Unexpected error解决办法
  8. 生成二维码,支持中文
  9. java上传文件至微软云
  10. 国内首秀元宇宙Live House圆满收官,百事可乐虚拟偶像真的好会!!