Remove the white background from the image and make it transparent

We are trying to do the following in Mathematica:
RMagick removes white background from image and makes it transparent

But with actual photos, it ends up looking lousy (for example, with a halo around the image).

Here is what we have tried so far:

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

Here is an example of what this does.

Original Image:

original image

The image with a white background is replaced without a background (or a pink background for demonstration here):

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

Any ideas for getting rid of this halo? Fine-tuning things like LevelPenalty, I can only get a halo to lose part of the image.

EDIT: So I can compare generosity solutions, please structure your solution as described above, namely an autonomous function called unground-something that takes an image and returns an image with a transparent background. Thank you all very much!

+81
image image-processing wolfram-mathematica masking
Nov 07 2018-11-11T00:
source share
9 answers

Perhaps, depending on the quality of the edge, you need to:

 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

Edit

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

Edit 2

Just to get an idea of โ€‹โ€‹the extent of the flaws of the halo and background in the image:

 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

+44
Nov 08 2018-11-12T00:
source share

This function implements the reverse mixture described by Mark Ransom for an additional small but visible improvement:

 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}] ] 

This is a background removal feature. The threshold parameter is used for initial binarization of the image, minSizeCorrection used to set the size limit for small junk e-mail components that should be removed after binarization.

 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] ] 

Function Testing:

 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] 

Example

A brief explanation of how this works:

  • Choose your favorite binariaztion method, which creates relatively precise sharp edges.

  • Apply it to the scaled image, then reduce the resulting mask size to its original size. This gives us smoothing. Most of the work is done.

  • For a slight improvement, blend the image against the background using the brightness of its negative value as alpha, then blend the resulting image over the original in a thin area around the edges ( edgemask ) to reduce the visibility of white pixels around the edges. The alpha channel corresponding to these operations is calculated (somewhat cryptic expression ImageMultiply/Add ).

  • Now we have an alpha channel estimate, so we can do the reverse mixture.

Steps 3 and 4 do not improve this, but the difference is visible.

+48
Nov 11 2018-11-11T00:
source share

I am going to speak in general, and not specifically with respect to Mathematica. I do not know if these operations are complicated or trivial.

The first step is to evaluate the alpha (transparency) level for the pixels on the edge of the image. You are using a strict threshold right now, so alpha is either completely transparent or 100% completely opaque. You must determine the range between the total whiteness of the background and the color, which, undoubtedly, is part of the image, and set the appropriate proportion - if it is closer in color to the background, its low alpha, and if closer to the dark cutoff then high alpha. After that, you can make adjustments based on the surrounding alpha values โ€‹โ€‹- the more the pixel is surrounded by transparency, the more likely it is to be transparent.

When you have alpha values, you need to reverse the mixture to get the right color. When the image is displayed on the background, it is mixed according to the alpha value using the formula c = bc*(1-a)+fc*a , where bc is the background color and fc is the foreground color. In your case, the background is white (255,255,255), and the foreground color is unknown, so we will cancel the formula: fc = (c - bc*(1-a))/a . When a=0 , the formula requires division by zero, but the color does not matter, so just use black or white.

+22
Nov 07 2018-11-11T00:
source share

Try applying the Mark Ransom approach with some help with the belisarius mask generation:

Find the border of the object:

 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

Set the alpha values:

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

Reverse color mix:

 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

Please note that some of the edges have white fluff? Compare this to the red outline in the first image. We need a better edge detector. Increasing the amount of erosion helps with fluff, but then the other sides become too transparent, so there is a compromise on the width of the edge mask. This is pretty good, though, given that there is no blur operation in itself.

It would be instructive to run the algorithm on different images to check its reliability, to see how automatic it is.

+11
Nov 11 2018-11-11T00:
source share

Just playing as a beginner is amazing how many tools are available.

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

+10
Nov 10 '11 at 16:44
source share

I'm completely new to image processing, but here's what I get after some games with the new morphological image processing features version 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

+9
Nov 08 2018-11-11T00:
source share

I recommend using Photoshop for this and save as PNG.

+6
Nov 07 2018-11-11T00:
source share

Possible steps you could take:

  • expand mask
  • blur it
  • using a mask, set the transparency by distance from white
  • using a mask, adjust the saturation so that earlier whiter colors are more saturated.
+5
Nov 07 '11 at 19:56
source share

Just replace any pixel that is โ€œnearly whiteโ€ with a pixel of the same RGB color and a Sigmoid gradient on the transparency channel. You can apply a linear transition from solid to transparent, but the sine or sigmoid or Tanh look more natural, depending on the sharpness of the edge you are looking for, they quickly move from medium to solid or transparent, but not in a step / binary way. which you have now.

Think of it this way:

Let's say that R, G, B are 0.0-1.0, and then denote white as one number as R + G + B = 1.0 * 3 = 3.0.

Taking a little of each color makes it a little โ€œoff-whiteโ€, but taking a little of just 3 makes it a lot more than a little from any. Say you allow a 10% reduction on any one channel: 1.0 * .10 = .1. Now spread this loss across all three and tie it between 0 and 1 for the alpha channel if it's less than .1, so (loss = 0.9) => 0 and (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]; 

For reference:

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

The only danger (or benefit?) That you have in this is that it does not bother the whites, who are actually part of the photograph. He removes all the whites. So if you have a photo of a white car, in the end it will have transparent patches. But from your example, this seems like the desired effect.

+3
Nov 07 2018-11-11T00:
source share



All Articles