scikit-imageで画像を縦・横に連結(montage)

Posted: | Tags: Python, scikit-image, 画像処理

scikit-imageのskimage.util.montage()を使うと、同じサイズの複数の画像を縦横に連結(結合)できる。

ここでは、以下の内容について説明する。

  • skimage.util.montage()の基本的な使い方
  • 余白部分の値(色)を指定: 引数fill
  • レイアウトを指定: 引数grid_shape
  • 枠線・境界線の幅を指定: 引数padding_width
  • カラー画像(三次元配列)を処理: 引数multichannel

PillowやOpenCVを使った画像の連結については以下の記事を参照。サイズが異なる画像の連結についても説明している。

なお、本記事におけるscikit-imageのバージョンは0.16.2

skimage.util.montage()の基本的な使い方

以下の同じ形状shapeの3つの二次元配列numpy.ndarrayを例とする。カラー画像(三次元配列)を使った例は後述。

import numpy as np

import skimage.util

a = np.arange(1, 7).reshape(2, 3)
print(a)
# [[1 2 3]
#  [4 5 6]]

b = a * 10
print(b)
# [[10 20 30]
#  [40 50 60]]

c = a * 100
print(c)
# [[100 200 300]
#  [400 500 600]]

skimage.util.montage()の第一引数にそれらの配列を要素とするリストを指定すると、縦横に連結されたnumpy.ndarrayが返される。

m = skimage.util.montage([a, b, c])
print(m)
# [[  1   2   3  10  20  30]
#  [  4   5   6  40  50  60]
#  [100 200 300 129 129 129]
#  [400 500 600 129 129 129]]

print(m.shape)
# (4, 6)

この例では、3つの配列が2 x 2のレイアウトに配置され、右下の足りない部分(余白)は全体の平均値である129で埋められている。これらは後述の引数で変更可能。

skimage.util.montage()内部では配列のリストがM行N列のK個の配列からなる形状(K, M, N)numpy.ndarrayとして処理される。

その形で引数に指定してもOK。

abc = np.array([a, b, c])
print(abc)
# [[[  1   2   3]
#   [  4   5   6]]
# 
#  [[ 10  20  30]
#   [ 40  50  60]]
# 
#  [[100 200 300]
#   [400 500 600]]]

print(abc.shape)
# (3, 2, 3)

print(skimage.util.montage(abc))
# [[  1   2   3  10  20  30]
#  [  4   5   6  40  50  60]
#  [100 200 300 129 129 129]
#  [400 500 600 129 129 129]]

個々の配列は同じ形状shapeでないと(K, M, N)numpy.ndarrayに変換できないためエラーとなる。

d = a[:, :2]
print(d)
# [[1 2]
#  [4 5]]

# skimage.util.montage([a, b, c, d])
# ValueError: could not broadcast input array from shape (2,3) into shape (2)

余白部分の値(色)を指定: 引数fill

デフォルトでは余白部分の値は全体の平均値となる。

m = skimage.util.montage([a, b, c])
print(m)
# [[  1   2   3  10  20  30]
#  [  4   5   6  40  50  60]
#  [100 200 300 129 129 129]
#  [400 500 600 129 129 129]]
print(np.mean(np.array([a, b, c])))
# 129.5

引数fillで余白を埋める値を指定できる。

print(skimage.util.montage([a, b, c], fill=0))
# [[  1   2   3  10  20  30]
#  [  4   5   6  40  50  60]
#  [100 200 300   0   0   0]
#  [400 500 600   0   0   0]]

レイアウトを指定: 引数grid_shape

デフォルトでは、行数(縦の連結個数)と列数(横の連結個数)が同数のレイアウトに連結される。

引数grid_shapeにタプル(行数, 列数)で縦横のレイアウトを指定できる。

print(skimage.util.montage([a, b, c], grid_shape=(1, 3)))
# [[  1   2   3  10  20  30 100 200 300]
#  [  4   5   6  40  50  60 400 500 600]]

