メモ:NumPyとPyTorchの配列について

 Stable Diffusion で画像を作るため、自前でAutomatic1111版っぽいGUIを作ろうとしています。とうとう画像データの中身を扱う必要が出てきたので、画像のデータ構造を一度勉強しようと思いました。特にPillow, NumPy, PyTorchの画像データの構造がいまいち分かっていなかったので、下のページを見てまとめてみました。
https://imagingsolution.net/program/python/numpy/python_numpy_pillow_image_convert/

モノクロ画像

モノクロ画像の場合のピクセルへのアクセス

 モノクロ画像でのアクセスを理解しておくと、カラー画像でアクセスする際の順序が理解しやすくなるので、まずはこちらから。

  • Pillow は (x, y) でピクセルにアクセス

  • NumPy は (y, x) で要素にアクセス

 Pillowは画像ライブラリであり、(x,y)の指定でピクセルにアクセスします。
 NumPyはもともと行列ライブラリであるため、2次元の画像データも行列の一種として扱います。行ベクトル(xベクトル)がy方向に集まった集合としてデータ構造を作っています。
 そのため、NumPyではまず行ベクトル(y方向)を指定してから、指定した行ベクトルの中の要素をx方向に指定します。
 ちなみに、この「大まか→細かい」というまとめ方は以降でも出てくるので、覚えておくと後々理解しやすくなります。

カラー画像

カラー画像の場合

ライブラリごとにインデックスの順序が異なるのでややこしいですが、以下を押さえておくと、それぞれの順序が分かりやすいです。

  • Pillow, NumPyはモノクロのピクセルをカラーに拡張している

  • PyTorchはチャネルごとに1枚の画像として扱う

 Pillowではモノクロ(1byte, 1チャネル)だったピクセルをカラー(3byte, 3チャネル)に拡張した、というスタンスのようです。そしてPillowのデータ構造はあくまで2次元データです。そのため、R,G,Bのチャネル値まで欲しい場合、NumPy等のように一度にインデックスを指定できず、以下の順序をとります。
1.  getpixel(x,y)でピクセルを取得
2. 得られたピクセルからR,G,Bの各チャネル値を取る

 NumPyでも同様に、モノクロをカラーに拡張した、というスタンスでデータ構造が作られています。そのため、行(y)→列(x)の順にピクセルを指定した後でチャネルを指定する、という順序になります。

 ここでPyTorchの登場。PyTorchのテンソルは行列を拡張したものです。そのため、PyTorchで扱う画像データの構造もNumPyと同様に行列の一種として扱うようです。つまりxベクトルをy方向にまとめたものが1枚の画像となります。
そしてPyTorch(厳密にはStable Diffusionをはじめとするライブラリ群で、ですが)では、チャネルごとに1枚の画像としてデータをまとめているようです。そのため、チャネル(c)→行(y)→列(x)の順で要素を指定します。

バッチも含めて考える

Stable Diffusion(厳密には以下略)では複数のカラー画像をバッチ処理できます。そのため、画像データ構造にはバッチのインデックスも入っています。
バッチがある場合のインデックス順はNumPyやPyTorchで同じ考え方で、バッチ→画像の順です。

インデックスをまとめると

PyTorch : (バッチ, c, y, x)
NumPy: (バッチ, y, x, c)
Pillow: (x, y)のみ。バッチやチャネルへアクセスは別途行う

この記事が気に入ったらサポートをしてみませんか?