如何使用 OpenCV 从图像中删除特定的标签/贴纸/对象?

问题描述

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

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

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

将 numpy 导入为 np将 cv2 导入为 cvimg = plt.imread('./images/001.jpg')贴纸 = plt.imread('./images/tag.png',1)diff_im = cv2.absdiff(img,贴纸)

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

解决方案

这是一个使用修改后的方法

接下来,我们不断缩小图像并在调整后的图像上应用模板匹配.我使用

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

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

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

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

导入 cv2将 numpy 导入为 np# 调整图像大小并保持纵横比def maintain_aspect_ratio_resize(图像,宽度=无,高度=无,inter=cv2.INTER_AREA):# 获取图像大小并初始化维度暗淡 = 无(h, w) = image.shape[:2]# 如果不需要调整大小,则返回原始图像如果宽度为无且高度为无:返回图片# 如果宽度没有,我们正在调整高度如果宽度为无:# 计算高度的比例并构造尺寸r = 高度/浮动(h)暗淡=(int(w * r),高度)# 如果没有高度,我们正在调整宽度别的:# 计算0idth的比例并构造维度r = 宽度/浮动(w)暗淡=(宽度,int(h * r))# 返回调整大小的图像return cv2.resize(图像,暗淡,插值=inter)# 加载模板,转换为灰度,进行canny边缘检测模板 = cv2.imread('template.PNG')模板 = cv2.cvtColor(模板,cv2.COLOR_BGR2GRAY)模板 = cv2.Canny(模板, 50, 200)(tH, tW) = 模板.shape[:2]cv2.imshow("模板", 模板)# 加载原图,转灰度original_image = cv2.imread('1.jpg')final = original_image.copy()灰色 = cv2.cvtColor(original_image, cv2.COLOR_BGR2GRAY)找到 = 无# 动态重新缩放图像以获得更好的模板匹配对于 np.linspace(0.2, 1.0, 20)[::-1] 中的比例:# 调整图像大小并跟踪比例调整大小=维护纵横比调整大小(灰色,宽度=int(gray.shape[1] * scale))r = gray.shape[1]/float(resized.shape[1])# 如果模板图像大小大于调整后的图像,则停止如果调整大小.shape[0] <tH 或 resized.shape[1] 找到[0]:找到 = (max_val, max_loc, r)# 计算边界框的坐标(_, max_loc, r) = 找到(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))# 在 ROI 上绘制边界框以移除cv2.rectangle(original_image, (start_x, start_y), (end_x, end_y), (0,255,0), 2)cv2.imshow('检测到', original_image)# 擦除不需要的 ROI(用白色填充 ROI)cv2.rectangle(最终,(start_x,start_y),(end_x,end_y),(255,255,255),-1)cv2.imshow('final', final)cv2.waitKey(0)

I have hundreds of images of jewelry products. Some of them have "best-seller" tag on them. The position of the tag is different from image to image. I want iterate over all images, and if an image has this tag then remove it. The resulted image will render the background over the removed object's pixels.

Example of an image with Tag/sticker/object:

Tag/sticker/object to remove:

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)

I want the resulted image to be like this:

解决方案

Here's an method using a modified Template Matching approach. Here's the overall strategy:

  • Load template, convert to grayscale, perform canny edge detection
  • Load original image, convert to grayscale
  • Continuously rescale image, apply template matching using edges, and keep track of the correlation coefficient (higher value means better match)
  • Find coordinates of best fit bounding box then erase unwanted ROI

To begin, we load in the template and perform Canny edge detection. Applying template matching with edges instead of the raw image removes color variation differences and gives a more robust result. Extracting edges from template image:

Next we continuously scale down the image and apply template matching on our resized image. I maintain aspect ratio with each resize using a old answer. Here's a visualization of the strategy

The reason we resize the image is because standard template matching using cv2.matchTemplate will not be robust and may give false positives if the dimensions of the template and the image do not match. To overcome this dimension issue, we use this modified approach:

  • Continuously resize the input image at various smaller scales
  • Apply template matching using cv2.matchTemplate and keep track of the largest correlation coefficient
  • The ratio/scale with the largest correlation coefficient will have the best matched ROI

Once the ROI is obtained, we can "delete" the logo by filling in the rectangle with white using

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)

相关文章