如何从图像中删除背景
How to remove the background from an image
我想把背景去掉,画出图中方框的轮廓(有multiple such images个背景相似)。我在 OpenCV 中尝试了多种方法,但是我无法确定可以帮助删除此图像背景的功能组合。尝试过的一些方法是:
- 边缘检测 - 由于背景本身也有自己的边缘,单独使用边缘检测(例如 Canny 和 Sobel)似乎并没有得到好的结果。
- 通道过滤/阈值 - 背景和前景都具有相似的白色,因此我无法找到正确的阈值来过滤前景。
- 轮廓检测 - 由于背景本身有很多轮廓,仅使用最大的轮廓区域(通常用于去除背景)也不起作用。
我愿意使用计算机视觉或深度学习(Python)中的工具来解决这个特定问题。
概念
在这种情况下,微调您用来扩大和侵蚀从图像中检测到的精巧边缘的内核非常有用。这是一个示例,其中膨胀内核为 np.ones((4, 2))
,腐蚀内核为 np.ones((13, 7))
:
代码
import cv2
import numpy as np
def process(img):
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img_blur = cv2.GaussianBlur(img_gray, (3, 3), 2)
img_canny = cv2.Canny(img_blur, 50, 9)
img_dilate = cv2.dilate(img_canny, np.ones((4, 2)), iterations=11)
img_erode = cv2.erode(img_dilate, np.ones((13, 7)), iterations=4)
return cv2.bitwise_not(img_erode)
def get_contours(img):
contours, _ = cv2.findContours(process(img), cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
cnt = max(contours, key=cv2.contourArea)
cv2.drawContours(img, [cv2.convexHull(cnt)], -1, (0, 0, 255), 2)
img = cv2.imread("image2.png")
get_contours(img)
cv2.imshow("result", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
输出
提供的两张图片的输出:
图片 1:
图 2:
备注
请注意,处理后的图像 (二进制) 在 cv2.bitwise_not(img_erode)
处反转。观察两个图像的处理版本(由上面定义的process()
函数返回),反转:
已处理图像 1:
已处理图像 2:
工具
最后,如果您碰巧有其他图像在上面的程序上无法正常工作,您可以使用OpenCV Trackbars调整传递给下面程序的方法的值:
import cv2
import numpy as np
def process(img, b_k, b_s, c_t1, c_t2, k1, k2, k3, k4, iter1, iter2):
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
b_k = b_k // 2 * 2 + 1
img_blur = cv2.GaussianBlur(img_gray, (b_k, b_k), b_s)
img_canny = cv2.Canny(img_blur, c_t1, c_t2)
img_dilate = cv2.dilate(img_canny, np.ones((k1, k2)), iterations=iter1)
img_erode = cv2.erode(img_dilate, np.ones((k3, k4)), iterations=iter2)
return cv2.bitwise_not(img_erode)
d = {"Blur Kernel": (3, 50),
"Blur Sigma": (2, 30),
"Canny Threshold 1": (50, 500),
"Canny Threshold 2": (9, 500),
"Dilate Kernel1": (4, 50),
"Dilate Kernel2": (2, 50),
"Erode Kernel1": (13, 50),
"Erode Kernel2": (7, 50),
"Dilate Iterations": (11, 40),
"Erode Iterations": (4, 40)}
cv2.namedWindow("Track Bars")
for i in d:
cv2.createTrackbar(i, "Track Bars", *d[i], id)
img = cv2.imread("image1.png")
while True:
img_copy = img.copy()
processed = process(img, *(cv2.getTrackbarPos(i, "Track Bars") for i in d))
contours, _ = cv2.findContours(processed, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
if contours:
cnt = max(contours, key=cv2.contourArea)
cv2.drawContours(img_copy, [cv2.convexHull(cnt)], -1, (0, 0, 255), 2)
cv2.imshow("result", img_copy)
if cv2.waitKey(1) & 0xFF == ord("q"):
break
cv2.waitKey(0)
cv2.destroyAllWindows()
如果为您提供了所需的完美样本,您可以尝试使用 SIFT 或 SURF 算法。我以 'intact' 数据集中的一个为例。 SIFT 算法将尝试将样本中的特征与实际图像进行匹配。
从那里,您可以清理匹配并找到单应性(RANSAC 在这种情况下效果最好)来找到轮廓。
测试图片:
参考图像(从测试图像中截取):
结果(您可以跳过灰度转换):
我在此处显示的代码是指在 Colab 中使用自定义参数执行的代码,您可以进一步调整。
import cv2
import numpy as np
from google.colab.patches import cv2_imshow
img = cv2.imread("reference_1.png", cv2.IMREAD_GRAYSCALE)
frame = cv2.imread("top.png", cv2.IMREAD_GRAYSCALE)
# if SIFT_create() gives problems, try downgrading opencv with
# pip uninstall opencv-python
# pip install opencv-contrib-python==3.4.2.17
sift = cv2.xfeatures2d.SIFT_create()
kp_image, desc_image = sift.detectAndCompute(img, None)
kp_frame, desc_frame = sift.detectAndCompute(frame, None)
index_params = dict(algorithm=0, trees=5)
search_params = dict()
flann = cv2.FlannBasedMatcher(index_params, search_params)
matches = flann.knnMatch(desc_image, desc_frame, k=2)
# clean the matches
good_points=[]
for m, n in matches:
if(m.distance < 0.6 * n.distance):
good_points.append(m)
query_pts = np.float32([kp_image[m.queryIdx].pt for m in good_points]).reshape(-1, 1, 2)
train_pts = np.float32([kp_frame[m.trainIdx].pt for m in good_points]).reshape(-1, 1, 2)
# find homography to find mask
matrix, mask = cv2.findHomography(query_pts, train_pts, cv2.RANSAC, 5.0)
matches_mask = mask.ravel().tolist()
h,w = img.shape
pts = np.float32([ [0,0],[0,h-1],[w-1,h-1],[w-1,0] ]).reshape(-1,1,2)
dst = cv2.perspectiveTransform(pts, matrix)
homography = cv2.polylines(frame, [np.int32(dst)], True, (255, 0, 0), 3)
cv2_imshow(homography)
您可以使用Rembg(删除图片背景的工具)。即使使用 pre-trained 权重,这也能很好地工作。我尝试了测试图像,这是我使用 Rembg 的结果
您可以使用 pip 下载 Rembg
pip install rembg
删除单个文件的背景
rembg i path/to/input.png path/to/output.png
删除文件夹中所有图像的背景
rembg p path/to/input path/to/output
- 边缘检测 - 由于背景本身也有自己的边缘,单独使用边缘检测(例如 Canny 和 Sobel)似乎并没有得到好的结果。
- 通道过滤/阈值 - 背景和前景都具有相似的白色,因此我无法找到正确的阈值来过滤前景。
- 轮廓检测 - 由于背景本身有很多轮廓,仅使用最大的轮廓区域(通常用于去除背景)也不起作用。
我愿意使用计算机视觉或深度学习(Python)中的工具来解决这个特定问题。
概念
在这种情况下,微调您用来扩大和侵蚀从图像中检测到的精巧边缘的内核非常有用。这是一个示例,其中膨胀内核为 np.ones((4, 2))
,腐蚀内核为 np.ones((13, 7))
:
代码
import cv2
import numpy as np
def process(img):
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img_blur = cv2.GaussianBlur(img_gray, (3, 3), 2)
img_canny = cv2.Canny(img_blur, 50, 9)
img_dilate = cv2.dilate(img_canny, np.ones((4, 2)), iterations=11)
img_erode = cv2.erode(img_dilate, np.ones((13, 7)), iterations=4)
return cv2.bitwise_not(img_erode)
def get_contours(img):
contours, _ = cv2.findContours(process(img), cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
cnt = max(contours, key=cv2.contourArea)
cv2.drawContours(img, [cv2.convexHull(cnt)], -1, (0, 0, 255), 2)
img = cv2.imread("image2.png")
get_contours(img)
cv2.imshow("result", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
输出
提供的两张图片的输出:
图片 1:
图 2:
备注
请注意,处理后的图像 (二进制) 在 cv2.bitwise_not(img_erode)
处反转。观察两个图像的处理版本(由上面定义的process()
函数返回),反转:
已处理图像 1:
已处理图像 2:
工具
最后,如果您碰巧有其他图像在上面的程序上无法正常工作,您可以使用OpenCV Trackbars调整传递给下面程序的方法的值:
import cv2
import numpy as np
def process(img, b_k, b_s, c_t1, c_t2, k1, k2, k3, k4, iter1, iter2):
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
b_k = b_k // 2 * 2 + 1
img_blur = cv2.GaussianBlur(img_gray, (b_k, b_k), b_s)
img_canny = cv2.Canny(img_blur, c_t1, c_t2)
img_dilate = cv2.dilate(img_canny, np.ones((k1, k2)), iterations=iter1)
img_erode = cv2.erode(img_dilate, np.ones((k3, k4)), iterations=iter2)
return cv2.bitwise_not(img_erode)
d = {"Blur Kernel": (3, 50),
"Blur Sigma": (2, 30),
"Canny Threshold 1": (50, 500),
"Canny Threshold 2": (9, 500),
"Dilate Kernel1": (4, 50),
"Dilate Kernel2": (2, 50),
"Erode Kernel1": (13, 50),
"Erode Kernel2": (7, 50),
"Dilate Iterations": (11, 40),
"Erode Iterations": (4, 40)}
cv2.namedWindow("Track Bars")
for i in d:
cv2.createTrackbar(i, "Track Bars", *d[i], id)
img = cv2.imread("image1.png")
while True:
img_copy = img.copy()
processed = process(img, *(cv2.getTrackbarPos(i, "Track Bars") for i in d))
contours, _ = cv2.findContours(processed, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
if contours:
cnt = max(contours, key=cv2.contourArea)
cv2.drawContours(img_copy, [cv2.convexHull(cnt)], -1, (0, 0, 255), 2)
cv2.imshow("result", img_copy)
if cv2.waitKey(1) & 0xFF == ord("q"):
break
cv2.waitKey(0)
cv2.destroyAllWindows()
如果为您提供了所需的完美样本,您可以尝试使用 SIFT 或 SURF 算法。我以 'intact' 数据集中的一个为例。 SIFT 算法将尝试将样本中的特征与实际图像进行匹配。 从那里,您可以清理匹配并找到单应性(RANSAC 在这种情况下效果最好)来找到轮廓。
测试图片:
我在此处显示的代码是指在 Colab 中使用自定义参数执行的代码,您可以进一步调整。
import cv2
import numpy as np
from google.colab.patches import cv2_imshow
img = cv2.imread("reference_1.png", cv2.IMREAD_GRAYSCALE)
frame = cv2.imread("top.png", cv2.IMREAD_GRAYSCALE)
# if SIFT_create() gives problems, try downgrading opencv with
# pip uninstall opencv-python
# pip install opencv-contrib-python==3.4.2.17
sift = cv2.xfeatures2d.SIFT_create()
kp_image, desc_image = sift.detectAndCompute(img, None)
kp_frame, desc_frame = sift.detectAndCompute(frame, None)
index_params = dict(algorithm=0, trees=5)
search_params = dict()
flann = cv2.FlannBasedMatcher(index_params, search_params)
matches = flann.knnMatch(desc_image, desc_frame, k=2)
# clean the matches
good_points=[]
for m, n in matches:
if(m.distance < 0.6 * n.distance):
good_points.append(m)
query_pts = np.float32([kp_image[m.queryIdx].pt for m in good_points]).reshape(-1, 1, 2)
train_pts = np.float32([kp_frame[m.trainIdx].pt for m in good_points]).reshape(-1, 1, 2)
# find homography to find mask
matrix, mask = cv2.findHomography(query_pts, train_pts, cv2.RANSAC, 5.0)
matches_mask = mask.ravel().tolist()
h,w = img.shape
pts = np.float32([ [0,0],[0,h-1],[w-1,h-1],[w-1,0] ]).reshape(-1,1,2)
dst = cv2.perspectiveTransform(pts, matrix)
homography = cv2.polylines(frame, [np.int32(dst)], True, (255, 0, 0), 3)
cv2_imshow(homography)
您可以使用Rembg(删除图片背景的工具)。即使使用 pre-trained 权重,这也能很好地工作。我尝试了测试图像,这是我使用 Rembg 的结果
您可以使用 pip 下载 Rembg
pip install rembg
删除单个文件的背景
rembg i path/to/input.png path/to/output.png
删除文件夹中所有图像的背景
rembg p path/to/input path/to/output