最近阅读了一篇文章——《基于内存映射文件的海量点云数据快速读取方法》,文中介绍的方法能够极大地提升点云读取速率,这为我们读取数以千万的点云数据提供了方法。
通常情况下,在C++中读文件,使用std::ifstream,写文件使用std::ofstream。这种传统的IO读取方式比较常见,但是读取文件速度较慢。
下面是基于IO的读文件C++代码

std::ifstream ifs(path, std::ios::in);
if (!ifs.is_open())
{std::cout << "文件不存在" << std::endl;ifs.close();return NULL;
}char ch;
ifs >> ch;
if (ifs.eof())
{std::cout << "文件为空" << std::endl;ifs.close();return NULL;
}
ifs.putback(ch);std::string data_;
while (ifs >> data_)
{}ifs.close();

回到重点,文中主要从两个方面对读取过程进行改进:(1)针对点云数据特有的格式,重写并简化了格式转换的函数,从而提高数据格式转换的效率;(2)通过在磁盘空间中开辟虚拟内存,省掉了由磁盘数据到内存空间的步骤,从而提高了数据读取的效率。

通常情况下,点云数据会被存储成ASCII形式文本,每一点占据一行
x1 y1 z1
x2 y2 z2

(空格部分通常是‘\t’或者’\n’)
文本格式点云数据的特点为:(1)每一行数据格式一致,具有重复性;(2)数据量大,一个文件中保存的三维点数量可达上千万个。

将ASCII形式的字符转换成数值类型并保存在内存中一共要包括三个步骤:(1)二进制数据读取;(2)数值类型转换;(3)内存赋值。

在读数据的时候,使用fgets()可以一次性读取一整行数据,但是一整行数据是包括x、y和z的坐标值的。并且此时读进来的是一个个字符,也就是char类型。因此,每读取一行便需要根据其空格对整行字符串进行分割。最后,将分割的结果中的每个字符转换为数值再进行拼接。例如,x = 123.5,分割之后其实是‘1’ ‘2’ ‘3’ ‘.’ ‘5’。使用atof()函数可以将字符转换为数值。(atof是C和C++标准库中的函数,不需要额外安装库)

经过作者测试发现,最消耗时间的步骤就是这个数据类型转换步骤。atof函数对输入的格式很广泛,牺牲了效率来满足数据的兼容性。所以作者针对点云数据的特点重新编写了转换函数,其实就是简化了atof,来提高效率。

这篇博客中实现了atof函数,代码如下

double myatof(const char *s)
{int sign, sign_e;//数值的符号,指数部分的符号int hasdot = 0;int hase = 0;double intpart = 0.0;//小数的整数部分double decpart = 0.0; //小数的小数部分int decdigit = 1; //小数的小数位数int exp = 0; //指数部分double ret;int i;//跳过开头的空格for (i = 0; isspace(s[i]); i++);//判断符号,如有,跳过sign = (s[i] == '-') ? -1 : 1;if (s[i] == '-' || s[i] == '+')i++;//判断浮点数//第一部分:for (; s[i] != '\0'; i++){if (isdigit(s[i])) //数字intpart = 10 * intpart + s[i] - '0';//计算小数的整数部分else if (s[i] == '.') //小数点{hasdot = 1;i++;break;}else if (s[i] == 'e' || s[i] == 'E') //科学计数符{hase = 1;i++;break;}else //非法字符return sign * intpart;}/*第一部分结束,有如下情况:1. 扫描数字知道非法字符或字符串结尾,2d 2342. 数字加小数点 2.3. 小数点 .4. 数字加科学计数符 3e5. 科学计数符 e   这种情况是非法表示,但最终计算的结果为0,因此可当作正常计算,不单独列出6. 非法字符, 直接退出*///第二部分,接着上述情况扫描//能进入下面循环,排除遇到字符串结尾,非法字符//因此只能遇到点号或科学计数符for (; s[i] != '\0'; i++){//第一种:.3 或 3.4,均为合法,计算小数的小数部分if (hasdot && isdigit(s[i]))decpart += (s[i] - '0') / pow(10, decdigit++);//第二种:.e 或 2.e 或 .2e 或 3.3e 第一种非法,但计算结果为0else if (hasdot && (s[i] == 'e' || s[i] == 'E')){hase = 1;i++;break;}//第三种:第一部分以e结束,3e eelse if (hase)break;//第四种:第一部分以点号结束,现在扫描非数字,非科学计数符的其他非法字符elsereturn sign * (intpart + decpart);}/*第三部分从第二部分退出后继续后面的程序,有如下情况:以科学计算符 e 结束第二部分,前面有小数点或者没有小数部分计算完,下面讨论指数部分*///判断指数部分符号sign_e = (s[i] == '-') ? -1 : 1;if (s[i] == '+' || s[i] == '-')i++;for(; s[i] != '\0'; i++){if(isdigit(s[i]))exp = exp * 10 + s[i] - '0';elsebreak;}ret = sign * ((intpart + decpart) * pow(10, sign_e * exp));return ret;
}

