【三维重建】第二章 Python及Pytorch基础
文章目录
- 前言
- Python基础
- 编程基础
- 面向对象编程(OPP)
- Matplotlib库
- NumPy库
- OpenCV库
- Pytorch基础
- 张量(Tensor)
- 自动微分/求导(Autograd)
- 数据集(Dataset) 和 数据加载器(DataLoader)
- 神经网络模块(nn.Module)
- 优化器(Optimizer)
- 损失函数(Loss Function)
- 参考资料
前言
本文内容为参考网络资源,学习整理得出,仅供学习使用,参考资料在文末,若有侵权,立即删除。
Python基础
编程基础
print
函数的原理是输出仅一个单独的变量。- 当字符串变量内含有单引号,就用双引号来表示该字符串;当字符串变量内含有双引号,就用单引号来表示该字符串。
- 想在字符串中插入其他变量,可使用“f字符串”的方法。如下:
str1 = "你好"
str2 = f"{str1},dzk"
- 转义字符如
\n
、\t
只有在print
中才能生效,单独对字符串使用无效。 - 集合是无序的、不可重复的元素的组合。
- 注意,请勿用大括号创建空集合,否则会被误认为是字典。
- 字典中的元素值可以是任何变量。
- 字典中的索引只能是数字或字符串,且一般都是字符串。
- 集合、元组、列表、字典四者之间可以无缝切换。
- 转换为集合用
set
; - 转换为元组用
tuple
; - 转换为列表用
list
; - 转换为字典用
dict
。(注:在使用dict
函数时,需搭配zip
函数,zip
函数可将两个容器内的元素配对)
- 转换为集合用
- 一个类包含一个
__init__
方法,前后均有两个下划线,每一个类都必须包含此方法。 - 子类在特殊方法里使用
super()
函数,就可以继承到父类的全部属性与方法。 - 掠夺的本质是,将另一个类的实例当作
self
的属性用,这样一来,被掠夺的类的数量就不设上限。
面向对象编程(OPP)
- 类和对象
把所有具有相同属性和功能的事物称为类;而对象则是类的实例。
- 类名首字母通常大写
- 实例对象通过
.
调用类的属性和方法 - 调用类属性一定是同一个空间值
- 面向对象的优点
- 封装
- 继承
- 多态
- 代码复用(可维护性和可扩展性)
- 基础语法
- 创建类
class Student:# 构造方法def __init__(self, name):self.name = name# 属性Sname = "Tom"# ...# 实例方法def study(self):print("学习")# 类方法@classmethoddef show_name(cls):print(f"姓名:{cls.Sname}")# 静态方法@staticmethoddef add(x, y):return x + y# ...# 析构函数def __del__(self):print(f"{self.name}被删除")
self
:实例对象调用类方法时,首先会自动将该实例对象传给self
参数(不一定叫self
,什么名字都行,只是self
比较规范)。- 构造方法
__init__
:self.__class__.类属性/类方法
可访问类对象(类属性或类方法)
- 析构函数
__del__
:用于释放对象在创建时分配的资源,在对象被销毁时,会自动调用该方法。 - 私有属性:属性前有
__
- 私有属性只能在类内部使用
- 在类外可以使用
对象名._类名__私有属性名
来对私有属性进行访问
- 私有方法:方法名前有
__
- 静态方法既可以被类对象调用也可以被实例对象调用
- 类的实例化
# tom = Student()
tom = Student("Tom")
- 销毁对象:
del tom
- 实例属性赋值:
实例对象.属性 = 值
- Python中一切皆对象
Student
为类对象,可以调用空间存储的属性和方法。如Student.Sname = "Tom"
;但调用方法时,如Student.study()
会报错,必须传参。可以直接调用类方法Student.show_name()
- 类的继承
class S1(Student):def __init__(self, name):# 调用父类的__init__方法super.__init__(name)#...#...
- 方法重写:调用函数时,先查找子类中方法,若找不到,才到基类中查找。
Matplotlib库
- Matplotlib是Python中的一个数据可视化包,可绘制2D或3D图。
- 导入
import matplotlib.pyplot as plt
- 设置图片大小
plt.figure()
- 绘图
plt.plot()
- 保存
plt.savefig()
- 设置x、y轴的刻度
plt.xticks()
、plt.yticks()
- 添加x、y轴的描述信息
plt.xlabel()
、plt.ylabel()
- 设置图标题
plt.title()
- 散点图
plt.scatter()
- 条形图
plt.bar()
- 直方图
plt.hist()
NumPy库
import numpy as np
- 生成数组
a = np.array([1, 2, 3, 4, 5])
a = np.arrange(1, 6)
- 数据类型:
a.dtype
- 调整数据类型:
a.astype()
- 调整小数位数:
a.round()
- 查看数组形状:
a.shape
- 修改数组形状:
a = a.reshape()
- 将数组转成1维数据:
a.flatten()
- 数组与数字进行计算时,数组中每一位置数字进行相应运算;数组维度相同时是对应位置的数字进行相应的计算;数组维度不同时,当行/列某一维度相同时,按照相同维度来进行每行/列进行计算。
- 读取数据
t1 = np.loadtxt(file_path, delimiter, dtype)
- 读取一行:
t1[1]
- 读取连续的多行:
t1[1:]
- 读取不连续的多行:
t1[[1, 3, 5]]
- 读取一列:
t1[:, 1]
- 读取连续多列:
t1[:, 1:]
- 读取不连续多列:
t1[:, [1, 3, 5]]
- 读取指定位置元素:
t1[1, 2]
- 读取多行多列(读取行列交叉点的位置元素):
t1[1:2, 1:2]
- 读取不相邻元素(读取的是(0,1)、(1,2)位置元素):
t1[[0, 1],[1, 2]]
- 数值修改
- 布尔索引
- 将
t1
中小于6的位置置为0;t1[t1<6] = 0
- 将
- 三元运算符
- 将
t1
中小于等于1的替换成0,其余的替换成-1:np.where(t1<=1, 0, -1)
- 将
clip
(裁剪)- 将
t1
中小于6的替换为6,大于8的替换为8:t1.clip(6, 8)
- 将
- 布尔索引
nan
与inf
nan
:表示不是一个数字- 两个
nan
是不相等的 - 判断是否为
nan
:np.isnan(t1)
nan
和任何值计算都为nan
- 两个
inf
:表示无穷大
- 转置:
t1.T
、t1.transpose()
、t1.swapaxes(1, 0)
- 常用统计函数
- 求和:
t1.sum()
- 均值:
t1.mean()
- 中值:
np.median()
- 最大值:
t1.max()
- 最小值:
t1.min()
- 极值(最大值和最小值之差):
np.ptp()
- 标准差:
t.std()
- 求和:
- 数组拼接
- 竖直拼接:
np.vstack(t1, t2)
- 水平拼接:
np.hstack(t1, t2)
- 竖直拼接:
- 获取最大值/最小值的位置
- 最大值位置:
np.argmax()
- 最小值位置:
np.argmin()
- 最大值位置:
- 创建全为0的数组:
np.zeros()
- 创建全为1的数组:
np.ones()
- 创建对角线元素为1的方阵:
np.eye()
- 生成随机数:
np.random.rand()
等;随机数种子:np.random.seed()
t1 = t2
、t1 = t2[:]
:t1
和t2
会相互影响;t1 = t2.copy()
:t1
和t2
互不影响
OpenCV库
- 详见OpenCV文档
- OpenCV是一个计算机视觉处理的开源软件库
- 数字图像
- 数字图像用0/1记录信息,通常接触的图像都是8位数图像,包含0~255灰度,其中0代表最黑,1代表最白。
- 图像分类
- 二值图像(由0、1构成二维矩阵)
- 灰度图
- 彩色图(每个像素由R、G、B三个分量表示)
- 图像基础操作
- 读取图像
img = cv.imread('test.jpg', 0)
- 显示图像
cv.imshow('image', img)
、cv.waitKey(0)
- 保存图像
cv.imwrite('image.png', img)
- 绘制几何图形
- 绘制直线
cv.line(图像, 直线起点, 直线终点, 线条颜色, 线条宽度)
- 绘制圆形
cv.circle(图像, 圆心, 半径, 线条颜色, 线条宽度)
- 绘制矩形
cv.rectangle(图像, 矩形左上角坐标, 矩形右下角坐标, 线条颜色, 线条宽度)
- 向图像中添加文字
cv.putText(图像, 要写入的文本数据, 文本要放置的位置, 字体, 字体大小, 字体颜色, 线条宽度, cv.LINE_AA)
- 获取图像属性
- 形状:
img.shape
- 图像大小(像素数):
img.size
- 数据类型:
img.dtype
- 形状:
- 图像通道的拆分与合并
# 拆分
b, g, r = cv.split(img)
# 合并
img = cv.merge((b, g, r))
- 色彩空间改变
cv.cvtColor(要转换的图像, 转换类型:cv.COLOR_BGR2GRAY或cv.COLOR_BGR2HSV)
- 算数操作
- 图像的加法
cv.add()
(要求两个图像有相同的大小和类型,且第二个图像可以是标量值) - 图像的混合
cv.addWeighted(img1, img1的权重(0~1之间), img2, img2的权重(1-img1的权重), 0)
(要求图像大小相同)
- 图像几何变换
- 图像缩放
cv2.resize(输入图像, 绝对尺寸, 相对尺寸fx, 相对尺寸fy, 插值方法)
- 插值方法
- 双线性插值法
cv2.INTER_LINEAR
- 最近邻插值
cv2.INTER_NEAREST
- 像素区域重采样(默认插值方法 )
cv2.INTER_AREA
- 双三次插值
cv2.INTER_CUBIC
- 双线性插值法
- 插值方法
- 图像平移
cv.warpAffine(输入图像, 2×3平移矩阵, 输出图像大小"(宽度, 高度)"形式)
- 图像旋转
cv2.getRotationMatrix2D(旋转中心, 旋转角度, 缩放比例)
会返回一个旋转矩阵 - 仿射变换:首先用
cv2.getAffineTransform()
创建2×3的变换矩阵,然后传给cv2.warpAffine()
进行旋转。 - 透射变换:通过函数
cv.getPerspectiveTransform()
得到3×3变换矩阵,然后cv.warpPerspective
应用该变换矩阵。 - 图像金字塔(用于机器视觉和图像压缩)
# 图像上采样
cv.pyrUp(img)
# 图像下采样
cv.pyrDown(img)
- 形态学操作
形态学转换是基于二进制图像的一些简单操作
- 邻接关系
- 4邻接
- D邻接
- 8邻接
- 连通性
- 4连通
- 8连通
- m连通
- 腐蚀和膨胀
- 腐蚀是使图像拥有更小的高亮区域,是求局部最小值的操作:
cv.erode(要处理的图像, 核结构, 腐蚀的次数(默认为1))
- 膨胀是使图像中的高亮部分扩张,是求局部最大值的操作:
cv.dilate(要处理的图像, 核结构, 膨胀的次数(默认为1))
- 腐蚀是使图像拥有更小的高亮区域,是求局部最小值的操作:
- 开闭运算
- 开运算:先腐蚀后膨胀,分离物体,消除小区域。
- 闭运算:先膨胀后腐蚀,消除/闭合物体里的孔洞。
cv.morphologyEx(要处理的图像, 处理方式, 核结构)
- 处理方式
- 开运算:
cv.MORPH_OPEN
- 闭运算:
cv.MORPH_CLOSE
- 开运算:
- 处理方式
- 礼帽和黑帽
- 礼帽运算:原图像与开运算后结果图之差
- 黑帽运算:闭运算后结果图与原图像之差
cv.morphologyEx(要处理的图像, 处理方式, 核结构)
- 处理方式
- 闭运算:
cv.MORPH_CLOSE
- 开运算:
cv.MORPH_OPEN
- 礼帽运算:
cv.MORPH_TOPHAT
- 黑帽运算:
cv.MORPH_BLACKHAT
- 闭运算:
- 处理方式
- 图像平滑
图像平滑就是去除其中的高频信息,保留低频信息。
- 图像噪声
- 椒盐噪声:随机出现的白点或黑点
- 高斯噪声:噪声密度函数服从高斯分布的一类噪声
- 均值滤波
cv.blur(输入图像, 卷积核的大小, 位置 默认核中心(默认为(-1, -1)), 边界类型)
- 高斯滤波(去除高斯噪声)
cv.GaussianBlur(输入图像, 高斯卷积核大小(宽高都应为奇数,可不同), 水平方向的标准差, 垂直方向的标准差(默认为0, 与水平方向标准差相同), 填充边界类型)
- 中值滤波(去除椒盐噪声)
cv.mediaBlur(输入图像, 卷积核大小)
- 直方图
- 灰度直方图
- 直方图的计算与绘制
cv.calHist(原图像, 通道, 掩膜图像, BIN的数目, 像素值范围)
(所有非空非None
参数都应加[]
) - 掩膜
掩膜是用选定的图像、图形或物体,对要处理的图像进行遮挡,来控制图像处理的区域。 - 直方图均衡化
直方图均衡化是把原始图像的灰度直方图从比较集中的某个灰度区间变成在更广泛灰度范围内的分布,从而增强图像对比度。dst = cv.equalizeHist(img)
- 自适应均衡化(分块直方图均衡化)
cv.createCLAHE(对比度限制(默认为40), 分块的大小(默认为8×8))
- 边缘检测
边缘检测的目的是标识数字图像中亮度变化明显的点
- 基于搜索
- 基于零穿越
- Sobel检测算子
Sobel_x_or_y = cv.Sobel(传入的图像, 图像的深度, 求导阶数dx, 求导阶数dy, Sobel算子的大小, 缩放导数的比例常数, 图像边界的模式)
- laplacian算子
laplacian = cv2.Laplacian(...)
- Canny边缘检测
canny = cv2.Canny(灰度图, minval, maxval)
- 模板匹配与霍夫检测
- 模板匹配
res = cv.matchTemplate(要匹配的图像, 模板, 实现模板匹配的算法)
- 实现模板匹配的算法
- 平方差匹配
CV_TM_SQDIFF
- 相关匹配
CV_TM_CCORR
- 利用相关系数匹配
CV_TM_CCOEFF
- 平方差匹配
- 实现模板匹配的算法
- 霍夫变换
霍夫变换用来提取图像中的直线和圆等几何形状- 笛卡尔坐标系中的一条直线,对应于霍夫空间中的一个点。
- 霍夫检测前需要进行二值化或进行
Canny
边缘检测 - 霍夫线检测
cv.HoughLines(检测的图像, ρ和θ的精确度, 阈值)
- 霍夫圆检测
cv.HoughCircles(...)
- 图像分割算法
- 漫水填充法
- 步骤
- 选取种子点
- 以种子点为中心,判断4邻域或8邻域的像素值与种子点像素值的差值,将差值小于阈值的像素点添加进区域中。
- 将新加入的像素点作为新的种子点,重复b操作,知到没有新的像素点被添加进该区域。
- 步骤
- 分水岭法
- 步骤
- 排序过程:首先对图像像素的灰度级进行排序,确定灰度值较小的像素点,该像素点即为开始注水点。
- 淹没过程:对每个最低点开始不断注水,不断淹没周围的像素点,不同注水处的水汇集在一起,形成分割线。
- 步骤
- SIFT/SURF算法
SIFT算法用来侦测与描述影像中的局部性特征,在空间尺度中寻找极值点,并提取出其位置、尺度、旋转不变量。
- 尺度空间极值检测
- 关键点定位
- 关键方向确定
- 关键点描述
SURF算法是SIFT算法的增强版
SIFT算法实现
- 实例化sift
sift = cv.xfeatures2d.SIFT_create()
- 检测关键点并计算
kp, des = sift.detectAndCompute(要检测的灰度图像, None)
返回关键点信息和关键点描述符 - 在图像上绘制关键点检测结果
cv.drawKeypoints(原始图像, 关键点信息, 输出图像, 颜色设置, 绘图功能的标识设置)
- 相机校准和三维重建
- 相机校准
校准相机需要输入真实世界的3D点以及对应图像中的2D坐标(3D点称为对象点,2D点称为图像点)- 查找
pattern
:cv.findChessboardCorners()
(cv.findCirclesGrid()
) - 提高准确性:
cv.cornerSubPix()
- 绘制
pattern
:cv.drawChessboardCorners()
- 校准:
cv.calibrateCamera()
- 解失真
- 优化相机矩阵:
cv.getOptimalNewCameraMatrix()
- 消除扭曲
cv.undistort()
方法cv.initUndistortRectifyMap()
结合cv.remap()
方法
- 优化相机矩阵:
- Re-projection error
- 估计参数的精确度
- 首先需要使用
cv.projectPoints()
将对象点转为图像点,然后计算误差。
- 查找
- 位姿估计
- 求出旋转向量与平移向量:
cv.solvePnP()
- 将3D点投影到图像平面:
cv.projectPoints()
- 绘制3D效果
- 求出旋转向量与平移向量:
Pytorch基础
- 深度学习:是机器学习的一个分支,以人工神经网络为基础,对数据进行特征学习的算法。
- 神经网络:是一种模仿生物神经网络的结构和功能的数学模型,用于对函数进行估计或近似。
- 神经元:神经网络中的基础单元,相互连接组成神经网络。
t=f(wTA+b)t = f(w^TA+b) t=f(wTA+b)
- b为偏置
- f为激活函数(如
tanh
、sigmoid
、relu
等) - t为神经元的输出
- 单层神经网络:最简单的神经网络形式
- 感知机:由两层神经网络组成,是一个简单二分类模型,给定阈值来判断数据属于哪一部分。
- 多层神经网络
- 输入层
- 输入层
- 隐藏层:可以有多层,每一层的神经元个数可以不确定。
- 全连接层:当前一层和前一层每个神经元相互链接。
- 进行的是
y=wx+by = wx+b y=wx+b
- 进行的是
- 激活函数:增强模型的非线性分割能力
- 常见激活函数
sigmoid
:(0, 1)tanh
:(-1, 1)relu
:max(0, x)ELU
- 常见激活函数
张量(Tensor)
张量是一个统称,0阶张量(标量)、1阶张量(向量)、2阶张量(矩阵)…n阶张量
- 创建
torch.Tensor()
- 空的
tensor
:torch.empty()
- 全为1的
tensor
:torch.ones()
- 全为0的
tensor
:torch.zeros()
- 随机值的
tensor
:torch.rand()
- 随机整数的
tensor
:torch.randint()
- 随机整数的
- 空的
- 常用方法
tensor
中只有一个元素可用,获取tensor
中的数据:tensor.item()
- 转为
numpy
类型:tensor.numpy()
- 获取形状:
tesor.size()
- 改变形状:
tensor.view()
- 获取维数:
tensor.dim()
- 获取最大值:
tensor.max()
- 转置:
tensor.t()
- 获取指定值并改变:
tensor[3, 3] = 6
tensor
的数据类型
- 获取
tensor
的数据类型:tensor.dtype
- 指定数据类型:
torch.tensor(array, dtype)
、torch.ones(array, dtype)
- 修改数据类型
tensor.float()
、tensor.long()
、tensor.int()
torch.Tensor
和torch.tensor
的区别- 全局(默认数据类型)是
tensor.float32
tensor.Tensor()
传入数字表示形状和torch.FloatTensor
相同tensor.Tensor
传入可迭代对象表示数据,类型为模型的数据类型。torch.tensor
为创建tensor
的方法
- 全局(默认数据类型)是
- 原地修改:
张量1.add_(张量2)
会直接修改张量1
的值 - GPU中
tensor
的使用- 实例化
device
- 实例化
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
- 转为CUDA支持的
tensor
:tensor.to(device)
自动微分/求导(Autograd)
自动求导两种模式:正向累积、反向累积(反向传递)。
requires_grad = True
:后续会被计算梯度,更新该参数,默认值为False
,tensor
所有的操作都会被记录在grad_fn
中(with torch.no_grad():
其中的操作不会被追踪)。out.backward()
:反向传播,计算梯度,可求出导数d out / dx
,调用x.grad
(累加梯度)即可获取导数值。- 每次反向传播前需要先把梯度置为0再进行后续操作。
tensor.data
- 当
requires_grad = False
时,tensor.data
和tensor
等价 - 当
requires_grad = True
时,tensor.data
仅获取tensor
中的数据。
- 当
tensor.numpy()
:当requires_grad = True
时,需要使用tensor.detach().numpy()
。
数据集(Dataset) 和 数据加载器(DataLoader)
- 数据集类(
torch.utils.data.Dataset
)- 定义类继承自
Dataset
基类 __len__
方法:能够实现通过全局len()
方法获取其中的元素的个数。__getitem__
方法:能够通过传入索引的方式获取数据,例如通过dataset[i]
获取其中的第i
条数据。
- 定义类继承自
- 数据加载类(
torch.utils.data.DataLoader
)dataloader = DataLoader(dataset = dataset, batch_size = 10, shuffle = True)
dataset
:提前定义好的dataset
的实例batch_size
;传入数据的batch
的大小,常用128,256等。shuffle
:bool
类型,表示是否在每次获取数据时提前打乱数据。num_workers
:加载数据的线程数
len(dataset)
:数据集的样本数len(dataloader)
:math.ceil(样本数/batch_size)
(即向上取整)
神经网络模块(nn.Module)
- 激活函数
nn.ReLU
nn.Sigmoid
nn.Tanh
nn.Softmax
- 模型层
nn.Linear
nn.Conv2d
nn.MaxPool2d
nn.Dropout2d
nn.Embedding
- 损失函数
nn.BCELoss
nn.MSELoss
nn.CrossEntropyLoss
nn.Parameter()
具有默认requires_grad = True
的属性nn.ParameterList()
可以将多个nn.Parameter()
组成一个列表nn.ParameterDict()
可以将多个nn.Parameter()
组成一个字典- 使用
module.parameters()
返回一个生成器,包括其结构下的所有parameters(实践中一般通过继承nn.Module
来构建模块类,并将所有含有需要学习的参数部分放在构造函数中) - 使用
nn.Module
管理子模块(的方法)children()
:返回生成器,包括模块下的所有子模块;named_children()
:在children()
的基础上,附加返回所有子模块的名字;modules()
:返回生成器,包括模块下的所有各个层级的模块,包括模块本身;named_modules()
:在modules()
的基础上,附加返回模块的名字,包括模块本身。
优化器(Optimizer)
- 梯度下降算法(BGD)(全局最优)
- 随机梯度下降法(SGD)(随机的从样本中抽出一个样本进行梯度的更新)
- 小批量梯度下降(MBGD)(找一波数据计算梯度,使用均值更新参数,是最常用的优化算法)
- 动量法【冲量法】(对梯度进行平滑处理,防止振幅过大)
- AdaGrad(自适应学习率)
- RMSProp(对学习率进行加权)
- Adam(动量法+RMSProp,学习率能够自适应,梯度的振幅不会过大,对梯度做平滑,且对梯度各个维度值做重新调整):
torch.optim.Adam()
- 深度学习模型大多是非凸的
损失函数(Loss Function)
损失函数用来衡量预测值与真实值之间的区别
- L2 Loss
l(y,y′)=12(y−y′)2l\left(y, y^{\prime}\right) = \frac{1}{2}\left(y - y^{\prime}\right)^{2} l(y,y′)=21(y−y′)2 - L1 Loss
l(y,y′)=∣y−y′∣l\left(y, y^{\prime}\right) = \left| y - y^{\prime} \right| l(y,y′)=∣y−y′∣
- Huber’s Robust Loss
l(y,y′)={∣y−y′∣−12if ∣y−y′∣>112(y−y′)2otherwisel\left(y,y^{\prime}\right) = \left\{ \begin{array}{ll} \left|y-y^{\prime}\right| - \frac{1}{2} & \text{if } \left|y-y^{\prime}\right| > 1 \\ \frac{1}{2}\left(y-y^{\prime}\right)^{2} & \text{otherwise} \end{array} \right. l(y,y′)={∣y−y′∣−2121(y−y′)2if ∣y−y′∣>1otherwise
参考资料
- 特别鸣谢:腾讯元宝提供学习指导
- 20分钟学完一遍python基础
- Python深度学习:Python基础
- 一鼓作气拿下Python的拦路虎:面向对象
- 一次性搞懂python面向对象编程
- 【python教程】数据分析——numpy、pandas、matplotlib
- 231017直播回放-一夜之间学会Matplotlib
- 黑马程序员人工智能教程_10小时学会图像处理OpenCV入门教程
- 从零学习 OpenCV4
- 一个视频搞懂PyTorch~~
- Pytorch 入门到精通全教程 卷积神经网络 循环神经网络
- 神经网络基础知识
- 7、深入剖析PyTorch nn.Module源码
- 4-3,nn.functional和nn.Module
- 07 自动求导【动手学深度学习v2】
- 72 优化算法【动手学深度学习v2】
- 09 Softmax 回归 + 损失函数 + 图片分类数据集【动手学深度学习v2】