羽毛剪裁边缘

时间:2019-05-03 11:51:56

标签: python opencv transparency crop

我正在尝试从图像中裁剪对象,然后将其粘贴到另一幅图像上。检查this answer中的方法,我已经成功地做到了。例如:

crop steps

代码(show_mask_applied.py):

import sys
from pathlib import Path
from helpers_cv2 import *
import cv2
import numpy

img_path = Path(sys.argv[1])

img      = cmyk_to_bgr(str(img_path))
threshed = threshold(img, 240, type=cv2.THRESH_BINARY_INV)
contours = find_contours(threshed)
mask     = mask_from_contours(img, contours)
mask     = dilate_mask(mask, 50)
crop     = cv2.bitwise_or(img, img, mask=mask)

bg      = cv2.imread("bg.jpg")
bg_mask = cv2.bitwise_not(mask)
bg_crop = cv2.bitwise_or(bg, bg, mask=bg_mask)

final   = cv2.bitwise_or(crop, bg_crop)

cv2.imshow("debug", final)

cv2.waitKey(0)
cv2.destroyAllWindows()

helpers_cv2.py

from pathlib import Path
import cv2
import numpy
from PIL import Image
from PIL import ImageCms
from PIL import ImageFile
ImageFile.LOAD_TRUNCATED_IMAGES = True

def cmyk_to_bgr(cmyk_img):
    img = Image.open(cmyk_img)
    if img.mode == "CMYK":
        img = ImageCms.profileToProfile(img, "Color Profiles\\USWebCoatedSWOP.icc", "Color Profiles\\sRGB_Color_Space_Profile.icm", outputMode="RGB")
    return cv2.cvtColor(numpy.array(img), cv2.COLOR_RGB2BGR)

def threshold(img, thresh=128, maxval=255, type=cv2.THRESH_BINARY):
    if len(img.shape) == 3:
        img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    threshed = cv2.threshold(img, thresh, maxval, type)[1]
    return threshed

def find_contours(img):
    kernel   = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (11,11))
    morphed  = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)
    contours = cv2.findContours(morphed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    return contours[-2]

def mask_from_contours(ref_img, contours):
    mask = numpy.zeros(ref_img.shape, numpy.uint8)
    mask = cv2.drawContours(mask, contours, -1, (255,255,255), -1)
    return cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)

def dilate_mask(mask, kernel_size=11):
    kernel  = numpy.ones((kernel_size,kernel_size), numpy.uint8)
    dilated = cv2.dilate(mask, kernel, iterations=1)
    return dilated

现在,我要使用羽化/光滑的边缘来代替锋利的边缘。例如(右图;在Photoshop中创建):

sharp vs feathered

我该怎么做?


可以在this repository上找到所有图像和代码。

2 个答案:

答案 0 :(得分:6)

您正在使用遮罩选择部分重叠图像。遮罩目前看起来像这样:

enter image description here

我们首先向该蒙版添加高斯模糊。

mask_blurred  = cv2.GaussianBlur(mask,(99,99),0)

我们明白了:

enter image description here

现在,剩下的任务是使用蒙版中的alpha值融合图像,而不是像现在一样将其用作逻辑运算符。

mask_blurred_3chan = cv2.cvtColor(mask_blurred, cv2.COLOR_GRAY2BGR).astype('float') / 255.
img = img.astype('float') / 255.
bg = bg.astype('float') / 255.
out  = bg * (1 - mask_blurred_3chan) + img * mask_blurred_3chan

以上代码段非常简单。首先,将蒙版转换为3通道图像(因为我们要对所有通道进行蒙版)。然后将图像转换为浮动图像,因为遮罩是在浮动点完成的。最后一行完成实际工作:对于每个像素,根据蒙版中的值混合bgimg图像。结果看起来像这样:

enter image description here

羽化的数量由高斯模糊中的内核大小控制。请注意,它必须是一个奇数。

此后,out(最终图像)仍处于浮点状态。可以使用以下命令将其转换回int

out = (out * 255).astype('uint8')

答案 1 :(得分:0)

尽管Paul92的答案绰绰有余,但我还是想将代码发布给以后的任何访客。

我正在裁切以消除某些产品照片中的白色背景。因此,主要目标是在保持产品完好无损的情况下摆脱白色。大多数产品照片在地面上都有阴影。它们要么是地面本身(褪色),要么是产品的阴影,或者两者都是。

虽然物体检测效果很好,但是这些阴影也算作物体的一部分。区分阴影和物体并不是真正必要的,但是会导致某些图像不是很理想。例如,检查图像(阴影)的左侧和底部。裁切/裁切明显可见,看起来也不是很好。

shadow cut

要解决这个问题,我想种植非矩形作物。使用口罩似乎可以很好地完成工作。下一个问题是使用羽化/模糊的边缘进行裁切,以便消除这些可见的阴影切口。使用help中的Paul92,我已经做到了。输出示例(注意缺少阴影切割,边缘更柔和):

softer edges

图像上的操作:

image operations

代码(show_mask_feathered.pyhelpers_cv2.py

import sys
from pathlib import Path
import cv2
import numpy
from helpers_cv2 import *

img_path = Path(sys.argv[1])

img      = cmyk_to_bgr(str(img_path))
threshed = threshold(img, 240, type=cv2.THRESH_BINARY_INV)
contours = find_contours(threshed)

dilation_length = 51
blur_length     = 51

mask         = mask_from_contours(img, contours)
mask_dilated = dilate_mask(mask, dilation_length)
mask_smooth  = smooth_mask(mask_dilated, odd(dilation_length * 1.5))
mask_blurred = cv2.GaussianBlur(mask_smooth, (blur_length, blur_length), 0)
mask_blurred = cv2.cvtColor(mask_blurred, cv2.COLOR_GRAY2BGR)

mask_threshed = threshold(mask_blurred, 1)
mask_contours = find_contours(mask_threshed)
mask_contour  = max_contour(mask_contours)

x, y, w, h   = cv2.boundingRect(mask_contour)

img_cropped  = img[y:y+h, x:x+w]
mask_cropped = mask_blurred[y:y+h, x:x+w]
background   = numpy.full(img_cropped.shape, (200,240,200), dtype=numpy.uint8)
output       = alpha_blend(background, img_cropped, mask_cropped)