1. 背景:
クラウド最適化歯車の背景には、将来的にリモートセンシング画像上でクラウドが直面する問題を解決することが考えられます。
リモートセンシング画像の特徴は主に大きくあります。一般的に1枚の画像は5~10GBで、衛星1個が毎日TBレベルずつ増加し、全世界が毎日PBレベルずつ増加します。衛星センサーの精度が向上するにつれて、個々の画像ファイルはますます大きくなります。
大きな問題は、保存が簡単ではないことです。ローカル データセンターで保存できない場合は、クラウドに保存する必要があります。ただし、大量のリモート センシング データがクラウドにアップロードされると、次のような多くの課題に直面します。
既存のソフトウェアの多くは、クラウド上のイメージ ファイル (特に S3 などのオブジェクト ストレージ) をリモートから読み取ることができません。分析を開く前に、ローカルにダウンロードする必要があります。
ソフトウェア用の S3 ドライバーも追加します。ファイルをリモートで分析するには、ファイルの内容全体を読み取る必要があります (これはリンク リスト ファイルであるため)。これはネットワーク経由で行われるため、効率に影響することに注意してください。
画像ファイル内の領域の一部 (タイルまたはサムネイル) が個別に取得されるため、ファイル全体をダウンロードするかアクセスする必要があります。タイルが画像全体の 1/N しか占めていない場合でも。
では、クラウド上のリモートセンシング画像データにアクセスする場合、一部の(できるだけ小さい)コンテンツのみにアクセスすることはできるのでしょうか?
2.TIFF形式
ここで TIFF ファイルの形式を共有します。
TIFF は、複数の画像を 1 つのファイルに保存できる、柔軟で適応性のあるファイル形式です。次に、各画像にはラベル ディレクトリ (複数のラベル) があり、ピクセル深度、ピクセルごとのバンド情報、RGB エンコード、およびその他の詳細情報が記録されます。
注: 上の図の属性間に順序の要件はなく、すべてオフセットによって検索されます。実際、これはリンクされたリストの形式になっています。
したがって、TIFF ファイル内のブロックの順序は非常に柔軟です。
3. GeoTIFF はクラウド向けに最適化されています
この目標を達成するには、次の能力が必要です。
クラウド ストレージ プロトコルは、レンジ モードでのクラウド上のファイルの読み取りをサポートします。
GeoTIFF 画像ファイルの場合、最初に「すべてのタグ データ」を読み取り、次に Offset オフセットを使用してターゲット データを読み取ることがサポートされています。
これら 2 つの条件のうち 1 つ目は、現在、主要なクラウド ベンダーのオブジェクト ストレージ (S3 など) でサポートされています。
重要なのは2番目の条件です。まず、GeoTIFF のすべての「メタデータ」をファイルの先頭に配置し、実際の Data データをファイルの最後に配置できますか。
このようにして、非常に大きな GeoTIFF リモート センシング画像にアクセスするには、HTTP GET を送信してファイル ヘッダーの 16K バイトを取得するだけで、ファイル内の残りのコンテンツの場所がわかります。次に何にアクセスしたいのか、オフセットに従ってHTTP GETを送信して、目的のXXバイトの画像データにアクセスすれば十分です。
すると、画像が細かくスライスされ、「タイル」に従って対象領域にアクセスできるようになります。ヘッダー内のタグ情報は既に存在しているため、特定の領域にアクセスするには、クラウド上の指定されたファイルのオフセットをOffsetに従って直接読み込むだけで済みます。
このモードでは、リモート センシング画像ファイルは依然として標準の GeoTIFF 形式ですが、クラウド アクセスにより適しています。つまり、cog形式のtiffファイルです。
4 COG フォーマット (クラウドに最適化された GeoTIFF)
クラウドに最適化された GeoTIFF 形式をより普及させるために、COG (Cloud Optimized GeoTIFF) 標準が特別に確立されました。
仕様はhttps://www.cogeo.org/を参照してください。
導入記事は次を参照してください: https://medium.com/planet-stories/cloud-native-geospatial-part-2-the-cloud-optimized-geotiff-6b3f15c696ed
tiff ファイルは画像ファイルであり、空間座標を含む tiff 画像ファイルです。これを geotiff と呼びます。geotiff は現実世界に比例しており、測定できます。クラウドに最適化された geotiff は、geotiff ブロックの保存方法を再編成した tiff ファイルです。関係は次のとおりです。
COG で使用される 2 つの主なデータ編成手法は、タイルと概要グラフです。データの圧縮により、データのオンライン送信もより効率的になります。
3.1 cogのデータ構成
タイル スライスでは、画像内に組み込みのスライスが作成されます。指定した領域のスライスをすばやく取得できる場合は、データの特定の部分にアクセスするだけで済みます。
概要マップは、同じ画像の複数のダウンサンプリング バージョンを作成します。ダウンサンプリングとは、元の画像から「ズームアウト」すると、多くの詳細が失われ(元の画像では現在の 1 ピクセルが 100 ピクセル、さらには 1000 ピクセルになる可能性があります)、そのデータ量も小さくなります。通常、GeoTIFF には、さまざまなズーム レベルに合わせて複数の概要が含まれます。これにより、レンダリング中にこの特定のピクセル値のみを返す必要があり、1000 ピクセルを表すためにどのピクセル値が使用されているかを調べる必要がないため、サーバーの応答が速くなりますが、これにより、ファイル全体。
4.2 クラウド最適化の原則
COG で必要な GeoTIFF の主なテクノロジは、ファイル内にタイルと概要を構築し、画像圧縮によって補完されます。完成した巨大なTIFFではなく、完成した画像を縦横のグリッドに合わせてタイルに分割し、必要な範囲のタイルのみを読み込むというものです。
フル解像度の tiff ファイル形式については、リンクBigTIFF、4 ギガバイト境界を突破する TIFFを参照してください。
4.3 操作方法
GeoTIFF はルールに従ってタイルと概要を編成し、(たとえば、Nginx を使用して) クラウドに保存します。そのため、HTTP 範囲リクエストは関連するファイル部分のみをリクエストできます。
クライアントがファイル全体の概要イメージをレンダリングしたい場合、すべてのピクセルをダウンロードする必要はなく、すでに作成された小さい概要イメージを要求するだけで済みます。HTTP 範囲リクエストはサーバー上の GeoTIFF ファイルの構造をサポートしているため、クライアントはファイル全体の必要な部分を簡単に見つけることができます。
タイルは、ファイル全体のごく一部を処理または視覚化する必要がある場合に役立ちます。これは概要の一部である場合もあれば、完全な解決策である場合もあります。ただし、タイルは領域の関連するすべてのバイトをファイルの同じ部分に編成するため、範囲リクエストで必要なものを取得できます。
GeoTIFF がオーバービューとタイルで「クラウドに最適化」されていない場合でも、データのリモート操作は機能します。実際にはデータのごく一部しか必要としない場合でも、ファイル全体または大部分がダウンロードされる可能性があるだけです。
5. ビルド手順
5.1 サーバーが範囲リクエストをサポートしているかどうかを検出する
http 応答ヘッダーに バイト としてAccept-Ranges がある場合 、サーバーが範囲リクエストをサポートしていることを意味します。 Content-Length は、 取得する画像のフル サイズを指定します。
5.2 リクエストの生成
サーバーが範囲リクエストをサポートしている場合は、Range ヘッダーを使用してそのようなリクエストを生成できます。次の図は、リクエストされた TIFF の 0 ~ 65536 バイトを示しています。
6. COGの生成
COG は GDAL を使用して生成できます。ここでは、C++ に基づく空間データ関数ライブラリである GDAL について簡単に説明します。ここでは、JAVA と Python に基づくインストールについて説明します。GDAL の依存関係をインストールするには Java を、GDAL をインストールするには Python を参照してください。
6.1 gdal スクリプトの生成
Java または Python 開発に基づいて, COG によって生成された関数は既存のプロジェクトに簡単に統合できます. さらに, GDAL 既製スクリプトも使用できます. これらのスクリプトは, Java に従って GDAL 依存関係でダウンロードされた圧縮パッケージを最初にインストールできます, release-1928-x64-dev\release-1928-x64\bin\gdal\apps にあります。
スクリプトを使用して通常の tif を COG に変換するには、次のようにします。
CD をスクリプト ディレクトリに
移動します。 次のスクリプトを実行して概要を生成します。
gdaladdo -r average 你的tif路径
以下のスクリプトを実行してCOGを生成します
gdal_translate 输入tif的路径 输出tif的路径 -co TILED=YES -co COMPRESS=DEFLATE -co COPY_SRC_OVERVIEWS=YES
主なパラメータの意味:
TILED=YES タイルを生成
COMPRESS=DEFLATE DEFLATE メソッドを使用して圧縮
COPY_SRC_OVERVIEWS=YES 元のデータから概要をコピー
6.2 Python プログラムによって生成されたコードの使用
def translateToCOG(in_ds, out_path):
"""
将dataset转为cog文件
:param in_ds: 输入dataset
:param out_path: 输出路径
:return:
"""
im_bands = in_ds.RasterCount
for i in range(im_bands):
# 获取nodata和波段统计值
nodataVal = in_ds.GetRasterBand(i + 1).GetNoDataValue()
maxBandValue = in_ds.GetRasterBand(i + 1).GetMaximum()
# 缺啥设置啥
if maxBandValue is None:
in_ds.GetRasterBand(i + 1).ComputeStatistics(0)
if nodataVal is None:
in_ds.GetRasterBand(i + 1).SetNoDataValue(0.0)
in_ds.BuildOverviews("NEAREST", [2, 4, 8, 16])
driver = gdal.GetDriverByName('GTiff')
driver.CreateCopy(out_path, in_ds,
options=["COPY_SRC_OVERVIEWS=YES",
"TILED=YES",
"COMPRESS=DEFLATE",
"INTERLEAVE=BAND"])
def warpDataset(in_ds, proj, resampling=1):
"""
转换空间参考
:param in_ds:输入dataset
:param proj: 目标空间参考wkt
:param resampling: 重采样方法
:return: 转换后的dataset
"""
RESAMPLING_MODEL = ['', gdal.GRA_NearestNeighbour,
gdal.GRA_Bilinear, gdal.GRA_Cubic]
resampleAlg = RESAMPLING_MODEL[resampling]
return gdal.AutoCreateWarpedVRT(in_ds, None, proj, resampleAlg)
def getTifDataset(fileDir, srid=None):
"""
返回tif文件dataset
:param fileDir:文件路径
:param srid:epsg_srid,若指定且不同于dataset,就将dataset转为该空间参考
:return:dataset
"""
dataset = gdal.Open(fileDir, gdal.GA_ReadOnly)
if dataset is None:
print(fileDir + "文件无法打开")
return
fileSrs = osr.SpatialReference()
fileSrs.ImportFromWkt(dataset.GetProjection())
if srid is None:
return dataset
else:
outSrs = osr.SpatialReference()
outSrs.ImportFromEPSG(srid)
if fileSrs.IsSame(outSrs):
return dataset
else:
return warpDataset(dataset, outSrs.ExportToWkt())
if __name__ == '__main__':
start = time.process_time()
inPath = "input.tif"
outputPath = "output.tif"
originDataset = getTifDataset(inPath, 4326)
translateToCOG(originDataset, outputPath)
originDataset = None
end = time.process_time()
print('Running time: %s Seconds' % (end - start))
6.3 結果の表示
生成された COG を確認および表示するには、 validate_cloud_optimized_geotiff.pyを使用して確認するなど、さまざまな方法があります。
python validate_cloud_optimized_geotiff.py test.tif
qgisビュー
レイヤー ウィンドウでレイヤー オブジェクトを右クリックし、プロパティを表示すると、範囲、バンド情報、空間参照などの COG のプロパティ情報を表示できます。
7. フロントエンド表示 (Openlayers に基づく)
Openlayers は、WebGLtile レイヤーと GeoTIFFSource データ ソースを更新し、geotiffjs で tif ソース データを解析し、WebGL でレンダリングすることで、GeoTIFF 画像のフロントエンド レンダリングとバンドの結合とストレッチの機能を実現しました。
以下のコンポーネントが使用されます
import GeoTIFFSource from "ol/source/GeoTIFF";
import Map from "ol/Map";
import WebGLTile from "ol/layer/WebGLTile";
import View from "ol/View";
メタ情報の取得
COG ファイルに格納されているバンド情報と nodata 値は geotiffjs を通じて取得できます。
なお、ここでの取得は機能をデモンストレーションするためのもので、表示のみであれば事前に取得する必要はなく、自身で指定したピクセルの実数値範囲を使用します。OLのGeoTIFFSourceはデフォルトでnormalize属性が有効になっており、バンドのピクセル範囲は、0 ~ 1 の比率に自動的に正規化されます。
/**
* 获取tif元信息
* @param url
* @returns noData,波段信息
*/
export async function getGeoTifMetaData(url: string) {
// 返回的是GeoTIFF对象 https://geotiffjs.github.io/geotiff.js/module-geotiff-GeoTIFF.html
const tiff = await fromUrl(url);
// 返回GeoTIFFImage https://geotiffjs.github.io/geotiff.js/module-geotiffimage-GeoTIFFImage.html
const image = await tiff.getImage();
const bands = [];
// 获取波段数
const samples = image.getSamplesPerPixel();
for (let i = 0; i < samples; i++) {
// 获取该波段信息
const element = image.getGDALMetadata(i);
bands.push(element);
}
// 获取nodata值
const noData = image.getGDALNoData();
return { noData, bands };
}
結果は次のとおりです。
レンダリング式を書く
OL の WebGL レイヤーは、式を使用してレイヤーのレンダリング スタイルを構成します。特定の構文については、「ExpressionValue」を参照してください。
const url = "http://localhost:8082/static/cogtest/cog_origin.tif";
const metaData = await this.getGeoTifMetaData(url);
// 取出元信息里的波段最大最小值
const bands = metaData.bands.map((item) => {
return {
min: parseFloat(item.STATISTICS_MINIMUM),
max: parseFloat(item.STATISTICS_MAXIMUM),
};
});
const style = {
color: [
"array",
// 这里123波段对应RGB,分别做线性拉伸,最大值*0.1为了效果比较好
// 若初始化GeoTIFFSource时normalize为true则不需要使用像素值拉伸,而是0-1之间的比例
["interpolate", ["linear"], ["band", 1], bands[0].min, 0, bands[0].max * 0.1, 1],
["interpolate", ["linear"], ["band", 2], bands[1].min, 0, bands[1].max * 0.1, 1],
["interpolate", ["linear"], ["band", 3], bands[2].min, 0, bands[2].max * 0.1, 1],
1,
],
};
ロードレイヤー
const source = new GeoTIFFSource({
// 默认为true,这里为了演示使用真实值拉伸设为false
normalize: false,
sources: [
{
url,
// 这里可以手动重设min,max,nodata等属性以覆盖tif原始信息
nodata: 0,
},
],
});
// GeoTIFF的view是异步的,里面存了COG的范围,空间参考等信息
source.getView().then((view) => {
const layer = new WebGLTile({
className: "WebGLTile",
style,
source,
extent: view.extent,
});
const map = new Map({
target: "map",
layers: [layer],
view: new View({
projection: "EPSG:3857",
}),
});
map.getView().fit(view.extent!, { padding: [50, 50, 50, 50] });
});
ディスプレイ効果