You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修改OpenCV-Python代码以识别相似图片中的特定形状?

问题:OpenCV模板匹配无法在相似图片中识别目标形状

我从img-001.png中截取目标保存为ant0.png,用以下OpenCV-Python代码能在img-001.png中识别多个ant0形状,但在相似图片(如img-002.png)中无法识别,请问该如何修改代码?

原代码:

import cv2
import numpy as np

img_rgb = cv2.imread('img-001.png')

print(img_rgb.shape)
# Convert it to grayscale
img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)
template = cv2.imread('ant0.png',0)
# Store width and heigth of template in w and h
w, h = template.shape[::-1]
# Perform match operations.
res = cv2.matchTemplate(img_gray,template,cv2.TM_CCOEFF_NORMED)
# Specify a threshold
# threshold = 0.84
threshold = 0.8
# Store the coordinates of matched area in a numpy array
loc = np.where(res >= threshold)
x=loc[0]
y=loc[1]
# Draw a rectangle around the matched region.

ant_list=[]
if len(x) and len(y):
    for pt in zip(*loc[::-1]):
        cv2.rectangle(img_rgb, pt, (pt[0] + w, pt[1] + h), (0,255,255), 2)
        print((pt[0] + w, pt[1] + h))
        cv2.imwrite("ant-check0.png", img_rgb)
        ant_list.append(pt[0])
        print('I found the ant')

else:
    print('there is no ant')

print(len(ant_list))
cv2.imshow('ant-check1.png', img_rgb)
cv2.waitKey(0)
cv2.destroyAllWindows()
解决方案

针对相似图片识别失败的问题,可从以下几个方面修改代码:

1. 更换更鲁棒的模板匹配方法

原代码使用cv2.TM_CCOEFF_NORMED,对光照、小形变适应性较弱。可尝试cv2.TM_SQDIFF_NORMED(匹配值越小越相似)或cv2.TM_CCORR_NORMED,并调整阈值逻辑:

# 使用TM_SQDIFF_NORMED时,阈值设为较小值,比如0.2
res = cv2.matchTemplate(img_gray, template, cv2.TM_SQDIFF_NORMED)
threshold = 0.2
loc = np.where(res <= threshold)  # 注意这里是<=判断

2. 加入模板多尺度匹配

如果目标在img-002.png中大小有变化,单尺寸模板会失效。可以遍历不同缩放比例,对模板缩放后再匹配:

# 定义缩放范围,比如0.8到1.2,步长0.1
scales = np.arange(0.8, 1.2, 0.1)
for scale in scales:
    # 缩放模板
    resized_template = cv2.resize(template, None, fx=scale, fy=scale, interpolation=cv2.INTER_AREA)
    w_scaled, h_scaled = resized_template.shape[::-1]
    # 跳过缩放后尺寸超过原图的情况
    if w_scaled > img_gray.shape[1] or h_scaled > img_gray.shape[0]:
        continue
    res = cv2.matchTemplate(img_gray, resized_template, cv2.TM_CCOEFF_NORMED)
    loc = np.where(res >= threshold)
    for pt in zip(*loc[::-1]):
        cv2.rectangle(img_rgb, pt, (pt[0] + w_scaled, pt[1] + h_scaled), (0,255,255), 2)
        ant_list.append(pt)

3. 优化图像预处理步骤

对原图和模板做预处理,减少光照、噪声影响:

# 高斯模糊去噪
img_gray = cv2.GaussianBlur(img_gray, (3,3), 0)
template = cv2.GaussianBlur(template, (3,3), 0)

# 或者直方图均衡化增强对比度
img_gray = cv2.equalizeHist(img_gray)
template = cv2.equalizeHist(template)

4. 调整阈值并加入非极大值抑制(NMS)

固定阈值容易漏检或误检,可动态调整阈值,同时用NMS去除重复匹配框:

# 动态设置阈值(取匹配最大值的0.7倍)
max_val = np.max(res)
threshold = max_val * 0.7
loc = np.where(res >= threshold)

# 非极大值抑制去重函数
def non_max_suppression(boxes, overlapThresh):
    if len(boxes) == 0:
        return []
    boxes = np.array(boxes)
    x1 = boxes[:,0]
    y1 = boxes[:,1]
    x2 = boxes[:,0] + w
    y2 = boxes[:,1] + h
    area = (x2 - x1 + 1) * (y2 - y1 + 1)
    idxs = np.argsort(y2)
    pick = []
    while len(idxs) > 0:
        last = len(idxs) - 1
        i = idxs[last]
        pick.append(i)
        xx1 = np.maximum(x1[i], x1[idxs[:last]])
        yy1 = np.maximum(y1[i], y1[idxs[:last]])
        xx2 = np.minimum(x2[i], x2[idxs[:last]])
        yy2 = np.minimum(y2[i], y2[idxs[:last]])
        w_box = np.maximum(0, xx2 - xx1 + 1)
        h_box = np.maximum(0, yy2 - yy1 + 1)
        overlap = (w_box * h_box) / area[idxs[:last]]
        idxs = np.delete(idxs, np.concatenate(([last], np.where(overlap > overlapThresh)[0])))
    return boxes[pick].tolist()

# 收集所有匹配框并去重
boxes = []
for pt in zip(*loc[::-1]):
    boxes.append([pt[0], pt[1]])
filtered_boxes = non_max_suppression(boxes, 0.3)
# 绘制去重后的匹配框
for box in filtered_boxes:
    pt = (box[0], box[1])
    cv2.rectangle(img_rgb, pt, (pt[0] + w, pt[1] + h), (0,255,255), 2)
    ant_list.append(pt[0])

5. 尝试基于特征的匹配(如SIFT)

如果模板和目标有较大形变,模板匹配效果差,可改用特征点匹配:

# 初始化SIFT检测器
sift = cv2.SIFT_create()
# 检测特征点和描述符
kp1, des1 = sift.detectAndCompute(template, None)
kp2, des2 = sift.detectAndCompute(img_gray, None)
# FLANN匹配器
FLANN_INDEX_KDTREE = 1
index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
search_params = dict(checks=50)
flann = cv2.FlannBasedMatcher(index_params, search_params)
matches = flann.knnMatch(des1, des2, k=2)
# 筛选优质匹配点
good = []
for m,n in matches:
    if m.distance < 0.7*n.distance:
        good.append(m)
# 匹配点足够时,计算单应性矩阵并绘制匹配区域
if len(good) > 10:
    src_pts = np.float32([kp1[m.queryIdx].pt for m in good]).reshape(-1,1,2)
    dst_pts = np.float32([kp2[m.trainIdx].pt for m in good]).reshape(-1,1,2)
    M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)
    h,w = template.shape
    pts = np.float32([[0,0],[0,h-1],[w-1,h-1],[w-1,0]]).reshape(-1,1,2)
    dst = cv2.perspectiveTransform(pts,M)
    img_rgb = cv2.polylines(img_rgb,[np.int32(dst)],True,(0,255,255),2)
else:
    print(f"Not enough matches found - {len(good)}/10")

内容的提问来源于stack exchange,提问作者dominga swopes

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 21:39:36