如果你使用的是realsense相机可以参见我封装好的代码: python RealSense相机 二次封装工具包_tycoer的博客-CSDN博客
问题: 最近在做 室内场景的三维重建, 需要同时储存深度图(depth_map)视频流和彩色图(color_img)的视频流, 即储存一系列连续的帧. realsense 提供了 .bag 文件格式用于记录视频流, 但该文件占用空间极大, 基本大概5~6秒的视频需要1G上下的硬盘空间(因为realsense在记录.bag文件时会同时记录多个视频流, 包含深度,彩色 , 左右emiter 的视频流, 当然也可以调api让其少记录几个视频流,但文件依旧很大), 故需要其他的一种文件格式来记录视频流以获得更小的储存空间:
方案如下(python 实现):
color_img.avi : 采用opencv 中的 cv2.VideoWriter直接以.avi格式录制, 以及cv2.VideoCapture(视频拆分) ;
depth_map.h5: 采用h5文件(由于depth_map采集出来的都是uint16的格式, 然而opencv不支持uint16的视频录制,仅支持uint8视频录制, 本人曾尝试多种编码例如'H265'的,但均不能正常录制, github上有一篇说是可以采取.oni格式, 但这个未尝试不置评): 很重要的一条是该depth_map需要使用cv2.imencode 进行编码, h5文件大小便会急剧下降, 代码:
#保存
import cv2
import h5py # 版本3.2.1
# 初始化 h5 文件
h5=h5py.File('./depth_map.hdf5','w') #创建一个空的h5文件
# 初始化 avi文件
fourcc = cv2.VideoWriter_fourcc(*'XVID')
avi = cv2.VideoWriter('color_img.avi',fourcc, 30, (1280, 720),True)
# cv2.VideoWriter参数:
# 1-需保存视频的文件名
# 2-解码参数
# 3-fps帧率
# 4-分辨率
# 5-True为彩色视频, False为黑白视频
id = 0
while True:
# 这里可能有一大段从相机api获取depth_map, color_img的代码
# 例如:
# depth_map, color_img = camera.get_frame() # camera.get_frame()是我瞎编的, 根据你自己
# 使用的相机api来写吧
# depth_map, color_img 应为 numpy.ndarray
# depth_map的典型shape为 (720, 1280) uint16, color_img的典型shape为 (720, 1280, 3) uint8
# 把彩色图写入 avi
avi.write(color_img)
# 把深度图写入 h5
res, depth_map_encode=cv2.imencode('.png',depth_map) # 编码depth_map 将depth_map 传入, res为bool, 用于判断是否编码成功
# .png的原因: opencv 支持将16位图片写入.png, .jpg仅能写入8位图片
h5[str(id)] = depth_map_encode # h5类似于字典, 即key 对应 data, 并且key不能重复
id += 1
'''
一般会设置某个按键 让相机退出视频流并结束循环
if 按键:
break
'''
h5.close() #关闭文件
将h5文件拆解成图片:
import os
import h5py # 版本3.2.1
import cv2
import numpy as np
def h52img(h5_path,save_dir='./'):
# h5_path: h5文件路径
# save_dir: 图片需要存入文件夹
h5 = h5py.File(h5_path, 'r')
os.makedirs(save_dir,exist_ok=True)
for key in h5.keys():
img=cv2.imdecode(np.array(h5[key]),-1) # 解码
img_name = os.path.join(save_dir, key) if key.endswith('.png') else os.path.join(save_dir, key +'.png' )# 判断文件名是否以 '.png' 结尾
cv2.imwrite(img_name ,img)
h5.close()
另外, 如果后续 使用opencv接口 cv2.imread读取 .png文件, 应设置flags = cv2.IMREAD_UNCHANGED 即 img = cv2.imread('xxx.png', flags = cv2.IMREAD_UNCHANGED), 才能正确的将 '.png' 文件读取为uint16格式单通道, 如果未设置flags = cv2.IMREAD_UNCHANGED 即 img=cv2.imread('xxx.png'), 将导致读取的'.png'文件为uint8三通道.
结果:
可以看到大约7秒的视频(二者分辨率均为1280x720, 帧率30fps), color_img.avi 大约仅有7M, depth_map.h5 大约仅有13.5M