羽化裁剪边缘
Feather cropped edges
我正在尝试从一张图片中裁剪一个对象,然后将其粘贴到另一张图片上。检查 this answer 中的方法,我已经成功地做到了。例如:
代码(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()
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
现在,我想裁剪 feathered/smooth 边缘,而不是锐利的边缘。例如(右图;在 Photoshop 中创建):
我该怎么做?
所有图片和代码都可以在this repository.
找到
您正在对叠加图像的 select 部分使用蒙版。面具目前看起来像这样:
我们先给这个遮罩添加高斯模糊。
mask_blurred = cv2.GaussianBlur(mask,(99,99),0)
我们做到这一点:
现在,剩下的任务是使用蒙版中的 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 通道图像(因为我们要屏蔽所有通道)。然后将图像转换为浮点数,因为遮罩是在浮点数中完成的。最后一行执行实际工作:对于每个像素,根据掩码中的值混合 bg
和 img
图像。结果如下所示:
羽化量由高斯模糊中内核的大小控制。注意必须是奇数。
在此之后,out
(最终图像)仍然是浮点数。它可以转换回 int 使用:
out = (out * 255).astype('uint8')
虽然 Paul92 的回答已经足够了,但我还是想 post 我的代码供以后的访客使用。
我进行这种裁剪是为了去掉一些产品照片中的白色背景。因此,主要目标是去除蛋白,同时保持产品完好无损。大多数产品照片在地面上都有阴影。它们要么是地面本身(褪色),要么是产品的阴影,或者两者兼而有之。
虽然物体检测工作正常,但这些阴影也算作物体的一部分。将阴影与物体区分开来并不是真正必要的,但它会导致一些图像不是很理想。例如,检查图像的左侧和底部(阴影)。 cut/crop 明显可见,看起来不太好看。
为了解决这个问题,我想进行非矩形裁剪。使用面具似乎可以很好地完成这项工作。下一个问题是用 feathered/blurred 边缘进行裁剪,这样我就可以摆脱这些可见的阴影切割。使用 of Paul92,我已经设法做到了。示例输出(注意缺少阴影切割,边缘更柔和):
对图像的操作:
代码(show_mask_feathered.py, helpers_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)
我正在尝试从一张图片中裁剪一个对象,然后将其粘贴到另一张图片上。检查 this answer 中的方法,我已经成功地做到了。例如:
代码(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()
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
现在,我想裁剪 feathered/smooth 边缘,而不是锐利的边缘。例如(右图;在 Photoshop 中创建):
我该怎么做?
所有图片和代码都可以在this repository.
找到您正在对叠加图像的 select 部分使用蒙版。面具目前看起来像这样:
我们先给这个遮罩添加高斯模糊。
mask_blurred = cv2.GaussianBlur(mask,(99,99),0)
我们做到这一点:
现在,剩下的任务是使用蒙版中的 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 通道图像(因为我们要屏蔽所有通道)。然后将图像转换为浮点数,因为遮罩是在浮点数中完成的。最后一行执行实际工作:对于每个像素,根据掩码中的值混合 bg
和 img
图像。结果如下所示:
羽化量由高斯模糊中内核的大小控制。注意必须是奇数。
在此之后,out
(最终图像)仍然是浮点数。它可以转换回 int 使用:
out = (out * 255).astype('uint8')
虽然 Paul92 的回答已经足够了,但我还是想 post 我的代码供以后的访客使用。
我进行这种裁剪是为了去掉一些产品照片中的白色背景。因此,主要目标是去除蛋白,同时保持产品完好无损。大多数产品照片在地面上都有阴影。它们要么是地面本身(褪色),要么是产品的阴影,或者两者兼而有之。
虽然物体检测工作正常,但这些阴影也算作物体的一部分。将阴影与物体区分开来并不是真正必要的,但它会导致一些图像不是很理想。例如,检查图像的左侧和底部(阴影)。 cut/crop 明显可见,看起来不太好看。
为了解决这个问题,我想进行非矩形裁剪。使用面具似乎可以很好地完成这项工作。下一个问题是用 feathered/blurred 边缘进行裁剪,这样我就可以摆脱这些可见的阴影切割。使用
对图像的操作:
代码(show_mask_feathered.py, helpers_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)