python RealSense相机 二次封装工具包

个人原创, 工作之初写的, 很多封装还是比较稚嫩,  一些注释后续会补上,  大部分 Realsense viewer.exe 中的功能都实现了

电脑插上realsense并安装驱动后可直接运行该文件, 按s拍照,按space录像,按esc退出,按d清空save_dir下所有图片

import cv2
import pyrealsense2 as rs
import numpy as np
import os
import json
import h5py
import time
class realsense:
    # 支持机型 415 435 515 (这仨实测过) 其他机型未知估计问题也不大
    def __init__(self,config=None):
        self.config=config if config!=None else rs.config()
        self.name,self.serial_number = self.getCamInfo()

    def start(self,resolution='720p',depth_preset=3,fps=30,
              color_scheme=0,histogram_equalization=True,enable_ir_emitter=True):
        '''
        开启realsense, 若想使用realsense, 必须先调用这一句

        Parameters
        ----------
        resolution :str, optional
            分辨率, 仅支持两种分辨率 '720p' -- (1280x720 长x宽). The default is '720p'.
        depth_preset : int, optional
            深度模式设置 区间[0,5],The default is 3.
            常用  3:高精度模式
                  4: 高密度模式
                  5: 中密度模式
        fps : 帧率, optional
            支持 30,16,8. The default is 30.
        color_scheme : int, optional
            深度图着色模式,区间[0,8]. The default is 0.
        histogram_equalization : bool, optional
            是否对对着色的深度图作直方图均布. The default is True.
        enable_ir_emitter :bool, optional
            ir发射器, 一般需要开启, 不开的话深度图质量会下降. The default is True.

        Returns
        -------
        None.

        '''


        # 开启realsense        
        if resolution=='480p':
            self.W,self.H=640, 480
        elif resolution=='720p':
            self.W,self.H=1280, 720
        else:
            self.W,self.H=1280, 720
            resolution = '720p'
            
        self.resolution=resolution
        self.fps=fps        
        
        if self.name[0] == 'Intel RealSense L515':
            self.config.enable_stream(rs.stream.depth,1024,768, rs.format.z16, fps)
            
        else:
            self.config.enable_stream(rs.stream.depth, self.W, self.H, rs.format.z16, fps)
            
        self.config.enable_stream(rs.stream.color, self.W, self.H, rs.format.bgr8, fps)
        self.pipeline = rs.pipeline()

        self.pipeline_profile = self.pipeline.start(self.config)
        
        # 设置彩色相机
        color_sensor=self.pipeline_profile.get_device().query_sensors()[1] 
        color_sensor.set_option(rs.option.sharpness,100) # 设置锐度为100(最大)
        
        # 设置深度模式
        self.depth_sensor=self.pipeline_profile.get_device().first_depth_sensor()
        self.depth_sensor.set_option(rs.option.visual_preset,depth_preset) # 设置深度模式