经过测试发现,这个重写的新方法比atof确实要省时间的,数据量越大,效果肯定越明显。观察这个代码发现,其实还可以继续简化

简化代码如下


int _isdigit(int c)
{if ((c >= '0' && c <= '9') || (c == '.')) return 1024;return 0;
}double myatof(const char* s)
{int hasdot = 0;double intpart = 0.0;//小数的整数部分double decpart = 0.0; //小数的小数部分int i = 0;for (; _isdigit(s[i]); ++i){if (s[i] >= '0' && s[i] <= '9') //数字intpart = 10 * intpart + s[i] - '0';//计算小数的整数部分else if (s[i] == '.') //小数点{hasdot = 1;i++;break;}}float val = 0.1;for (; _isdigit(s[i]); ++i){//第一种:.3 或 3.4,均为合法,计算小数的小数部分if (hasdot && s[i] >= '0' && s[i] <= '9'){//decpart += (s[i] - '0') / pow(10, decdigit++);decpart = decpart + (s[i] - '0') * val;val = val * 0.1;}}return intpart + decpart;
}

简化之后,速度进一步大幅度提升。

格式转换步骤的简化

完整的点云数据流程还包括内存接收数据环节。容器或动态数组的创建和数据赋值效率比固定内存创建和赋值效率低一到两个数量级,因此在处理大量数据时,首选定长数组或直接开辟内存。

整个转换流程如下:(1)逐行读文件获取文件行数N,关闭文件;(2)开辟长度为N的点云数据结构类型内存空间;(3)gets()方法逐行读文件获取字符串;(4)对字符串进行分割;(5)自定义函数将字符串转换为数值赋值到内存地址中。

在知道要读取的点云个数之后,在堆区开辟固定大小内存来存放数据能够进一步节省时间,但是文中介绍到在IO读取点云数据方面仍热有待优化。

于是,不再采用IO读取数据的方式,而改用内存映射来进行快速读取。

通过文件内存映射操作可获取数据区指针,由于文本文件以ASCII码保存,因此这里的数据区指针定义为char*类型。获取数据指针后,通过该指针实现数据的任意访问,而非IO读取,可以明显提升效率。

基于内存映射方式,完整的文本格式点云数据读取流程如下:(1)对文件内存映射获取数据指针p,文件字节数n;(2)通过指针p遍历长度为n的映射内存数据,查找统计换行符数量N;(3)开辟长度为N的点云数据结构类型固定内存空间;(4)遍历p指向的映射内存数据,分割字符串,同时将其转换为double类型数值,赋值到新开辟内存空间中。

此处附上内存映射部分代码

CString str = path.c_str();
USES_CONVERSION;
LPCWSTR wszClassName = A2CW(W2A(str));
str.ReleaseBuffer();DWORD error_code;
HANDLE hFile = CreateFile(wszClassName,GENERIC_READ | GENERIC_WRITE,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);if (hFile == INVALID_HANDLE_VALUE)
{error_code = GetLastError();cout << " CreateFile fail" << error_code << endl;return NULL;
}//创建一个文件映射内核对象
HANDLE hFileMap = CreateFileMapping(hFile,NULL,PAGE_READWRITE,NULL,NULL,L"Resource");if (hFileMap == NULL)
{error_code = GetLastError();cout << "CreateFileMapping fail: " << error_code << endl;return NULL;
}//将文件数据映射到进程的地址空间
char* pMapData = (char*)MapViewOfFile(hFileMap,FILE_MAP_ALL_ACCESS,NULL,NULL,NULL);if (pMapData == NULL)
{error_code = GetLastError();cout << " MapViewOfFile fail: " << error_code << endl;return NULL;
}//读取数据
char* pBuf = pMapData;

以上代码源自此处
其他参考如下:
1.CreateFileMapping , OpenFileMapping, MapViewOfFile, UnmapViewOfFile 和 FlushViewOfFile
2.C++ CreateFileMapping 内存映射实现快速读取文件
3.C\C++对大文件的快速读写(内存映射)

这些博主其实将内存映射用到的函数用法介绍的很清楚,此处就不多赘述。

内存映射+堆上开辟固定长度内存+简化atof函数能够大幅度提升文本读取效率,18s能够完成2000+万个点云数据读取和重建。

本文思想和方法源自于下述文章
[1]赵文.基于内存映射文件的海量点云数据快速读取方法[J].铁路计算机应用,2017,26(06):39-42.

