[英]Using opencv to find the most similar image that contains another image
如果標題不清楚,假設我有一個圖像列表 (10k+),並且我有一個正在搜索的目標圖像。
這是目標圖像的示例:
這是我想要搜索以找到“相似”的圖像示例(ex1、ex2 和 ex3):
這是我做的匹配(我使用 KAZE)
from matplotlib import pyplot as plt
import numpy as np
import cv2
from typing import List
import os
import imutils
def calculate_matches(des1: List[cv2.KeyPoint], des2: List[cv2.KeyPoint]):
"""
does a matching algorithm to match if keypoints 1 and 2 are similar
@param des1: a numpy array of floats that are the descriptors of the keypoints
@param des2: a numpy array of floats that are the descriptors of the keypoints
@return:
"""
# bf matcher with default params
bf = cv2.BFMatcher(cv2.NORM_L2)
matches = bf.knnMatch(des1, des2, k=2)
topResults = []
for m, n in matches:
if m.distance < 0.7 * n.distance:
topResults.append([m])
return topResults
def compare_images_kaze():
cwd = os.getcwd()
target = os.path.join(cwd, 'opencv_target', 'target.png')
images_list = os.listdir('opencv_images')
for image in images_list:
# get my 2 images
img2 = cv2.imread(target)
img1 = cv2.imread(os.path.join(cwd, 'opencv_images', image))
for i in range(0, 360, int(360 / 8)):
# rotate my image by i
img_target_rotation = imutils.rotate_bound(img2, i)
# Initiate KAZE object with default values
kaze = cv2.KAZE_create()
kp1, des1 = kaze.detectAndCompute(img1, None)
kp2, des2 = kaze.detectAndCompute(img2, None)
matches = calculate_matches(des1, des2)
try:
score = 100 * (len(matches) / min(len(kp1), len(kp2)))
except ZeroDivisionError:
score = 0
print(image, score)
img3 = cv2.drawMatchesKnn(img1, kp1, img_target_rotation, kp2, matches,
None, flags=2)
img3 = cv2.cvtColor(img3, cv2.COLOR_BGR2RGB)
plt.imshow(img3)
plt.show()
plt.clf()
if __name__ == '__main__':
compare_images_kaze()
這是我的代碼的結果:
ex1.png 21.052631578947366
ex2.png 0.0
ex3.png 42.10526315789473
沒關系,它能夠判斷 ex1 相似,ex2 不相似。 但是它聲明 ex3 是相似的(甚至比 ex1 更相似),任何額外的預處理或后處理(可能是 ml?假設 ml 實際上有用)或者只是我可以對我的方法進行的更改,這些更改可以只保留ex1 相似,而不是 ex3?
(注意我創建的這個分數是我在網上找到的。不確定它是否是 go 關於它的准確方法)
在下面添加了更多示例
另一組例子:
這就是我正在尋找的
我希望上面的圖像與中間和底部的圖像相似(注意:我將目標圖像旋轉 45 度並將其與下面的圖像進行比較。)
特征匹配(如下面的答案所述)在發現與第二張圖像的相似性方面很有用,但對第三張圖像卻沒有(即使在正確旋轉之后)
您可以使用模板匹配,您想要檢測它是否在其他圖像中的圖像就是模板。 我將那個小圖像保存在template.png
中,其他三個圖像保存在img1.png
、 img2.png
和img3.png
中。
我定義了一個 function,它利用cv2.matchTemplate
計算模板是否在圖像中的置信度。 在每張圖像上使用 function,得到最高置信度的是包含模板的圖像:
import cv2
template = cv2.imread("template.png", 0)
files = ["img1.png", "img2.png", "img3.png"]
for name in files:
img = cv2.imread(name, 0)
print(f"Confidence for {name}:")
print(cv2.matchTemplate(img, template, cv2.TM_CCOEFF_NORMED).max())
Confidence for img1.png:
0.8906427
Confidence for img2.png:
0.4427919
Confidence for img3.png:
0.5933967
cv2.imread
方法的第二個參數設置為0
,將模板圖像讀取為灰度:import cv2
template = cv2.imread("template.png", 0)
files = ["img1.png", "img2.png", "img3.png"]
for name in files:
img = cv2.imread(name, 0)
cv2.matchTemplate
來檢測每個圖像中的模板。 您可以使用許多檢測方法,但為此我決定使用cv2.TM_CCOEFF_NORMED
方法: print(f"Confidence for {name}:")
print(cv2.matchTemplate(img, template, cv2.TM_CCOEFF_NORMED).max())
function 的 output 的取值范圍在0
到1
之間,如您所見,它成功檢測到第一個圖像最有可能包含模板圖像(它具有最高的置信度) 。
如果檢測哪個圖像包含模板還不夠,並且您想要可視化,您可以嘗試以下代碼:
import cv2
import numpy as np
def confidence(img, template):
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
template = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)
res = cv2.matchTemplate(img, template, cv2.TM_CCOEFF_NORMED)
conf = res.max()
return np.where(res == conf), conf
files = ["img1.png", "img2.png", "img3.png"]
template = cv2.imread("template.png")
h, w, _ = template.shape
for name in files:
img = cv2.imread(name)
([y], [x]), conf = confidence(img, template)
cv2.rectangle(img, (x, y), (x + w, y + h), (0, 0, 255), 2)
text = f'Confidence: {round(float(conf), 2)}'
cv2.putText(img, text, (x, y), 1, cv2.FONT_HERSHEY_PLAIN, (0, 0, 0), 2)
cv2.imshow(name, img)
cv2.imshow('Template', template)
cv2.waitKey(0)
import cv2
import numpy as np
cv2.matchTemplate
方法需要灰度圖像,因此將 2 個圖像轉換為灰度:def confidence(img, template):
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
template = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)
cv2.matchTemplate
方法檢測圖像中的模板,返回置信度最高的點的position,返回置信度最高的點: res = cv2.matchTemplate(img, template, cv2.TM_CCOEFF_NORMED)
conf = res.max()
return np.where(res == conf), conf
files = ["img1.png", "img2.png", "img3.png"]
template = cv2.imread("template.png")
h, w, _ = template.shape
confidence
function,得到檢測到的模板左上角的xy position和檢測的置信度:for name in files:
img = cv2.imread(name)
([y], [x]), conf = confidence(img, template)
cv2.rectangle(img, (x, y), (x + w, y + h), (0, 0, 255), 2)
text = f'Confidence: {round(float(conf), 2)}'
cv2.putText(img, text, (x, y), 1, cv2.FONT_HERSHEY_PLAIN, (0, 0, 0), 2)
cv2.imshow(name, img)
cv2.imshow('Template', template)
cv2.waitKey(0)
我不確定,如果給定的圖像類似於您的實際任務或數據,但是對於這種圖像,您可以嘗試簡單的模板匹配,參見。 這個 OpenCV 教程。
基本上,我只是通過一些修改實現了本教程:
import cv2
import matplotlib.pyplot as plt
# Read images
examples = [cv2.imread(img) for img in ['ex1.png', 'ex2.png', 'ex3.png']]
target = cv2.imread('target.png')
h, w = target.shape[:2]
# Iterate examples
for i, img in enumerate(examples):
# Template matching
# cf. https://docs.opencv.org/4.5.2/d4/dc6/tutorial_py_template_matching.html
res = cv2.matchTemplate(img, target, cv2.TM_CCOEFF_NORMED)
# Get location of maximum
_, max_val, _, top_left = cv2.minMaxLoc(res)
# Set up threshold for decision target found or not
thr = 0.7
if max_val > thr:
# Show found target in example
bottom_right = (top_left[0] + w, top_left[1] + h)
cv2.rectangle(img, top_left, bottom_right, (0, 255, 0), 2)
# Visualization
plt.figure(i, figsize=(10, 5))
plt.subplot(1, 2, 1), plt.imshow(img[..., [2, 1, 0]]), plt.title('Example')
plt.subplot(1, 2, 2), plt.imshow(res, vmin=0, vmax=1, cmap='gray')
plt.title('Matching result'), plt.colorbar(), plt.tight_layout()
plt.show()
這些是結果:
----------------------------------------
System information
----------------------------------------
Platform: Windows-10-10.0.16299-SP0
Python: 3.9.1
PyCharm: 2021.1.1
Matplotlib: 3.4.1
OpenCV: 4.5.1
----------------------------------------
編輯:為了強調來自不同 colors 的信息,可以使用HSV 顏色空間中的色調通道進行模板匹配:
import cv2
import matplotlib.pyplot as plt
# Read images
examples = [
[cv2.imread(img) for img in ['ex1.png', 'ex2.png', 'ex3.png']],
[cv2.imread(img) for img in ['ex12.png', 'ex22.png', 'ex32.png']]
]
targets = [
cv2.imread('target.png'),
cv2.imread('target2.png')
]
# Iterate examples and targets
for i, (ex, target) in enumerate(zip(examples, targets)):
for j, img in enumerate(ex):
# Rotate last image from second data set
if (i == 1) and (j == 2):
img = cv2.rotate(img, cv2.ROTATE_90_CLOCKWISE)
h, w = target.shape[:2]
# Get hue channel from HSV color space
target_h = cv2.cvtColor(target, cv2.COLOR_BGR2HSV)[..., 0]
img_h = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)[..., 0]
# Template matching
# cf. https://docs.opencv.org/4.5.2/d4/dc6/tutorial_py_template_matching.html
res = cv2.matchTemplate(img_h, target_h, cv2.TM_CCOEFF_NORMED)
# Get location of maximum
_, max_val, _, top_left = cv2.minMaxLoc(res)
# Set up threshold for decision target found or not
thr = 0.6
if max_val > thr:
# Show found target in example
bottom_right = (top_left[0] + w, top_left[1] + h)
cv2.rectangle(img, top_left, bottom_right, (0, 255, 0), 2)
# Visualization
plt.figure(i * 10 + j, figsize=(10, 5))
plt.subplot(1, 2, 1), plt.imshow(img[..., [2, 1, 0]]), plt.title('Example')
plt.subplot(1, 2, 2), plt.imshow(res, vmin=0, vmax=1, cmap='gray')
plt.title('Matching result'), plt.colorbar(), plt.tight_layout()
plt.savefig('{}.png'.format(i * 10 + j))
plt.show()
新結果:
我們可以使用cv2.matchTemplate
方法來檢測圖像在另一個圖像中的位置,但是對於您的第二組圖像,您需要旋轉。 此外,我們還需要考慮 colors。
cv2.matchTemplate
將接受一個圖像、一個模板(另一個圖像)和一個模板檢測方法,並將返回一個灰度數組,其中灰度數組中最亮的點將是模板在該點上最有信心的點.
我們可以在 4 個不同的角度使用模板,並使用導致最高置信度的那個。 當我們檢測到與模板匹配的可能點時,我們使用 function (我們將自己定義)來檢查模板中最常見的 colors 是否存在於我們檢測到的圖像的補丁中。 如果不是,則忽略補丁,無論返回的置信度如何。
import cv2
import numpy as np
def frequent_colors(img, vals=3):
colors, count = np.unique(np.vstack(img), return_counts=True, axis=0)
sorted_by_freq = colors[np.argsort(count)]
return sorted_by_freq[-vals:]
def get_templates(img):
template = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
for i in range(3):
yield cv2.rotate(template, i)
def detect(img, template, min_conf=0.45):
colors = frequent_colors(template)
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
conf_max = min_conf
shape = 0, 0, 0, 0
for tmp in get_templates(template):
h, w = tmp.shape
res = cv2.matchTemplate(img_gray, tmp, cv2.TM_CCOEFF_NORMED)
for y, x in zip(*np.where(res > conf_max)):
conf = res[y, x]
if conf > conf_max:
seg = img[y:y + h, x:x + w]
if all(np.any(np.all(seg == color, -1)) for color in colors):
conf_max = conf
shape = x, y, w, h
return shape
files = ["img1_2.png", "img2_2.png", "img3_2.png"]
template = cv2.imread("template2.png")
for name in files:
img = cv2.imread(name)
x, y, w, h = detect(img, template)
cv2.rectangle(img, (x, y), (x + w, y + h), (0, 0, 255), 2)
cv2.imshow(name, img)
cv2.imshow('Template', template)
cv2.waitKey(0)
import cv2
import numpy as np
frequent_colors
,它將接收圖像並返回圖像中最頻繁的 colors。 可選參數val
是返回多少 colors; 如果val
為3
,則返回最頻繁的 3 個 colors:def frequent_colors(img, vals=3):
colors, count = np.unique(np.vstack(img), return_counts=True, axis=0)
sorted_by_freq = colors[np.argsort(count)]
return sorted_by_freq[-vals:]
get_templates
,它將接收圖像,並以 4 個不同的角度生成圖像(灰度) - 原始、順時針 90、180 和逆時針 90:def get_templates(img):
template = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
for i in range(3):
yield cv2.rotate(template, i)
detect
,它將接收一個圖像和一個模板圖像,並返回圖像上檢測到的模板的邊界框的 x、y、w、h,對於這個 function,我們將使用frequent_colors
和前面定義的get_templates
函數。 min_conf
參數將是將檢測分類為實際檢測所需的最小置信度:def detect(img, template, min_conf=0.45):
colors
中。 此外,定義主圖像的灰度版本: colors = frequent_colors(template)
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
conf_max = min_conf
shape = 0, 0, 0, 0
cv2.matchTemplate
方法得到圖像上檢測到的模板的灰度數組: for tmp in get_templates(template):
h, w = tmp.shape
res = cv2.matchTemplate(img_gray, tmp, cv2.TM_CCOEFF_NORMED)
conf_min
的檢測到的模板的 x、y 坐標,並將置信度存儲在變量conf
中。 如果conf
大於初始最大置信度變量( conf_max
) ,則繼續檢測模板中所有三個最常見的 colors 是否存在於圖像的補丁中: for y, x in zip(*np.where(res > conf_max)):
conf = res[y, x]
if conf > conf_max:
seg = img[y:y + h, x:x + w]
if all(np.any(np.all(seg == color, -1)) for color in colors):
conf_max = conf
shape = x, y, w, h
0, 0, 0, 0
: return shape
detect
function 來獲取邊界框的 x、y、w、h。 使用cv2.rectangle
方法在圖像上繪制邊界框:files = ["img1_2.png", "img2_2.png", "img3_2.png"]
template = cv2.imread("template2.png")
for name in files:
img = cv2.imread(name)
x, y, w, h = detect(img, template)
cv2.rectangle(img, (x, y), (x + w, y + h), (0, 0, 255), 2)
cv2.imshow(name, img)
cv2.imshow('Template', template)
cv2.waitKey(0)
首先,數據出現在圖表中,你不能從他們的數值數據中得到重疊的值嗎?
您是否嘗試過對從白藍到藍紅的顏色變化進行邊緣檢測,將一些圓圈擬合到這些邊緣,然后檢查它們是否重疊?
由於輸入數據非常受控(沒有有機照片或視頻),也許您不必 go ML 路線。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.