#        self.depth_sensor.set_option(rs.option.emitter_enabled, 1 if enable_ir_emitter else 0)
        self.depth_scale = self.depth_sensor.get_depth_scale()    # 获取深度比例因子

        self.default_align = rs.align(rs.stream.color) # 定义一个默认 对齐参数
        self.default_colorizer=rs.colorizer() # 定义一个默认 深度图着色器
        self.default_colorizer.set_option(rs.option.color_scheme,color_scheme)
        self.default_colorizer.set_option(rs.option.histogram_equalization_enabled,
                                          histogram_equalization)
        

    def stop(self):
        # 关闭realsense
        self.pipeline.stop()
        
    def getCamInfo(self):
        # 获取realsense 型号 及 出厂序列号
        ctx = rs.context()
        serial_numbers=()
        names=()
        if len(ctx.devices) > 0:
            for i in ctx.devices:
                serial_number=i.get_info(rs.camera_info.serial_number)
                name=i.get_info(rs.camera_info.name)
                serial_numbers+=(serial_number,)
                names+=(name,)
                print ('Found device: ',
                name, ' ',
                serial_number)
        else:
            print("No Device Connected")
        return names,serial_numbers
    
    #######################raw_frame#######################
    # raw_frame的单独处理
    # 以应对 循环,或 单帧需要多种处理的情况
    def rawFrame(self):
        # 获取 一帧raw_frame
        raw_frames =self.pipeline.wait_for_frames()
        return raw_frames
    
    def rawProcess(self,raw_frames,*post_filter,align=None):
        '''
        处理raw_frame, 拆分其为 raw_color_frame,raw_depth_frame

        Parameters
        ----------
        raw_frames : obj
            DESCRIPTION.
        *post_filter : 滤波器
            可不填.
        align : obj, optional
            若为None则使用默认的对齐参数, 否则使用用户定义的对齐参数. The default is None.

        Returns
        -------
        
        raw_color_frame : obj
            DESCRIPTION.
        raw_depth_frame : obj
            DESCRIPTION.

        '''
        if len(post_filter)!=0:
            for i in post_filter:
                raw_frames=i.process(raw_frames)
            raw_frames=raw_frames.as_frameset()
            
        # 对齐
        aligned_frame=self.default_align.process(raw_frames) if align==None\
        else align.process(raw_frames)
        raw_depth_frame = aligned_frame.get_depth_frame()
        raw_color_frame = aligned_frame.get_color_frame()
        return raw_color_frame,raw_depth_frame
    
    def rawColorize(self,raw_depth_frame,colorizer=None):
        # 着色
        raw_depth_frame_colorized=self.default_colorizer.colorize(raw_depth_frame) \
        if colorizer==None else colorizer.colorize(raw_depth_frame)
        return raw_depth_frame_colorized
        
    def raw2Numpy(self,raw_frame):
        # raw_frame转为 numpy 的array
        numpy_frame=np.asanyarray(raw_frame.get_data())
        return numpy_frame
    
    #########################################################
    
    def numpyFrame(self,*post_filter,colorizer=None,align=None):
        # 单次拍照,直接将图像转为numpy.array--即raw_frame函数的集合体
        # 共返回3个array, 分别为
        # color_img: 彩色图片 uint8,3通道
        # depth_map: 深度图   uint16,单通道
        # depth_img: 着色后的深度图 uint8, 3通道
    
        color_depth_frame =self.pipeline.wait_for_frames()
        if len(post_filter)!=0:
            for i in post_filter:
                color_depth_frame=i.process(color_depth_frame)
            color_depth_frame=color_depth_frame.as_frameset()
        # 官方文档明确说明, 滤波器应先于对齐
        # 对齐
        aligned_frame=self.default_align.process(color_depth_frame)
        raw_depth_frame = aligned_frame.get_depth_frame()
        raw_color_frame = aligned_frame.get_color_frame()
        # 如果未自定义colorizer,则使用预定义的colorizer
        raw_depth_frame_colorized=self.default_colorizer.colorize(raw_depth_frame) \
        if colorizer==None else colorizer.colorize(raw_depth_frame)
        depth_map = np.asanyarray(raw_depth_frame.get_data())
        color_img = np.asanyarray(raw_color_frame.get_data())
        depth_img = np.asanyarray(raw_depth_frame_colorized.get_data())
        return color_img,depth_map,depth_img
    
    def getCamPara(self,save=False,save_dir='./cam_para',unaligned=False):
        
        # 获取color_sensor 及 depth_sensor的相机内参, 畸变系数
        frames = self.pipeline.wait_for_frames()
        aligned_frames = self.default_align.process(frames)
        depth_frame = aligned_frames.get_depth_frame()
        color_frame = aligned_frames.get_color_frame()
        
        # 下面两行的所求的内参为相机的原始内参, 但经对齐后相机内参有变
        if unaligned==True:
            color_intr = self.pipeline_profile.get_stream(rs.stream.color).as_video_stream_profile().get_intrinsics()
            depth_intr = self.pipeline_profile.get_stream(rs.stream.depth).as_video_stream_profile().get_intrinsics()
        # 下两行语句为相机对齐后的内参, 有别于原始内参
        else:
            color_intr = color_frame.profile.as_video_stream_profile().intrinsics
            depth_intr = depth_frame.profile.as_video_stream_profile().intrinsics
        
        depth_to_color_extr = depth_frame.profile.get_extrinsics_to(color_frame.profile)

        depth_cam_matrix=np.array([
                        [depth_intr.fx,0,depth_intr.ppx],
                        [0,depth_intr.fy,depth_intr.ppy],
                        [0,            0,             1]])
        depth_scale = self.depth_sensor.get_depth_scale()    
        depth_dist_coeff=np.array(depth_intr.coeffs)

        color_cam_matrix=np.array([
                        [color_intr.fx,0,color_intr.ppx],
                        [0,color_intr.fy,color_intr.ppy],
                        [0,            0,             1]])
    
        color_dist_coeff=np.array(color_intr.coeffs)
        cam_para={'color_cam_matrix':color_cam_matrix,
                  'color_dist_coeff':color_dist_coeff,
                  'depth_cam_matrix':depth_cam_matrix,
                  'depth_dist_coeff':depth_dist_coeff,
                  'depth_scale':depth_scale,
                  'W':color_intr.width,
                  'H':color_intr.height}
        if save==True:
            cam_para_json={'color_cam_matrix':color_cam_matrix.tolist(),
                           'color_dist_coeff':color_intr.coeffs,
                          
                           'depth_cam_matrix':depth_cam_matrix.tolist(),
                           'depth_dist_coeff':depth_intr.coeffs,
                           'depth_scale':depth_scale,
                          
                           'W':color_intr.width,
                           'H':color_intr.height}
            
            if os.path.exists(save_dir)==False:
                os.mkdir(save_dir)
            name =input('请输入文件名: ')
            if len(name)!=0:
                if not name.endswith('.json'):
                    name = ''.join([name,'.json'])
                path=os.path.join(save_dir,name)
                with open(path,'w') as f:
                    json.dump(cam_para_json,f,indent=1)
                print('已保存相机参数至',path)
            else:
                print('未检测到文件名输入!')
        return color_intr,depth_intr,depth_to_color_extr,cam_para
    
    def videoStream(self,*post_filter,max_shotcut=100,colorizer=None,align=None,
                    save_depth_img=True,save_depth_map=True,
                    record_color_img=True,record_depth_img=True,record_depth_map=True,
                    color_img_video_name='color_img.avi',depth_img_video_name='depth_img.avi',
                    depth_map_h5_name='depth_map.h5',save_dir='./realsense'):
        os.makedirs(save_dir,exist_ok=True)
        video_save_dir = save_dir+'/video'
        os.makedirs(video_save_dir,exist_ok=True)        

        print('按s拍照,按space录像,按esc退出,按s清空save_dir下所有图片,图片或视频保存于', save_dir)
        shotcut=0
        frame_id=0
        win_name='Realsense'+ '(' +self.resolution + ')'
        
        fourcc = cv2.VideoWriter_fourcc(*'XVID')
        # cv2.VideoWriter参数:
        # 1-需保存视频的文件名
        # 2-解码参数
        # 3-fps帧率
        # 4-分辨率
        # 5-True为彩色视频, False为黑白视频
        if record_color_img==True:
            color_img_video_path=os.path.join(video_save_dir,color_img_video_name)
            color_img_video= cv2.VideoWriter(color_img_video_path,
                                             fourcc,self.fps, (self.W,self.H),True)
        if record_depth_img==True:
            depth_img_video_path=os.path.join(video_save_dir,depth_img_video_name)
            depth_img_video= cv2.VideoWriter(depth_img_video_path,
                                             fourcc,self.fps, (self.W,self.H),True)
        if record_depth_map==True:
            depth_map_h5_path=os.path.join(video_save_dir,depth_map_h5_name)
            depth_map_h5=h5py.File(depth_map_h5_path,'w')
            
        cv2.namedWindow(win_name, cv2.WINDOW_NORMAL)
        #录像参数初始化
        recording=False 
        while 1:
            # try用于预防相机松动,连接线接触不良等以外情况导致的程序卡死
            try:
                key = cv2.waitKey(1) & 0xFF    
                color_img, depth_map, depth_img=self.numpyFrame(*post_filter,colorizer=colorizer,align=align)
                color_depth=np.hstack((color_img, depth_img))
                cv2.imshow(win_name, color_depth)
                # 录像
                if key== 32 and recording==False:
                    recording = True
                    print('recording start')
                    cv2.setWindowTitle(win_name, win_name+'--recording...')
                elif key== 32 and recording==True:
                    recording = False
                    print('recording end')
                    cv2.setWindowTitle(win_name,win_name)
                if recording==True:
                    frame_id+=1
                    if record_color_img==True:
                        color_img_video.write(color_img)
                    if record_depth_img==True:
                        depth_img_video.write(depth_img)
                    if record_depth_map==True:
                        depth_map_name='depth_map'+str(frame_id).zfill(6)+'.png'
                        depth_map_buffer=cv2.imencode('.png',depth_map)[1]
                        depth_map_h5[depth_map_name]=depth_map_buffer
    
                # 拍照
                if key== ord('s'):
                    self.saveImg(color_img,shotcut,name='color_img',suffix='.jpg',save_dir=save_dir)
                    if save_depth_map==True:
                        self.saveImg(depth_map,shotcut,name='depth_map',suffix='.png',save_dir=save_dir)
                    if save_depth_img==True:
                        self.saveImg(depth_img,shotcut,name='depth_img',suffix='.jpg',save_dir=save_dir)
                    print('shotcut'+str(shotcut))
                    shotcut+=1
                    if shotcut==max_shotcut:
                        shotcut=0
                        
                # 删除文件夹下所有color_img,depth_map,depth_img
                if key==ord('d'):
                    for i in os.listdir(save_dir):
                        if 'color_img' in i or 'depth_map' in i or 'depth_img' in i:
                            path = os.path.join(save_dir,i)
                            if path.endswith('.jpg') or path.endswith('.png'):
                                os.remove(path)
                    print('图片已清空')
                    shotcut=0
                
                # 退出
                if key== 27: # esc
                    break
            
            # 若意外发生则关闭循环
            except:
                print('error!')
                break
        cv2.destroyAllWindows()
        if record_depth_map==True:
            depth_map_h5.close()
        if record_depth_img==True:
            depth_img_video.release()
        if record_color_img==True:
            color_img_video.release()
        print('video stream end')

    # 凡带filter字段的函数均为滤波器,如需使用, 请输入予函数中的 post_filter参数
    # filter 下注释为参数取值范围(左闭右闭)
    def align(self,alignto='color'):
        if alignto=='color' or alignto==0:
            alignto=rs.stream.color
        elif alignto=='depth' or alignto==1:
            alignto=rs.stream.depth
        elif alignto=='any' or alignto==2:
            alignto=rs.stream.any
        align=rs.align(alignto)
        return align
    
    def filterTemporal(self, alpha=0.5, delta=20):
        # 时间滤波器
        # alpha [0:1]
        # delta [0:100]
        filter_temporal = rs.temporal_filter()
        filter_temporal.set_option(rs.option.filter_smooth_alpha,alpha)
        filter_temporal.set_option(rs.option.filter_smooth_delta,delta)
        return filter_temporal
    def filterThreshold(self,min_distance=0,max_distance=16):
        # 距离滤波, [min_distance,max_distance]区间内的深度予以保留, 其他区间深度置0
        # distance [0:16] 单位m
        filter_threshold=rs.threshold_filter()
        filter_threshold.set_option(rs.option.min_distance,min_distance)
        filter_threshold.set_option(rs.option.max_distance,max_distance)
        return filter_threshold
    def filterSpatial(self,magnitude=2,alpha=0.5,delta=20):
        # magnitude [0:5] 整数
        # alpha [0:1]
        # delta [0:100]
        # 保边平滑滤波器
        # 根据根据realsense官方文档, 该滤波器 功能与 cv2.edgePreservingFilter (flags = cv2.RECURS_FILTER)完全一致
        # 即原论文中的'recursive filter'
        # 注意此处flag 不为 cv2.NORMCONV_FILTER, 即原论文中normalized convolution fliter 由于该方法对导数采用后处理导致计算效率极低
        # recursive filter 效率高于 normalized convolution fliter,但二者效果相近
        # 待复现,原因为cv2.edgePreservingFilter 仅支持uint8 图片处理
        # 参见realsense文档https://github.com/IntelRealSense/librealsense/blob/master/doc/post-processing-filters.md

        filter_spatial=rs.spatial_filter()
        filter_spatial.set_option(rs.option.filter_magnitude,magnitude)
        filter_spatial.set_option(rs.option.filter_smooth_alpha,alpha)
        filter_spatial.set_option(rs.option.filter_smooth_delta,delta)
        return filter_spatial
    
    def filterDecimation(self,magnitude=2):
        # 降采样滤波
        # magnitude [0:5] 整数
        filter_decimation=rs.decimation_filter()
        filter_decimation.set_option(rs.option.filter_magnitude,magnitude)
        return filter_decimation
    def filterDisparity(self):
        # 不建议使用
        disparity = rs.disparity_transform()
        return disparity
    
    def filterHoleFilling(self,filling_mode=1):
        # filling_mode [0:3] 整数
        filter_hole_filling=rs.hole_filling_filter()
        filter_hole_filling.set_option(rs.option.holes_fill,filling_mode)
        return filter_hole_filling
    
    def colorizer(self,color_scheme=0,histogram_equalization=True):
        # colorizer[0,8]
        # histogram_equalization[True,False]
        # 着色器
        colorizer=rs.colorizer()
        colorizer.set_option(rs.option.color_scheme,color_scheme)
        colorizer.set_option(rs.option.histogram_equalization_enabled,histogram_equalization)
        return colorizer
    
    def saveImg(self,img,num=0,name='',suffix='.jpg',zfill=4,save_dir ='./realsense'):
        path=''.join([save_dir,'/',name,str(num).zfill(zfill),suffix])
        res=cv2.imwrite(path,img)
        return res
        
    def readColorDepth(self, read_dir='./realsense'):
        # 读取文件夹下所有的 color_img,depth_map,depth_img
        color_img=()
        depth_map=()
        depth_img=()
        
        for i in os.listdir(read_dir):
            path=os.path.join(read_dir,i)
            if 'color_img' in i:
                color_img+=(cv2.imread(path),)
            elif 'depth_map' in i:
                depth_map+=(cv2.imread(path,-1),) #cv2.IMREAD_ANYDEPTH=2
            elif 'depth_img' in i:
                depth_img+=(cv2.imread(path),)
        return color_img, depth_map, depth_img  
    
