Intel Realsense API 使用记录(Python)

官方API文档

官方API链接——pyrealsense2
官方示例链接——examples

获得相机不同传感器之间的外参转换矩阵以及内参矩阵

python代码

import pyrealsense2 as rs

pipeline = rs.pipeline()
config = rs.config()
config.enable_stream(rs.stream.depth, 640, 480, rs.format.z16, 30)
config.enable_stream(rs.stream.color, 1280, 720, rs.format.rgb8, 30)
cfg = pipeline.start(config)
device = cfg.get_device()
name = device.get_info(rs.camera_info.name)
print(name)
profile = cfg.get_stream(rs.stream.depth)
profile1 = cfg.get_stream(rs.stream.color)
intr = profile.as_video_stream_profile().get_intrinsics()
intr1 = profile1.as_video_stream_profile().get_intrinsics()
extrinsics = profile1.get_extrinsics_to(profile)
print(extrinsics)
print("深度传感器内参:", intr)
print("RGB相机内参:", intr1)

运行结果如下:

Intel RealSense L515
rotation: [0.999965, -0.00754261, -0.0034929, 0.00762357, 0.999688, 0.0237769, 0.00331248, -0.0238027, 0.999711]
translation: [-0.000389581, -0.0143939, 0.0066153]
深度传感器内参: [ 640x480  p[306.57 254.527]  f[461.453 461.469]  None [0 0 0 0 0] ]
RGB相机内参: [ 1280x720  p[658.633 353.42]  f[898.366 898.432]  Brown Conrady [0.151657 -0.50863 -0.000700379 -0.000860805 0.471284] ]

注意
不同分辨率的深度相机和RGB相机对应不同的内参参数,但是外参矩阵是一样的(上述代码的外参指的是RGB相机转换到深度相机的转换矩阵),可以观察到RGB相机有畸变参数,深度相机无畸变参数

python代码

import pyrealsense2 as rs

pipeline = rs.pipeline()
config = rs.config()
config.enable_stream(rs.stream.depth, 640, 480, rs.format.z16, 30)
config.enable_stream(rs.stream.color, 960, 540, rs.format.bgr8, 30)
profile = pipeline.start(config)
frames = pipeline.wait_for_frames()
depth = frames.get_depth_frame()
color = frames.get_color_frame()
# 获取内参
depth_profile = depth.get_profile()
print(depth_profile)
# <pyrealsense2.video_stream_profile: Depth(0) 640x480 @ 30fps Z16>
print(type(depth_profile))
# <class 'pyrealsense2.pyrealsense2.stream_profile'>
print(depth_profile.fps())
# 30
print(depth_profile.stream_index())
# 0
print(depth_profile.stream_name())
# Depth
print(depth_profile.stream_type())
# stream.depth
print('', depth_profile.unique_id)
# <bound method PyCapsule.unique_id of <pyrealsense2.video_stream_profile: Depth(0) 640x480 @ 30fps Z16>>

color_profile = color.get_profile()
print(color_profile)
# <pyrealsense2.video_stream_profile: Color(0) 960x540 @ 30fps BGR8>
print(type(color_profile))
# <class 'pyrealsense2.pyrealsense2.stream_profile'>
print(depth_profile.fps())
# 30
print(depth_profile.stream_index())
# 0

cvsprofile = rs.video_stream_profile(color_profile)
dvsprofile = rs.video_stream_profile(depth_profile)

color_intrin = cvsprofile.get_intrinsics()
print(color_intrin)
# 960x540  p[493.975 265.065]  f[673.775 673.824]  Brown Conrady [0.151657 -0.50863 -0.000700379 -0.000860805 0.471284] 
depth_intrin = dvsprofile.get_intrinsics()
print(depth_intrin)
# [ 640x480  p[306.57 254.527]  f[461.453 461.469]  None [0 0 0 0 0] ]
extrin = depth_profile.get_extrinsics_to(color_profile)
print(extrin)
# rotation: [0.999965, 0.00762357, 0.00331248, -0.00754261, 0.999688, -0.0238027, -0.0034929, 0.0237769, 0.999711]
# translation: [0.000304107, 0.0142351, -0.00695471]

外参转换矩阵的含义

从官方API找到
可以看到最终得到的外参矩阵的平移向量是以米作为单位的,这一点要注意

获取设备的传感器信息

