在 python 中, yaml 文件可以通过自带的 yaml
模块读取。然而读取 opencv 生成的 yaml 文件时发现,opencv 生成的 yaml 文件的格式跟标准的 yaml 文件有所不同,不能直接用 yaml
模块读取,需要用 opencv 提供的 API 读取。
在 opencv 中,可以通过FileStorage
类读取 yaml 文件。然而,官方文档仅对 c++ 版的FileStorage
有比较详细的说明,而对 python 版的介绍较少,故对 python 版的接口进行了梳理。
样例文件
下面是一个存有相机标定参数的 yaml 文件,命名为 camera.yaml
%YAML:1.0
---
CameraExtrinsicMat: !!opencv-matrix
rows: 1
cols: 4
dt: d
data: [1.363278865814209,-0.028192594647407532,-0.0095728300511837,-0.02877083793282509]
#[height,pitch,roll,yaw]
CameraMat: !!opencv-matrix
rows: 3
cols: 3
dt: d
data: [ 1863.498779296875, 0., 1007.947998046875, 0.,1863.498779296875, 550.4793090820313, 0., 0., 1. ]
#[fx,0,u0,0,fy,v0,0,0,1]
DistCoeff: !!opencv-matrix
rows: 1
cols: 5
dt: d
data: [-0.36602261662483215,0.13647031784057617, -0.0006322107510641217, -9.143427450908348e-05,0]
ImageSize: [ 1920, 1080 ]
ReprojectionError: 0
DistModel: plumb_bob
yaml 文件以 key-value 对的形式储存数据,相当于一个哈希表。可以看到,在上面的 yaml 文件中,主要有以下几种数据类型:
- opencv-matrix: 这是 opencv 规定的矩阵类型
- 列表(数组):即文件中的 “ImageSize”。在 opencv 中也叫 sequence。
- 数值:即文件中的 “ReprojectionError”。在 opencv 中称 real。
- 字符串:文件中的 “DistModel” 储存的是字符串。
下面将介绍如何用 opencv-python 读取 camera.yaml
中不同种类的数值
使用流程
opencv-python 读取 yaml 文件的基本流程如下:
- 实例化
FileStorage
- 根据 key 获得
FileNode
,并转换它的数据类型
yamlpath = "./camera.yaml"
cv_file = cv2.FileStorage(yamlpath, cv2.FILE_STORAGE_READ) # 实例化一个 FileStorage
mat = cv_file.getNode("...").mat() # getNode 成员函数将获得一个 FileNode 类型的对象,mat() 函数将 FileNode 转换为矩阵
可以看到 FileStorage
通过 getNode 成员函数得到 FileNode
类型的对象。FileNode
类型再作为桥梁,转换为我们想要的数据类型。下面介绍 FileNode
类进行数据转换的方法。
- 转换为矩阵
mat = cv_file.getNode("...").mat()
- 转换为实数
val = cv_file.getNode("...").real()
- 转换为字符串
str_ = cv_file.getNode("...").string()
- 转换为列表(数组):将 FileNode 转换为列表较为麻烦。下面只介绍如何将 FileNode 转换为一个存有实数的列表
res = []
for i in range(filenode.size()):
res.append(filenode.at(i).real())
实例:读取 camera.yaml
介绍完 opencv-python 读取 yaml 文件的流程后,最后介绍下如何用前面的知识来读取 camera.yaml
文件。
具体代码如下:
def readListInFileNode(filenode):
assert(filenode.isSeq())
res = []
for i in range(filenode.size()):
res.append(filenode.at(i).real())
return res
def readCameraCfg(yamlpath):
cv_file = cv2.FileStorage(yamlpath, cv2.FILE_STORAGE_READ)
res = {
"CameraExtrinsicMat": cv_file.getNode("CameraExtrinsicMat").mat(),
"CameraMat": cv_file.getNode("CameraMat").mat(),
"DistCoeff": cv_file.getNode("DistCoeff").mat(),
"ImageSize": readListInFileNode(cv_file.getNode("ImageSize")),
"ReprojectionError": cv_file.getNode("ReprojectionError").real(),
"DistModel": cv_file.getNode("DistModel").string()
}
return res
filepath = "/home/yxhuang/data/CICV/task4/task4/camera.yaml"
camera_cfg = readCameraCfg(filepath)
print(camera_cfg)