よちよちpython

独習 python/Qpython/Pydroid3/termux

3次元配列の操作や単色パネル生成の実験

画像の配列は3次元になっていて、操作が非常に捉えにくい。

適当に配列をこねくり回すだけのアホな投稿。
前もやった気がする・・・



3次元配列の軸の入れ替え実験


Excelシートを想像する。
各セルを「セル[行数][列数] 」と表すとする。
3行3列の配列は以下のようにあらわされる。

import numpy as np
import matplotlib.pyplot as plt

lst = []
for i in range(3):
    for j in range(3):
        lst.append("セル[{}][{}]".format(i,j))
        
lst = np.array(lst).reshape(3,3)
lst
array([['セル[0][0]', 'セル[0][1]', 'セル[0][2]'],
       ['セル[1][0]', 'セル[1][1]', 'セル[1][2]'],
       ['セル[2][0]', 'セル[2][1]', 'セル[2][2]']], dtype='<U8')



さて、Excelシートが3枚あったとする。
シートの名前をそれぞれ「red」「green」「blue」という。3枚合わせてRGBレンジャー。・・・

上記でやったように、各シートのセルを番号で「red[行数][列数]」で表す関数を作ってみる。
シート名と作る範囲の行数・列数の変数名は以下。

  • シート名 : sheet_name
  • 行数 : row
  • 列数 : col
def make_array(sheet_name, row, col):
    lst = []
    for i in range(row):
        for j in range(col):
            lst.append("{}[{}][{}]".format(sheet_name, i, j))
    lst = np.array(lst)    .reshape(row, col)
    
    return lst

# 実行(3行3列の範囲で)
red = make_array("red", 3, 3)

# 表示
red
array([['red[0][0]', 'red[0][1]', 'red[0][2]'],
       ['red[1][0]', 'red[1][1]', 'red[1][2]'],
       ['red[2][0]', 'red[2][1]', 'red[2][2]']], dtype='<U9')

3行3列のシート「red」のインデックス表が完成した。
同じようにして、「green」「blue」も作る。

green = make_array("green", 3, 3)
blue = make_array("blue", 3, 3)

3種類の配列ができた。

つぎに、作った「red」「green」「blue」の配列を横に連結する。

rgb_concat = np.concatenate([red, green, blue], axis=1)
rgb_concat
array([['red[0][0]', 'red[0][1]', 'red[0][2]', 'green[0][0]',
        'green[0][1]', 'green[0][2]', 'blue[0][0]', 'blue[0][1]',
        'blue[0][2]'],
       ['red[1][0]', 'red[1][1]', 'red[1][2]', 'green[1][0]',
        'green[1][1]', 'green[1][2]', 'blue[1][0]', 'blue[1][1]',
        'blue[1][2]'],
       ['red[2][0]', 'red[2][1]', 'red[2][2]', 'green[2][0]',
        'green[2][1]', 'green[2][2]', 'blue[2][0]', 'blue[2][1]',
        'blue[2][2]']], dtype='<U11')

横に連結した。3行9列になっている。一応確認。

rgb_concat.shape
(3, 9)



次に、これを(3, 3, 3)の形にreshapeで変形させたあと、transpose()メソッドで軸を入れ替える操作を行う。

rgb_transpose201 = rgb_concat.reshape(3,3,3).transpose(2,0,1)
rgb_transpose201
array([[['red[0][0]', 'green[0][0]', 'blue[0][0]'],
        ['red[1][0]', 'green[1][0]', 'blue[1][0]'],
        ['red[2][0]', 'green[2][0]', 'blue[2][0]']],

       [['red[0][1]', 'green[0][1]', 'blue[0][1]'],
        ['red[1][1]', 'green[1][1]', 'blue[1][1]'],
        ['red[2][1]', 'green[2][1]', 'blue[2][1]']],

       [['red[0][2]', 'green[0][2]', 'blue[0][2]'],
        ['red[1][2]', 'green[1][2]', 'blue[1][2]'],
        ['red[2][2]', 'green[2][2]', 'blue[2][2]']]], dtype='<U11')

3つの塊に別れた配列ができた。
最も細かい単位の配列は全て「red」「green」「blue」の順番で並んでいる。
なんとなくRGBの画像の配列っぽい。

1番上の塊は、列番号が全て「0」、真ん中の塊の列番号は「1」、最後の塊の列番号は「2」になっている。

