Python, OpenCVで画像を縦・横に連結 (hconcat, vconcat, np.tile)
PythonのOpenCVで画像を縦・横に連結(結合)するには、cv2.vconcat()
, cv2.hconcat()
を使う。vはvertical(縦)、hはhorizontal(横)の意味。
引数として画像(NumPy配列ndarray
)のリストを渡すと、リストの中の画像を縦または横に連結したndarray
が返る。幅または高さが揃っていないとエラーになるので、サイズが異なる画像はあらかじめリサイズする必要がある。
ここでは以下の場合についてサンプルコードと合わせて説明する。
- 縦に連結(結合):
cv2.vconcat()
- 幅が等しい画像を縦に連結
- 同一画像を繰り返し縦に連結
- 幅が異なる画像を縦に連結
- 横に連結(結合):
cv2.hconcat()
- 高さが等しい画像を横に連結
- 同一画像を繰り返し横に連結
- 高さが異なる画像を横に連結
- 縦・横にタイル状に連結(結合)
- 同じサイズの画像を縦・横にタイル状に連結
- 同一画像を縦・横にタイル状に連結
- 異なるサイズの画像を縦・横にタイル状に連結
例として2つの画像を読み込んで使う。
import cv2
import numpy as np
im1 = cv2.imread('data/src/lena.jpg')
im2 = cv2.imread('data/src/rocket.jpg')
なお、同じ画像を繰り返し並べる場合はNumPyの関数numpy.tile()
が便利。詳細は以下の記事を参照。
また、Pillow, scikit-imageでの画像の連結については以下の記事を参照。同じサイズの場合はscikit-imageが便利。枠線や境界線も追加できる。
縦に連結(結合): cv2.vconcat()
幅が等しい画像を縦に連結
幅が等しい画像を縦に連結する場合はcv2.vconcat()
がそのまま使える。
im_v = cv2.vconcat([im1, im1])
cv2.imwrite('data/dst/opencv_vconcat.jpg', im_v)
同一画像を繰り返し縦に連結
同一画像を繰り返し並べる場合はcv2.vconcat()
でもいいがnp.tile()
も使える。
im_v_np = np.tile(im1, (2, 1, 1))
cv2.imwrite('data/dst/opencv_vconcat_np.jpg', im_v_np)
幅が異なる画像を縦に連結
サイズが異なる画像を連結するための関数を用意しておくと便利。ここでは小さい方に合わせてリサイズしている。
shape[0]
が高さ、shape[1]
が幅を表す。
def vconcat_resize_min(im_list, interpolation=cv2.INTER_CUBIC):
w_min = min(im.shape[1] for im in im_list)
im_list_resize = [cv2.resize(im, (w_min, int(im.shape[0] * w_min / im.shape[1])), interpolation=interpolation)
for im in im_list]
return cv2.vconcat(im_list_resize)
im_v_resize = vconcat_resize_min([im1, im2, im1])
cv2.imwrite('data/dst/opencv_vconcat_resize.jpg', im_v_resize)
リサイズした画像のリストを作成するのにリスト内包表記を使っている。
- 関連記事: Pythonリスト内包表記の使い方
横に連結(結合): cv2.hconcat()
基本的には縦に連結する場合と同じ考え方。
高さが等しい画像を横に連結
高さが等しい画像を横に連結する場合はcv2.hconcat()
がそのまま使える。
im_h = cv2.hconcat([im1, im1])
cv2.imwrite('data/dst/opencv_hconcat.jpg', im_h)
同一画像を繰り返し横に連結
同一画像を繰り返し並べる場合はcv2.hconcat()
でもいいがnp.tile()
も使える。
im_h_np = np.tile(im1, (1, 2, 1))
cv2.imwrite('data/dst/opencv_hconcat_np.jpg', im_h_np)
高さが異なる画像を横に連結
縦の連結と同じく、サイズが異なる画像を連結するための関数を用意しておくと便利。ここでは小さい方に合わせてリサイズしている。
def hconcat_resize_min(im_list, interpolation=cv2.INTER_CUBIC):
h_min = min(im.shape[0] for im in im_list)
im_list_resize = [cv2.resize(im, (int(im.shape[1] * h_min / im.shape[0]), h_min), interpolation=interpolation)
for im in im_list]
return cv2.hconcat(im_list_resize)
im_h_resize = hconcat_resize_min([im1, im2, im1])
cv2.imwrite('data/dst/opencv_hconcat_resize.jpg', im_h_resize)
縦・横にタイル状に連結(結合)
同じサイズの画像を縦・横にタイル状に連結
cv2.vconcat()
とcv2.hconcat()
を組み合わせて、画像を縦・横にタイル状に連結できる。
二次元のリスト(配列)を引数として同じサイズの画像を連結する関数は以下のように定義できる。
def concat_tile(im_list_2d):
return cv2.vconcat([cv2.hconcat(im_list_h) for im_list_h in im_list_2d])
im1_s = cv2.resize(im1, dsize=(0, 0), fx=0.5, fy=0.5)
im_tile = concat_tile([[im1_s, im1_s, im1_s, im1_s],
[im1_s, im1_s, im1_s, im1_s],
[im1_s, im1_s, im1_s, im1_s]])
cv2.imwrite('data/dst/opencv_concat_tile.jpg', im_tile)
例では便宜上縮小した同じ画像を使っているが、画像処理の係数を変えた結果を並べて比較したりするときなどに便利。
同一画像を縦・横にタイル状に連結
同一画像を繰り返し並べる場合はこれまでのようにnp.tile()
でもOK。
第二引数reps
に(縦の繰り返し数、横の繰り返し数、1)
のように繰り返し数を指定する。グレースケール画像(二次元ndarray
)の場合は(縦の繰り返し数、横の繰り返し数)
なので注意。
np.tile()
についての詳細は以下の記事を参照。
異なるサイズの画像を縦・横にタイル状に連結
異なるサイズの画像を縦・横にタイル状に連結する場合、上で定義したリサイズして連結する関数を利用する。
def concat_tile_resize(im_list_2d, interpolation=cv2.INTER_CUBIC):
im_list_v = [hconcat_resize_min(im_list_h, interpolation=cv2.INTER_CUBIC) for im_list_h in im_list_2d]
return vconcat_resize_min(im_list_v, interpolation=cv2.INTER_CUBIC)
im_tile_resize = concat_tile_resize([[im1],
[im1, im2, im1, im2, im1],
[im1, im2, im1]])
cv2.imwrite('data/dst/opencv_concat_tile_resize.jpg', im_tile_resize)