【问题标题】:How to remove specific tag/sticker/object from images using OpenCV?如何使用 OpenCV 从图像中删除特定的标签/贴纸/对象?
【发布时间】:2019-10-01 13:59:28
【问题描述】:

我有数百张珠宝产品的图片。其中一些带有“畅销书”标签。标签的位置因图像而异。我想遍历所有图像,如果图像具有此标签,则将其删除。生成的图像将在已移除对象的像素上呈现背景。

带有标签/贴纸/对象的图像示例:

要移除的标签/贴纸/对象:

import numpy as np
import cv2 as cv

img = plt.imread('./images/001.jpg')
sticker = plt.imread('./images/tag.png',1)
diff_im = cv2.absdiff(img, sticker)

我希望结果图像是这样的:

【问题讨论】:

标签: python image opencv image-processing computer-vision


【解决方案1】:

这是一种使用修改后的Template Matching 方法的方法。以下是总体策略:

  • 加载模板,转灰度,进行canny边缘检测
  • 加载原图,转灰度图
  • 不断重新缩放图像,使用边缘应用模板匹配,并跟踪相关系数(值越高表示匹配越好)
  • 找到最佳拟合边界框的坐标,然后删除不需要的 ROI

首先,我们加载模板并执行 Canny 边缘检测。应用与边缘而不是原始图像匹配的模板可以消除颜色变化差异并提供更稳健的结果。从模板图像中提取边缘:

接下来,我们不断缩小图像并在调整后的图像上应用模板匹配。我使用old answer 在每次调整大小时保持纵横比。这是战略的可视化

我们调整图像大小的原因是因为使用cv2.matchTemplate 的标准模板匹配不可靠,如果模板和图像的尺寸不匹配,可能会出现误报。为了克服这个维度问题,我们使用了这种修改后的方法:

  • 以各种更小的比例不断调整输入图像的大小
  • 使用cv2.matchTemplate 应用模板匹配并跟踪最大相关系数
  • 相关系数最大的比率/尺度将具有最佳匹配的 ROI

获得 ROI 后,我们可以通过使用白色填充矩形来“删除”徽标

cv2.rectangle(final, (start_x, start_y), (end_x, end_y), (255,255,255), -1)

import cv2
import numpy as np

# Resizes a image and maintains aspect ratio
def maintain_aspect_ratio_resize(image, width=None, height=None, inter=cv2.INTER_AREA):
    # Grab the image size and initialize dimensions
    dim = None
    (h, w) = image.shape[:2]

    # Return original image if no need to resize
    if width is None and height is None:
        return image

    # We are resizing height if width is none
    if width is None:
        # Calculate the ratio of the height and construct the dimensions
        r = height / float(h)
        dim = (int(w * r), height)
    # We are resizing width if height is none
    else:
        # Calculate the ratio of the 0idth and construct the dimensions
        r = width / float(w)
        dim = (width, int(h * r))

    # Return the resized image
    return cv2.resize(image, dim, interpolation=inter)

# Load template, convert to grayscale, perform canny edge detection
template = cv2.imread('template.PNG')
template = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)
template = cv2.Canny(template, 50, 200)
(tH, tW) = template.shape[:2]
cv2.imshow("template", template)

# Load original image, convert to grayscale
original_image = cv2.imread('1.jpg')
final = original_image.copy()
gray = cv2.cvtColor(original_image, cv2.COLOR_BGR2GRAY)
found = None

# Dynamically rescale image for better template matching
for scale in np.linspace(0.2, 1.0, 20)[::-1]:

    # Resize image to scale and keep track of ratio
    resized = maintain_aspect_ratio_resize(gray, width=int(gray.shape[1] * scale))
    r = gray.shape[1] / float(resized.shape[1])

    # Stop if template image size is larger than resized image
    if resized.shape[0] < tH or resized.shape[1] < tW:
        break

    # Detect edges in resized image and apply template matching
    canny = cv2.Canny(resized, 50, 200)
    detected = cv2.matchTemplate(canny, template, cv2.TM_CCOEFF)
    (_, max_val, _, max_loc) = cv2.minMaxLoc(detected)

    # Uncomment this section for visualization
    '''
    clone = np.dstack([canny, canny, canny])
    cv2.rectangle(clone, (max_loc[0], max_loc[1]), (max_loc[0] + tW, max_loc[1] + tH), (0,255,0), 2)
    cv2.imshow('visualize', clone)
    cv2.waitKey(0)
    '''

    # Keep track of correlation value
    # Higher correlation means better match
    if found is None or max_val > found[0]:
        found = (max_val, max_loc, r)

# Compute coordinates of bounding box
(_, max_loc, r) = found
(start_x, start_y) = (int(max_loc[0] * r), int(max_loc[1] * r))
(end_x, end_y) = (int((max_loc[0] + tW) * r), int((max_loc[1] + tH) * r))

# Draw bounding box on ROI to remove
cv2.rectangle(original_image, (start_x, start_y), (end_x, end_y), (0,255,0), 2)
cv2.imshow('detected', original_image)

# Erase unwanted ROI (Fill ROI with white)
cv2.rectangle(final, (start_x, start_y), (end_x, end_y), (255,255,255), -1)
cv2.imshow('final', final)
cv2.waitKey(0)

【讨论】:

  • 非常感谢@nathancy。对 resize 方法的热烈掌声。这种方法效果很好。只是一个问题:最终的图像颜色有点改变,我怎样才能保持原来的颜色?
  • 我不确定您所说的最终图像颜色已更改是什么意思。它们在屏幕截图图像中看起来相同。您能否详细说明最终图像颜色的更改位置?去除 logo 的区域可能会略有不同,因为我们用白色填充 ROI,这可能与周围的背景颜色不同
  • 我的错;我使用 plt 来绘制/显示图像,因此颜色发生了变化。非常感谢。
  • @nathancy 如果有多个匹配项怎么办,这只会删除最后一个匹配项正确吗?
  • @forthelulx 目前它只选择单个最佳匹配。您可以通过设置阈值来修改它以捕获多个。如果它比某个预定义的阈值更好,则认为它是匹配的
【解决方案2】:

使用cv.matchTemplatedocumentation 中提供了一个示例。

找到对象后,只需绘制一个负厚度的矩形,将其填充为白色。

【讨论】:

  • 谢谢;似乎模板是正确的方法。 @nathancy 谢天谢地添加了 resize 方法。
猜你喜欢
  • 2011-01-11
  • 2020-10-07
  • 2020-10-17
  • 1970-01-01
  • 2017-07-23
  • 2018-02-24
  • 1970-01-01
  • 2016-08-21
  • 2020-11-18
相关资源
最近更新 更多