2つ以上の画像を比較したい時等、Pythonであれば簡単に画像を横と縦に連結させることができます。ここではOpenCVのhconcatとvconcatを使って画像連結をする方法を紹介します。
こんにちは。wat(@watlablog)です。
画像処理プログラミングを進めていると、画像同士の連結をしたくなる時があります!ここでは2つの画像を横や縦に連結する方法を紹介します!
画像連結の概要と使用場面
画像連結の方法(図解)
2つの画像「image1.JPG」と「image2.JPG」を仮定して連結する方法を解説していきます。
取り扱う画像は必ずしも同じサイズというわけではありません。以下の図のように、異なるサイズを連結する場合を考えてみましょう。
今回紹介する画像連結プログラムはOpenCVのvconcatやhconcatという関数を使いますが、これらの関数は縦に連結する時は横方向のサイズが、横に連結する時は縦方向のサイズが等しくないとエラーになります。
画像連結プログラムで懸念されるエラー内容
異なるサイズの画像をOpenCVのhconcatで連結させようとした場合のエラー内容を先に紹介しておきます。その内容は以下のようになります。
「cv2.error: OpenCV(4.1.0)~error: (-215:Assertion failed) src[i].dims <= 2 && src[i].rows == src[0].rows && src[i].type() == src[0].type() in function 'cv::hconcat'」
なんじゃこりゃ???
特にメッセージが英語の文になっているわけでは無く、プログラムコードのような文が羅列されていて、何が言いたいのかよくわかりませんよね。
というのも、OpenCV系のエラーはCライブラリを使っているので、エラーがわかりにくいことで有名のようです(以下Stack Overflowの質疑応答のやりとりを引用させて頂きました)。
PythonのOpenCVはCライブラリのラッパーなので、エラーの原因を探るのはかなり辛いです。ありがちなのは画像のカラーモード。グレースケールだと動いたりすることもあります。 – Kenji Noguchi 16年11月5日 0:38
Stack Overflow:OpenCV Errorが発生します
今回はあえてサイズの異なる画像同士を連結させようとしてエラーを出していますが、知らない人にとってはかなり悩む所ではないでしょうか?
では、サイズの異なる画像同士の連結方法について説明します。
エラーを解決する方法:リサイズする
エラーを解決する方法はずばりリサイズです。どうしてもサイズを変更したくない時は背景に黒一色の画像を用意して小さい方の画像を貼り付けて使用する方法も考えられますが、今回は画像そのもののリサイズで対応する方法を紹介します。
以下の図がエラーを回避しつつサイズの異なる画像を連結する方法です。
※横方向に連結する場合を描いています。
原理は簡単で、連結する辺の長さの大きい方に合わせるように小さい方の画像をリサイズするだけです。
連結させる辺だけサイズを変えてしまうと、アスペクト比がおかしくなってしまうので、ここでは変化倍率としてリサイズに使った比を残った辺に対して乗算しています。
画像連結が必要とされる場面
パノラマ画像作成に使用する
単純な連結だけでは全然足りませんが、360°分の写真があれば処理次第でパノラマ画像を作ることができます。
パノラマ画像については以下のPhotoshopのページが参考になります。
外部リンク:Photoshopでのパノラマ画像の作成と編集
画像連結技術はこのようなパノラマ画像に使用されている基本技術の1つでもあります。
Webページの1枚絵を作る
ブログやWeb関連の仕事をやっている人は、時にWebページ全体のイメージ画像が欲しい時があります。
客先にプレゼンする時や、ページの画像解析をするのに、いちいちスクロールしてキャプチャした画像1つ1つ区切って行うよりも、1枚の画像として表現した方が効率的ですね。
画像連結技術はただ画像を並べて比較する以外にも様々な目的によって使われる基本的な技術です。
Pythonで画像を連結するコード
サンプル画像
今回使用する2つの画像は「Pythonでフォルダ内の画像を自動一括リサイズする方法」でリサイズした以下の画像を使います。
2つの画像を「横」に連結するコード:hconcat
以下のコードが2つの画像を横に連結させるコードです。
関数としてimage_hcombinemを作成して使うスタイルです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
import cv2 # 2つの画像を横に連結する関数 def image_hcombine(im_info1, im_info2): path1 = im_info1[0] # 1つ目の画像のパス path2 = im_info2[0] # 2つ目の画像のパス color_flag1 = im_info1[1] # 1つ目の画像のカラー/グレー判別値 color_flag2 = im_info2[1] # 2つ目の画像のカラー/グレー判別値 img1 = cv2.imread(path1, color_flag1) # 1つ目の画像を読み込み img2 = cv2.imread(path2, color_flag2) # 2つ目の画像を読み込み print(img1.shape[:3], img2.shape[:3]) # リサイズ前の画像サイズを表示(検証用) # 1つ目の画像に対しカラーかグレースケールかによって読み込みを変える if color_flag1 == 1: h1, w1, ch1 = img1.shape[:3] # 画像のサイズを取得(グレースケール画像は[:2] else: h1, w1 = img1.shape[:2] # 2つ目の画像に対しカラーかグレースケールかによって読み込みを変える if color_flag2 == 1: h2, w2, ch2 = img2.shape[:3] # 画像のサイズを取得(グレースケール画像は[:2] else: h2, w2 = img2.shape[:2] # 2つの画像の縦サイズを比較して、大きい方に合わせて一方をリサイズする if h1 < h2: # 1つ目の画像の方が小さい場合 h1 = h2 # 小さい方を大きい方と同じ縦サイズにする w1 = int((h2 / h1) * w2) # 縦サイズの変化倍率を計算して横サイズを決定する img1 = cv2.resize(img1, (w1, h1)) # 画像リサイズ else: # 2つ目の画像の方が小さい場合 h2 = h1 # 小さい方を大きい方と同じ縦サイズにする w2 = int((h1 / h2) * w1) # 縦サイズの変化倍率を計算して横サイズを決定する img2 = cv2.resize(img2, (w2, h2)) # 画像リサイズ print(img1.shape[:3], img2.shape[:3]) # リサイズ後の画像サイズを表示(検証用) img = cv2.hconcat([img1, img2]) # 2つの画像を横方向に連結 return img # ここからメイン実行文 im_info1 = ['image1.JPG', 1] # 1つ目の画像情報を設定([パス, カラー:1/グレー:0]) im_info2 = ['image2.JPG', 1] # 2つ目の画像情報を設定([パス, カラー:1/グレー:0]) # 2つの画像を横に連結する関数を実行 img = image_hcombine(im_info1, im_info2) # 画像を保存 cv2.imwrite('image_out.JPG', img) |
少しif文を使いすぎな気もしますが、先ほど図解で説明したリサイズを行うために、1枚目の画像の方が大きい場合と小さい場合の両方に対応させるために致し方なかったのです。
このコードでは関数で連結画像を作って、戻り値として帰ってきた画像を本文で保存するという流れです。
2つの画像を「縦」に連結するコード:vconcat
以下のコードは先ほどとほぼ同じですが、vcancat関数を使って縦方向に連結させています。違いはリサイズ判定のhとwが入れ替わっている所と、hconcatがvconcatに変更されている所だけです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
import cv2 # 2つの画像を横に連結する関数 def image_hcombine(im_info1, im_info2): path1 = im_info1[0] # 1つ目の画像のパス path2 = im_info2[0] # 2つ目の画像のパス color_flag1 = im_info1[1] # 1つ目の画像のカラー/グレー判別値 color_flag2 = im_info2[1] # 2つ目の画像のカラー/グレー判別値 img1 = cv2.imread(path1, color_flag1) # 1つ目の画像を読み込み img2 = cv2.imread(path2, color_flag2) # 2つ目の画像を読み込み print(img1.shape[:3], img2.shape[:3]) # リサイズ前の画像サイズを表示(検証用) # 1つ目の画像に対しカラーかグレースケールかによって読み込みを変える if color_flag1 == 1: h1, w1, ch1 = img1.shape[:3] # 画像のサイズを取得(グレースケール画像は[:2] else: h1, w1 = img1.shape[:2] # 2つ目の画像に対しカラーかグレースケールかによって読み込みを変える if color_flag2 == 1: h2, w2, ch2 = img2.shape[:3] # 画像のサイズを取得(グレースケール画像は[:2] else: h2, w2 = img2.shape[:2] # 2つの画像の横サイズを比較して、大きい方に合わせて一方をリサイズする if w1 < w2: # 1つ目の画像の方が小さい場合 w1 = w2 # 小さい方を大きい方と同じ横サイズにする h1 = int((w2 / w1) * h2) # 横サイズの変化倍率を計算して縦サイズを決定する img1 = cv2.resize(img1, (w1, h1)) # 画像リサイズ else: # 2つ目の画像の方が小さい場合 w2 = w1 # 小さい方を大きい方と同じ横サイズにする h2 = int((w1 / w2) * h1) # 横サイズの変化倍率を計算して縦サイズを決定する img2 = cv2.resize(img2, (w2, h2)) # 画像リサイズ print(img1.shape[:3], img2.shape[:3]) # リサイズ後の画像サイズを表示(検証用) img = cv2.vconcat([img1, img2]) # 2つの画像を縦方向に連結 return img # ここからメイン実行文 im_info1 = ['image1.JPG', 1] # 1つ目の画像情報を設定([パス, カラー:1/グレー:0]) im_info2 = ['image2.JPG', 1] # 2つ目の画像情報を設定([パス, カラー:1/グレー:0]) # 2つの画像を縦に連結する関数を実行 img = image_hcombine(im_info1, im_info2) # 画像を保存 cv2.imwrite('image_out.JPG', img) |
実行結果
横方向連結画像
hconcatを使ったコードの実行結果です。
image1.JPGが適切にリサイズされて連結されています。
縦方向連結画像
vconcatを使ったコードの実行結果です。
先ほどと同様にリサイズが適切に行われ、縦方向に連結されました。
まとめ
本記事ではPythonのOpenCVで2つの画像を連結させる時に懸念されるエラーとその回避方法を解説しました。
また、画像連結は意外と色々な場面で使われていることもわかり、最後にPythonコードを横連結、縦連結の2種類紹介しました。
画像連結もPythonなら楽々!これからその応用にも手を出したいですね!
Twitterでも関連情報をつぶやいているので、wat(@watlablog)のフォローお待ちしています!
コメント