概要
複数の画像を連結して1枚にして返す用事があったので、NumpyやOpenCVを利用して動作を確認した。だいたいNumPyで愚直に実行。Pillowは利用していない。
バージョン情報
- Python 3.6.5
- opencv-python==3.4.2.17
サンプル画像
かわいいワンコが仲睦まじく駆けている下記3画像を用いる。
水平に連結
単純に水平に繋げるだけであれば、OpenCV(かなにか)で読み込んでNumPyでaxis=1を指定してconcatすれば済む。
import cv2, numpy as np from matplotlib import pylab as plt # dog1〜3.jpegという名前で保存された3枚の画像をOpenCVで読み込む images = [cv2.imread('data/concat_images/dog{}.jpeg'.format(i)) for i in range(1, 4)] # axis=1でconcat hcon_image = np.concatenate(images, axis=1) plt.imshow(cv2.cvtColor(hcon_image, cv2.COLOR_BGR2RGB))
垂直に連結
続いて垂直版。水平版はaxis=1で連結したが、垂直版はaxis=0で連結する。
# axis=0でconcat vcon_image = np.concatenate(images, axis=0) plt.imshow(cv2.cvtColor(vcon_image, cv2.COLOR_BGR2RGB))
画像の一部を被せて垂直に連結
画像の一部の座標が重複する状態で被せてconcatする。
例えば100ピクセルほど上に被せる場合、単純に対象イメージの上100ピクセルを切った上でconcatする方法が考えられる。
# 3枚の画像を読み込む images = [cv2.imread('data/concat_images/dog{}.jpeg'.format(i)) for i in range(1, 4)] # 2と3の上100pixelを切り取る images = [images[0], images[1][100:], images[2][100:]] vcon_image = np.concatenate(images, axis=0) plt.imshow(cv2.cvtColor(vcon_image, cv2.COLOR_BGR2RGB))
2つの画像を混ぜる
OpenCVにAddWeightedという2つの画像をalphaを指定して混ぜる関数が用意されている。
cv.AddWeighted(src1, alpha, src2, beta, gamma, dst)
試しに画像1をalpha=0.7, 画像2をalpha=0.5にして混ぜてみる。
なお今回使う画像はすべてサイズは同じものなのでサイズを揃える等の前処理は必要ない前提。
dest = np.zeros(images[0].shape) added_images = cv2.addWeighted(images[0], 0.7, images[1], 0.5, gamma=1.0, dst=dest) plt.imshow(cv2.cvtColor(added_images, cv2.COLOR_BGR2RGB))
上記だと背景の位置がズレているので、適当にもう少し背景が近づくようにしてみる。ざっくり90ピクセルくらい近づければ良さそう。
dest = added_images2 = cv2.addWeighted(images[0][:, :-90], 0.3, images[1][:, 90:], 0.8, gamma=1.0)#, dst=dest) plt.imshow(cv2.cvtColor(added_images2, cv2.COLOR_BGR2RGB))
ちょっとブレてるけど背景を固定で取っていればこの手法で動いている物体だけが複数登場するような画像ができあがるはず。
1つの画像の上に別画像を貼り付ける
1つの大きいキャンバスを用意しておいて、そこの上にペタペタと画像を貼り付ける。
Pillowのpaste使うと楽だけど本稿ではNumpy的なやり方で貼り付ける。
# 800*500の黒背景を用意 img = np.zeros((500, 800, 3), dtype=np.uint8) # 左上に画像1を貼る x, y = images[0].shape[:2] img[0:x, 0:y, :] = images[0] # 右下に画像2を貼る img[500-x:500, 800-y:800, :] = images[0] plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
楕円形に切り抜いて貼り付け
写真を楕円形に切り抜いた上で、上の例のように1枚の絵の中にペタペタと貼り付ける。
楕円形はcv2.ellipseで生成できる。
# 楕円形のマスクを生成 # thickness=-1にすると生成する楕円の中身がfillされる y, x = images[0].shape[:2] mask = np.zeros(images[0].shape, np.uint8) cv2.ellipse(mask, center=(int(x / 2), int(y / 2)), axes=(int(x / 2), int(y / 2)), angle=0, startAngle=0, endAngle=365, color=(255, 255, 255), thickness=-1) # 対象画像にbitwise_andでmaskをかける img = images[0] dest = cv2.bitwise_and(img, mask) plt.imshow(cv2.cvtColor(dest, cv2.COLOR_BGR2RGB))
こうしてできた画像1〜3をリサイズしつつ貼り付けてみる。
# 画像1〜3を楕円で切り抜いてx, y共に半分にリサイズする masked_images = [] for i in range(3): img = images[i] y, x = img.shape[:2] mask = np.zeros(img.shape, np.uint8) half_xy = (int(x / 2), int(y / 2)) cv2.ellipse(mask, center=half_xy, axes=half_xy, angle=0, startAngle=0, endAngle=360, color=(255, 255, 255), thickness=-1) dest = cv2.bitwise_and(img, mask) dest = cv2.resize(dest, (half_xy[0], half_xy[1])) masked_images.append(dest) # 500*300の黒背景を用意 img = np.zeros((300, 500, 3), dtype=np.uint8) # 左上に画像1を x, y = masked_images[0].shape[:2] img[0:x, 0:y, :] = masked_images[0] # 中央に画像2を img[int(300/2 - x/2):int(300/2 + x/2), int(500/2 - y/2):int(500/2 + y/2), :] = masked_images[1] # 右下に画像2を貼る img[300-x:300, 500-y:500, :] = masked_images[2] plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
あ、透過になってるわけではないのでmaskの左上の黒が隣の画像に被ってしまっている。
元画像のmaskした箇所を一度消して、その後に貼り付ける画像を加算する形にすれば良さそう。
# 貼り付け先の背景画像 BG_X = 500 BG_Y = 420 bg = np.zeros((BG_Y, BG_X, 3), dtype=np.uint8) for i in range(3): img = images[i] # 画像のリサイズ y, x = img.shape[:2] half_xy = (int(x / 2), int(y / 2)) half_img = cv2.resize(img, (half_xy[0], half_xy[1])) # サイズの取り直し y, x = half_img.shape[:2] half_xy = (int(x / 2), int(y / 2)) # 楕円のmask生成 mask = np.zeros(half_img.shape, np.uint8) cv2.ellipse(mask, center=half_xy, axes=half_xy, angle=0, startAngle=0, endAngle=360, color=(1, 1, 1), thickness=-1) # 貼り付ける位置のx, yを計算 paste_x = int((BG_X - half_xy[0]) / 3 * i) paste_y = int((BG_Y - half_xy[1]) / 3 * i) # 楕円と被る箇所の背景画像を0に bg[paste_y:paste_y+y, paste_x:paste_x+x, :] *= np.where(mask == 1, 0, 1).astype(np.uint8) # 0になった箇所に貼り付け画像を加算する bg[paste_y:paste_y+y, paste_x:paste_x+x, :] += np.where(mask == 1, half_img, 0) plt.figure(figsize=(7, 7)) plt.imshow(cv2.cvtColor(bg, cv2.COLOR_BGR2RGB))
改定履歴
Author: Masato Watanabe, Date: 2019-01-15, 記事投稿