print(skimage.util.montage([a, b, c], grid_shape=(3, 1)))
# [[  1   2   3]
#  [  4   5   6]
#  [ 10  20  30]
#  [ 40  50  60]
#  [100 200 300]
#  [400 500 600]]

元の配列より少ないとエラーとなる。

# print(skimage.util.montage([a, b, c], grid_shape=(1, 2)))
# IndexError: list index out of range

元の配列より多いと足りない部分(余白)は引数fillの値で埋められる。上述の通り、デフォルトは平均値。

print(skimage.util.montage([a, b, c], grid_shape=(2, 3)))
# [[  1   2   3  10  20  30 100 200 300]
#  [  4   5   6  40  50  60 400 500 600]
#  [129 129 129 129 129 129 129 129 129]
#  [129 129 129 129 129 129 129 129 129]]

print(skimage.util.montage([a, b, c], grid_shape=(2, 3), fill=0))
# [[  1   2   3  10  20  30 100 200 300]
#  [  4   5   6  40  50  60 400 500 600]
#  [  0   0   0   0   0   0   0   0   0]
#  [  0   0   0   0   0   0   0   0   0]]

枠線・境界線の幅を指定: 引数padding_width

引数padding_widthを指定すると、枠線および境界線の幅を指定できる。これまでの例のようにデフォルトはpadding_width=0なので枠線も境界線もなし。

print(skimage.util.montage([a, b, c], padding_width=1))
# [[129 129 129 129 129 129 129 129 129]
#  [129   1   2   3 129  10  20  30 129]
#  [129   4   5   6 129  40  50  60 129]
#  [129 129 129 129 129 129 129 129 129]
#  [129 100 200 300 129 129 129 129 129]
#  [129 400 500 600 129 129 129 129 129]
#  [129 129 129 129 129 129 129 129 129]]

枠線・境界線の値も引数fillで指定できる。

print(skimage.util.montage([a, b, c], padding_width=1, fill=0))
# [[  0   0   0   0   0   0   0   0   0]
#  [  0   1   2   3   0  10  20  30   0]
#  [  0   4   5   6   0  40  50  60   0]
#  [  0   0   0   0   0   0   0   0   0]
#  [  0 100 200 300   0   0   0   0   0]
#  [  0 400 500 600   0   0   0   0   0]
#  [  0   0   0   0   0   0   0   0   0]]

カラー画像(三次元配列)を処理: 引数multichannel

カラー画像は三次元配列として読み込まれる。

三次元配列をskimage.util.montage()で処理するには、引数multichannelTrueとする。

import skimage.io
import skimage.util

a = skimage.io.imread('data/src/lena.jpg')
print(a.shape)
# (225, 400, 3)

b = a // 2
c = a // 3

m = skimage.util.montage([a, b, c], multichannel=True)
print(m.shape)
# (450, 800, 3)

skimage.io.imsave('data/dst/skimage_montage_default.jpg', m)

元画像。

lena

結果画像。

skimage.util.montage() default

デフォルトはmultichannel=Falseなので省略するとエラーになる。要注意。

# skimage.util.montage([a, b, c])
# ValueError: Input array has to be either 3- or 4-dimensional

上の結果画像から分かるように、デフォルトでは余白部分は各色(各チャンネル)の平均値の色で埋められる。

引数fillで任意の色を指定可能。(R, G, B)のタプルで指定する。

m_fill = skimage.util.montage([a, b, c], fill=(255, 128, 0), multichannel=True)

skimage.io.imsave('data/dst/skimage_montage_fill.jpg', m_fill)

skimage.util.montage() fill

そのほか、grid_shapepadding_widthの使い方は二次元配列と同じ。

m_1_3_pad = skimage.util.montage([a, b, c],
                                 fill=(0, 0, 0),
                                 grid_shape=(1, 3),
                                 padding_width=10,
                                 multichannel=True)

print(m_1_3_pad.shape)
# (245, 1240, 3)

skimage.io.imsave('data/dst/skimage_montage_1_3_pad.jpg', m_1_3_pad)

skimage.util.montage() grid_shape, padding_width

関連カテゴリー

関連記事