軸の入れ替えのtranspose(2,0,1)の(2,0,1)は軸の順番を示している。
この順番にした理由は、たまたまこの軸の順番で入れ替えたらRGBっぽかった、ということで。

ちなみに軸の順番を違う並びに変えると出来る配列も全然違うものになる。
(1,2,0)の順でやってみる。

rgb_transpose120 = rgb_concat.reshape(3,3,3).transpose(1,2,0)
rgb_transpose120
array([[['red[0][0]', 'red[1][0]', 'red[2][0]'],
        ['red[0][1]', 'red[1][1]', 'red[2][1]'],
        ['red[0][2]', 'red[1][2]', 'red[2][2]']],

       [['green[0][0]', 'green[1][0]', 'green[2][0]'],
        ['green[0][1]', 'green[1][1]', 'green[2][1]'],
        ['green[0][2]', 'green[1][2]', 'green[2][2]']],

       [['blue[0][0]', 'blue[1][0]', 'blue[2][0]'],
        ['blue[0][1]', 'blue[1][1]', 'blue[2][1]'],
        ['blue[0][2]', 'blue[1][2]', 'blue[2][2]']]], dtype='<U11')

これはRGBの配列っぽくない。

単色画像の生成実験


画像のRGB値は0~255までの整数を取ります。
RGBのそれぞれが0か255しか取らないとすると、その組み合わせと色は次のようになる。
255を1に置き換えてあらわすと、

R G B
0 0 0
0 0 1
0 1 0
0 1 1 青緑
1 0 0
1 0 1
1 1 0
1 1 1

の8色ができる。
表の一番左の列は赤、次の列は緑、最後の列は青、で並んでいる。

「赤」列は「red」の配列、「緑」列は「green」配列、「青」列は「blue」配列の3つを用意する。

最終的に、

「黒」「青」「緑」「青緑」
「赤」「紫」「黄」「白」

の形で、単色が各10行10列(10×10ピクセル)ずつで並ぶようにする。
3つの配列に0か255の数字を入れる。



赤の列は00001111で並んでいるので、

0 0 0 0
255 255 255 255

となるよう、それぞれ10行10列の範囲で数を埋める。

同様に、緑の配列は00110011なので

0 0 255 255
0 0 255 255

青の配列は01010101なので

0 255 0 255
0 255 0 255


配列作成

ゼロ

a_0 = np.zeros((10,10))
a_0
array([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]])

255

a_255 = a_0.copy()
a_255[:,:]=255
a_255
array([[255., 255., 255., 255., 255., 255., 255., 255., 255., 255.],
       [255., 255., 255., 255., 255., 255., 255., 255., 255., 255.],
       [255., 255., 255., 255., 255., 255., 255., 255., 255., 255.],
       [255., 255., 255., 255., 255., 255., 255., 255., 255., 255.],
       [255., 255., 255., 255., 255., 255., 255., 255., 255., 255.],
       [255., 255., 255., 255., 255., 255., 255., 255., 255., 255.],
       [255., 255., 255., 255., 255., 255., 255., 255., 255., 255.],
       [255., 255., 255., 255., 255., 255., 255., 255., 255., 255.],
       [255., 255., 255., 255., 255., 255., 255., 255., 255., 255.],
       [255., 255., 255., 255., 255., 255., 255., 255., 255., 255.]])



赤の配列

0 0 0 0
255 255 255 255

結果論だが、↓は.reshape(-1,1)で1列に並び替えたら上手く行ったので変形させている。

red_1 = np.concatenate([a_0, a_0, a_0, a_0], 0) # 1段目
red_2 = np.concatenate([a_255, a_255, a_255, a_255], 0) # 2段目

red = np.concatenate([red_1,red_2], 1).reshape(-1,1)

#red
red.shape
(800, 1)



緑の配列

0 0 255 255
0 0 255 255

green_1 = np.concatenate([a_0, a_0, a_255, a_255], 0) # 1段目
green_2 = np.concatenate([a_0, a_0, a_255, a_255], 0) # 2段目

green = np.concatenate([green_1,green_2], 1).reshape(-1,1)

#green



青の配列

0 255 0 255
0 255 0 255

blue_1 = np.concatenate([a_0, a_255, a_0, a_255], 0) # 1段目
blue_2 = np.concatenate([a_0, a_255, a_0, a_255], 0) # 2段目

blue = np.concatenate([blue_1,blue_2], 1).reshape(-1,1)

