【问题标题】:Overlay contour onto new image将轮廓叠加到新图像上
【发布时间】:2021-09-06 07:39:12
【问题描述】:

我正在从图像中提取对象并将其叠加到背景上以生成用于计算机视觉任务的场景。每次生成场景时都会添加随机定位、大小、旋转和过滤。

我面临的问题是,除非对象是一个完美的垂直矩形,否则在覆盖图像时会出现黑色边框。

叠加时如何使轮廓外的区域透明?

在下面找到一些示例代码和输出。

import cv2
from matplotlib import pyplot as plt
from skimage import io

#Import Card
card = io.imread('https://i.ebayimg.com/thumbs/images/g/BZoAAOSwj0RfkZuD/s-l225.jpg') 
plt.imshow(card)

#Import Background
background = io.imread('https://www.robots.ox.ac.uk/~vgg/data/dtd/images/cracked/cracked_0049.jpg') 
plt.imshow(background)

#Find contours and extract card
edged=cv2.Canny(card,30,200)
contours, hierarchy=cv2.findContours(edged,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)
contour = [c for c in contours if c.size >=1300][0]

#Crop out background
x,y,w,h = cv2.boundingRect(contour)
card_cropped = card[y:y+h, x:x+w]

#Resize
card_cropped_resized = cv2.resize(card_cropped, (int(card_cropped.shape[1]/0.8), int(card_cropped.shape[0]/0.8)))

#Generate Scene
card_cropped_resized_grayscale = cv2.cvtColor(card_cropped_resized, cv2.COLOR_RGB2GRAY)
background_grayscale = cv2.cvtColor(background, cv2.COLOR_RGB2GRAY)

h, w = card_cropped_resized_grayscale.shape[:2]
hh, ww = background_grayscale.shape[:2]

yoff = round(hh/4)
xoff = round(ww/4)

xMin = xoff
yMin = yoff
xMax = xoff+w
yMax = yoff+h

scene = background_grayscale.copy()
scene[yMin:yMax, xMin:xMax] = card_cropped_resized_grayscale

plt.imshow(cv2.cvtColor(scene,cv2.COLOR_GRAY2RGB))