class realsenseManager:
    # 多相机时使用
    def __init__(self):
        self.serial_numbers=self.getCamInfo()[1]
        
    def getCamInfo(self):
        # 获取realsense 型号 及 出厂序列号
        ctx = rs.context()
        serial_numbers=()
        names=()
        if len(ctx.devices) > 0:
            for i in ctx.devices:
                serial_number=i.get_info(rs.camera_info.serial_number)
                name=i.get_info(rs.camera_info.name)
                serial_numbers+=(serial_number,)
                names+=(name,)
                print ('Found device: ',
                name, ' ',
                serial_number)
        else:
            print("No Device Connected")
        return names,serial_numbers
    
    def enableDevice(self,serial_number):
        # 可以输入相机的序列号
        # 如果输入的序列号为1,2,3... 则默认为用户指定 第几个 设备
        if type(serial_number)==int and serial_number<10:
            serial_number=self.serial_numbers[serial_number]            
        config = rs.config()
        config.enable_device(serial_number)
        return realsense(config)


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


if __name__ == '__main__':
    cam=realsense()  # 实例化
    
    # 打开相机
    cam.start(resolution='720p',depth_preset=3,
              color_scheme=0,histogram_equalization=True,enable_ir_emitter=1)
    color_intr,depth_intr,depth_to_color_extr,cam_para=cam.getCamPara()
    
    # 定义滤波器
    filter_holefilling = cam.filterHoleFilling(1)
    filter_spatial = cam.filterSpatial(5,0.25,50)
    filter_decimation = cam.filterDecimation(3)
    
    # 开启视频流
    cam.videoStream(filter_decimation,filter_spatial,max_shotcut=100,save_dir='./test')

    # 获取相机参数
    # color_intr,depth_intr,depth_to_color_extr,cam_para\
    # =cam.getCamPara(save=False,save_dir='./cam_para')

    # 关闭相机
    cam.stop()


    # 拆解录制的深度图视频
    # h52img(./realsense/depth_map.h5, save_dir='./realsense/depth_map')

    # 拆解彩色视频, 百度吧网上多的是

你可能感兴趣的:(python,计算机视觉,开发语言)