python代码

import pyrealsense2 as rs
import pyrealsense2 as rs

pipeline = rs.pipeline()
config = rs.config()
pipeline_wrapper = rs.pipeline_wrapper(pipeline)
pipeline_profile = config.resolve(pipeline_wrapper)
device = pipeline_profile.get_device()
for s in device.sensors:
    print(s.get_info(rs.camera_info.name))

cfg = pipeline.start(config)
device1 = cfg.get_device()
for s in device1.sensors:
    print(s.get_info(rs.camera_info.name))

运行结果如下:

L500 Depth Sensor
RGB Camera
Motion Module
L500 Depth Sensor
RGB Camera
Motion Module

分析

config.resolve(pipeline_wrapper)
pipeline.start(config)

以上两个语句得到的都是pipeline_profile的类,有get_device的方法

获得深度图单位和米之间的映射

python代码

# Create a pipeline
pipeline = rs.pipeline()
# Start streaming
profile = pipeline.start()

# Getting the depth sensor's depth scale (see rs-align example for explanation)
depth_sensor = profile.get_device().first_depth_sensor()
depth_scale = depth_sensor.get_depth_scale()
print("Depth Scale is: ", depth_scale)

运行结果如下:

Depth Scale is:  0.0002500000118743628

在这里插入图片描述
下面还有另外一种方法:

扫描二维码关注公众号,回复: 13349676 查看本文章

python代码

# First import the library
import pyrealsense2 as rs


# Create a context object. This object owns the handles to all connected realsense devices
pipeline = rs.pipeline()

# Configure streams
config = rs.config()
config.enable_stream(rs.stream.depth, 640, 480, rs.format.z16, 30)

# Start streaming
pipeline.start(config)

frames = pipeline.wait_for_frames()
depth = frames.get_depth_frame()
# Provide the scaling factor to use when converting from get_data() units to meters
print("depth_scale:", depth.get_units())

运行结果如下:

depth_scale: 0.0002500000118743628

解释说明

get_depth_scale的目标是获得深度图单位与米单位之间的映射关系,所以上述表示一个深度图单位等于0.00025米。第二个例子通过get_data方法获得的数值与米单位之间需要乘以一个比例系数。

深度图与RGB图配准

python 代码

# First import the library
import pyrealsense2 as rs
# Import Numpy for easy array manipulation
import numpy as np
# Import OpenCV for easy image rendering
import cv2

# Create a pipeline
pipeline = rs.pipeline()

# Create a config and configure the pipeline to stream
#  different resolutions of color and depth streams
config = rs.config()

# Get device product line for setting a supporting resolution
pipeline_wrapper = rs.pipeline_wrapper(pipeline)
pipeline_profile = config.resolve(pipeline_wrapper)
device = pipeline_profile.get_device()
device_product_line = str(device.get_info(rs.camera_info.product_line))

found_rgb = False
for s in device.sensors:
    if s.get_info(rs.camera_info.name) == 'RGB Camera':
        found_rgb = True
        break
if not found_rgb:
    print("The demo requires Depth camera with Color sensor")
    exit(0)

config.enable_stream(rs.stream.depth, 640, 480, rs.format.z16, 30)

if device_product_line == 'L500':
    config.enable_stream(rs.stream.color, 1280, 720, rs.format.bgr8, 30)
else:
    config.enable_stream(rs.stream.color, 640, 480, rs.format.bgr8, 30)

# Start streaming
profile = pipeline.start(config)

# Getting the depth sensor's depth scale (see rs-align example for explanation)
depth_sensor = profile.get_device().first_depth_sensor()
depth_scale = depth_sensor.get_depth_scale()
print("Depth Scale is: ", depth_scale)

# We will be removing the background of objects more than
#  clipping_distance_in_meters meters away
clipping_distance_in_meters = 1  # 1 meter
clipping_distance = clipping_distance_in_meters / depth_scale

# Create an align object
# rs.align allows us to perform alignment of depth frames to others frames
# The "align_to" is the stream type to which we plan to align depth frames.
align_to = rs.stream.color
align = rs.align(align_to)

# Streaming loop
try:
    while True:
        # Get frameset of color and depth
        frames = pipeline.wait_for_frames()
        # frames.get_depth_frame() is a 640x360 depth image

        # Align the depth frame to color frame
        aligned_frames = align.process(frames)

        # Get aligned frames
        aligned_depth_frame = aligned_frames.get_depth_frame()  # aligned_depth_frame is a 640x480 depth image
        color_frame = aligned_frames.get_color_frame()

        # Validate that both frames are valid
        if not aligned_depth_frame or not color_frame:
            continue

        depth_image = np.asanyarray(aligned_depth_frame.get_data())
        color_image = np.asanyarray(color_frame.get_data())
        # the size of color_frame is (720,1280,3)
        # Remove background - Set pixels further than clipping_distance to grey
        grey_color = 153
        depth_image_3d = np.dstack((depth_image, depth_image, depth_image))
        # depth image is 1 channel, color is 3 channels
        # depth_image_3d shape is (720,1280,3)
        bg_removed = np.where((depth_image_3d > clipping_distance) | (depth_image_3d <= 0), grey_color, color_image)
        # the size of bg_removed is (720,1280,3)
        # Render images:
        #   depth align to color on left
        #   depth on right
        depth_colormap = cv2.applyColorMap(cv2.convertScaleAbs(depth_image, alpha=0.03), cv2.COLORMAP_JET)
        images = np.hstack((bg_removed, depth_colormap))

        cv2.namedWindow('Align Example', cv2.WINDOW_NORMAL)
        cv2.imshow('Align Example', images)
        key = cv2.waitKey(1)
        # Press esc or 'q' to close the image window
        if key & 0xFF == ord('q') or key == 27:
            cv2.destroyAllWindows()
            break
finally:
    pipeline.stop()

在这里插入图片描述

解释说明

无论初始对于彩色流或是深度流是如何设置分辨率的,经过align.process之后的彩色流和深度流都是(720,1280)大小的(这个大小根据彩色流的设置而定),虽然深度流需要重构为三维来让彩色流和深度流拼接。虽然深度流的大小转换为和彩色流一样的大小,但是分辨率还是根据深度流的设置参数而定的,与大小无关

获取深度图中像素点的深度值

python代码

import cv2
import numpy as np
import pyrealsense2 as rs

pipeline = rs.pipeline()
config = rs.config()
config.enable_stream(rs.stream.depth, 640, 480, rs.format.z16, 30)
# Start streaming
profile = pipeline.start(config)
while True:
    frames = pipeline.wait_for_frames()
    depth_frames = frames.get_depth_frame()
    depth_image = np.asarray(depth_frames.get_data())
    depth_colormap = cv2.applyColorMap(cv2.convertScaleAbs(depth_image, alpha=0.03), cv2.COLORMAP_JET)
    distance = depth_frames.get_distance(100, 200)
    print(distance)
    cv2.imshow("depth image:", depth_colormap)
    key = cv2.waitKey(1)
    if key == 27:
        break
pipeline.stop()

运行结果如下:

0.6515000462532043
0.6460000276565552
0.6455000042915344
0.6427500247955322
0.6450000405311584

解释说明

get_distance()方法得到的数据是以米为单位的
在这里插入图片描述

使用API进行拍照

python代码

import cv2
import numpy as np
import pyrealsense2 as rs
import os

# 配置
pipe = rs.pipeline()
cfg = rs.config()
cfg.enable_stream(rs.stream.color, 1280, 720, rs.format.rgb8, 30)

i = 0
profile = pipe.start(cfg)

while True:
    # 获取图片帧
    frameset = pipe.wait_for_frames()
    color_frame = frameset.get_color_frame()
    color_img = np.asanyarray(color_frame.get_data())

    # 更改通道的顺序为RGB
    cv2.namedWindow('RealSense', cv2.WINDOW_AUTOSIZE)
    cv2.imshow('RealSense', color_img)
    k = cv2.waitKey(1)
    # Esc退出,
    if k == 27:
        cv2.destroyAllWindows()
        break
    # 输入空格保存图片
    elif k == ord(' '):
        i = i + 1
        cv2.imwrite(os.path.join("D:\\Realsense\\pic_capture", str(i) + '.jpg'), color_img)
        print("Frames{} Captured".format(i))

pipe.stop()

解释说明

只要将数据转换为numpy数组的方式,就可以通过opencv库进行图片的保存

Realsense获取像素点在相机坐标系下的三维坐标

python代码

import pyrealsense2 as rs
import numpy as np
import cv2
import json


def get_aligned_images():
    frames = pipeline.wait_for_frames()  # 等待获取图像帧
    aligned_frames = align.process(frames)  # 获取对齐帧
    aligned_depth_frame = aligned_frames.get_depth_frame()  # 获取对齐帧中的depth帧
    color_frame = aligned_frames.get_color_frame()  # 获取对齐帧中的color帧

    ############### 相机参数的获取 #######################
    intr = color_frame.profile.as_video_stream_profile().intrinsics  # 获取相机内参
    depth_intrin = aligned_depth_frame.profile.as_video_stream_profile().intrinsics  # 获取深度参数(像素坐标系转相机坐标系会用到)
    camera_parameters = {
    
    'fx': intr.fx, 'fy': intr.fy,
                         'ppx': intr.ppx, 'ppy': intr.ppy,
                         'height': intr.height, 'width': intr.width,
                         'depth_scale': profile.get_device().first_depth_sensor().get_depth_scale()
                         }
    # 保存内参到本地
    with open('D:\\Realsense\\intrinsics.json', 'w') as fp:
        json.dump(camera_parameters, fp)
    #######################################################

    depth_image = np.asanyarray(aligned_depth_frame.get_data())  # 深度图(默认16位)
    depth_image_8bit = cv2.convertScaleAbs(depth_image, alpha=0.03)  # 深度图(8位)
    depth_image_3d = np.dstack((depth_image_8bit, depth_image_8bit, depth_image_8bit))  # 3通道深度图
    color_image = np.asanyarray(color_frame.get_data())  # RGB图

    # 返回相机内参、深度参数、彩色图、深度图、齐帧中的depth帧
    return intr, depth_intrin, color_image, depth_image_3d, aligned_depth_frame


pipeline = rs.pipeline()  # 定义流程pipeline
config = rs.config()  # 定义配置config
config.enable_stream(rs.stream.depth, 640, 480, rs.format.z16, 30)  # 配置depth流
config.enable_stream(rs.stream.color, 960, 540, rs.format.bgr8, 30)  # 配置color流
profile = pipeline.start(config)  # 流程开始
align_to = rs.stream.color  # 与color流对齐
align = rs.align(align_to)
while True:
    intr, depth_intrin, rgb, depth, aligned_depth_frame = get_aligned_images()  # 获取对齐的图像与相机内参
    # 定义需要得到真实三维信息的像素点(x, y),本例程以中心点为例
    x = 320
    y = 240
    dis = aligned_depth_frame.get_distance(x, y)  # (x, y)点的真实深度值
    print("distance:",dis)
    camera_coordinate = rs.rs2_deproject_pixel_to_point(intr, [x, y], dis)
    # (x, y)点在相机坐标系下的真实值,为一个三维向量。
    # 其中camera_coordinate[2]仍为dis,camera_coordinate[0]和camera_coordinate[1]为相机坐标系下的xy真实距离。
    print(camera_coordinate)

    cv2.imshow('RGB image', rgb)  # 显示彩色图像

    key = cv2.waitKey(1)
    # Press esc or 'q' to close the image window
    if key & 0xFF == ord('q') or key == 27:
        pipeline.stop()
        break
cv2.destroyAllWindows()

解释说明

获得像素点在相机坐标系下的三维坐标之后,通过手眼标定就可以转化为在机械臂基底坐标系下的坐标,进而执行下一步操作。所得到的三维坐标应该是以米为单位的。

对rs2_deproject_pixel_to_point的官方说明

解释说明

官方说明链接

要点解释:

像素坐标:

通过SDK提供的图像流都关联一个独立的2D以像素为单位的坐标系。[0,0]点位于左上角,[w-1,h-1]点位于右下角。w和h分别代表列和行,从相机的角度来看,x轴指向右边,y轴指向下边。这个坐标系就是所谓的像素坐标系,用来索引特定的像素点。

点坐标:

通过SDK提供的图像流都关联一个独立的3D以为单位的坐标系。这个坐标系的原点[0,0,0]指的是物理成像仪的中心。在这个空间中,x轴正向指向右,y轴正向指向下,z轴正向指向前。该空间中的坐标称为“点”,用于描述三维空间中可能在特定图像中可见的位置。

相机内参

