48
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

Organization

OpenCVを使ってサイゼリヤの間違い探しを簡単に解いちゃう!!

この記事はリンク情報システムの2021新春アドベントカレンダー Tech Connectのリレー記事です。
engineer.hanzomon のグループメンバによってリレーされます。
(リンク情報システムのFacebookはこちらから)

2021新春アドベントカレンダー Tech Connect インデックスはこちら


はじめに

明けましておめでとうございます。
最近私はOpenCVの齧っておりまして、これを使い何か面白いこと出来ないかと思い、サイゼリヤの間違い探しを何とかして解けんじゃね?と思いチャレンジしてみました。

環境, 利用技術

Windows10 Pro
Python 3.8.1
OpenCV 4.3
Numpy

今回解く間違い探し

サイゼリヤさんのHPにある間違い探しの画像を使用させていただきました。
リンクはこちら

今回はこちらの間違い探しを解いてみます。
saize_6.png
(2020年7月 暑い夏、たくさん食べて明るく元気になろう!)
Copyright Saizeriya Co,. Ltd All rights reserved.

実装

左右の画像差分を抽出

左右の画像差がなるべく小さくなるよう余白(padding)を選び、absdiffで差分抽出します。

公式の画像は、左右の端に白帯があったり、左右同じ大きさで分けても若干ずれていたりします。
その為、左右差が一番少ない部分をまずは探し当てます。(これが一番面倒)

    # 余白を取り除いたときに2つの画像が最も一致するような適切な余白(padding)の幅を見つける
    img_src = img
    img_diffs = []
    paddings = []
    for padding in range(1, 50):
        # 画像の余白を削除
        img = img_src[:, padding:-padding]

        # 画像を左右で分割する
        height, width, channels = img.shape[:3]
        img1 = img[:, :width//2]
        img2 = img[:, width//2:]

        # 画像サイズを合わせる(小さい方に)
        img1, img2 = FitImageSize_small(img1, img2)

        # 2つの画像の差分を算出
        img_diff = cv2.absdiff(img2, img1)
        img_diff_sum = np.sum(img_diff)

        img_diffs.append((img_diff, img_diff_sum))
        paddings.append(padding)

    # 差分が最も少ないものを選ぶ
    img_diff, _ = min(img_diffs, key=lambda x: x[1])
    index = img_diffs.index(min(img_diffs, key=lambda x: x[1]))
    cv2.imshow("img_diff", img_diff)

diff.PNG

右側画像の切り取り。

次に、「左右の余白を削除」「左右を分割」「右側を表示」を行います。

    padding = paddings[index]
    # 画像の余白を削除
    img = img_src[:, padding:-padding]
    # 画像を左右で分割する
    height, width, channels = img.shape[:3]
    img1 = img[:, :width//2]
    img2 = img[:, width//2:]
    cv2.imshow("img2",img2)

right.PNG

差分抽出結果と、右画像を合成

差分画像に合わせるために、右画像はグレースケール化します。

    # 画像サイズを合わせる(小さい方に)
    img2, img_diff = FitImageSize_small(img2, img_diff)
    # 画像合成
    add = ImageComposition(img2, img_diff)
    cv2.imshow("add",add)

重ね合わせ.PNG

じゃーん!。。。。あれなんか足りない...?

間違いは全部で10個のはず...
解答数え.PNG

よく探したらここでした。。。

位置や物が異なる場合はわかりやすかったのですが、色だけが異なる場合は弱いですね。。。
10個目さがし.PNG

ソースコード全体

import cv2
import math
import numpy as np

# --------------------------------------------------- #
# 画像合成                                             #
# --------------------------------------------------- #
def ImageComposition(img2, result):
    img3 = cv2.cvtColor(img2,cv2.COLOR_BGR2GRAY) # グレースケール化
    img3 = cv2.cvtColor(img3,cv2.COLOR_GRAY2BGR) # グレースケールのままカラー画像にする
    # コントラスト、明るさを変更する。
    img3 = adjust(img3, alpha=0.25)
    add = cv2.add(img3, result) # 画像を合成する
    return add

# α  はゲイン (gain) 、βはバイアス (bias)
def adjust(img, alpha=1.0, beta=0.0):
    # 積和演算を行う。
    dst = alpha * img + beta
    # [0, 255] でクリップし、uint8 型にする。
    return np.clip(dst, 0, 255).astype(np.uint8)

def FitImageSize_small(img1, img2):
    # height
    if img1.shape[0] > img2.shape[0]:
        height = img2.shape[0]
        width = img1.shape[1]
        img1 = cv2.resize(img1, (width, height))
    else:
        height = img1.shape[0]
        width = img2.shape[1]
        img2 = cv2.resize(img2, (width, height))

    # width
    if img1.shape[1] > img2.shape[1]:
        height = img1.shape[0]
        width = img2.shape[1]
        img1 = cv2.resize(img1, (width, height))
    else:
        height = img2.shape[0]
        width = img1.shape[1]
        img2 = cv2.resize(img2, (width, height))
    return img1, img2

try:
    img = cv2.imread('元画像パス')

    if img is None:
        print('ファイルを読み込めません')
        import sys
        sys.exit()

    cv2.imshow("img", img)

    # 余白を取り除いたときに2つの画像が最も一致するような適切な余白(padding)の幅を見つける
    img_src = img
    img_diffs = []
    paddings = []
    for padding in range(1, 50):
        # 画像の余白を削除
        img = img_src[:, padding:-padding]

        # 画像を左右で分割する
        height, width, channels = img.shape[:3]
        img1 = img[:, :width//2]
        img2 = img[:, width//2:]

        # 画像サイズを合わせる(小さい方に)
        img1, img2 = FitImageSize_small(img1, img2)

        # 2つの画像の差分を算出
        img_diff = cv2.absdiff(img2, img1)
        img_diff_sum = np.sum(img_diff)

        img_diffs.append((img_diff, img_diff_sum))
        paddings.append(padding)

    # 差分が最も少ないものを選ぶ
    img_diff, _ = min(img_diffs, key=lambda x: x[1])
    index = img_diffs.index(min(img_diffs, key=lambda x: x[1]))
    cv2.imshow("img_diff", img_diff)


    padding = paddings[index]
    # 画像の余白を削除
    img = img_src[:, padding:-padding]
    # 画像を左右で分割する
    height, width, channels = img.shape[:3]
    img1 = img[:, :width//2]
    img2 = img[:, width//2:]
    cv2.imshow("img2",img2)

    # 画像サイズを合わせる(小さい方に)
    img2, img_diff = FitImageSize_small(img2, img_diff)
    # 画像合成
    add = ImageComposition(img2, img_diff)
    cv2.imshow("add",add)

    cv2.waitKey(0)
    cv2.destroyAllWindows()
except:
    import sys
    print("Error:", sys.exc_info()[0])
    print(sys.exc_info()[1])
    import traceback
    print(traceback.format_tb(sys.exc_info()[2]))

まとめ

今回は、サイゼリヤさんの公式サイトの画像を引用させていただき解いてみましたが、
理想を言えば店頭で使えるよう、スマホで間違い探しを写真で撮り、スマホ上で上記のような差分抽出が出来ると良いですよね。
(今回はそこまでの余力はなかったです。。。)

明日は@marumomijiです!引き続きよろしくお願いします!


リンク情報システム株式会社では一緒に働く仲間随時募集しています!
また、お仕事のご依頼、ビジネスパートナー様も募集しております。お気軽にご連絡ください。
Facebookはこちら

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
48
Help us understand the problem. What are the problem?