如何裁剪或删除图像中的白色背景
How to crop or remove white background from an image
我正在尝试使用 OpenCV 和 Python 比较图像。
考虑这些图像:
两者都有一双相同的鞋子,设置为白色背景。唯一的区别是第一个的背景比第二个高。
我想知道如何以编程方式裁剪两者的白色背景,以便我只剩下那双鞋。
我必须补充一点,我无法手动裁剪背景。
This link 尽管它使用 PIL,但它对类似的问题非常适合我。请注意,它会生成一个矩形图像,由非白色的 top/right/bottom/left-most 像素界定。在您的情况下,它应该提供相同大小的相同图像。
我猜测代码可以很容易地调整为仅与 OpenCV 函数一起使用。
您在评论中的要求:The shoes are on a white background. I would like to completely get rid of the border; as in be left with a rectangular box with either a white or a transparent background, having the length and width of the shoes in the picture.
然后我裁剪目标区域的步骤:
- Convert to gray, and threshold
- Morph-op to remove noise
- Find the max-area contour
- Crop and save it
#!/usr/bin/python3
# Created by Silencer @ Whosebug
# 2018.01.23 14:41:42 CST
# 2018.01.23 18:17:42 CST
import cv2
import numpy as np
## (1) Convert to gray, and threshold
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
th, threshed = cv2.threshold(gray, 240, 255, cv2.THRESH_BINARY_INV)
## (2) Morph-op to remove noise
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (11,11))
morphed = cv2.morphologyEx(threshed, cv2.MORPH_CLOSE, kernel)
## (3) Find the max-area contour
cnts = cv2.findContours(morphed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2]
cnt = sorted(cnts, key=cv2.contourArea)[-1]
## (4) Crop and save it
x,y,w,h = cv2.boundingRect(cnt)
dst = img[y:y+h, x:x+w]
cv2.imwrite("001.png", dst)
结果:
Kinght 的 效果很好。就我而言,我还有 CMYK 图像。当我裁剪它们时,我得到不正确的(鲜艳的颜色)输出。而且 OpenCV 似乎不支持 CMYK。所以我需要一种方法将 CMYK 图像转换为 RGB,然后用 OpenCV 打开它。我是这样处理的:
import cv2
import numpy
from PIL import Image
from PIL import ImageCms
# force opening truncated/corrupt image files
from PIL import ImageFile
ImageFile.LOAD_TRUNCATED_IMAGES = True
img = "shoes.jpg"
img = Image.open(img)
if img.mode == "CMYK":
# color profiles can be found at C:\Program Files (x86)\Common Files\Adobe\Color\Profiles\Recommended
img = ImageCms.profileToProfile(img, "USWebCoatedSWOP.icc", "sRGB_Color_Space_Profile.icm", outputMode="RGB")
# PIL image -> OpenCV image; see
img = cv2.cvtColor(numpy.array(img), cv2.COLOR_RGB2BGR)
## (1) Convert to gray, and threshold
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
th, threshed = cv2.threshold(gray, 240, 255, cv2.THRESH_BINARY_INV)
## (2) Morph-op to remove noise
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (11,11))
morphed = cv2.morphologyEx(threshed, cv2.MORPH_CLOSE, kernel)
## (3) Find the max-area contour
cnts = cv2.findContours(morphed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2]
cnt = sorted(cnts, key=cv2.contourArea)[-1]
## (4) Crop and save it
x,y,w,h = cv2.boundingRect(cnt)
dst = img[y:y+h, x:x+w]
# add border/padding around the cropped image
# dst = cv2.copyMakeBorder(dst, 10, 10, 10, 10, cv2.BORDER_CONSTANT, value=[255,255,255])
cv2.imshow("image", dst)
cv2.waitKey(0)
cv2.destroyAllWindows()
# create/write to file
# cv2.imwrite("001.png", dst)
我在 github 上找到了这个。
https://imagemagick.org/script/download.php
import pgmagick
def remove_background(image, background=None):
"""Returns a copy of `image` that only contains the parts that is distinct
from the background. If background is None, returns parts that are
distinct from white."""
if background is None:
background = pgmagick.Image(image.size(), 'white')
elif isinstance(background, pgmagick.Image):
blob = pgmagick.Blob()
background.write(blob)
background = pgmagick.Image(blob, image.size())
else:
background = pgmagick.Image(image.size(), background)
background.composite(image, 0, 0, pgmagick.CompositeOperator.DifferenceCompositeOp)
background.threshold(25)
blob = pgmagick.Blob()
image.write(blob)
image = pgmagick.Image(blob, image.size())
image.composite(background, 0, 0, pgmagick.CompositeOperator.CopyOpacityCompositeOp)
return image
虽然问题已经回答的很透彻了,但是我还是想分享一个简单的只依赖于numpy的版本:
import numpy as np
def remove_background(image, bg_color=255):
# assumes rgb image (w, h, c)
intensity_img = np.mean(image, axis=2)
# identify indices of non-background rows and columns, then look for min/max indices
non_bg_rows = np.nonzero(np.mean(intensity_img, axis=1) != bg_color)
non_bg_cols = np.nonzero(np.mean(intensity_img, axis=0) != bg_color)
r1, r2 = np.min(non_bg_rows), np.max(non_bg_rows)
c1, c2 = np.min(non_bg_cols), np.max(non_bg_cols)
# return cropped image
return image[r1:r2+1, c1:c2+1, :]
通过 PIL,您可以将白色背景转换为透明背景:
from PIL import Image
def convertImage():
img = Image.open("hi.png")
img = img.convert("RGBA")
datas = img.getdata()
newData = []
for item in datas:
if item[0] == 255 and item[1] == 255 and item[2] == 255:
newData.append((255, 255, 255, 0))
else:
newData.append(item)
img.putdata(newData)
img.save("./New.png", "PNG")
print("Successful")
convertImage()
这是输出示例:
我正在尝试使用 OpenCV 和 Python 比较图像。
考虑这些图像:
两者都有一双相同的鞋子,设置为白色背景。唯一的区别是第一个的背景比第二个高。
我想知道如何以编程方式裁剪两者的白色背景,以便我只剩下那双鞋。
我必须补充一点,我无法手动裁剪背景。
This link 尽管它使用 PIL,但它对类似的问题非常适合我。请注意,它会生成一个矩形图像,由非白色的 top/right/bottom/left-most 像素界定。在您的情况下,它应该提供相同大小的相同图像。
我猜测代码可以很容易地调整为仅与 OpenCV 函数一起使用。
您在评论中的要求:The shoes are on a white background. I would like to completely get rid of the border; as in be left with a rectangular box with either a white or a transparent background, having the length and width of the shoes in the picture.
然后我裁剪目标区域的步骤:
- Convert to gray, and threshold
- Morph-op to remove noise
- Find the max-area contour
- Crop and save it
#!/usr/bin/python3
# Created by Silencer @ Whosebug
# 2018.01.23 14:41:42 CST
# 2018.01.23 18:17:42 CST
import cv2
import numpy as np
## (1) Convert to gray, and threshold
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
th, threshed = cv2.threshold(gray, 240, 255, cv2.THRESH_BINARY_INV)
## (2) Morph-op to remove noise
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (11,11))
morphed = cv2.morphologyEx(threshed, cv2.MORPH_CLOSE, kernel)
## (3) Find the max-area contour
cnts = cv2.findContours(morphed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2]
cnt = sorted(cnts, key=cv2.contourArea)[-1]
## (4) Crop and save it
x,y,w,h = cv2.boundingRect(cnt)
dst = img[y:y+h, x:x+w]
cv2.imwrite("001.png", dst)
结果:
Kinght 的
import cv2
import numpy
from PIL import Image
from PIL import ImageCms
# force opening truncated/corrupt image files
from PIL import ImageFile
ImageFile.LOAD_TRUNCATED_IMAGES = True
img = "shoes.jpg"
img = Image.open(img)
if img.mode == "CMYK":
# color profiles can be found at C:\Program Files (x86)\Common Files\Adobe\Color\Profiles\Recommended
img = ImageCms.profileToProfile(img, "USWebCoatedSWOP.icc", "sRGB_Color_Space_Profile.icm", outputMode="RGB")
# PIL image -> OpenCV image; see
img = cv2.cvtColor(numpy.array(img), cv2.COLOR_RGB2BGR)
## (1) Convert to gray, and threshold
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
th, threshed = cv2.threshold(gray, 240, 255, cv2.THRESH_BINARY_INV)
## (2) Morph-op to remove noise
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (11,11))
morphed = cv2.morphologyEx(threshed, cv2.MORPH_CLOSE, kernel)
## (3) Find the max-area contour
cnts = cv2.findContours(morphed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2]
cnt = sorted(cnts, key=cv2.contourArea)[-1]
## (4) Crop and save it
x,y,w,h = cv2.boundingRect(cnt)
dst = img[y:y+h, x:x+w]
# add border/padding around the cropped image
# dst = cv2.copyMakeBorder(dst, 10, 10, 10, 10, cv2.BORDER_CONSTANT, value=[255,255,255])
cv2.imshow("image", dst)
cv2.waitKey(0)
cv2.destroyAllWindows()
# create/write to file
# cv2.imwrite("001.png", dst)
我在 github 上找到了这个。
https://imagemagick.org/script/download.php
import pgmagick
def remove_background(image, background=None):
"""Returns a copy of `image` that only contains the parts that is distinct
from the background. If background is None, returns parts that are
distinct from white."""
if background is None:
background = pgmagick.Image(image.size(), 'white')
elif isinstance(background, pgmagick.Image):
blob = pgmagick.Blob()
background.write(blob)
background = pgmagick.Image(blob, image.size())
else:
background = pgmagick.Image(image.size(), background)
background.composite(image, 0, 0, pgmagick.CompositeOperator.DifferenceCompositeOp)
background.threshold(25)
blob = pgmagick.Blob()
image.write(blob)
image = pgmagick.Image(blob, image.size())
image.composite(background, 0, 0, pgmagick.CompositeOperator.CopyOpacityCompositeOp)
return image
虽然问题已经回答的很透彻了,但是我还是想分享一个简单的只依赖于numpy的版本:
import numpy as np
def remove_background(image, bg_color=255):
# assumes rgb image (w, h, c)
intensity_img = np.mean(image, axis=2)
# identify indices of non-background rows and columns, then look for min/max indices
non_bg_rows = np.nonzero(np.mean(intensity_img, axis=1) != bg_color)
non_bg_cols = np.nonzero(np.mean(intensity_img, axis=0) != bg_color)
r1, r2 = np.min(non_bg_rows), np.max(non_bg_rows)
c1, c2 = np.min(non_bg_cols), np.max(non_bg_cols)
# return cropped image
return image[r1:r2+1, c1:c2+1, :]
通过 PIL,您可以将白色背景转换为透明背景:
from PIL import Image
def convertImage():
img = Image.open("hi.png")
img = img.convert("RGBA")
datas = img.getdata()
newData = []
for item in datas:
if item[0] == 255 and item[1] == 255 and item[2] == 255:
newData.append((255, 255, 255, 0))
else:
newData.append(item)
img.putdata(newData)
img.save("./New.png", "PNG")
print("Successful")
convertImage()
这是输出示例: