こんにちは。初投稿です。
先日サイゼリヤに行った際の待ち時間20分ぐらい数人でサイゼリヤの間違い探しをやって、見つけられませんでした...(ムズすぎる...)
ということで、画像処理で解けないかな〜と思い、やってみました。OpenCVのいい勉強にもなりました。
#やりたいこと
OpenCVのライブラリを使って、サイゼリヤが公式で出している画像データ(https://www.saizeriya.co.jp/entertainment/) を加工して間違い探しを自動化したい!!!
実際にやることは、
- 画像を加工する(余白の削除、半分に分割)
- 画像の差分を計算
- 差分情報を元の画像に表示
という感じです。
コードはGitHubにあげてみました。
#実行環境
macOS Mojave 10.14.4
Python 3.6.7
OpenCV 3.4.1
#画像を加工する
ダウンロードすると、「比較する画像がくっついている」+「謎の余白がある」ということに気がつきます。
(周りも白なのでわかりづらい...) サイゼリヤのサイトより引用
まずはじめに余白部分の削除からしたいと思います。
空白の削除の流れは、
- グレースケール画像にする
- 2値化する
- 輪郭を抽出する
- 輪郭の中から座標が最小になるものと最大になるものをx,yそれぞれ取得して切り取る
https://qiita.com/trami/items/e25eb70a59a51ae4f7ba#どのようにして余白削除を行うのかの記事を参考にさせていただいております。
OpenCVでのグレースケール化の関数が
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) # グレースケール化
2値化の関数が
r, binary = cv2.threshold(gray, 0, 255,cv2.THRESH_OTSU) #2値化
画像の切り取りの関数が
img = img[y1:y2,x1:x2] #(x1,y1)から(x2,y2)を切り取る
という感じです。
これらを組み合わせていくとここまでのコードが
import cv2
import numpy as np
img = cv2.imread('diff.png') #画像の読み込み
height, width, d = img.shape #高さ、幅、深さ
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) # グレースケール化
r, binary = cv2.threshold(gray, 0, 255,cv2.THRESH_OTSU) #しきい値200で2値化
contours = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)[0]
#輪郭の抽出、余白の削除
x1 = [] #x座標の最小値
y1 = [] #y座標の最小値
x2 = [] #x座標の最大値
y2 = [] #y座標の最大値
for i in range(1, len(contours)):
ret = cv2.boundingRect(contours[i])
x1.append(ret[0])
y1.append(ret[1])
x2.append(ret[0] + ret[2])
y2.append(ret[1] + ret[3])
x1_min = min(x1)
y1_min = min(y1)
x2_max = max(x2)
y2_max = max(y2)
img = img[0:600, x1_min:x2_max] #余白部分の切り取り
実行結果が以下の通りです。うまくカットできていることがわかります。
###次に画像を半分にカットします。
height, width,d= img.shape #高さ、幅、深さ(切り出したものの大きさ)
midle = int(width/2)
img1 = img[0:height,0:midle-3]
img2 = img[0:height,midle-8:width-11] #実は、横幅が少し違う...
ここでやばかったのは、実は2つの画像の端のキレている部分が若干違うということでした。
その調整は結局手作業でカットする部分を調整しました。
#画像の差分を計算
次に画像の差分を計算します。
画像の差分はnumpy配列同士の引き算でもできますが、実際に実行してみると差分がわかりづらい(少しの色の違い、場所の少しのずれも差分として表示してしまう)ので、今回はOpenCVのabsdiff関数を使いました。
absdiff関数は、二つの画像の差の絶対値を求めることができます。
#差分を表示
result = np.copy(img1) #結果の画像を格納する配列
add = np.copy(img1) #結果の画像を格納する配列
#result = img1-img2 # 差分の計算
result = cv2.absdiff(img1, img2) # 差分の計算(absdiff)
参考に配列同士の引き算で実行した時の結果です。
流石にこれはどこが違うのかわからない...
#差分情報を元の画像に表示
これでは、どこに差があるかわからないので、元の画像と合成したいと思います。
元のグレー画像+差分画像(カラー)を合成します。
使う関数はadd関数です。また、cvtCOLOR関数で元のカラー画像を
######カラー3次元配列→グレー2次元配列→グレー3次元配列
で処理します。
img3 = cv2.cvtColor(img2,cv2.COLOR_BGR2GRAY) # グレースケール化
img3 = cv2.cvtColor(img3,cv2.COLOR_GRAY2BGR) # グレースケールのままカラー画像にする
print(img3.shape)
print(result.shape)
add = cv2.add(img3,result) # 画像を合成する
#画像の表示
cv2.imshow('all',img)
cv2.imshow('image',add)
cv2.imshow('result',result)
cv2.waitKey(0) #何かしらのキーが押されるまで待つ
cv2.destroyAllWindows() #すべてのWindowを破棄
これでだいたいどこが違うかわかるようになりました。
###って8個しか見つからない!!!!
あと2つ教えてください...
あくまで、補助するツールでした。