geotrellis使用(十六)使用缓冲区分析的方式解决投影变换中边缘数据值计算的问题

Geotrellis系列文章链接地址http://www.cnblogs.com/shoufengwei/p/5619419.html

目录

  1. 前言
  2. 问题探索
  3. 采样说明
  4. 实现方案
  5. 总结

一、前言

       上一篇文章讲了通过Geotrellis导出自定义的Tiff文件(见geotrellis使用(十四)导出定制的GeoTiff),但是实际中有时会有BUG,就是数据值发生非常明显的变化,理论上只进行了切割、重投影操作,数据值不应该会发生特别大的变化。今天认认真真查找了下问题,发现是因为采样方式造成的。

二、问题探索

       使用QGIS打开导出的Tiff文件,形状、位置、投影等信息都正确,甚至大部分数据值都正确,唯一出现问题的地方就是边缘,边缘出现了很多不正常的值。经过试验不同的投影方式、采样方式、数据类型,发现只有在投影方式选择4326(原始数据投影方式是墨卡托-3857),采样方式选择三次卷积法内插等几种重采样方式的时候才会出现边缘的问题,那么很明显导致该问题的原因肯定是投影的时候选择的采样方式造成的,发现问题是解决问题的第一步。

三、采样说明

       什么是采样?先来看一下百度百科对重采样的定义。

就是根据一类象元的信息内插出另一类象元信息的过程。在遥感中,重采样是从高分辨率遥感影像中提取出低分辨率影像的过程。

       简单的说采样就是根据栅格图中坐标点周围的一些值重新计算该点的值。这里我们虽然没有进行降低分辨率操作但是由于改变了投影方式,各坐标点的数据肯定是要重新计算的,所以需要用到重采样。那么为什么采样会造成边缘数据值出现偏差呢?

       很简单,重采样要根据坐标点周围的几个点的值来重新计算当前点的值,在图像边缘处,只有部分临近点有数据,其他无数据的地方会用NODATA值来替代,所以计算结果当然会出问题。

       下面简单介绍一下在Geotrellis中支持的采样方式以及其几种常用的采样方式的简单原理。在Geotrellis中写好了以下几种采样方式:

编号 英文名称 中文名称
1 NearestNeighbor 最邻近内插法
2 Bilinear 双线性内插法
3 CubicConvolution 三次卷积法内插
4 CubicSpline 三次样条插值
5 Lanczos 正交相似变换
6 ... ...

       最近邻插值法是最简单的插值方法。也称作零阶插值,就是令变换后像素值等于距它最近的输入像素值。所以采用该方法边缘值计算不会出现问题。

       双线性内插法取(x,y)点周围的4邻点,在y方向(或x方向)内插两次,再在x方向(或y方向)内插一次,得到(x,y)点的值f(x,y)。
设4个邻点分别为(i,j),(i,j+1),(i+1,j),(i+1,j+1),i代表左上角为原点的行数,j代表列数。设α=x-i,β=y-j,过(x,y)作直线与x轴平行,与4邻点组成的边相交于点(i,y)和点(i+1,y)。先在y方向内插,计算交点的值f(i,y)和f(i+1,y)。f(i,y)即由f(i,j+1)与f(i,j)内插计算而来。简单的说就是选周围的四个点,然后做一条水平的线,按照线性求出水平线与四个点组成的四边形的交点的值,然后根据这两个值再计算出该点的值,理论上使用Bilinear也应该会出现边缘问题,但是实际测试并没有出现。查看其源码,发现其实现原理是根据四个点进行一个加权计算,所以边缘处有值,只是不够准确。

       三次卷积法内插法计算精度高,但计算量大,它考虑坐标点周围的16个邻点值,具体公式不在这里罗列,可以参考(http://wenku.baidu.com/link?url=mvyjK0h98UAldYFr_L0-qW-3Rj73uW_yMz0Jwo4ulbWUIfzdAI9f_qOqv_rVqhlTDmEU3xW6vLxp8JTTDtTeCsBGmcb1pmkUfhv-XlkAB6O)。

       三次样条插值是通过一系列形值点的一条光滑曲线,数学上通过求解三弯矩方程组得出曲线函数组的过程。简单说就是找插值结果是光滑的。其他采样方式不在哲理具体介绍。

       理论上插值结果越精确则需要的邻点就越多,边缘处就越容易出问题。可能Geotrellis中采样代码写的并不完善是导致边缘问题的因素之一,也许随着Geotrellis的更新,边缘问题会自动解决。但是目前来看我们必须要想一个办法来解决这个问题,下面就是本文重点要讲的——使用缓冲区分析的方式解决投影变换中边缘数据值计算的问题。

四、实现方案

1.缓冲区分析

       之前在做矢量数据栅格化的时候已经讲解过一次(见geotrellis使用(十)缓冲区分析以及多种类型要素栅格化)。这里用到缓冲区分析的思想,首先将要导出的区域做一个缓冲区分析,将范围扩大,然后根据扩大后的区域进行切割、重投影、数据类型转换等工作,待处理完毕之后再根据原始区域进行切割,这样虽然投影变换时的边缘问题依然存在,但是有问题的边界比实际需要的边界大,在用原始数据切割的时候,“有问题的边界”自然就被去掉了,就能得到一个正确的结果。下面来看具体实现。

2.扩大区域

       这一步很简单,Geotrellis中已经写好了缓冲区分析的函数,直接调用即可,代码如下:

poly.buffer(3 * cellWidth)

       其中ploy是原始区域,cellWidth是栅格数据的分辨率,这里相当于将面扩大3个像素,保证有足够的邻点。有了扩大后的区域之后,按照上文讲述的方式处理数据即可。

3.裁剪结果

       将得到的处理结果按照原始区域进行切割即可得到最终结果,但是Geotrellis中并没有提供不规则切割的方式,只能按照矩形切割。所以我们只能按照不规则区域的外接矩形进行切割,而原始区域又不一定是矩形,即使按照外接矩形切割一样会在很多地方包含扩大后的边界,得不到理想的效果。为了解决这一问题可以先将处理结果按照原始区域进行mask操作,然后切割,便会得到正确的结果。实现代码如下:

val mask = tile.mask(extent, poly)
val realTile = GeoTiff(mask, extent, crs)
                .raster
                .crop(poly.envelope)

       其中poly为原始区域,extent为缓冲区分析后的面的外接矩形,crs为数据投影方式,poly.envelope获取原始区域的外接矩形。这样第一行实现了mask操作,第二行先将mask的结果转为Geotiff然后进行crop(切割)操作。

五、总结

       以上就是通过使用缓冲区分析的方式解决投影变换中边缘数据值计算过程中出现偏差的问题。看似简单的原理与实现过程,其实同样可以上升到哲学的高度去思考。当我们解决一个问题的时候,如果不能有所突破何不换个角度考虑绕过这个问题,采取迂回的方式。当然该方法不止能解决重采样造成的问题,凡是涉及到边缘值计算的都可以采用该方法,下一篇文章我将讲解如何使用该方法解决瓦片计算过程中的边缘问题。最后申明这么好的方法并不是我想出来的,要归功于吴老板(具体姓名不在这里透露(●'◡'●))。

posted @ 2016-08-25 13:44  shoufengwei  阅读(1355)  评论(0编辑  收藏  举报