使用 opencv 查找包含另一张图像的最相似图像

mik*_*123 5 python opencv machine-learning image-processing image-recognition

如果标题不清楚,假设我有一个图像列表(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()
Run Code Online (Sandbox Code Playgroud)

这是我的代码的结果:

ex1.png 21.052631578947366
ex2.png 0.0
ex3.png 42.10526315789473
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明 在此处输入图片说明 在此处输入图片说明

没毛病!它能够分辨出 ex1 相似而 ex2 不相似,但是它指出 ex3 相似(甚至比 ex1 更相似)。任何额外的预处理或后处理(可能是 ml,假设 ml 实际上有用)或只是我可以对我的方法做的更改,以便仅保持 ex1 相似而不是 ex3?

(注意我创建的这个分数是我在网上找到的。不确定这是否是一种准确的方法)

在下面添加了更多示例

另一组例子:

这是我正在寻找的

在此处输入图片说明

我希望上面的图像与中间和底部的图像相似(注意:我将目标图像旋转 45 度并将其与下面的图像进行比较。)

特征匹配(如下面的答案所述)在发现与第二张图像的相似性方面很有用,但不是第三张图像(即使在正确旋转之后)

在此处输入图片说明

在此处输入图片说明

在此处输入图片说明

Ann*_*Zen 7

检测最相似的图像

代码

您可以使用模板匹配,其中您要检测是否在其他图像中的图像就是模板。我将那个小图像保存在、 中template.png,其他三张图像保存在img1.pngimg2.png和中img3.png

我定义了一个函数,利用cv2.matchTemplate来计算模板是否在图像中的置信度。在每幅图像上使用该函数,产生最高置信度的图像是包含模板的图像:

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())
Run Code Online (Sandbox Code Playgroud)

输出:

Confidence for img1.png:
0.8906427
Confidence for img2.png:
0.4427919
Confidence for img3.png:
0.5933967
Run Code Online (Sandbox Code Playgroud)

说明:

  1. cv2.imread导入opencv模块,并通过将方法的第二个参数设置为灰度来读入模板图像0
import cv2

template = cv2.imread("template.png", 0)
Run Code Online (Sandbox Code Playgroud)
  1. 定义您想要确定哪些图像包含模板的图像列表:
files = ["img1.png", "img2.png", "img3.png"]
Run Code Online (Sandbox Code Playgroud)
  1. 循环遍历文件名并将每个文件名作为灰度图像读入:
for name in files:
    img = cv2.imread(name, 0)
Run Code Online (Sandbox Code Playgroud)
  1. 最后,您可以使用cv2.matchTemplate来检测每个图像中的模板。有很多检测方法可以使用,但为此我决定使用以下cv2.TM_CCOEFF_NORMED方法:
    print(f"Confidence for {name}:")
    print(cv2.matchTemplate(img, template, cv2.TM_CCOEFF_NORMED).max())
Run Code Online (Sandbox Code Playgroud)

该函数的输出范围在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)
Run Code Online (Sandbox Code Playgroud)

输出:

在此输入图像描述

说明:

  1. 导入必要的库:
import cv2
import numpy as np
Run Code Online (Sandbox Code Playgroud)
  1. 定义一个将接收完整图像和模板图像的函数。由于该cv2.matchTemplate方法需要灰度图像,因此将2张图像转换为灰度图像:
def confidence(img, template):
    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    template = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)
Run Code Online (Sandbox Code Playgroud)
  1. 使用该cv2.matchTemplate方法检测图像中的模板,并返回置信度最高的点的位置,返回置信度最高的点:
    res = cv2.matchTemplate(img, template, cv2.TM_CCOEFF_NORMED)
    conf = res.max()
    return np.where(res == conf), conf
Run Code Online (Sandbox Code Playgroud)
  1. 定义您想要确定哪一个包含模板的图像列表,并读入模板图像:
files = ["img1.png", "img2.png", "img3.png"]
template = cv2.imread("template.png")
Run Code Online (Sandbox Code Playgroud)
  1. 获取模板图像的大小,以便稍后在图像上绘制矩形:
h, w, _ = template.shape
Run Code Online (Sandbox Code Playgroud)
  1. 循环文件名并读取每个图像。使用confidence我们之前定义的函数,获取检测到的模板左上角的 xy 位置以及检测的置信度:
for name in files:
    img = cv2.imread(name)
    ([y], [x]), conf = confidence(img, template)
Run Code Online (Sandbox Code Playgroud)
  1. 在图像的角上画一个矩形,然后将文本放在图像上。最后展示一下图片:
    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)
Run Code Online (Sandbox Code Playgroud)
  1. 另外,显示模板进行比较:
cv2.imshow('Template', template)
cv2.waitKey(0)
Run Code Online (Sandbox Code Playgroud)


小智 -2

首先,数据出现在图表中,你不能从它们的数值数据中得到重叠的值吗?

您是否尝试过对从白蓝色到蓝红色的颜色变化进行一些边缘检测,将一些圆圈拟合到这些边缘,然后检查它们是否重叠?

由于输入数据受到严格控制(没有有机摄影或视频),也许您不必走 ML 路线。