その他

テンプレートマッチングを用いたエッジ検出画像照合システムの構築手法について

その他
この記事は約6分で読めます。

はじめに

こんにちは。

今週はAIエージェント開発の話題から一呼吸おいて、画像処理の内容です!

今回は業務の一環として取り組んでいる、ロゴ画像照合システムについて僕が現状考えている技術内容を皆さんに共有します。

開発に使用するテンプレートマッチングを使用しますので、みなさんもぜひ遊んでみてください〜

なお、先週の記事も添付していますので、ぜひ一読いただければ幸いです!

今回やること

今回は、ロゴ画像照合システムの核となるテンプレートマッチング手法について紹介します。

特に、入力画像がどのような状態であっても安定した検出を実現するため、入力する2枚の画像に対してエッジ処理(Canny処理)を適用した上でテンプレートマッチングを行います。

これにより、画像の色味や明暗の変化に依存しない画像照合処理を実装してみます。

画像処理

テンプレートマッチング

テンプレートマッチングとは、入力画像の中からあらかじめ用意したテンプレート画像と一致する領域を探索し、入力画像上の各位置における類似度を算出して、最も高い類似度を示す位置を検出する手法です。

テンプレートマッチング概要図

テンプレートマッチングでは、基本的な評価指標としてSAD(Sum of Absolute Differences)、SSD(Sum of Squared Differences)、NCC(Normalized Cross-Correlation)、ZNCC(Zero-means Normalized Cross-Correlation)の4つが用いられることが一般的です。

今回はエッジ処理により二値的な特徴を持つ画像を扱うため、ZNCC(Zero-means Normalized Cross-Correlation)を採用します。

ZNCC

ZNCCを数式で表現すると複雑に見えますが、OpenCVはとても充実しているのでこれを利用することで、誰でも簡単に実装することができます!

エッジ処理(Canny処理)

エッジ処理とは、入力画像中の物体のエッジを検出し、エッジ部分の画素を255(白)、それ以外を0(黒)とすることで、色情報や明暗情報を削減した2値画像を得る手法です。

下記のコード例では、Canny処理において低閾値を50、高閾値を150に設定しています。

この2つの閾値により、150以上の勾配強度を持つ部分は確実にエッジとして検出され、50未満の部分はノイズとして除去されます。

target_path = sys.argv[1]
template_path = sys.argv[2]

target_gray = cv2.imread(target_path, cv2.IMREAD_GRAYSCALE)
template_gray = cv2.imread(template_path, cv2.IMREAD_GRAYSCALE)
target_color = cv2.imread(target_path, cv2.IMREAD_COLOR) #カラー画像用

edges_target = cv2.Canny(target_gray, 50, 150)
edges_template = cv2.Canny(template_gray, 50, 150)

この処理をすることで入力とする画像が次のように変換されます。

入力画像
処理結果

処理結果の画像をテンプレートマッチングの対象とすれば、より正確な検出ができると思います。

実装

ここまでの処理で、入力画像とテンプレート画像に対してエッジ処理を施した画像を入力として、テンプレートマッチングを実装します。

冒頭で述べた通り、今回はエッジ処理を対象としたテンプレートマッチングのため、類似度指標としてZNCC(Zero-mean Normalized Cross-Correlation)を用います。

数式で表現すると複雑でしたが、OpenCVの関数cv2.matchTemplateの第三引数にcv2.TM_CCOEFF_NORMEDを指定するだけで、この指標によるテンプレートマッチングを簡単に実行できます。

result_edges = cv2.matchTemplate(edges_target, edges_template, cv2.TM_CCOEFF_NORMED) #テンプレートマッチング_, max_val_edge, _, max_loc_edge = cv2.minMaxLoc(result_edges)

h, w = template_gray.shape
bottom_right = (max_loc_edge[0] + w, max_loc_edge[1] + h)
result_img = target_color.copy() #カラー画像に変換
cv2.rectangle(result_img, max_loc_edge, bottom_right, (0, 0, 255), 2)

cv2.imwrite("dst_matchImg.png", result_img)

テンプレートマッチングの結果

弊社のロゴを対象としてテンプレートマッチングをしてみた結果、次の図のように結果を得ることができました。

入力画像
テンプレート画像
検出結果

このようにエッジ処理をした画像に対してテンプレートマッチングをしてみると、やや角度のある入力画像でも適切にロゴの検出をすることができるようになりました。

偽物画像に対する検出結果

テンプレートマッチングでは元画像とテンプレート画像がどのくらい一致しているかを走査する手法と説明しましたが、例えば元画像が偽物のロゴであった場合にはどうなるでしょうか?

今回の入力画像の例で使用した弊社ロゴを偽物風に加工し、試してみます。

偽物画像の例
検出結果の例

結果としては、このようにロゴの部分に検出はされませんでしたが、画像上部に検出がされる結果となってしまいました。

ロゴが検出されなかったのは良い結果ですが、画像のどこであっても検出がされてしまえば、照合システムとしての機能が損なわれてしまうので考えないとですね…

おわりに

いかがでしたでしょうか。

今回は、ロゴの検出をすることを目的に、画像処理の基本的な手法であるテンプレートマッチングをやってみました。

また、入力とする2枚の画像に対してエッジ処理をした上でのテンプレートマッチングは色味や明暗を削減しているためか、うまい具合に検出ができていることを確認できました。

今回作成したソースコードは以下のGit Hubから使用することができます。

ご自身で入力画像とテンプレートを用意してぜひ遊んでみてください!

GitHub - morishitaimpl/template_matching: テンプレートマッチングができます。
テンプレートマッチングができます。. Contribute to morishitaimpl/template_matching development by creating an account on GitHub.

今後の課題

偽物画像に対してのテンプレートマッチングの結果、ロゴは検出されませんでしたが、画像中のどこかが検出されるという例がありました。

本来は検出されないのが好ましい処理結果なので、今後は過検出がされないような処理を考えていきます。

参考URL

Archives
画像認識の手法「テンプレートマッチング」の仕組み - Qiita
画像同士で類似している部分を探索するため手法の一つであるテンプレートマッチングの理論と実装についてまとめています。 テンプレートマッチングとは 入力画像の中からテンプレート画像と最も類似する場所を探索することをテンプレートマッチングと呼びます 入力画像 テンプレート...
cv2.Canny(): Canny法によるエッジ検出の調整をいい感じにする - Qiita
エッジとは物体同士や背景との境界のことを指し、エッジ検出とは一般に画像中の画素値の変化、輝度勾配が大きい部分を検出することでエッジを検出する画像処理を指します。 (画像は(