从图像中移除白色背景并使其透明

我们试图在 Mathematica 做以下事情。

但是如果是真实的照片,最后看起来会很糟糕(就像照片周围有一个光环)。

以下是我们迄今为止所做的尝试:

unground0[img_] := With[{mask = ChanVeseBinarize[img, TargetColor->{1.,1.,1.}]},
Rasterize[SetAlphaChannel[img, ImageApply[1-#&, mask]], Background->None]]]

这里有个例子。

原图:

original image

用白色背景替换为无背景(或者,为了演示的目的,用粉色背景)的图像:

image with transparent background -- actually a pink background here, to make the halo problem obvious

有办法摆脱那个光环吗?调整像“水平惩罚”这样的东西,我只能让光环消失,代价是丢失一些图像。

编辑: 所以我可以比较赏金的解决方案,请像上面一样构造你的解决方案,即一个自包含的函数命名为 unground-某事,采取一个图像和返回一个透明背景的图像。

21561 次浏览

我建议使用 Photoshop,并保存为 PNG。

你可以采取的步骤:

  • 扩张面罩
  • 模糊了
  • 使用蒙版,设置透明度从白色的距离
  • 使用蒙版,调整饱和度,使以前较多的白色更加饱和。

我只是泛泛地说,不是特指 Mathematica。我不知道这些操作是困难的还是琐碎的。

第一步是估计图像边缘像素的 alpha (透明度)级别。现在您使用的是一个严格的阈值,所以 alpha 要么是0% 完全透明,要么是100% 完全不透明。你应该定义一个范围,在背景的总白色和无可争议的图像的一部分颜色之间,并设置一个适当的比例-如果它的颜色更接近背景,它是低阿尔法,如果它更接近深色的截止值,那么它是一个高阿尔法。之后,你可以根据周围的 alpha 值进行调整——一个像素被透明度包围的越多,它本身就越有可能是透明的。

一旦你有了 alpha 值,你需要做一个反向混合,以获得适当的颜色。当一幅图像显示在背景上时,它是根据使用公式 c = bc*(1-a)+fc*a的 alpha 值进行混合的,其中 bc是背景色,fc是前景色。在您的例子中,背景是白色的(255,255,255) ,前景颜色是未知的,所以我们反转公式: fc = (c - bc*(1-a))/a。当 a=0的公式要求除以零,但颜色并不重要,所以只要使用黑色或白色。

只要替换任何像素是“几乎接近白色”与相同的 RGB 颜色的像素和西格莫德梯度的透明通道。你可以使用从固体到透明的线性转换,但是正弦曲线、西格莫德曲线或唐曲线看起来更自然,这取决于你所寻找的边缘的锐度,它们迅速地从介质转换为固体或透明,但不是以逐步或二进制的方式,这就是你现在所拥有的。

这样想:

假设 R,G,B 分别是0.0-1.0,然后将白色表示为单个数字 R + G + B = 1.0 * 3 = 3.0。

把每种颜色都去掉一点会让它看起来有点“灰白色”,但是把所有3种颜色都去掉一点,就会让它比任何一种颜色都去掉一点。假设你允许在任意一个通道上减少10% : 1.0 * 。10 = 。1,现在把这个损失分摊到所有三个通道上,并把它绑定在 α 通道的0和1之间,如果它小于。1,这样(损失 = 0.9) = > 0和(损失 = 1.0) = > 1:

threshold=.10;
maxLoss=1.0*threshold;
loss=3.0-(R+G+B);
alpha=If[loss>maxLoss,0,loss/maxLoss];
(* linear scaling is used above *)
(* or use 1/(1 + Exp[-10(loss - 0.5maxLoss)/maxLoss]) to set sigmoid alpha *)
(* Log decay: Log[maxLoss]/Log[loss]
(for loss and maxLoss <1, when using RGB 0-255, divide by 255 to use this one *)


setNewPixel[R,G,B,alpha];

参考资料:

maxLoss = .1;
Plot[{ 1/(1 + Exp[-10(loss - 0.5maxLoss)/maxLoss]),
Log[maxLoss]/Log[loss],
loss/maxLoss
}, {loss, 0, maxLoss}]

唯一的危险(或好处?)你在这里,是这并不关心白色的实际上是照片的一部分。它能去除所有的白色。因此,如果你有一个白色的汽车图片,它将最终有透明的补丁在它。但从你的例子来看,这似乎是一个理想的效果。

我对图像处理完全是个新手,但下面是我在使用了版本8的一些新的形态学图像处理功能后得到的结果:

mask = DeleteSmallComponents[
ColorNegate@
Image[MorphologicalComponents[ColorNegate@img, .062,
Method -> "Convex"], "Bit"], 10000];
Show[Graphics[Rectangle[], Background -> Red,
PlotRangePadding -> None], SetAlphaChannel[img, ColorNegate@mask]]

image

也许吧,这取决于你需要的边缘质量:

img = Import@"http://i.stack.imgur.com/k7E1F.png";
mask = ChanVeseBinarize[img, TargetColor -> {1., 1., 1.}, "LengthPenalty" -> 10]
mask1 = Blur[Erosion[ColorNegate[mask], 2], 5]
Rasterize[SetAlphaChannel[img, mask1], Background -> None]

enter image description here

剪辑

Stealing a bit from @Szabolcs

img2 = Import@"http://i.stack.imgur.com/k7E1F.png";
(*key point:scale up image to smooth the edges*)
img = ImageResize[img2, 4 ImageDimensions[img2]];
mask = ChanVeseBinarize[img, TargetColor -> {1., 1., 1.}, "LengthPenalty" -> 10];
mask1 = Blur[Erosion[ColorNegate[mask], 8], 10];
f[col_] := Rasterize[SetAlphaChannel[img, mask1], Background -> col,
ImageSize -> ImageDimensions@img2]
GraphicsGrid[\{\{f@Red, f@Blue, f@Green}}]

enter image description here

点击放大

编辑2

只是为了了解一下 光环的范围和图像中的背景缺陷:

img = Import@"http://i.stack.imgur.com/k7E1F.png";
Join[{img}, MapThread[Binarize, {ColorSeparate[img, "HSB"], {.01, .01, .99}}]]

enter image description here

ColorNegate@ImageAdd[EntropyFilter[img, 1] // ImageAdjust, ColorNegate@img]

enter image description here

只是作为一个初学者玩玩而已——有这么多可用的工具真是令人惊讶。

b = ColorNegate[
GaussianFilter[MorphologicalBinarize[i, {0.96, 0.999}], 6]];
c = SetAlphaChannel[i, b];
Show[Graphics[Rectangle[], Background -> Orange,
PlotRangePadding -> None], c]

下面是一个尝试实现马克 · 兰塞姆的方法,在贝利萨留斯面具一代的帮助下:

确定物体的边界:

img1 = SetAlphaChannel[img, 1];
erosionamount=2;
mb = ColorNegate@ChanVeseBinarize[img, TargetColor -> {1., 1., 1},
"LengthPenalty" -> 10];
edge = ImageSubtract[Dilation[mb, 2], Erosion[mb, erosionamount]];


ImageApply[{1, 0, 0} &, img, Masking ->edge]

figure edge

设置 alpha 值:

edgealpha = ImageMultiply[ImageFilter[(1 - Mean[Flatten[#]]^5) &,
ColorConvert[img, "GrayScale"], 2, Masking -> edge], edge];
imagealpha = ImageAdd[edgealpha, Erosion[mb, erosionamount]];
img2 = SetAlphaChannel[img, imagealpha];

反向混色:

img3 = ImageApply[Module[{c, \[Alpha], bc, fc},
bc = {1, 1, 1};
c = {#[[1]], #[[2]], #[[3]]};
\[Alpha] = #[[4]];
If[\[Alpha] > 0, Flatten[{(c - bc (1 - \[Alpha]))/\[Alpha], \[Alpha]}], {0., 0.,
0., 0}]] &, img2];


Show[img3, Background -> Pink]

pink background

注意到有些边缘有白色的绒毛了吗?与第一张图片中的红色轮廓进行比较。我们需要一个更好的边缘检测器。增加侵蚀量有助于模糊,但是其他边变得太透明,所以边缘掩模的宽度有一个折衷。不过还是很不错的考虑到没有侠影行动这回事。

在各种图像上运行该算法来测试其鲁棒性,看看它的自动化程度,将是有益的。

这个函数实现了 Mark Ransom 描述的反向混合,为了一个额外的小的但是可见的改进:

reverseBlend[img_Image, alpha_Image, bgcolor_] :=
With[
{c = ImageData[img],
a = ImageData[alpha] + 0.0001, (* this is to minimize ComplexInfinitys and considerably improve performance *)
bc = bgcolor},


ImageClip@
Image[Quiet[(c - bc (1 - a))/a, {Power::infy,
Infinity::indet}] /. {ComplexInfinity -> 0, Indeterminate -> 0}]
]

这是背景去除功能。threshold参数用于图像的初始二值化,minSizeCorrection参数用于调整二值化后要去除的小垃圾组件的尺寸限制。

removeWhiteBackground[img_, threshold_: 0.05, minSizeCorrection_: 1] :=
Module[
{dim, bigmask, mask, edgemask, alpha},
dim = ImageDimensions[img];
bigmask =
DeleteSmallComponents[
ColorNegate@
MorphologicalBinarize[ColorNegate@ImageResize[img, 4 dim], threshold],
Round[minSizeCorrection Times @@ dim/5]];
mask = ColorNegate@
ImageResize[ColorConvert[bigmask, "GrayScale"], dim];
edgemask =
ImageResize[
ImageAdjust@DistanceTransform@Dilation[EdgeDetect[bigmask, 2], 6],
dim];
alpha =
ImageAdd[
ImageSubtract[
ImageMultiply[ColorNegate@ColorConvert[img, "GrayScale"],
edgemask], ImageMultiply[mask, edgemask]], mask];
SetAlphaChannel[reverseBlend[img, alpha, 1], alpha]
]

测试功能:

img = Import["http://i.stack.imgur.com/k7E1F.png"];


background =
ImageCrop[
Import["http://cdn.zmescience.com/wp-content/uploads/2011/06/\
forest2.jpg"], ImageDimensions[img]];


result = removeWhiteBackground[img]


ImageCompose[background, result]
Rasterize[result, Background -> Red]
Rasterize[result, Background -> Black]

Sample

工作原理简介:

  1. 选择您最喜欢的二值化方法,产生相对精确的锐利的边缘

  2. 将其应用于高倍图像,然后将获得的 mask缩放到原始大小。这样我们就可以反走样了。大部分工作已经完成了。

  3. 对于一个小的改进,混合图像到背景上使用其底片的亮度作为阿尔法,然后混合获得的图像在原始的边缘周围的一个薄区域(edgemask) ,以减少边缘上的白色像素的可见性。计算与这些操作相对应的 alpha 通道(有点神秘的 ImageMultiply/Add表达式)。

  4. 现在我们已经估算出了阿尔法通道,所以我们可以进行反向混合。

步骤3和步骤4并没有提高那么多,但是差异是显而易见的。