简体   繁体   English

如何从图像中裁剪或删除白色背景

[英]How to crop or remove white background from an image

I am trying to compare images using OpenCV and Python.我正在尝试使用 OpenCV 和 Python 比较图像。

Consider these images:考虑这些图像:


图像 900 x 726


图像 900 x 675


Both feature an identical pair of shoes, set to a white background.两者都有一双相同的鞋子,设置为白色背景。 The only difference being that the first has a taller background than the second.唯一的区别是第一个的背景比第二个高。

I want to know how to programmatically crop the white backgrounds of both so that I'm left with only the pair of shoes.我想知道如何以编程方式裁剪两者的白色背景,这样我就只剩下那双鞋了。

I must add that it won't be possible for me to manually crop the backgrounds.我必须补充一点,我无法手动裁剪背景。

You requirement in the comment: 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.您在评论中要求: 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. 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.

Then my steps to crop the target regions:然后我裁剪目标区域的步骤:

  1. Convert to gray, and threshold转换为灰度和阈值
  2. Morph-op to remove noise Morph-op 消除噪音
  3. Find the max-area contour找到最大面积轮廓
  4. Crop and save it裁剪并保存
#!/usr/bin/python3
# Created by Silencer @ Stackoverflow 
# 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)

Result:结果:

Kinght's solution works well. Kinght 的解决方案运行良好。 In my case, I also have CMYK images.就我而言,我也有 CMYK 图像。 When I crop them, I get incorrect (vivid colors) output.当我裁剪它们时,我得到了不正确的(鲜艳的颜色)输出。 And it seems OpenCV doesn't support CMYK.而且似乎 OpenCV 不支持 CMYK。 So I needed a way to convert CMYK images to RGB, and then open it with OpenCV.所以我需要一种方法将 CMYK 图像转换为 RGB,然后用 OpenCV 打开它。 This is how I handled it:我是这样处理的:

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 https://stackoverflow.com/q/14134892/2202732
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)

by PIL you can convert white background to transparent:通过 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()

and here is the output sample:这是输出样本: 在此处输入图像描述

This link worked perfectly for me for a similar problem, although it uses PIL.这个链接对我来说非常适合解决类似的问题,尽管它使用了 PIL。 Note that it will result in a rectangular image, bounded by the top/right/bottom/left-most pixels that are not white.请注意,这将产生一个矩形图像,由非白色的顶部/右侧/底部/最左侧像素界定。 In your case, it should give identical images with the same size.在您的情况下,它应该提供相同大小的相同图像。

I am guessing the code could be easily adapted to work with OpenCV functions only.我猜代码可以很容易地调整为仅适用于 OpenCV 函数。

I found this on github.我在github上找到了这个。

https://imagemagick.org/script/download.php 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

Although the question has been answered thoroughly already, I would like to share a simple version that relies on numpy only:虽然这个问题已经得到了彻底的回答,但我想分享一个仅依赖于 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, :]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM