收藏 分享(赏)

《点云库pcl学习教程》第4章-输入、输出(io).pptx

上传人:天天快乐 文档编号:1385932 上传时间:2018-07-09 格式:PPTX 页数:82 大小:1.58MB
下载 相关 举报
《点云库pcl学习教程》第4章-输入、输出(io).pptx_第1页
第1页 / 共82页
《点云库pcl学习教程》第4章-输入、输出(io).pptx_第2页
第2页 / 共82页
《点云库pcl学习教程》第4章-输入、输出(io).pptx_第3页
第3页 / 共82页
《点云库pcl学习教程》第4章-输入、输出(io).pptx_第4页
第4页 / 共82页
《点云库pcl学习教程》第4章-输入、输出(io).pptx_第5页
第5页 / 共82页
点击查看更多>>
资源描述

1、第4章 输入、输出(IO),PCL中所有的处理都是基于点云展开的,利用不同的设备获取点云、存储点云等都是点云处理前后必须做的流程,PCL中有自己设计的内部PCD文件格式,为此,设计读写该格式以及与其他3D文件格式之间进行转化的接口类都是很必要的,目前PCL内部支持对常用的3D格式文件的打开和存储操作,以及与PCD内部格式之间的互相转化。本章首先对PCL中支持的点云获取设备(如kinect)以及OpenNI开源框架作一个基本介绍,其次对PCL中的I/O模块及相关类进行简单说明,最后通过应用实例来展示如何对PCL中I/O模块进行灵活运用。,本章各小节目录,4.1 I/O涉及的设备及相关概念简介4.

2、2 PCL中I/O模块及类介绍4.3 应用实例解析,4.1 I/O涉及的设备及相关概念简介,4.1.1 OpenNI开源框架OpenNI(开放式自然交互)来源于由业界领导的一个非营利性组织,创建于2010年11月,专注于提高和改善自然交互设备与应用软件的互操作能力。其官方网站于12月8号正式公开,主要成员之一是PrimeSense公司(Kinect的核心芯片正是由这家公司提供),其他成员还包括开发ROS的机器人公司Willow Garage,以及游戏公司Side-Kick等。OpenNI是一个多语言、跨平台的框架,它定义了一套用于编写通用自然交互应用的API。OpenNI的主要目的就是形成标准

3、的API,便于下面两个接口之间进行通信:(1)视觉和音频传感器(用来感知周围环境信息)。,(2)视觉和音频感知中间件(用来对应用场景中所记录的音频和视觉数据进行分析与理解,例如能够接收一份可见的图像数据并返回从中检测到的手掌位置信息)。OpenNI提供了一组基于传感器设备实现的API和另外一组由中间件组件实现的API,打破了传感器和中间件之间的依赖关系。这样,使用OpenNI API开发应用程序时就不需要在各种中间件模块的上层操作上浪费时间,可以做到一次编写、随处部署。OpenNI的这种分层设计机制允许中间件开发者可以直接基于最原始的数据格式编写算法,而不管这些数据是由何种传感器设备产生,同时

4、也让传感器生产商制造的设备能用于任何OpenNI兼容的应用程序。OpenNI的这套标准化API使得自然交互应用开发人员,可以利用由传感器输入并计算过的数据类型,很方便地跟踪处理现实生活中的场景(例如,可以是表示人体全身的数据,也可以是表示手的位置数据,或者仅仅是深度图里面的一组像素等)。这样可以保证编写应用程序的时候,不用考虑传感器或中间件供应商相关的细节。图4-1展示了OpenNI框架的应用概念,分为三层:(1)应用层:基于OpenNI实现的自然交互应用软件。(2)中间件接口层:代表OpenNI本身,提供了传感器和中间件之间的交互接口。(3)硬件设备层:列出了捕捉视觉和音频数据的多种硬件设备

5、。,4.1.2 OpenNI兼容设备OpenNI目前已成为PCL集成进来的第一个设备相关的第三方库,用来抓取OpenNI兼容设备中的点云数据。上节描述的OpenNI应用框架说明,只要底层的设备传感器设备与OpenNI兼容,都可以作为点云数据输入源,图4-2则展示了目前流行的OpenNI兼容设备。其中,Primesense Reference Design、Microsoft Kinect和Asus XtionPro这3种摄像头设备均进行了OpenNI兼容性测试。选择其中任何一个设备进行点云数据采集,都可以经OpenNI处理后转化为标准数据供上层应用使用。如今,OpenNI已成为微软xbox36

6、0配件kinect在PC上的开源驱动中必须安装的一个API。,4.2 PCL中I/O模块及类介绍,PCL中I/O库提供了点云文件输入输出相关的操作类,并封装了OpenNI兼容的设备源数据获取接口,可直接从众多感知设备获取点云图像等数据。I/O模块利用21个类与28个函数实现了对点云的获取、读入、存储等相关操作,其依赖于pcl_common和pcl_octree模块以及OpenNI外部开发包。,4.2.1 I/O模块中类以及全局函数说明I/O模块中目前共有21个类,随着RGDB设备的流行必将引入更多的设备相关的I/O扩展,以后有可能增加以下几类。1. class pcl:FileReader类F

7、ileReader定义了PCD文件的读取接口,主要用做其他读取类的父类。从它继承的子类必须实现自己的读取函数,即该类中的纯虚函数其继承关系如图4-3所示。但为了保持向后兼容,提供了FILE_V6版本文件读取的实现函数。,类FileReader关键成员函数:virtual intreadHeader(const std:string& file_name, sensor_msgs:PointCloud2& cloud, Eigen:Vector4f& origin, Eigen:Quaternionf& orientation, int& file_version, int& data_type

8、, unsigned int& data_idx, const int offset=0)=0纯虚函数,定义读取点云文件头的接口函数,其参数意义:file_name读取文件的文件名。cloud存储读取后的点云数据,但只填充文件头(关于PCD文件格式详见本章后面实例分析)。origin点云获取原点,该参数只有在文件版本大于FILE_V7才存在,否则为NULL。orientation点云获取方向,该参数只有在文件版本大于FILE_V7才存在,否则为NULL。file_version文件版本(FILE_V7或者FILE_V6)。,data_type数据类型(二进制置为1,ASCII码置为0)。dat

9、a_idx数据偏移文件头末尾的偏移量。offset文件头偏移文件开始的偏移量。virtual int read(const std:string&file_name, sensor_msgs:PointCloud2& cloud, Eigen:Vector4f& origin, Eigen:Quaternionf& orientation, int& file_version, const int offset=0)=0为纯虚函数,定义读取文件数据的接口函数,读取文件中的点云数据存储到cloud对象中,其他参数同上函数。int read(const std:string &file_name,

10、 sensor_msgs:PointCloud2& cloud, const int offset = 0)功能同上函数(仅适用于FILE_V6版本文件获取,因为sensor_msgs:PointCloud2不包含传感器原点和方向数据,若读取高版本数据会产生警告信息)。,templateint read(const std:string& file_name, pcl:PointCloud& cloud, const int offset=0)功能同上函数,只是带有模板参数的成员函数。2. class pcl:FileWriter类FileWriter与FileReader对应,是写入PCD文

