将轮廓叠加到新图像上

时间:2021-06-22 17:19:52

标签: python opencv computer-vision cv2

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

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

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

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

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)

card 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))

generated scene

2 个答案:

答案 0 :(得分:2)

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

输入:

enter image description here

enter image description here

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)

结果:

enter image description here

答案 1 :(得分:1)

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

背景:

enter image description here

卡片:

enter image description here

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)

结果:

enter image description here

这是使用这张卡片图片的结果:

卡片:

enter image description here

结果:

enter image description here

相关问题