#blue



横に結合して軸の入れ替え


「黒」「青」「緑」「青緑」
「赤」「紫」「黄」「白」

「red」「green」「blue」の各配列は、10行10列の塊が横に4つ、縦に2つ並んだ40行20列を1列に並び替えたものになっている。
配列「red」「green」「blue」を縦に繋げて、3次元40行20列でreshapeし、transpose(2,1,0)で軸を入れ替える操作を行う。

なぜこのような操作をしているかというと、たまたまこうしたら上手く行ったから。( ^ω^)・・・

img = np.concatenate([red, green, blue], axis=0).reshape(3,40,20).transpose(2,1,0)

img = img.reshape(20,40,3)

plt.imshow(img.astype('uint8'))
<matplotlib.image.AxesImage at 0x21293252b88>

20200911134113

# 保存
plt.imsave("panel.jpg", img.astype('uint8'))

単色パネルを目的の並びで生成できた。



結論 配列操作の手順


  1. RGBの各配列で、見たままの形に並べる。
  2. 各色の配列で、x行1列に変形する。
  3. 結合して変形して軸を入れ替える
    • np.concatenate([array1, array2, array3, ・・・], axis=0).reshape(次元, 最終形の列数, 最終形の行数).transpose(2,1,0)
  4. 更に(最終形の列数, 最終形の行数、次元)にreshape
  5. plt.imshowで.astype('uint8')をつけないと「Clipping input data to the valid ナンチャラの警告が出る。」



同じ操作で配列がどのように並び替えられたか確認。

# 手順1
RED = make_array("RED",4,5)
GREEN = make_array("GREEN",4,5)
BLUE = make_array("BLUE",4,5)

# 手順2
RED = RED.reshape(-1,1)
GREEN = GREEN.reshape(-1,1)
BLUE = BLUE.reshape(-1,1)

# 手順3
COLOR = np.concatenate([RED, GREEN, BLUE], axis=0).reshape(3,5,4).transpose(2,1,0)

# 手順4
COLOR = COLOR.reshape(4,5,3)

print(COLOR.shape)


# 最終表示
COLOR
(4, 5, 3)





array([[['RED[0][0]', 'GREEN[0][0]', 'BLUE[0][0]'],
        ['RED[0][4]', 'GREEN[0][4]', 'BLUE[0][4]'],
        ['RED[1][3]', 'GREEN[1][3]', 'BLUE[1][3]'],
        ['RED[2][2]', 'GREEN[2][2]', 'BLUE[2][2]'],
        ['RED[3][1]', 'GREEN[3][1]', 'BLUE[3][1]']],

       [['RED[0][1]', 'GREEN[0][1]', 'BLUE[0][1]'],
        ['RED[1][0]', 'GREEN[1][0]', 'BLUE[1][0]'],
        ['RED[1][4]', 'GREEN[1][4]', 'BLUE[1][4]'],
        ['RED[2][3]', 'GREEN[2][3]', 'BLUE[2][3]'],
        ['RED[3][2]', 'GREEN[3][2]', 'BLUE[3][2]']],

       [['RED[0][2]', 'GREEN[0][2]', 'BLUE[0][2]'],
        ['RED[1][1]', 'GREEN[1][1]', 'BLUE[1][1]'],
        ['RED[2][0]', 'GREEN[2][0]', 'BLUE[2][0]'],
        ['RED[2][4]', 'GREEN[2][4]', 'BLUE[2][4]'],
        ['RED[3][3]', 'GREEN[3][3]', 'BLUE[3][3]']],

       [['RED[0][3]', 'GREEN[0][3]', 'BLUE[0][3]'],
        ['RED[1][2]', 'GREEN[1][2]', 'BLUE[1][2]'],
        ['RED[2][1]', 'GREEN[2][1]', 'BLUE[2][1]'],
        ['RED[3][0]', 'GREEN[3][0]', 'BLUE[3][0]'],
        ['RED[3][4]', 'GREEN[3][4]', 'BLUE[3][4]']]], dtype='<U11')



どういう法則で並んでるんだろ・・・



追記
配列の結合にnumpy.stack()を使うと新たな次元方向に結合可能と。



今回の続き。
【Numpy】np.stackを使った2次元配列の3次元方向への結合と画像生成の実験 - よちよちpython
形状や軸の入れ替えなど訳の分からないことをしなくとも簡単にできる方法。