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

dre*_*ves 81 wolfram-mathematica image image-processing masking

我们尝试在Mathematica中执行以下操作:
RMagick从图像中删除白色背景并使其透明

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

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

unground0[img_] := With[{mask = ChanVeseBinarize[img, TargetColor->{1.,1.,1.}]},
  Rasterize[SetAlphaChannel[img, ImageApply[1-#&, mask]], Background->None]]]
Run Code Online (Sandbox Code Playgroud)

这是一个例子.

原始图片:

原始图像

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

透明背景的图像 - 实际上是粉红色的背景,使光环问题变得明显

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

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

Sza*_*lcs 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}]
  ]
Run Code Online (Sandbox Code Playgroud)

这是背景删除功能.该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]
  ]
Run Code Online (Sandbox Code Playgroud)

测试功能:

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]
Run Code Online (Sandbox Code Playgroud)

样品

它的工作原理简要说明:

  1. 选择您喜欢的binariaztion方法,生成相对精确的锐边

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

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

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

步骤3和4没有那么多改善,但差异是显而易见的.

  • 案件的阴影仍然在第二个数字中作为底部的灰色带... (2认同)
  • 哦,伙计,我希望有一个PHP版本 (2认同)

Dr.*_*ius 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]
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

编辑

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}}]
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

点击放大

编辑2

只是为了了解图像中光晕和背景缺陷的程度:

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

在此输入图像描述

ColorNegate@ImageAdd[EntropyFilter[img, 1] // ImageAdjust, ColorNegate@img]
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述


Mar*_*som 22

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

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

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

  • 很好的答案.Alpha估计实际上是一个完整的研究领域,例如http://ai.stanford.edu/~ruzon/alpha/ (3认同)
  • 同意,很好的答案; 谢谢马克!对于赏金(当stackoverflow允许我添加一个)虽然我计划与任何完全实现的解决方案看起来最好.到目前为止belisarius,我在想. (2认同)

JxB*_*JxB 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]
Run Code Online (Sandbox Code Playgroud)

数字边缘

设置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];
Run Code Online (Sandbox Code Playgroud)

反色混合:

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]
Run Code Online (Sandbox Code Playgroud)

粉红色的背景

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

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


cor*_*ion 10

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

b = ColorNegate[
    GaussianFilter[MorphologicalBinarize[i, {0.96, 0.999}], 6]];
c = SetAlphaChannel[i, b];
Show[Graphics[Rectangle[], Background -> Orange, 
     PlotRangePadding -> None], c]
Run Code Online (Sandbox Code Playgroud)


Ale*_*kov 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]]
Run Code Online (Sandbox Code Playgroud)

图片

  • 我认为dreeves试图摆脱边缘处的锯齿状线条. (3认同)

ang*_*ent 6

我建议使用Photoshop进行此操作并保存为PNG.

  • 也许您可以发布由PhotoShop脚本生成的图像版本(无需人工干预:-)作为参考 - 我们知道我们必须要击败... (7认同)
  • 好点,但是photoshop用来做这件事的算法是什么?(当然我们想要自动化这一点,而不是在photoshop中为每张图片点击魔棒.) (5认同)
  • 有一个原因,Adobe可以为他们的软件收取500个smakeroos ;-). (5认同)
  • 顺便说一句,我认为这是一个有用的事情要指出(我很容易成为一个Mathematica书呆子的大人物,我可能不会发生Photoshop!).事实证明它甚至可以在photoshop中编写脚本,所以这甚至可能是最好的答案,如果Photoshop正在做一些非常聪明的东西,不能用一个小的mathematica程序复制. (3认同)

Mr.*_*ard 5

您可以采取的步骤:

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