物理の駅 Physics station by 現役研究者

テクノロジーは共有されてこそ栄える

SPNG形式(バイナリ形式で複数の画像を1ファイルに統合した独自形式)のPythonでの読み込み方

諸事情で複数の画像を簡易的に1つのファイルに統合したいことがあり、またそのファイルをPythonから読み込みたい需要があった。

Numpyを使ったバイナリファイルの読み込み方、OpenCVを使ったデコードの仕方などの知識を使う。

V00000001_L0_VX0001_VY0000_mat0_080.spng&8&426991 とJSONに書いてある場合、データ範囲は8バイト目から426991バイト分なので、以下のように読む。

import cv2
import numpy as np

f = open("V00000001_L0_VX0001_VY0000_mat0_080.spng", "rb")
data = np.fromfile(f, np.uint8, 426991, "", 8)
dst = cv2.imdecode(data, -1)
cv2.imshow("window", dst)
cv2.waitKey(0)

データ範囲の指定がないSPNGから全ての画像を読む方法は以下の通り。最初の8バイトは画像のデータサイズ(unsigned long long = uint64_t)を意味する。

import os
import numpy as np
import cv2
import matplotlib.pyplot as plt
import struct

filename = "ImageFilterWithStream_GPU_0_00000000_0_024_bin.spng"
size = os.path.getsize(filename)
f = open(filename, "rb")
while True:
    if f.tell() == size:break
    n_bytes = struct.unpack('Q', f.read(8))[0]
    data = np.fromfile(f, np.uint8, n_bytes, "", 0)
    dst = cv2.imdecode(data, -1)
    plt.imshow(dst)
    plt.show()

サンプルデータ

https://1drv.ms/u/s!Ap9xAxIuzM0xvIwQ3C62NhsqgRoDfA

画像データのリスト images がある状態で、SPNGファイルとして出力する方法

import numpy as np
import cv2
import struct
filename = "test.spng"

f = open(filename, "wb")
for frame in images:
    ret, encoded = cv2.imencode(".png",frame)
    f.write(struct.pack("Q", len(encoded)))
    encoded.tofile(f)