10
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

[python][openCV]類似画像探索で一致するはずが、しなかった話

Last updated at Posted at 2022-12-09

はじめに

車両メータに表示される特定の警告灯を見つけ出す処理を作成する。
その時に準備した画像が一致するはずなのにしなかった件について書く。

参考記事

この記事はスーパーマリオの「?ブロック」をプレイ画像の中から探し出す。
これを利用してメータ警告灯の中にエアバッグがあるか判定する。

使用画像

chr_meter.jpg[探索される画像]

chr_meter.jpg

airbag.jpg[探索する画像]

airbag.jpg

大きいですね、この地点で何が起こるかわかっても、そっと見守ってください

動作環境

  • windows 11
  • python 3.10.5
  • opencv-python 4.6.0.66

処理内容

失敗パターン

画像を読み込みテンプレートマッチングを行う

import cv2
from IPython.display import Image, display
from matplotlib import pyplot as plt

def imshow(img):
    ret, encoded = cv2.imencode(".jpg", img)
    display(Image(encoded))

# 入力画像、テンプレート画像を読み込む。
img = cv2.imread("chr_meter.jpg")  # 探索される画像
templ = cv2.imread("airbag.jpg")  # 探索する画像

# テンプレートマッチングを行う。
result = cv2.matchTemplate(img, templ, cv2.TM_CCOEFF_NORMED)

結果をカラーバーで表示する

fig, ax = plt.subplots(figsize=(10, 5))
im = ax.imshow(result, cmap="jet")
fig.colorbar(im)

plt.show()

image.png

全体的にピンボケしたような画像になる

マッチした箇所を枠で囲む

# 最も類似度が高い位置を取得する。
minVal, maxVal, minLoc, maxLoc = cv2.minMaxLoc(result)
print(f"max value: {maxVal}, position: {maxLoc}")
max value: 0.1377682238817215, position: (582, 71)

# 描画する。
tl = maxLoc[0], maxLoc[1]
br = maxLoc[0] + templ.shape[1], maxLoc[1] + templ.shape[0]

dst = img.copy()
cv2.rectangle(dst, tl, br, color=(0, 255, 0), thickness=2)
imshow(dst)

最大類似度は13%と低く、その場所を見つかった場所とするため
image.png

変なところを検出した

原因

メータ画像のエアバッグ警告灯は小さく、
準備したエアバッグ警告灯は大きいので
同じ画像として判定できない

成功パターン

画像を読み込みテンプレートマッチングを行う

import cv2
from IPython.display import Image, display
from matplotlib import pyplot as plt

def imshow(img):
    ret, encoded = cv2.imencode(".jpg", img)
    display(Image(encoded))

# 入力画像、テンプレート画像を読み込む。
img = cv2.imread("chr_meter.jpg")  # 入力画像

templ = cv2.imread("airbag.jpg")  # テンプレート画像
# ☆追記:画像を0.1倍に縮小
height = templ.shape[0]
width = templ.shape[1]
templ = cv2.resize(templ , (int(width*0.1), int(height*0.1)))

# テンプレートマッチングを行う。
result = cv2.matchTemplate(img, templ, cv2.TM_CCOEFF_NORMED)

templ = cv2.resize(templ , (int(width*0.1), int(height*0.1)))
width*0.1で幅、height*0.1で高さを10%にリサイズして小さくしている

imshow(templ) #小さくした画像を表示する

縮小前後の画像を探索される画像と並べてみた
(qiitaはメータ画像の拡大率が変化するのでエクセルに張り付けて検証)
image.png
縮小したことで探索される画像の警告灯と同じぐらいのサイズとなった

結果をカラーバーで表示する

以降のコードは上記と同じ

fig, ax = plt.subplots(figsize=(10, 5))
im = ax.imshow(result, cmap="jet")
fig.colorbar(im)

plt.show()

image.png

先ほどのピンボケのような画像でなく、ピントが合ったような画像になり、
エアバッグ警告灯の部分が小さいが赤くなっている。

マッチした箇所を枠で囲む

# 最も類似度が高い位置を取得する。
minVal, maxVal, minLoc, maxLoc = cv2.minMaxLoc(result)
print(f"max value: {maxVal}, position: {maxLoc}")
max value: 0.7951300740242004, position: (462, 423)

# 描画する。
tl = maxLoc[0], maxLoc[1]
br = maxLoc[0] + templ.shape[1], maxLoc[1] + templ.shape[0]

dst = img.copy()
cv2.rectangle(dst, tl, br, color=(0, 255, 0), thickness=2)
imshow(dst)

類似度は79%になり
image.png
エアバッグの警告灯をとらえることに成功!

考察(失敗パターンの探索位置)

エクセルで画像の透明度を上げてを重ねてみると
image.png
探索する画像の黒い部分、黒くない部分の割合が
この部分が一番マッチしている様な気がする。

まとめ

「プログラムは書いた通りにしか動かない」と聞くことがあるが
コンピュータは書かれた通り「この大きい画像に一番近いところを示す」ので
まさしくそのとおりであると思う。

それに対して自分は「この画像が中にあるのになぜ見つけ出せないんだ?」
と思うのは自分が「大きさの変化を頭の中で置き換えて探している」からで
その処理を書いてやらないと成功しないなと感じた事例でした。

10
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?