宙畑 Sorabatake

機械学習

衛星データからの石油タンク検出② Tellusの衛星データ用いて検出結果を確認する

前回の記事で学習させたモデルを使って、Tellus上の画像を使って石油タンクの検出ができるか試してみます。

本記事は、技術評論社から出版されている月刊誌Software Designに宙畑が寄稿した連載「衛星データプラットフォームTellusハンズオン」の内容から、一部修正して掲載するものです。

前回までの記事では、衛星データを利用した物体検出のデータセットと石油タンクを検知するモデルについて紹介してきました。

今回の記事では、いよいよTellus上で公開されている衛星データを使って、石油タンクの検出にトライしてみたいと思います。

本記事で紹介しているコードはGithubでも公開しています。
https://github.com/sorabatake/softwaredesign_2

Tellus Traveler で目的の衛星データを見つける

前回の記事で石油タンクを物体検出できるモデルができたので、Tellusで衛星データを取得し予測をしてみます。

今回は衛星データを閲覧できるTellus Satellite Data Traveler(Tellus Traveler) のviewerを利用して、Tellusの解析環境上から API で衛星データを取得し、解析を行います。

Tellus Traveler viewer(アクセスにはTellusの会員登録が必要です)
https://www.tellusxdp.com/traveler/viewer

Tellus Travelerで利用する衛星データの検索

Tellus Traveler 上で、左メニューの検索から、AOI(Area of Interest:興味領域)で取得したい範囲を選択します。

今回は地図上からも石油タンクの形状がわかりやすい東京湾沿岸の一部を指定しました。

また、検索時に必要な衛星データセットを指定します。今回は、前回のモデルで利用したデータに近い高解像度の光学データが欲しいため、ASNARO-1を選択しています。

検索条件に当てはまる衛星データの一覧が左側に表示され、
右側にデータが地図上に青くマッピングされる

検索をすると、AOIで指定した領域が含まれる衛星データが一覧で表示されます。

詳細ページでデータの情報を確認

天気の影響で石油タンクが隠れてしまっている可能性もあるので、データ毎の詳細ボタンで内容を確認し、利用したいデータの詳細ページから、そのデータセットとシーンの情報をメモしておきます。

今回は次のデータを利用しました。

データセット名:【Tellus公式】ASNARO-1_L1B(ID: 50f05191-a0d5-4377-be8d-a880ec54ae27)
シーン名: 20210120014907000_20210120141246583_AS1_D01_L1B(ID: 5b552df4-9ee7-4387-b1a6-f8874dc6ba86)

Tellusの開発環境でAPIを利用してデータを取得する

Tellusで開発環境を申請すると、TellusのAPIから衛星データを取得できるようになります。

開発環境はアカウント登録とは別に申込みを行う必要があります。提供まで時間がかかる場合があります。問合せフォームより、お問い合わせください。

Tellusマイページ上でAPIのトークン情報も取得しておきます。
※APIのトークンの発行はTellusのHow to Useをご確認ください。https://www.tellusxdp.com/ja/howtouse/dev/install_development_
environment.html

Tellus Traveler の API 仕様は次のページで確認することが可能です。

Tellus Traveler API仕様ページ(要ログイン)
https://www.tellusxdp.com/docs/travelers/

まずは作業で必要になるライブラリを呼び出します。

import requests
import json
import matplotlib.pyplot as plt
from PIL import Image
from io import BytesIO
from osgeo import gdal

今回は GDAL という座標に紐付いた画像データ等を扱うことのできるライブラリを利用します。

続いて、APIで衛星画像ファイルを取得していきます。まずは先程Tellus Traveler上でメモをした、データセットとシーンで利用可能なファイル一覧を取得します。

# Tellusのマイページ上で取得したAPIトークンを入力
TOKEN = 'xxxxxxxxxxxxxxxxxxxxxxxxx'
# Tellus Travelerで探したデータのidを指定
dataset_id = '50f05191-a0d5-4377-be8d-a880ec54ae27'
scene_id = '5b552df4-9ee7-4387-b1a6-f8874dc6ba86'
files_url = f'https://www.tellusxdp.com/api/traveler/v1/datasets/{dataset_id}/data/{scene_id}/files/'


# TellusのAPIはBearer認証のため、必要な情報をリクエストヘッダに設定
headers = {
    'Authorization': 'Bearer ' + TOKEN,
    'Content-Type' : 'application/json',
}


# 上で設定したヘッダ情報を利用して、APIへGETリクエストを送信し結果を受け取る
response = requests.get(files_url, headers=headers)
json.loads(response.content)

すると、次のようなJSON形式でファイル一覧情報を見ることができます。その中から利用する、20210120141246583_AS1_D01_L1B_MUL.TIF というファイルの id である 11 をメモします。

{'results': [{'id': 1,
   'name': '20210120014907000_20210120141246583_AS1_D01_L1B_thumb.png',
   'status': 'uploaded',
   'size_bytes': 280157},
...
{'id': 11,
   'name': '20210120141246583_AS1_D01_L1B_MUL.TIF',
   'status': 'uploaded',
   'size_bytes': 758136430},
...
{'id': 19,
   'name': '20210120141246583_AS1_D01_L1B_MUL_DIMAPTHM.PNG',
   'status': 'uploaded',
   'size_bytes': 1055817}]}

このファイルは座標情報を持った画像データ形式の GeoTIFF で保存されていて、GDALで中身を読み取ることができます。

ファイル名の終わりに付いている MUL というのはマルチスペクトル(Multispectral)でカラー画像を意味していて、他にもファイル一覧からは、20210120141246583_AS1_D01_L1B_PAN.TIF というファイルもあり、こちらの PAN はパンクロマティック(Panchromatic)で白黒の画像を意味しています。パンクロマティック画像の方が画像の分解能が高い(細かいところまで見られる)ので、利用するタスクによって使い分けることが可能です。

