[英]How do I remove the background from this kind of image?
我想刪除此圖像的背景以僅獲取此人。 我有數千張這樣的圖像,基本上,一個人和一個有點發白的背景。
我所做的是使用像 canny 邊緣檢測器或 sobel 過濾器(來自skimage
庫)這樣的邊緣檢測器。 然后我認為可以做的是,將邊緣內的像素變白,而將邊緣內的像素變黑。 之后,可以對原始圖像進行蒙版以僅獲取人的照片。
然而,使用canny邊緣檢測器很難得到一個封閉的邊界。 使用 Sobel 過濾器的結果還不錯,但是我不知道如何從那里開始。
編輯:
是否也可以去除右手和裙子之間以及頭發之間的背景?
以下代碼應該可以幫助您入門。 您可能希望使用程序頂部的參數來微調您的提取:
import cv2
import numpy as np
#== Parameters =======================================================================
BLUR = 21
CANNY_THRESH_1 = 10
CANNY_THRESH_2 = 200
MASK_DILATE_ITER = 10
MASK_ERODE_ITER = 10
MASK_COLOR = (0.0,0.0,1.0) # In BGR format
#== Processing =======================================================================
#-- Read image -----------------------------------------------------------------------
img = cv2.imread('C:/Temp/person.jpg')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
#-- Edge detection -------------------------------------------------------------------
edges = cv2.Canny(gray, CANNY_THRESH_1, CANNY_THRESH_2)
edges = cv2.dilate(edges, None)
edges = cv2.erode(edges, None)
#-- Find contours in edges, sort by area ---------------------------------------------
contour_info = []
_, contours, _ = cv2.findContours(edges, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)
# Previously, for a previous version of cv2, this line was:
# contours, _ = cv2.findContours(edges, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)
# Thanks to notes from commenters, I've updated the code but left this note
for c in contours:
contour_info.append((
c,
cv2.isContourConvex(c),
cv2.contourArea(c),
))
contour_info = sorted(contour_info, key=lambda c: c[2], reverse=True)
max_contour = contour_info[0]
#-- Create empty mask, draw filled polygon on it corresponding to largest contour ----
# Mask is black, polygon is white
mask = np.zeros(edges.shape)
cv2.fillConvexPoly(mask, max_contour[0], (255))
#-- Smooth mask, then blur it --------------------------------------------------------
mask = cv2.dilate(mask, None, iterations=MASK_DILATE_ITER)
mask = cv2.erode(mask, None, iterations=MASK_ERODE_ITER)
mask = cv2.GaussianBlur(mask, (BLUR, BLUR), 0)
mask_stack = np.dstack([mask]*3) # Create 3-channel alpha mask
#-- Blend masked img into MASK_COLOR background --------------------------------------
mask_stack = mask_stack.astype('float32') / 255.0 # Use float matrices,
img = img.astype('float32') / 255.0 # for easy blending
masked = (mask_stack * img) + ((1-mask_stack) * MASK_COLOR) # Blend
masked = (masked * 255).astype('uint8') # Convert back to 8-bit
cv2.imshow('img', masked) # Display
cv2.waitKey()
#cv2.imwrite('C:/Temp/person-masked.jpg', masked) # Save
輸出:
如果您希望不使用紅色填充背景而是使其透明,您可以在解決方案中添加以下幾行:
# split image into channels
c_red, c_green, c_blue = cv2.split(img)
# merge with mask got on one of a previous steps
img_a = cv2.merge((c_red, c_green, c_blue, mask.astype('float32') / 255.0))
# show on screen (optional in jupiter)
%matplotlib inline
plt.imshow(img_a)
plt.show()
# save to disk
cv2.imwrite('girl_1.png', img_a*255)
# or the same using plt
plt.imsave('girl_2.png', img_a)
如果您願意,可以調整一些 png 壓縮參數以使文件更小。
下圖為白色背景。 或黑色 - http://imgur.com/a/4NwmH
vs2017 的工作示例。
設置紅色背景但保存藍色..
還添加了透明示例。
如何去除女孩的身體,只留下照片中的裙子? 有任何想法嗎?
# == https://stackoverflow.com/questions/29313667/how-do-i-remove-the-background-from-this-kind-of-image
import cv2
import numpy as np
from matplotlib import pyplot as plt
#== Parameters =======================================================================
BLUR = 21
CANNY_THRESH_1 = 10
CANNY_THRESH_2 = 200
MASK_DILATE_ITER = 10
MASK_ERODE_ITER = 10
MASK_COLOR = (0.0,0.0,1.0) # In BGR format
#== Processing =======================================================================
#-- Read image -----------------------------------------------------------------------
img = cv2.imread('img/SYxmp.jpg')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
#-- Edge detection -------------------------------------------------------------------
edges = cv2.Canny(gray, CANNY_THRESH_1, CANNY_THRESH_2)
edges = cv2.dilate(edges, None)
edges = cv2.erode(edges, None)
#-- Find contours in edges, sort by area ---------------------------------------------
contour_info = []
_, contours, _ = cv2.findContours(edges, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)
for c in contours:
contour_info.append((
c,
cv2.isContourConvex(c),
cv2.contourArea(c),
))
contour_info = sorted(contour_info, key=lambda c: c[2], reverse=True)
max_contour = contour_info[0]
#-- Create empty mask, draw filled polygon on it corresponding to largest contour ----
# Mask is black, polygon is white
mask = np.zeros(edges.shape)
cv2.fillConvexPoly(mask, max_contour[0], (255))
#-- Smooth mask, then blur it --------------------------------------------------------
mask = cv2.dilate(mask, None, iterations=MASK_DILATE_ITER)
mask = cv2.erode(mask, None, iterations=MASK_ERODE_ITER)
mask = cv2.GaussianBlur(mask, (BLUR, BLUR), 0)
mask_stack = np.dstack([mask]*3) # Create 3-channel alpha mask
#-- Blend masked img into MASK_COLOR background --------------------------------------
mask_stack = mask_stack.astype('float32') / 255.0 # Use float matrices,
img = img.astype('float32') / 255.0 # for easy blending
masked = (mask_stack * img) + ((1-mask_stack) * MASK_COLOR) # Blend
masked = (masked * 255).astype('uint8') # Convert back to 8-bit
plt.imsave('img/girl_blue.png', masked)
# split image into channels
c_red, c_green, c_blue = cv2.split(img)
# merge with mask got on one of a previous steps
img_a = cv2.merge((c_red, c_green, c_blue, mask.astype('float32') / 255.0))
# show on screen (optional in jupiter)
#%matplotlib inline
plt.imshow(img_a)
plt.show()
# save to disk
cv2.imwrite('img/girl_1.png', img_a*255)
# or the same using plt
plt.imsave('img/girl_2.png', img_a)
cv2.imshow('img', masked) # Displays red, saves blue
cv2.waitKey()
根據@jedwards 的回答,與 opencv4 一起使用時,會出現此錯誤:
Traceback (most recent call last):
File "save.py", line 26, in <module>
_, contours, _ = cv2.findContours(edges, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)
ValueError: not enough values to unpack (expected 3, got 2)
函數cv2.findContours()
已更改為僅返回輪廓和層次結構
你應該改成這樣:
contours, _ = cv2.findContours(edges, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)
在獲得不完整的邊緣(如您所見)后,您可以運行閉合形態(一系列膨脹和侵蝕)(必須根據需要/邊緣狀態設置大小和迭代)。
現在假設您在主體周圍有一個恆定的邊緣,使用任何類型的填充算法 (blob) 組合邊緣對象外的所有點,然后取負數,為您提供對象內部的蒙版.
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.