この記事ではpython-openCVを使って以下の画像のように、
【左の画像】+【真ん中の画像の黒以外の領域】
という画像を作成する方法をご紹介します。
「こんなの簡単そうじゃん!」
と思ったそこのあなた。
これ作るの意外とめんどくさいんですよ。
しかしながら、この画像を作成する過程で様々なことを勉強できるはずです。
python初学者にとっては学びの多い内容になっていると思います。
ぜひ最後までご覧ください。
それでは早速やっていきます!
※pythonをインストールしていない方はまずは以下からご覧ください。
必要なライブラリ
◆openCV
◆numpy
openCVですが、こいつはなかなか曲者でして、バージョンによって、コードの書き方が微妙に違うみたいです。
以降で紹介するコードが正常に動作しない場合、バージョン違いの影響である可能性がありますので、もしそのような状況に陥った場合は、バージョンを上記のものに合わせるか、あなたのバージョンに適した形にコードを修正してください。
※openCVのインストール方法については以下で解説しています。
画像を合成する方法:cv2.add
サンプルコードの紹介に移る前に、今回紹介するスキルのキーポイントをご紹介します。
今回のように、openCVで画像を合成する場合、cv2.addというスキルを用います。
完成後配列=cv2.add(画像1の配列, 画像2の配列)
使い方はこれだけ。
これで、2枚の画像のそれぞれのピクセルにおける輝度やBGR値、HSV値を合計した配列を作成することができます。
今回はこのcv2.addを使って、冒頭に紹介したような2枚の画像を合成したような画像を作成していきたいと思います。
キーポイントを理解できたところで、さっそくこのcv2,.addの実際の使い方をご紹介していきます。
※このcv2.addは合成する2枚の画像のサイズ(縦、横のピクセル数)が一致していないとうまく動作しません。
今回はあらかじめサイズのそろった画像を準備しているため、なにも特別な処理をしなくても動作しますが、あなたがもし違うサイズの画像を準備しているのであれば、まずはじめに2枚の画像のサイズを揃える作業が必要になります。
ご注意ください。
画像を合成するサンプルコード
まずはじめによくある失敗例からご紹介します。
◆サンプルコード①
#ライブラリインポート
import cv2
import numpy as np
#画像の読み込み⇒合成
pic1=cv2.imread('pic1.jpg',cv2.IMREAD_COLOR)
pic2=cv2.imread('pic2.jpg',cv2.IMREAD_COLOR)
pic3=cv2.add(pic2,pic1)
#画像の出力
cv2.imwrite('pic3.jpg',np.array(pic3))
コードを簡単に解説すると、単純に二枚の画像をcv2.imreadで読み込んで、先ほど紹介したcv2.addで合成しただけです。
こいつを実行すると以下の画像のようになります。
※pic1.jpgが左、pic2.jpgが真ん中、pic3.jpgが右です。
一方、冒頭で説明した作りたかった画像は以下です。
なんか違いますよね。
先ほども説明しましたが、今回つかったcv2.addは各ピクセルのデータを単純に足し合わせただけのものです。
左の画像が比較的明るい(=輝度値が大きい)ので、真ん中の画像の明るい部分を足した結果、明るくなりすぎてしまったというわけです。
(真ん中の画像の周囲の黒い部分は輝度値がほぼゼロなので足しても何も変化はありません。)
つまり、今回のような画像を作ろうと思ったら、次のような手順を踏む必要があります。
※説明の都合上以下のようにそれぞれの画像をA、Bと呼ばせていただきます。
◆画像合成の手順
①Bの画像を二値化する(物体内部にも黒い場所はあるので以下の画像のようになる)
②①でつくった画像の輪郭を抽出する
③②で抽出した輪郭のうち、最大のものを選択する
(これで中心部の物体の外縁輪郭が取れる)
④③で選択した輪郭の内部を輝度255で塗りつぶす(以下の画像のようになる)
⑤Aの画像に対して、④の画像で白い場所はゼロ、黒場所は変化なし、という処理を行う。(以下の画像のようになる)
⑥もともとの画像Bと⑤で作った画像をcv2.addで合成する
これで完成です。
ほら、めちゃくちゃめんどくさいでしょ?
つまり冒頭では、以下のようなイメージ図で説明していましたが、
実際は以下のようなことを行っていたわけです。
処理の流れをイメージできたところで、最後にこの処理を実行してくれるサンプルコードをご紹介します。
◆最終的なサンプルコード
#ライブラリインポート
import cv2
import numpy as np
#画像の読み込み
pic1=cv2.imread('pic1.jpg',cv2.IMREAD_COLOR)
pic2=cv2.imread('pic2.jpg',cv2.IMREAD_COLOR)
#二値化処理
pic2gray=cv2.imread('pic2.jpg',cv2.IMREAD_GRAYSCALE)
ret, thresh = cv2.threshold(pic2gray, 5, 255, cv2.THRESH_BINARY)
#輪郭抽出
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_LIST,cv2.CHAIN_APPROX_NONE)
max_cnt =max(contours, key=lambda x: cv2.contourArea(x))
#マスク画像の作成
pic2thresh = cv2.drawContours(pic2gray, [max_cnt], -1, 255, -1)
cv2.imwrite('pic2thresh2.jpg',np.array(pic2thresh))
#画像合成前処理
pic2[pic2thresh<255]=[0,0,0]
pic1[pic2thresh==255]=[0,0,0]
cv2.imwrite('pic2thres3.jpg',np.array(pic1))
#画像合成
pic3=cv2.add(pic1,pic2)
cv2.imwrite('add.jpg',np.array(pic3))
長いですね。
もしかしたらもっと簡単なやり方があるかもしれません。
知っている方がいればコメント欄から教えてください。
おわりに
というわけで今回はpython-openCVを使って、2枚の画像を重ね合わせたような画像を作成する方法をご紹介しました。
画像処理は様々な場面で役に立ちますので、ぜひひとつひとつ習得していきましょう。
このように、私のブログでは様々なプログラミングスキルを紹介しています。
今は仕事中で時間がないかもしれませんが、ぜひ通勤時間中などに他の記事も読んでいただけると嬉しいです。
⇒興味をもった方は【ヒガサラ】で検索してみてください。
確実にスキルアップできるはずです。
最後に、この記事が役に立ったという方は、ぜひ応援よろしくお願いします。
↓ 応援ボタン
それではまた!
コメント
こんにちは。
コードの部分で気になったのですが、np.を何箇所か使っているため、imprt numpy as np と書く必要があると思います。
ほんとですね(^_^;)
ご指摘ありがとうございます。
修正させていただきましました。