流的2D和3D坐标系的转换关系是通过相机内参来描述的,包含在rs2_intrinsics结构体中。不同的RealSense设备的内参是不同的,rs2_intrinsics结构体必须要能够描述由这些设备产生的图像。最基本的假设如下所示:

  • 图像可以是任意大小的
  • width和height字段分别描述图像中的列数和行数
  • 图像的视野有所不同
  • fx和fy字段将图像的焦距描述为像素宽度和高度的倍数
  • 投影的中心不一定是图像的中心
  • ppx和ppy字段描述主点(投影中心)的像素坐标
  • 图像可能包含畸变
  • model字段描述了几种受支持的畸变模型中的哪一种用于校准图像,coeffs字段提供了一个最多五个系数的数组来描述畸变模型。

了解图像的内在相机参数可以执行两个基本的映射操作

  • Projection
    • projection将流的3D坐标空间中获取一个点,将其映射到流图像的2D像素位置,可以使用函数rs2_project_point_to_pixel(…)
  • Deprojection
  • deprojection获取流图像上的2D像素位置以及以米为单位指定的深度,并将其映射到流的关联3D坐标空间中的三维点位置。由函数rs2_deproject_pixel_to_point(…) 提供。

畸变模型

  • 无畸变模型
    • 图像没有畸变,就像是由理想化的针孔相机产生的一样,这通常是某些硬件或软件算法使物理成像仪产生的图像不失真的结果,但可以简单地指示该图像是从已经不失真的某个或多个其他图像导出的。无失真的图像具有用于投影和去投影的闭合形式公式,并可用于两者rs2_project_point_to_pixelrs2_deproject_pixel_to_point
  • Modified Brown-Conrady 畸变模型
    • 畸变图像根据Brown-Conrady畸变模型的变化进行校准。这个畸变模型提供了从未畸变点到畸变点映射的闭合公式,而从畸变点映射到未畸变点的映射需要迭代或查询表。因此,符合Modified Brown-Conrady 的图像通过调用rs2_project_point_to_pixel(…) 来实现去畸变。这个模型被 Intel RealSense D415 彩色图像流使用。
  • Inverse Brown-Conrady 畸变模型
    • 畸变图像根据反Brown-Conrady畸变模型的变化进行校准。这个畸变模型提供了从畸变点到未畸变点映射的闭合公式,而从未畸变点映射到畸变点的映射需要迭代或查询表。因此,符合Inverse Brown-Conrady Distortion 的图像通过调用 rs2_deproject_pixel_to_point(…) 来完成去畸变。这个模型被 RealSense SR300’s 深度图像流和红外图像流使用。
      尽管投影和去投影不能始终用于一副图像是不方便的,通过让RealSense设备始终支持从深度图像中去投影,支持投影到彩色图像,这种不便性已经降至最低。因此,始终可以将深度图像映射到一组3D点(点云),并且始终可以发现3D对象将出现在彩色图像上的位置。

相机外参

每种图像流的三维坐标系是不同的,比如说,通常来说深度图像是通过一个或多个红外成像仪生成的,而彩色流是通过一个独立的彩色成像仪形成的。这些不同的流所对应的三维坐标系之间的关系是通过外参进行描述的,包含在rs2_extrinsics的结构体中。 基本的假设如下:

  • 成像仪可能位于不同的位置,但是这些成像仪是被严格地固定在同一个物理设备上。
    • translation字段包含成像仪之间物理位置的3D平移向量,以为单位。
  • 成像仪的方向可能不同,但严格安装在同一物理设备上。
    • rotation字段包含成像仪物理方向之间的3x3正交旋转矩阵。
  • 所有三维坐标系均以米为单位指定
    • 在两个坐标系之间的转换中不需要任何形式的缩放。
  • 所有坐标系均为右手坐标系,且具有正交基
    • 在两个坐标系之间的转换中不需要任何类型的镜像/倾斜。
      通过了解两个流之间的外部参数,可以将点从一个坐标空间转换到另一个坐标空间,这可以通过调用rs2_transform_point_to_point(…) 来完成。此操作定义为使用3x3旋转矩阵和3分量平移向量的标准仿射变换。外部参数可以通过在设备支持的任何两个流之间调用rs2_get_extrinsics(…) 来检索,或者通过get_extrinsics_to(…) 使用rs2_stream_profile对象来检索。进行这样的操作不需要预先启用流,设备的外参被假定为与流的内容无关,并且在程序的生命周期内对于给定设备外参被视为常数。