11、件的类接口定义,可以作为其他写入类的父类。从它继承的子类必须实现自己的写入函数,即该类中的纯虚函数,其继承关系如图4-4所示。,类FileWriter关键成员函数:virtual int write(const std:string点云写入到对应文件的纯虚函数接口定义,其参数意义:file_name写入文件的文件名。cloud需要写入的点云对象。origin写入文件头的点云获取原点,默认为(0, 0, 0, 0)。orientation写入文件头的点云获取方向。binary设置写入时的类型(true为二进制,false为ASCII码,默认为ASCII码)。,templateintwrite(c

12、onst std:string& file_name, const pcl:PointCloud& cloud, const bool binary=false)功能同上函数,只是添加了模板参数的模板成员函数。3. class pcl:Grabber类Grabber为PCL1.X对应的设备驱动接口的基类定义,继承关系如图4-5所示。,类Grabber关键成员函数:templateboost:signals2:connectionregisterCallback(const boost:function& callback)提供回调函数指针,当获取每帧图像或点云数据时都会启动回调函数:templ

13、atebool providesCallback() const判断是否提供回调函数:virtual void start()=0启动设备,开始传输数据流:virtual void stop()=0停止设备上的数据流传输:virtual std:string getName() const = 0,返回明确的子类名字:virtual bool isRunning() const = 0判断是否在传输数据流:virtual float getFramesPerSecond() const = 0获取FPS帧率,即每秒多少帧数据。4. class openni_wrapper:OpenNIDevi

14、ce类OpenNIDevice定义OpenNI设备的基类,继承该基类可以实现不同的OpenNI设备子类,用于获取包括红外数据、RGB数据、深度图像数据等。继承关系如图4-6所示,目前包括如下设备Primesense PSDK,Microsoft Kinect,Asus Xtion Pro/Live。,类OpenNIDevice关键成员函数:bool findCompatibleImageMode(const XnMapOutputMode& output_mode, XnMapOutputMode& mode)const throw()查询是否有与output_mode对应的图像模式匹配的输出

15、模式,如果有则返回true并且存储兼容模式在mode中,否返回值为false。例如设备支持30Hz的VGA模式,而请求输出为30Hz的QVGA模式则通过下采样是可以兼容的,但是设备支持25Hz的VGA而请求为30Hz的SXGA就不兼容。bool findCompatibleDepthMode(const XnMapOutputMode& output_mode, XnMapOutputMode& mode)const throw()功能和参数同上,只是针对深度图像的模式,并非上述彩色图像的模式。bool isImageModeSupported(const XnMapOutputMode& o

16、utput_mode) const throw(),只判断是否支持该output_mode所给定的图像模式,支持返回true,否则返回false。bool isDepthModeSupported(const XnMapOutputMode& output_mode) const throw()只判断是否支持该output_mode所给定的深度图像模式,支持返回true,否则返回false。const XnMapOutputMode& getDefaultImageMode() const throw()const XnMapOutputMode& getDefaultDepthMode()

17、const throw()const XnMapOutputMode& getDefaultIRMode() const throw()以上三个函数分别是获取默认的RGB、深度、红外图像的输出模式。void setImageOutputMode(const XnMapOutputMode& output_mode),void setDepthOutputMode(const XnMapOutputMode& output_mode)void setIROutputMode(const XnMapOutputMode& output_mode)以上三个函数分别是设置RGB、深度、红外图像的输出模

18、式。XnMapOutputMode getImageOutputMode() constXnMapOutputMode getDepthOutputMode() constXnMapOutputMode getIROutputMode() const以上三个函数分别是获取当前的RGB、深度、红外图像的输出模式。void setDepthRegistration(bool on_off)设置深度图像是否与RGB图像对齐。bool isDepthRegistrationSupported() const throw()判断设备是否支持深度图像与RGB图像对齐void setSynchronizat

19、ion(bool on_off),设置设备是否同步输出RGB和深度图像。bool isSynchronized() const throw()判断设备是否同步输出RGB和深度图像,如果是返回true,否则返回false。bool isSynchronizationSupported() const throw()判断设备是否支持同步输出RGB和深度图像。bool isDepthCropped() const返回深度图像是否被裁剪过,是为true,否则为false。void setDepthCropping(unsigned x, unsigned y, unsigned width, unsi

20、gned height)打开深度图像的裁剪,x、y分别为两个方向上的起始裁剪位置,width和height分别为保留的x与y方向上的尺寸。float getImageFocalLength(int output_x_resolution=0) const throw(),获取RGB图像的焦距长度(像素)。float getDepthFocalLength(int output_x_resolution=0) const throw()获取深度图像的焦距长度(像素)。float getBaseline() const throw()获取基线长度。virtual void startImageSt

21、ream()virtual void stopImageStream()上面一对函数为启动和停止RGB数据流的采集。virtual void startDepthStream()virtual void stopDepthStream()上面一对函数为启动和停止深度数据流的采集。virtual void startIRStream()virtual void stopIRStream(),上面一对函数为启动和停止红外数据流的采集。bool hasImageStream() const throw()bool hasDepthStream() const throw()bool hasIRStr

22、eam() const throw()以上三个函数分别判断设备是否支持RGB、深度、红外图像数据采集,如果是返回true,否则返回false。virtual bool isImageStreamRunning() const throw()virtual bool isDepthStreamRunning() const throw()virtual bool isIRStreamRunning() const throw()以上三个函数分别判断设备是否正在进行RGB、深度、红外图像数据采集。CallbackHandle registerImageCallback(const ImageCal

23、lbackFunction& callback, void* cookie=NULL) throw()bool unregisterImageCallback(const CallbackHandle& callbackHandle) throw(),上面一对函数分别为RGB图像数据流的回调函数注册与注销。CallbackHandle registerDepthCallback(const DepthImageCallbackFunction& callback, void* cookie=NULL) throw()bool unregisterDepthCallback(const Call

24、backHandle& callbackHandle) throw()上面一对函数分别为深度图像数据流的回调函数注册与注销。CallbackHandle registerIRCallback(const IRImageCallbackFunction& callback, void* cookie=NULL) throw()bool unregisterIRCallback(const CallbackHandle& callbackHandle)throw()上面一对函数分别为红外图像数据流的回调函数注册与注销。,const char* getSerialNumber() const thr

25、ow()获取设备对应的序列号,注意该返回值有可能为空字符串。const char* getConnectionString() const throw()获取设备连接字符串,一般格式为vendorID/productIDBusID/DeviceID。const char* getVendorName() const throw()const char* getProductName() const throw()unsigned short getVendorID() const throw()unsigned short getProductID() const throw()以上4个函数分

26、别获取厂商和产品的名字字符串及ID编号。unsigned char getBus() const throw()获取设备所在的USB总线。,unsigned char getAddress() const throw()获取设备所在USB地址。void setRGBFocalLength(float focal_length)void setDepthFocalLength(float focal_length)以上两个函数分别设定RGB与深度图像获取时的焦距大小。5. class openni_wrapper:DeviceKinect6. class openni_wrapper:Devic

27、ePrimesense7. class openni_wrapper:DeviceXtionPro以上3个类分别封装了Kinect、Primesense、XtionPro相关设备操作和数据获取操作实现,其详细接口参考其父类OpenNIDevice的关键函数说明。8. Class openni_wrapper:DeviceONI,封装了利用ONI文件回放虚拟类kinect设备的操作和数据获取操作实现,其详细接口参考其父类OpenNIDevice的关键函数说明。9. Class openni_wrapper:OpenNIDriver类OpenNIDriver采用单例模式实现对底层驱动的封装,里面包

28、含一xn:Context对象,提供给所有设备使用。该类提供了枚举和访问所有设备的方法实现。类OpenNIDevice关键成员函数:unsigned updateDeviceList()枚举所有系统可以获取的设备列表,返回获取设备的数目。unsigned getNumberDevices() const throw()获取系统可用设备的数目。,boost:shared_ptr createVirtualDevice(const std:string& path, bool repeat, bool stream) const从ONI文件创建一虚拟设备,其中path为ONI文件的路径,repeat

29、设置是否虚拟设备支持无限循环从ONI文件获取数据,stream设置虚拟设备是支持数据流形式的数据获取还是引发式的数据获取。boost:shared_ptr getDeviceByIndex(unsigned index) const返回一设备,index为给定的设备索引。boost:shared_ptr getDeviceBySerialNumber(const std:string& serial_number) const返回一设备,serial_number为给定的设备的序列号。boost:shared_ptr getDeviceByAddress(unsigned char bus,

30、unsigned char address) const,返回一设备,bus为USB设备总线号,address为USB设备地址。const char* getSerialNumber(unsigned index) const throw()获取索引为index的设备序列号,但该设备未被创建。const char* getConnectionString(unsigned index) const throw()获取索引为index的设备连接字符串,但该设备未被创建。void stopAll()停止所有设备。static OpenNIDriver为静态成员函数,获取设备连接字符串存储在conn

31、ection_string,设备厂商及产品ID,存储在,为静态成员函数,获取设备连接字符串存储在connection_string,设备厂商及产品ID,存储在vendorId、productId中。10. Class openni_wrapper:OpenNIException类OpenNIException封装一般的异常处理实现,其关键成员函数如下:virtual const char* what() const throw()返回异常消息字符串。const std:string &getFunctionName() const throw()返回发生异常的函数名。const std:str

32、ing &getFileName() const throw()返回发生异常的文件名。unsigned getLineNumber() const throw()返回发生异常的行号。,11. Class openni_wrapper:Image类Image是简单的图像数据封装基类,其继承关系如图4-7所示。virtual bool isResizingSupported(unsigned input_width, unsigned input_height, unsigned output_width, unsigned output_height) const = 0纯虚函数,具体实现见子类

33、,判断图像是否支持尺寸变换,input_width、input_height为设定的宽度和高度,output_width、output_height变换后的宽度和高度。,virtual void fillRGB(unsigned width, unsigned height, unsigned char *rgb_buffer, unsigned rgb_line_step=0) const = 0用RGB数据填充用户给定的rgb_buffer,返回图像的宽度和高度为width、height,隔rgb_line_step行输出到输出缓存中。virtual Encoding getEncodin

34、g() const = 0返回原始编码的方式。void fillRaw(unsigned char *rgb_buffer) const throw()用原始数据填充用户给定的rgb_buffer。virtual void fillGrayscale(unsigned width, unsigned height, unsigned char* gray_buffer, unsigned gray_line_step=0)const = 0用灰度数据填充给定的gray_buffer,其他参数参考函数,fillRGB。unsigned getWidth() const throw()unsign

35、ed getHeight() const throw()以上两个函数分别获取图像宽度和高度。unsigned getFrameID() const throw()获取帧的ID号。Unsigned longgetTimeStamp() const throw()获取图像的时间戳。const xn:ImageMetaData &getMetaData() const throw()获取图像原始OpenNI格式数据。12. Class openni_wrapper:ImageBayerGRBG13. Class openni_wrapper:ImageRGB24,14. Class openni_w

36、rapper:ImageYUV422以上3个类分别实现了对原始数据BayerGRBG、RGB24、YUV422到图像转化接口,详细参考其父类关键函数说明。15. Class pcl:OpenNIGrabber类OpenNIGrabber实现对OpenNI设备(例如Primesense PSDK,Microsoft Kinect,Asus XTion Pro/Live)数据的采集接口,详细参考其父类Grabber关键函数说明。16. class pcl:PCDReader17. class pcl:PLYReader以上两个类分别是PCD、PLY文件格式读入接口的实现,详细参考其父类pcl:Fi

37、leReader。18. class pcl:PLYWriter19. Class pcl:PCDWriter以上两个类分别是PCD、PLY文件格式写出接口的实现,,详细参考其父类pcl:FileWriter。20. Class pcl:PCLIOException类PCLIOException是I/O相关的异常处理接口实现,详细参考其父类PCLException。,4.2.2 I/O模块其他关键成员说明PCL_EXPORTS int pcl:io:saveOBJFile(const std:string& file_name, const pcl:TextureMesh& tex_mesh,

38、 unsigned precision=5)该函数实现对TextureMesh保存到OBJ文件,file_name为OBJ文件名,tex_mesh为网格模型数据,precision为保存时的精度(默认为5)。PCL_EXPORTS int pcl:io:saveOBJFile(const std:string& file_name, const pcl:PolygonMesh& mesh, unsigned precision=5)功能同上,存储对象为PolygonMesh。int pcl:io:loadPCDFile(const std:string& file_name, sensor_m

39、sgs:PointCloud2& cloud),打开一版本V6.0的PCD文件,file_name为文件名,cloud存储读入的点云数据。templateint pcl:io:loadPCDFile(const std:string& file_name, pcl:PointCloud& cloud)打开任何类型的PCD点云文件,file_name为文件名,cloud存储读入的点云数据。int pcl:io:savePCDFile(const std:string& file_name, const sensor_msgs:PointCloud2& cloud, constEigen:Vect

40、or4f& origin=Eigen:Vector4f:Zero(), const Eigen:Quaternionf& orientation=Eigen:Quaternionf:Identity(), const bool binary_mode=false)保存点云到PCD文件,file_name文件名,cloud需要保存的点云数据,origin为获取点云的原点,orientation为获取点云的方向,binary_mode设置是否保存为二进,制格式,默认为false。templateint pcl:io:savePCDFile(const std:string& file_name,

41、const pcl:PointCloud& cloud, bool binary_mode=false)保存点云到PCD文件,file_name文件名,cloud需要保存的点云数据,binary_mode设置是否保存为二进制格式,默认为false。templateint pcl:io:savePCDFileASCII(const std:string& file_name, const pcl:PointCloud& cloud)以ASCII方式保存点云到PCD文件,file_name文件名,cloud需要保存的点云数据。templateint pcl:io:savePCDFileBinary

42、(const std:string& file_name, const,pcl:PointCloud& cloud)以二进制方式保存点云到PCD文件,file_name文件名,cloud需要保存的点云数据。void pcl:throwPCLIOException(const char function_name, const char file_name, unsigned line_number, const char format, )异常处理函数,function_name发生异常的函数名,file_name发生异常的文件名,发生异常的行号line_number,发生异常的抛出消息for

43、mat。templateint pcl:io:loadPLYFile(const std:string& file_name, pcl:PointCloud& cloud)打开ply文件,file_name文件名,cloud保存打开的点云数据。template,int pcl:io:savePLYFile(const std:string& file_name, const pcl:PointCloud& cloud, bool binary_mode=false)保存点云到PLY文件,file_name文件名,cloud需要保存的点云数据,binary_mode设置是否保存为二进制格式,默认

44、为false。templateint pcl:io:savePLYFileASCII(const std:string& file_name, const pcl:PointCloud& cloud)templateint pcl:io:savePLYFileBinary(const std:string& file_name, const pcl:PointCloud& cloud)以上两个函数分别以为ASCII和二进制方式保存点云到ply文件,file_name文件名,cloud需要保存的点云数据。PCL_EXPORTS int pcl:io:savePLYFile(const std:s

45、tring&,file_name, const pcl:PolygonMesh& mesh, unsigned precision=5)保存PolygonMesh对象到PLY文件,file_name为PLY文件名,mesh为需要保存的对象数据,precision为保存精度(默认为5)。CL_EXPORTS void pcl:io:saveRgbPNGFile(const std:string &file_name, const unsigned char rgb_image, int width, int height)保存RGB数据为PNG文件,file_name为PNG文件名,rgb_im

46、age为RGB数据,width、height为图像数据的宽度和高度。PCL_EXPORTS void pcl:io:saveMonoPNGFile(const std:string& file_name, const unsigned char mono_image, int width, int height)功能同上,只是保存的数据为灰度图像格式。PCL_EXPORTS void pcl:io:saveShortPNGFile(const std:string& file_name, const unsigned short short_image, int width, int,heig

47、ht)功能同上,只是保存的数据为16位灰度图像格式。templatevoid pcl:io:savePNGFile(const std:string& file_name, const pcl:PointCloud& cloud)保存点云中RGB域为png文件,cloud为包含RGB域的点云对象,file_name为png文件名。PCL_EXPORTS int pcl:io:saveVTKFile(const std:string& file_name, const pcl:PolygonMesh& triangles, unsigned precision=5)保存PolygonMesh对象

48、数据为VTK文件,file_name为VTK文件名,triangles为需要保存的数据,precision为保存精度(默认为5)。,4.3 应用实例解析,4.3.1 PCD(点云数据)文件格式本小节描述PCD(点云数据)文件格式,及其他在点云库(PCL)中应用的方法。1. 为什么用一种新的文件格式?PCD文件格式并非白费力气地做重复工作,现有的文件结构因本身组成的原因不支持由PCL库引进n维点类型机制处理过程中的某些扩展,而PCD文件格式能够很好地补足这一点。PCD不是第一个支持3D点云数据的文件类型,尤其是计算机图形学和计算几何学领域,已经创建了很多格式来描述任意多边形和激光扫描仪获取的点云

49、。包括下面几种格式:(1)PLY是一种多边形文件格式,由Stanford大学的Turk等人设计开发。(2)STL是3D Systems公司创建的模型文件格式,主要应用于CAD、CAM领域;,(3)OBJ是从几何学上定义的文件格式,首先由Wavefront Technologies开发;(4)X3D是符合ISO标准的基于XML的文件格式,表示3D计算机图形数据;(5)其他许多种格式。以上所有的文件格式都有缺点,在下一节会讲到。这是很自然的,因为它们是在不同时间为了不同的使用目的所创建的,那时今天的新的传感器技术和算法都还没有发明出来。2. PCD版本在点云库(PCL)1.0版本发布之前,PCD文件格式有不同的修订号。这些修订号用PCV_Vx来编号(例如,PCD_V5、PCD_V6、PCD_V7等等),代表PCD文件的0.x版本号。然而PCL中PCD文件格式的正式发布是0.7,

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 实用文档 > 简明教程

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报