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

时间:2011-11-07 19:48:17

标签: image image-processing wolfram-mathematica masking

我们正在尝试在Mathematica中执行以下操作:
RMagick remove white background from image and make it transparent

但实际照片看起来很糟糕(就像图像周围有光环一样)。

这是我们到目前为止所尝试的内容:

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

任何摆脱光环的想法?调整像LevelPenalty这样的东西,我只能以丢失一些图像为代价来消除光环。

编辑:所以我可以比较赏金的解决方案,请像上面那样构建你的解决方案,即一个名为unground的自包含函数,它可以获取图像并返回具有透明背景的图像。非常感谢大家!

9 个答案:

答案 0 :(得分:48)

此功能实现了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. 选择您最喜欢的binariaztion方法,以产生相对精确的锐边

  2. 将其应用于放大的图像,然后将获得的mask缩小到原始大小。这给了我们抗锯齿。大部分工作已经完成。

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

  4. 现在我们估算了alpha通道,以便我们可以进行反向混合。

  5. 步骤3& 4并没有那么多改善,但差别是显而易见的。

答案 1 :(得分:44)

也许,取决于您需要的边缘质量:

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

Click to enlarge

修改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

答案 2 :(得分:22)

我将会说一般,而不是特别提到Mathematica。我不知道这些操作是困难还是微不足道。

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

一旦有了alpha值,就需要进行反向混合以获得正确的颜色。当图像在背景上显示时,使用公式c = bc*(1-a)+fc*a根据alpha值进行混合,其中bc是背景颜色,fc是前景色。在您的情况下,背景为白色(255,255,255),前景色为未知,因此我们颠倒公式:fc = (c - bc*(1-a))/a。当a=0公式要求除以零时,但颜色无关紧要,所以只需使用黑色或白色。

答案 3 :(得分:11)

这是尝试实施Mark Ransom的方法,在belisarius的面具生成的帮助下:

找到对象的边界:

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

请注意一些边缘是否有白色模糊?将其与第一张图像中的红色轮廓进行比较。我们需要更好的边缘检测器。增加侵蚀量有助于模糊,但是其他侧面变得太透明,因此边缘掩模的宽度存在折衷。不过,考虑到本身没有模糊操作,这是非常好的。

在各种图像上运行算法以测试其稳健性,看看它是多么自动化将是有益的。

答案 4 :(得分:10)

只是作为一个初学者来玩 - 令人惊讶的是有多少工具可用。

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

答案 5 :(得分:9)

我对图像处理完全不熟悉,但这是我在使用版本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

答案 6 :(得分:6)

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

答案 7 :(得分:5)

您可以采取的步骤:

  • 扩大面具
  • 模糊
  • 使用遮罩,通过与白色的​​距离设置透明度
  • 使用遮罩,调整饱和度,使之前更白的颜色更饱和。

答案 8 :(得分:3)

使用相同RGB颜色的像素和透明通道上的Sigmoid渐变替换任何“几乎接近白色”的像素。您可以应用从实体到透明的线性过渡,但Sinusoid或Sigmoid或Tanh看起来更自然,取决于您正在寻找的边缘的锐度,它们会迅速从介质移动到实心或透明,但不是逐步/二进制方式,这就是你现在所拥有的。

这样想:

假设R,G,B各自为0.0-1.0,那么我们将白色表示为单个数字,因为R + G + B = 1.0 * 3 = 3.0。

取出一点颜色,使它有点“灰白色”,但只需要花一点时间就可以获得更多的颜色,而不是任何颜色。假设您允许任何一个通道减少10%:1.0 * .10 = .1,现在将这种损失扩散到所有三个通道并将其绑定在0和1之间,如果它小于.1,那么( loss = 0.9)=> 0和(loss = 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}]

你在这方面唯一的危险(或好处?)是,这并不关心实际上属于照片的白人。它会删除所有白人。因此,如果你有一辆白色汽车的照片,它最终会有透明的补丁。但是从你的例子来看,这似乎是一种理想的效果。