【问题讨论】:

    标签: python opencv computer-vision cv2


    【解决方案1】:

    在尝试您的代码时,我的轮廓出现错误。所以我做了一些改变,最值得注意的是,使用阈值而不是 Canny 边缘作为轮廓的基础。为了去除黑色边框,我只是在得到它的轮廓之前侵蚀了阈值图像。你可以通过简单地编辑你的 x,y,w,h 来删除周围的几个像素来做同样的事情。你得到黑色边框的原因是你的 Canny 边缘图像有噪点,这使得轮廓更大。但主要是因为卡片的形状不是矩形,而是顶部比底部窄。所以矩形边界框将是顶部或底部最大的尺寸,黑色是实际区域和边界框之间的尺寸。在 Canny 边缘运算之后添加中值滤波器会改善该部分,但不会改善形状问题。

    输入:

    import cv2
    from matplotlib import pyplot as plt
    from skimage import io
    
    #Import Card
    card = io.imread('https://i.ebayimg.com/thumbs/images/g/BZoAAOSwj0RfkZuD/s-l225.jpg') 
    plt.imshow(card)
    #plt.show()
    
    #Import Background
    background = io.imread('https://www.robots.ox.ac.uk/~vgg/data/dtd/images/cracked/cracked_0049.jpg') 
    plt.imshow(background)
    #plt.show()
    
    #Find contours and extract card
    #edged=cv2.Canny(card,30,200)
    edged = cv2.cvtColor(card, cv2.COLOR_BGR2GRAY)
    edged = cv2.threshold(edged, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT , (5,5))
    edged = cv2.morphologyEx(edged, cv2.MORPH_ERODE, kernel)
    plt.imshow(edged)
    #plt.show()
    
    
    #contours, hierarchy=cv2.findContours(edged,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)
    #contour = [c for c in contours if c.size >=1300][0]
    contours = cv2.findContours(edged, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    contours = contours[0] if len(contours) == 2 else contours[1]
    contour = max(contours, key=cv2.contourArea)
    
    
    #Crop out background
    x,y,w,h = cv2.boundingRect(contour)
    card_cropped = card[y:y+h, x:x+w]
    
    #Resize
    card_cropped_resized = cv2.resize(card_cropped, (int(card_cropped.shape[1]/0.8), int(card_cropped.shape[0]/0.8)))
    
    #Generate Scene
    card_cropped_resized_grayscale = cv2.cvtColor(card_cropped_resized, cv2.COLOR_RGB2GRAY)
    background_grayscale = cv2.cvtColor(background, cv2.COLOR_RGB2GRAY)
    
    h, w = card_cropped_resized_grayscale.shape[:2]
    hh, ww = background_grayscale.shape[:2]
    
    yoff = round(hh/4)
    xoff = round(ww/4)
    
    xMin = xoff
    yMin = yoff
    xMax = xoff+w
    yMax = yoff+h
    
    scene = background_grayscale.copy()
    scene[yMin:yMax, xMin:xMax] = card_cropped_resized_grayscale
    
    plt.imshow(cv2.cvtColor(scene,cv2.COLOR_GRAY2RGB))
    plt.show()
    
    cv2.imwrite("scene_grayscale.jpg",scene)
    

    结果:

    【讨论】:

    • 该代码适用于示例卡。然而,我在一张更激进的倾斜卡片上进行了测试,它产生了一个边框。 example。对不起,如果我没有在我的问题中说清楚,但我正在寻找一种无论矩形卡的方向如何都能工作的解决方案。
    【解决方案2】:

    考虑到您的担忧,这里有一个在 Python/OpenCV 中使用掩码合成的更强大的解决方案。我们从卡片的轮廓创建一个填充遮罩,并将卡片和遮罩裁剪到轮廓的边界框。接下来,我们将卡片插入到所需位置的背景中。我们将遮罩插入到与背景大小相同的黑色背景图像中。然后我们将原始背景和新背景与插入的卡片进行合成,使用蒙版来控制使用哪个。

    背景:

    卡片:

    import cv2
    import numpy as np
    from matplotlib import pyplot as plt
    from skimage import io    
    
    #Import Card
    card = io.imread('https://i.ebayimg.com/thumbs/images/g/BZoAAOSwj0RfkZuD/s-l225.jpg') 
    
    # Apply median filter to card
    card_median = cv2.medianBlur(card, 3)
    
    #Import Background
    background = io.imread('https://www.robots.ox.ac.uk/~vgg/data/dtd/images/cracked/cracked_0049.jpg') 
    hh, ww = background.shape[:2]
    
    #Find edges of card
    edged=cv2.Canny(card_median,30,200)
    plt.imshow(edged)
    plt.show()
    
    # get largest contour
    contours = cv2.findContours(edged, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    contours = contours[0] if len(contours) == 2 else contours[1]
    cntr = max(contours, key=cv2.contourArea)
    
    # draw white filled contour on black background the size of card 
    card_mask = np.zeros_like(card)
    cv2.drawContours(card_mask, [cntr], 0, (255,255,255), -1)
    
    # erode contour just a little to ensure contour encloses no black from outside card
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT , (3,3))
    card_mask = cv2.morphologyEx(card_mask, cv2.MORPH_ERODE, kernel)
    
    #Get bounding box
    x,y,w,h = cv2.boundingRect(cntr)
    
    # crop card and card_mask
    card_cropped = card[y:y+h, x:x+w]
    card_mask_cropped = card_mask[y:y+h, x:x+w]
    
    # define insert location and size
    yoff = round(hh/4)
    xoff = round(ww/4)
    
    xMin = xoff
    yMin = yoff
    xMax = xoff+w
    yMax = yoff+h
    
    # insert cropped card into background
    scene = background.copy()
    scene[yMin:yMax, xMin:xMax] = card_cropped
    
    # insert card_mask into black background the size of background image (and make single channel)
    mask = np.zeros_like(background)
    mask[yMin:yMax, xMin:xMax] = card_mask_cropped
    mask = mask[:,:,0]
    
    #composite scene with background using mask
    scene_masked = cv2.bitwise_and(scene, scene, mask=mask)
    background_masked = cv2.bitwise_and(background, background, mask=(255-mask))
    result = cv2.add(scene_masked,background_masked)
    
    # show results
    plt.imshow(scene)
    plt.show()
    plt.imshow(mask, cmap='gray')
    plt.show()
    plt.imshow(result)
    plt.show()
    
    # save results
    result = cv2.cvtColor(result, cv2.COLOR_RGB2BGR)
    cv2.imwrite("card_composite.jpg",result)
    

    结果:

    这是使用此卡片图像的结果:

    卡片:

    结果:

    【讨论】:

      猜你喜欢
      • 2021-01-05
      • 2013-07-01
      • 2023-03-09
      • 1970-01-01
      • 1970-01-01
      • 2020-03-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多