🦔

PythonでSeparateなTiffを作る

2023/03/11に公開

Tiffは言わずとしたら画像ファイルのフォーマットですが、そのピクセルの値はChunkyに保持されます。

RGBAな画像でChunkyだとこんなの感じな配列になる。

1ピクセル目のR, 1ピクセル目のG, 1ピクセル目のB, 1ピクセル目のA,
2ピクセル目のR, 2ピクセル目のG, 2ピクセル目のB, 2ピクセル目のA,
3ピクセル目のR, 3ピクセル目のG, 3ピクセル目のB, 3ピクセル目のA,
4ピクセル目のR, 4ピクセル目のG, 4ピクセル目のB, 4ピクセル目のA,
...

現在出回っているTiffのほとんどはこの方式ですが、実はChunky以外にもSeparateという方式があります。

RGBAな画像でSeparateだとこんなの感じな配列になる。色ごとに配列が別れているからSeparate。

1ピクセル目のR, 2ピクセル目のR, 3ピクセル目のR, 4ピクセル目のR, ...,
1ピクセル目のG, 2ピクセル目のG, 3ピクセル目のG, 4ピクセル目のG, ...,
1ピクセル目のB, 2ピクセル目のB, 3ピクセル目のB, 4ピクセル目のB, ...,
1ピクセル目のA, 2ピクセル目のA, 3ピクセル目のA, 4ピクセル目のA, ...

Tiffを自分で作る際、特に意識をしなかったらChunkyにファイルが作られます。

ライブラリやビュワーがSeparateに対応していないケースもあるため、普通であればそれでいいのですが、今回どうしてもSeparateなファイルを作る必要に迫られたので、調べたことをまとめておきます[1]

import numpy
import math
import tifffile
from PIL import Image
import matplotlib.pyplot as plt

data = numpy.zeros((768, 512, 4), 'uint8')
for i in range(768):
    for j in range(512):
        data[i,j,0] = 255 if j % 3 == 0 else 0
        data[i,j,1] = 255 if j % 3 == 1 else 0
        data[i,j,2] = 255 if j % 3 == 2 else 0
        data[i,j,3] = math.floor((2 - i % 3) / 2 * 255)
	
pil_img = Image.fromarray(data)
pil_img.save('test.png')

デモとして幅512px高さ768px、X方向へは赤緑青と繰り返し、Y方向へは不透明度が1,0.5,0と周期的に変化するデータを用意します。

with Image.open('test.png') as img:
    print(img.width, img.height)
    im_crop = img.crop((0, 0, 20, 20))
    plt.imshow(numpy.array(im_crop))

デモ用に保存したpngを開くとこんな感じ。

普通にChunkyなTiffとして保存するだけだったら以下のようにするだけ。

tifffile.imwrite('test1.tif', data)

SeparateなTiffを作るにはまずdataの配列の構造を入れ替えてやる必要があります。

numpy.transpose(data[0:4, 0:4, :],(2, 0, 1)) 

こんなふうに(768, 512, 4)だった配列を(4, 768, 512)としてあげる。そのうえでplanarconfigというフラグを2に設定してやれば完成。

tifffile.imwrite('test2.tif', numpy.transpose(data,(2, 0, 1)), planarconfig=2)

できたファイルの中を確認

まずはChunkyなTiff

from tifffile import TiffFile
with TiffFile('test1.tif') as tif:
    for page in tif.pages:
        for tag in page.tags:
            print(tag.name, tag.value)

続いてSeparateなTiff

with TiffFile('test2.tif') as tif:
    for page in tif.pages:
        for tag in page.tags:
            print(tag.name, tag.value)

PlanarConfigurationがPLANARCONFIG.SEPARATEとなっていることが確認できる。

脚注
  1. この令和の世に普通に生きてたらSeparateなTiffを欲しくなることなんてないのだろう、ぜんぜん資料が見つからんかった。 ↩︎

Discussion