M5Stack Core2に超巨大な画像を表示する方法

投稿者:

みなさんこんにちは佐々木です。

さて今回はM5Stack Core2に超強大な画像を表示する方法をご紹介します。例として幅高さ24800 x 21000大阪市のオルソ写真M5Stack Core2に表示させてみました。

仕組み

超巨大な画像をタイル状のJPEG画像に分割して必要な部分のみを組み合わせて表示しています。縮小率を変えた画像を用意することで段階的な拡大縮小をサポートしています。数万枚に及ぶ分割後の画像ファイルはSDカードに保存しています。

スワイプした時に遅延なく画像を動かせるようにPSRAMをデコード済み画像のキャシュとして使っています。キャッシュに表示対象の画像があればキャッシュからLCDにデータを転送します。キャッシュに表示対象の画像がなければファイルからキャッシュに画像を読み込みんでからLCDに転送します。

タイルサイズは一般的な256 x 256ではなく160 x 160としました。JPEG画像のデコード時間が短くなるため操作時のレスポンスが良くなります。またJPEGクオリティを少し下げてデコード時間が短くなるようにしました。

画像データの作り方

分割後のタイル画像

タイル状の画像フォーマットはDeep Zoomファイル形式(DZI)を採用しました。変換用のツールがいくつかあり今回はdeepzoom.pyを使いました。

deepzoom.pyのインストール方法は次の通りです。

$ python3 -m venv myenv
$ . myenv/bin/activate
$ git clone https://github.com/openzoom/deepzoom.py.git
$ cd deepzoom.py
$ python setup.py install

アルファ付き画像だと分割に失敗してしまうので一旦JPEG画像に変換します(大阪市のオルソ画像はアルファ付きTIFF画像でした)。

$ python -c "from PIL import Image;Image.MAX_IMAGE_PIXELS=1000000000;im=Image.open('./osaka_ortho.tif');im.convert('RGB').save('./osaka_ortho.jpg',quality=100)"

以下のスクリプトをファイルに保存し、

# dzi.py
import os
import deepzoom
from PIL import Image

Image.MAX_IMAGE_PIXELS=1000000000

# Specify your source image
SOURCE = 'osaka_ortho.jpg'
OUTPUT = 'output/osaka_ortho.dzi'
TILESIZE = 160
FMT = 'jpg'
QUALITY = 0.9

# Create Deep Zoom Image creator with weird parameters
creator = deepzoom.ImageCreator(
    tile_size=TILESIZE,
    tile_overlap=0,
    tile_format=FMT,
    image_quality=QUALITY,
    resize_filter="bicubic",
)

# Create Deep Zoom image pyramid from source
creator.create(SOURCE, OUTPUT)

num_levels = creator.descriptor.num_levels
max_level = num_levels - 1
dim = creator.descriptor.get_dimensions(max_level)
width = dim[0]
height = dim[1]
print(f"{max_level} {TILESIZE} {width} {height} {FMT}")

JPEG変換後の元画像と同じディレクトリに置いて実行します。画像の分割には少し時間がかかります。

$ python dpi.py

outputディレクトリにosaka_ortho.dziファイルとosaka_ortho_filesディレクトリが出力されます。osaka_ortho_filesディレクトリに分割後の画像が`0~15`の数字のディレクトリに分けて保存されます。これらの数字はlevelと呼ばれています。この中で一番数字の大きなディレクトリに元画像と同じ解像度の画像が分割されて保存されています。数字が1つ小さくなるごとに1/2サイズの解像度の画像が分割されて保存されます。

osaka_ortho.dziファイルにはDeep Zoomファイル形式の情報がXML形式で書き込まれていますが今回は使わないので捨てます。また7以下のディレクトリには極端に小さな解像度(256未満)の画像しか入っていないのでこれも捨てます。

あとはoutputディレクトリをSDカードにコピーします。ファイル数が多いのでコピーにかなり時間がかかります。

スケッチ

ここからスケッチをダウンロード&解凍、フォルダ名m5_osaka_ortho-mainからm5_osaka_orthoに変更しm5_osaka_ortho.inoを開いてM5Stack Core2に書き込んでください。M5Stack Core2が起動すると画像が表示されると思います。スワイプで画像の移動、Zoom Inで拡大、Zoom Outで縮小、Resetで画像が初期位置に戻ります。

他の画像を使いたい場合は以下の部分を書き換えてください。

// m5_osaka_ortho.ino
// 最小レベル,最大レベル,タイルサイズ,元画像幅,元画像高さ,画像ディレクトリ,画像ファイルの拡張子
TileImage::ImageSource osakaOrthoImage(8, 15, 160, 24800, 21000, "/output/osaka_ortho_files", "jpg");
// タイルサイズが256の場合、25を設定
M5TileImageViewer viewer(M5.Lcd, osakaOrthoImage, 64); // If tileSize is 256, specify 25.

Webブラウザ向けには大きな画像の拡大縮小をスムーズに行うためのライブラリDeepZoomOpenSeadragonといったものがありますが、M5StackやArduinoなど組み込みマイコン向けにこういったライブラリがなかったので実装してみました。

ぜひご活用ください!