深度图格式

如上所述,使用rs2_intrinsics 结构体和rs2_deproject_pixel_to_point(…) 函数将2D像素坐标映射到3D点坐标需要那个像素点的以米为单位的信息。这个SDK公开的某些像素格式,包含每个像素的深度信息,进而可以被这个函数直接使用。其他图像不包含每个像素的深度信息,也因此一般用于投影而不是去投影。

处理模块助手

SDK提供两种主要的与图像映射有关的处理模块。

  • 点云
  • 帧对齐

点云

作为API的一部分,我们提供了一个处理块,用于从深度和颜色帧创建点云和相应的纹理映射。从深度图像创建的点云是深度流的三维坐标系中的一组点。下面演示如何创建点云对象:

python代码
import pyrealsense2 as rs
pc = rs.pointcloud()
points = pc.calculate(depth_frame)
pc.map_to(color_frame)
帧对齐

通常在处理颜色和深度图像时,需要将每个像素从一个图像映射到另一个图像。SDK提供了一个处理块,用于将图像彼此对齐,生成一组具有相同分辨率的帧,并允许轻松映射像素。
下面演示如何创建对齐对象:

python 代码
import pyrealsense2 as rs
align = rs.align(rs.stream.color)
aligned_frames = align.proccess(depth_and_color_frameset)
color_frame = aligned_frames.first(rs.stream.color)
aligned_depth_frame = aligned_frames.get_depth_frame()

附录:模块细节

要成功利用此SDK的投影功能,无需知道插入了哪种型号的Intel RealSense设备。但是,开发人员可以利用给定设备的某些已知属性。

Intel RealSense SR300

  • 深度图像始终与红外图像像素对齐
  • 深度图像和红外图像具有相同的内参
  • 深度和红外图像将始终使用 Inverse Brown-Conrady 畸变模型
  • 深度和红外之间的外参转换是单位变换
  • 像素坐标可以深度流和红外流之间互换使用
  • 彩色流照片没有畸变
  • 在这些设备上投影到彩色图像时,可以完全跳过畸变步骤

Intel RealSense D400 Series

  • 默认情况下会校正左右红外图像(不使用Y16格式)
  • 这两个红外流具有相同的内参
  • 这两个红外流没有畸变
  • 左右红外图像之间没有旋转(旋转矩阵为单位矩阵)
  • 左右红外图像之间只有一个轴上有平移(translation[1]和translation[2]是0)
  • 像素坐标的y分量可以在左右红外流之间互换使用(共享同一个坐标轴)
  • 立体视差通过一种反比例关系与深度相关,并且可以通过 1/rs2_get_depth_scale(…) 查询视差为1的点的距离。
  • RS2_FORMAT_Z16不同,视差值为零是有意义的。对于“无限远”的物体,会出现视差为零的立体匹配,这些物体距离如此之远,以至于两台成像仪之间的视差可以忽略不计。相比之下,存在一个最大的可能视差(最近的距离)。
  • 目前视差仅仅在D400系列上存在。通过使用advanced_mode 控制可以修改视差值。

获取双目相机的基线(baseline)

python代码

import pyrealsense2 as rs

config = rs.config()
config.enable_stream(rs.stream.infrared, 1)
config.enable_stream(rs.stream.infrared, 2)
pipeline = rs.pipeline()
profile = pipeline.start(config)
ir1_stream = profile.get_stream(rs.stream.infrared, 1)
ir2_stream = profile.get_stream(rs.stream.infrared, 2)
extrinsic = ir2_stream.get_extrinsics_to(ir1_stream)
baseline = extrinsic.translation[0]
print("baseline in meter:", baseline)

运行结果

baseline in meter: 0.09494169056415558

参考文献

realsense相机两种获取相机内外参的方式
python opencv 4.1.0 cv2.convertScaleAbs()函数 (用于Intel Realsense D435显示depth图像)
Intel Realsense使用
RealSense获取图像中坐标的三维真实距离
Intel Realsense D435 如何获取摄像头的内参?get_profile() video_stream_profile() get_intrinsics()
intel realsense计算指定像素真实深度与像素坐标系转相机坐标系

猜你喜欢

转载自blog.csdn.net/m0_46649553/article/details/121115998