Pythonでテンプレートマッチング実装
Pythonでテンプレートマッチングを実装しました.類似度尺度には,正規化相互相関(NCC)・相互相関係数(ZNCC)・差の二乗和(SSD)・差の絶対和(SAD)の四種類を使用しました.
使用データ
画像は以下の画像を使用.マッチング対象画像
パターン画像
正規化相互相関(NCC)
マッチングをかける画像と,パターン画像の輝度値をベクトルとみなしてコサイン類似度を求める感じdef NCC(image1, image2): vec1, vec2 = image1.reshape(-1), image2.reshape(-1) numer = np.dot(vec1, vec2.T) denom = np.sqrt(np.sum(vec1 ** 2)) / np.sqrt(np.sum(vec2 ** 2)) if denom == 0: return 0 return numer / denom def matching(image1, image2): image = image1.astype(np.double) pattern = image2.astype(np.double) height1, width1 = image.shape height2, width2 = pattern.shape output = np.zeros(image.shape) for i in range(height2 / 2, height1 - height2 / 2): for j in range(width2 / 2, width1 - width2 / 2): score = NCC(image[i-height2/2:i+height2/2+1, j-width2/2:j+width2/2+1], pattern) output[i,j] = score output /= np.max(output) maxidx = np.unravel_index(output.argmax(), output.shape) result = cv2.cvtColor(image1, cv2.COLOR_GRAY2RGB) cv2.rectangle(result, (maxidx[1] - width2 / 2, maxidx[0] - height2 / 2), (maxidx[1] + width2, maxidx[0] + height2), (0,255,0), 1) cv2.imshow("matching", result) cv2.waitKey(0) cv2.destroyAllWindows()
相互相関係数(ZNCC)
平均値を引いてます.明るさ変化に対して強くなるようです.def ZNCC(image1, image2): vec1, vec2 = image1.reshape(-1), image2.reshape(-1) vec1, vec2 = vec1 - np.mean(vec1), vec2 - np.mean(vec2) numer = np.dot(vec1, vec2.T) denom = np.sqrt(np.sum(vec1 ** 2)) / np.sqrt(np.sum(vec2 ** 2)) if denom == 0: return 0 return numer / denom def matching(image1, image2): image = image1.astype(np.double) pattern = image2.astype(np.double) height1, width1 = image.shape height2, width2 = pattern.shape output = np.zeros(image.shape) for i in range(height2 / 2, height1 - height2 / 2): for j in range(width2 / 2, width1 - width2 / 2): score = ZNCC(image[i-height2/2:i+height2/2+1, j-width2/2:j+width2/2+1], pattern) output[i,j] = score output /= np.max(output) maxidx = np.unravel_index(output.argmax(), output.shape) result = cv2.cvtColor(image1, cv2.COLOR_GRAY2RGB) cv2.rectangle(result, (maxidx[1] - width2 / 2, maxidx[0] - height2 / 2), (maxidx[1] + width2, maxidx[0] + height2), (0,255,0), 1) cv2.imshow("matching", result) cv2.waitKey(0) cv2.destroyAllWindows()
差の二乗和(SSD)
def SSD(image1, image2): vec1, vec2 = image1.reshape(-1), image2.reshape(-1) return np.sum((vec1 - vec2) ** 2) def matching(image1, image2): image = image1.astype(np.double) pattern = image2.astype(np.double) height1, width1 = image.shape height2, width2 = pattern.shape output = np.ones(image.shape) * 10000 for i in range(height2 / 2, height1 - height2 / 2): for j in range(width2 / 2, width1 - width2 / 2): score = SSD(image[i-height2/2:i+height2/2+1, j-width2/2:j+width2/2+1], pattern) output[i,j] = score maxidx = np.unravel_index(output.argmin(), output.shape) result = cv2.cvtColor(image1, cv2.COLOR_GRAY2RGB) cv2.rectangle(result, (maxidx[1] - width2 / 2, maxidx[0] - height2 / 2), (maxidx[1] + width2, maxidx[0] + height2), (0,255,0), 1) cv2.imshow("matching", result) cv2.waitKey(0) cv2.destroyAllWindows()
差の絶対和(SAD)
def SAD(image1, image2): vec1, vec2 = image1.reshape(-1), image2.reshape(-1) return np.sum(np.abs(vec1 - vec2)) def matching(image1, image2): image = image1.astype(np.double) pattern = image2.astype(np.double) height1, width1 = image.shape height2, width2 = pattern.shape output = np.ones(image.shape) * 10000 for i in range(height2 / 2, height1 - height2 / 2): for j in range(width2 / 2, width1 - width2 / 2): score = SAD(image[i-height2/2:i+height2/2+1, j-width2/2:j+width2/2+1], pattern) output[i,j] = score maxidx = np.unravel_index(output.argmin(), output.shape) result = cv2.cvtColor(image1, cv2.COLOR_GRAY2RGB) cv2.rectangle(result, (maxidx[1] - width2 / 2, maxidx[0] - height2 / 2), (maxidx[1] + width2, maxidx[0] + height2), (0,255,0), 1) cv2.imshow("matching", result) cv2.waitKey(0) cv2.destroyAllWindows()
出力結果
4つともこんな感じで出力されます.矩形の位置も同じになりました.どの方法がいいのかはいまいち分からなかったので,また色々試したいと思います.参考文献
中京大学 工学部 橋本学 テンプレートマッチングの魅力http://isl.sist.chukyo-u.ac.jp/Archives/SSII2013TS-Hashimoto.pdf