note.nkmk.me

Python, OpenCVで画像を縦・横に連結 (hconcat, vconcat, np.tile)

Date: 2018-02-06 / tags: Python, OpenCV, 画像処理
このエントリーをはてなブックマークに追加

PythonのOpenCVで画像を縦・横に連結(結合)するには、cv2.vconcat(), cv2.hconcat()を使う。vはvertical(縦)、hはhorizontal(横)の意味。

引数として画像(NumPy配列ndarray)のリストを渡すと、リストの中の画像を縦または横に連結した画像ndarrayが返る。幅または高さが揃っていないとエラーになるので、サイズが異なる画像はあらかじめリサイズする必要がある。

同じ画像を繰り返し並べる場合はNumPyの関数numpy.tile()が便利。

numpy.tile()を使う場合、カラー画像(三次元ndarray)は第二引数reps(縦の繰り返し数、横の繰り返し数、1)のように繰り返し数を指定する。グレースケール画像(二次元ndarray)は(縦の繰り返し数、横の繰り返し数)

以下の3通りについてサンプルコードと合わせて説明する。

  • 縦に連結(結合)
  • 横に連結(結合)
  • 縦・横にタイル状に連結(結合)

例として2つの画像を読み込んで使う。

import cv2
import numpy as np

im1 = cv2.imread('data/src/lena.jpg')
im2 = cv2.imread('data/src/rocket.jpg')

なお、Pillowでの画像の連結については以下の記事を参照。

スポンサーリンク

縦に連結(結合)

幅が等しい画像を縦に連結

幅が等しい画像を縦に連結する場合はcv2.vconcat()がそのまま使える。

im_v = cv2.vconcat([im1, im1])
cv2.imwrite('data/dst/opencv_vconcat.jpg', im_v)

Python OpenCV concat 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 OpenCV concat v resize

リサイズした画像のリストを作成するのにリスト内包表記を使っている。

横に連結(結合)

基本的には縦に連結する場合と同じ考え方。

高さが等しい画像を横に連結

高さが等しい画像を横に連結する場合はcv2.hconcat()がそのまま使える。

im_h = cv2.hconcat([im1, im1])
cv2.imwrite('data/dst/opencv_hconcat.jpg', im_h)

Python OpenCV concat 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)

Python OpenCV concat 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)

Python OpenCV concat tile

例では便宜上縮小した同じ画像を使っているが、画像処理の係数を変えた結果を並べて比較したりするときなどに便利。

同一画像を縦・横にタイル状に連結

同一画像を繰り返し並べる場合はこれまでのようにnp.tile()でもOK。

第二引数reps(縦の繰り返し数、横の繰り返し数、1)のように繰り返し数を指定する。グレースケール画像(二次元ndarray)の場合は(縦の繰り返し数、横の繰り返し数)なので注意。

異なるサイズの画像を縦・横にタイル状に連結

異なるサイズの画像を縦・横にタイル状に連結する場合、上で定義したリサイズして連結する関数を利用する。

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)

Python OpenCV concat tile resize

スポンサーリンク
シェア
このエントリーをはてなブックマークに追加

関連カテゴリー

関連記事