海量点云数据快速读取方法相关推荐

  1. matlab体素化,一种三维激光点云数据快速体素化处理方法与流程

    本发明涉及一种三维激光点云数据快速体素化处理方法. 背景技术: 目前,三维激光扫描系统快速发展,由于研究的需要,往往需要把不具有空间长度信息的点数据转为具有三维空间信息的立方体,如何使用软件进行快速. ...

  2. R语言入门——数据快速读取与查看(含实例代码和参数讲解)

    R语言数据读取 介绍 引言 结构安排 数据读取函数 文本数据 readLines函数 键盘键入数据 scan函数讲解 表格数据 .xlsx文件介绍 表格数据函数参数介绍 快速读入参数介绍 竞赛数据练习 ...

  3. OMI产品介绍(含气溶胶产品及数据下载读取方法)

    本文介绍参考:OMI卫星数据介绍(包含气溶胶产品) - ENVI-IDL技术殿堂 - 博客园 1. Aura卫星及其搭载的传感器 Aura (在拉丁文中表示空气)于2004年7月15日发射升空,是由多 ...

  4. 织梦php标签获取多条数据_在织梦标签中使用SQL实现多个数据的读取方法

    内容页中: {dede:field name='mid' runphp='yes'} $aid=@me; $Query = "Select title from `dede_taglist` ...

  5. 基于三维点云数据的主成分分析方法(PCA)的python实现

    主成分分析(PCA)获取三维点云的坐标轴方向和点云法向量 # 实现PCA分析和法向量计算,并加载数据集中的文件进行验证import open3d as o3d # import os import n ...

  6. 三维点云数据的读取和三维曲面重建matlab仿真

    目录 1.算法概述 2.仿真效果 3.MATLAB仿真源码 1.算法概述 虽然Delaunay三角剖分算法可以实现网格曲面重建,但是其应用主要在二维剖分,在三维空间网格生成中遇到了问题.因为在三维点云 ...

  7. 三维点云数据处理软件供技术原理说明_海量点云数据处理理论与技术

    海量点云数据处理理论与技术 作者:程效军,贾东峰,程小龙 主编 出版时间:2014年版 内容简介 程效军.贾东峰.程小龙编著的<海量点云数据处理理论与技术>共分8章.第1章绪论,简要介绍海 ...

  8. 点云平面提取_基于LiDAR点云数据滤波方法

    基于LiDAR点云数据滤波方法 机载激光雷达所获取的数据被称为"点云(points cloud)"它在三维空间中呈现出随机分布的形状.在点云中,有些点属于真实的地形表面的点,有些点 ...

  9. 斯坦福的著名小兔子模型的点云数据_传统方法的点云分割以及PCL中分割模块

    之前在微信公众号中更新了以下几个章节 1,如何学习PCL以及一些基础的知识 2,PCL中IO口以及common模块的介绍 3,  PCL中常用的两种数据结构KDtree以及Octree树的介绍 有兴趣 ...

最新文章

  1. [unreal4入门系列之二] 下载和安装虚幻4游戏引擎
  2. ubantu系统下修改计算机名字
  3. Web框架中的ORM框架
  4. OO ALV 实现方式 ALV TABLE 之 栏位属性
  5. 【STM32】STM32CubeMX教程--功能介绍
  6. 【odoo12填坑日记】field.selection引号使用规范
  7. 数据包络分析--CCR模型
  8. 后台经验分享:如何做权限管理系统设计?
  9. 单应性矩阵的理解及求解
  10. 华为手机克隆在哪个文件夹_华为手机克隆软件怎么使用?手机克隆APP操作步骤以及下载地址详细介绍[多图]...
  11. 操作系统教程(第6版) 预习笔记
  12. 计算机组装主机怎么拆,【电脑组装知识网】电脑主机组装教程之戴尔显示器底座拆卸教程...
  13. TCP连接大量CLOSE_WAIT状态问题排查
  14. 盘点3种Python爬虫 中文乱码 的处理方法
  15. python编程:从入门到实践 阅读笔记
  16. 悉尼大学理学院计算机科学,澳洲悉尼大学理学院中国留学生
  17. 远程服务器返回错误: (500) 内部服务器错误解决办法
  18. 怎样阅读论文(台湾彭明辉)
  19. matlab 谐波电压含有量,电流平均值谐波检测方法MATLAB仿真
  20. 租房注意事项(北京)

热门文章

  1. 请基于Keil uVision5软件写出led流水灯代码
  2. 一、可行性分析研究报告-模板
  3. Erlang OTP
  4. 计算机画图水印怎么可以消除,如何去掉图片水印 画图工具简单去掉图片水印教程-电脑教程...
  5. MySQL 常用数据类型说明
  6. MSDN 2008 下载地址
  7. dlib 怎么安装vs2017_dlib库+vs2017详细配置流程
  8. Android:哪有什么互联网寒冬?只是你穿的少而已,系统工程师面试问题
  9. 尘梦回还服务器在维护中是什么意思,20191211维护公告解读
  10. html导出excel代码,html页面导出为excel表格(示例代码)