目的であるカラー画像の GeoTIFF ファイルを取得するため、APIからファイルのダウンロードURLを取得します。

file_id = 11
api_download_url = f'https://www.tellusxdp.com/api/traveler/v1/datasets/{dataset_id}/data/{scene_id}/files/{file_id}/download-url/'


# 画像ダウンロードURL生成POSTリクエストを送信し、生成されたURLを受け取る
response = requests.post(api_download_url, headers=headers)
tif_download_url = json.loads(response.content)['download_url']

続いてダウンロードを行いますが、ファイルサイズが約700MBあり、ダウンロードを安定化させるために、分割してダウンロードします。

# streamオプションを有効にし、画像をチャンク(塊)に分割したデータを一つのファイルに書き込んでいく
tif_file_path = './ASNARO.tif'
with requests.get(tif_download_url, stream=True) as r:
    r.raise_for_status()
    with open(tif_file_path, 'wb') as f:
        for chunk in r.iter_content(chunk_size=8192): 
            f.write(chunk)

ダウンロードが完了したら、ファイルをGDALで読み込んでいきます。

src = gdal.Open(tif_file_path)

今回利用している、ASNARO-1のセンサーは表1の6バンドを搭載しています。

表1 ASNARO-1のセンサーで取得しているバンド一覧
バンド1 Ocean Blue(400~450nm)
バンド2 Blue(450~520nm)
バンド3 Green(520~600nm)
バンド4 Red(630~690nm)
バンド5 Red Edge(705~745nm)
バンド6 NIR(760~860nm)

トレーニングデータで利用した光学データと同じになるように、RGBの値(バンド2~4)を抜き取りPNG画像形式に変換していきます。

バンド2である、青色の部分だけ取得してデータの中身を確認してみましょう。次のコードで、指定したバンドの画像情報を配列形式で取得できます。

b2 = src.GetRasterBand(2).ReadAsArray()

一般的な画像と異なり、値が 16bit(0〜65,535) で表現されていますが、実際は 30,000 以下の値がほとんどなので、確認のためにヒストグラムで最大値まで表示してみます。

# Matplotlibのpyplot.hist関数を利用してヒストグラムを表示する
plt.hist(b2.flatten(), bins=100, range=(1, b2.max()))
plt.title("BlueBand", fontsize=10)
ASNARO-1のバンド2(Blue)のヒストグラム

このデータでは400付近に値が集中していることがわかります。

続いて、実際にデータを座標でプロットしてみます。ヒストグラムでも確認したように最大値は4,000近くあり、そのままですと全体的に暗い画像になるため、最大値を1,200にして表示します。

plt.imshow(b2, vmax=1200, cmap='Blues')
最大値を1,200にして画像化

画像の輪郭が見えてきました。

取得したデータの前処理・解析

GDALを用いて扱いやすい画像サイズとPNG形式に変換していきます。

GDALのTranslate関数を利用することで、一気に画像切り抜きと、PNG形式への変換が可能ですが、渡すパラメータが多いので説明していきます。

gdal.Translate('./tellus_travel_test.png', # 出力ファイル名
               src,
               srcWin=(3500,3400,1280,1280), 
               scaleParams=[[0,1200]], 
               format='PNG',
               outputType=gdal.GDT_Byte, 
               bandList=[4,3,2])

まず、srcWinは (切り抜き開始x座標, 切り抜き開始y座標, x座標切り抜き幅, y座標切り抜き幅) で指定します。先程の、座標プロット画像を参考に欲しい地点の座標を決めます。

scaleParamsでは、出力の際に利用するデータの範囲を決めることができ、ヒストグラムでも確認した1,200までのデータに絞ります。

format では PNG 形式を指定して、outputTypeでPNG画像形式の8bit(0~255)で出力するために gdal.GDT_Byte を指定します。

最後に、bandList では、PNG形式用に RGB の順番に渡していきたいので、4バンド目(赤)、3バンド目(緑)、2バンド目(青)を順に指定します。
保存された画像を確認します。

tellus_travel_test_image = Image.open('./tellus_travel_test.png')
tellus_travel_test_image.show()
Tellusから取得した衛星画像 Credit : Original data provided by NEC

写真の上部分に石油タンクが存在することが確認できたので、この画像を前回の記事で作成したYOLOXの機械学習モデルで解析し、石油タンクの物体検出ができるかを確認します。

# 前回作成した学習済みモデルデータ
MODEL_PATH = "./YOLOX_outputs/airbus_config/best_ckpt.pth"

# 画像サイズをモデルの学習時に利用していた大きさに揃える
resized_test_image = tellus_travel_test_image.resize((640, 640))
resized_test_image.save('resized_test.jpg')




!python ./YOLOX/tools/demo.py image \
    -f airbus_config.py \
    -c {MODEL_PATH} \
    --path ./resized_test.jpg \
    --tsize 640 \
    --save_result \
    --device gpu
Tellus上で公開されている衛星データから石油タンクを検出した結果(茶丸)
Credit : Original data provided by NEC

一部石油タンクが認識されていなかったり、左側で石油タンク以外のものを石油タンクと誤認識したりしていますが、Tellus上のデータでも物体検出を行えるということが確認できました。

以上のように、公開されているデータセットやモデルで目的対象の物体検知モデルを作成して、Tellus上で取得したデータを用いて、海外のデータセットだけでなく日本のデータに対しても解析を行えるようになりました。

本記事で紹介しているコードはGithubでも公開しています。
https://github.com/sorabatake/softwaredesign_2

TellusTravelerでほかのデータも取得して同じように検出できるか試して見てはいかがでしょうか。