后面要从事相关的GIS开发工作,之前并没有系统接触过测绘相关的专业知识,是一个新的领域了,有好多新的概念及专业名词。把工作中遇见和学习的知识,按照项目分类进行下记录,一方面加深新知识的记忆,另一方面方便后面遇见相关问题再次查阅和学习。
一.gdal之gdal2tiles.py转化工作
刚着手开发了一段时间GIS的文件切片工作,主要是对现成的python源码gdal2tiles.py进行一个c++的转化。gdal2tiles.py是GDAL库中用于生成TMS瓦片的python代码,支持谷歌墨卡托EPSG:3857与经纬度EPSG:4326两种瓦片,输出png格式图像。
1.知识链接
哈哈,先放几个大佬的链接,知识太深奥了,先拿着后面慢慢学习。
(1)瓦片及坐标系的一些概念:地图瓦片坐标系定义及计算原理_高德地图瓦片坐标-CSDN博客
(2)别人家的一个类似的源码处理标注,主要是一些注释和文字解释写的容易明白,主要是源码里的英文注释,个人太水了看不懂,唔.......How it works(14) GDAL2Tiles源码阅读-CSDN博客
2.gdal2tiles源码输出目录引出的坐标系和瓦片概念
代码转化完了,把输出的结果给了前端使用,前端问文件结果是按照什么逻辑分的目录结构,中间又提到了瓦片、金字塔之类的名词,好吧问到我了,我哪知道,然后就开始百度了,生成的目录结构如下(这里是基于大地坐标系切出的瓦片结构):
最外层的0-13:切分出了13级瓦片结构。
上面是瓦片的层级图(也称为地图金字塔)。瓦片层级的下一层文件夹(如6805、13611、13612等)名称代表切出瓦片的唯一x坐标编号,最内层的图片名称(6185.png等)代表切出瓦片的唯一y坐标编号,这里的唯一我理解的是:整个瓦片切分结构是已经确定的了,不会变化,地球上的每个经纬度都能在每个瓦片层级上找到其所在的瓦片位置(以瓦片x、y来定位),就是存在一个映射关系。
3.GetGeoTransform函数引出的仿射矩阵
这个到很好搜出来相关的资料,是文件像素点与实际地理信息建立的一个映射关系,根据仿射矩阵的6个参数,输入像素点计算实际的地理经纬度,或者反推。
我这里做个这么个事,也不是太确定对不对,有大佬懂的话,欢迎指点。手头有俩文件,一个影像文件、一个高程文件,配套的,其中影像文件没有仿射矩阵,无法计算其经纬度信息,然后就依据高程文件给影像文件手动设立了仿射矩阵,也到能对的上,做了如下处理:
(a)左上角经纬度、旋转系数,这4个参数没动,直接拿着给赋值了。
(b)空间分辨率俩个值,按照反比处理了,把两个文件的栅格长宽之比的反比作为常数乘以空间分辨率,就得到了新的分辨率:dstReso = srcReso * srcLen / dstLen.
4.影像文件与高程文件以及图片通道
这个是在处理输入数据时了解的,输入的两个tif文件,
影像文件是一个3通道的(关于图片的通道,就是三原色嘛,红绿蓝,三个通道控制三原色的亮度不同,就叠加形成了五颜六色图像。有意思的还有一个相关的概念-灰度图,就是当三个通道每个像素颜色的值即亮度都相同,图像就没有颜色了,显示灰色吧好像),这里通过gdal读数据的时候术语叫“波段”,波段的每个像素点的值就是该像素的亮度;
高程文件是一个1通道的,其波段的像素点值不代表颜色亮弱,而代表该像素点高度,可以和影像文件配合使用,获取某点的地理信息。
二.openCV库的的使用
1.cv::imread使用的坑
(1)imread读取失败
使用该函数读取一张图片时,老是读取失败,一直提示下面错误:
[ WARN:0@3.003] global loadsave.cpp:248 cv::findDecoder imread_('
解决办法:需要根据自己的调试环境,将附加依赖感项的opencv库改成相应的debug或release版本,我这里用的debug模式,但两种模式的库都添加了,去掉release库,就能够读取成功了。
(2)16位深度图片读取异常
在处理高程文件的时候,需要生成相应的png图片供前端使用,由于精度的需要,将高程文件转化位了16位深度的png图片。下图是生成的16位、8位图片对比效果:
左边是16位深度图片,右侧为8位深度图片。
在使用8位图片时,8位图片范围在0-255,处理方式为每个像素点的代表高度=像素点值/100*基准高程值,数据相对正确,但是每差一个像素点,高度差10m左右,精度不够;
在使用16位图片时,16位图片范围在0-65535,处理方式为 每个像素点的代表高度=像素点值/10000*基准高程值。差异就在这里体现了,比如某一点,gdal解析出来该像素点值=9278;但使用各种图片软件查看其值=36,而且使用cv::imread()函数进行像素点数据值读取也是36.
出现了不可避免的问题,就把以前的问题也带出来了:
自己调用gdal库生成png图片时,只生成了一个波段,而且再次使用gdal打开该文件调用函数查看是一个波段,为什么各种软件查看包括cv::imread()函数调用查看时,都是有rgb三种波段?
首先,我怀疑gdal中的波段概念跟rgb通道概念不是一码事,网上找资料看了几个,但的确是一个东西;后面,怀疑gdal、opencv生成图片的方式有差异,于是我更改了图片生成方式,用gdal生成像素矩阵,然后调用cv::Mat根据该像素矩阵生成图片,结果明明使用的CV_16UC1(16位无符号整型一通道图片)格式生成的图片,但再次读取格式就变成了8位无符号整型3通道图片。至此基本上确定了是cv::imread()函数的问题了,函数原型为imread( const String& filename, int flags = IMREAD_COLOR ),其参数2代表图像读取方式,默认方式IMREAD_COLOR (代表总是按照3通道数据处理),将参数改为IMREAD_UNCHANGED(按照原格式进行数据处理),果然数据格式跟写入时一样了(16位无符号整型一通道图片),读像素点值也正确了。
排查罪魁祸首(还是自己菜,不理解、没用好函数,哈哈)cv::imread()最终结果再加上查找的资料,其实也可以解释为啥看图软件总是把1波段图像读成3波段了,显示器渲染的问题吧,目前市场主流的硬件屏幕都是8位深度的渲染,然后中间可能就有个默认处理的算法了,然后就看不出来16位深度图片的真实值了。