Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Remove White Background from an Image and Make It Transparent

We're trying to do the following in Mathematica - RMagick remove white background from image and make it transparent.

But with actual photos it ends up looking lousy (like having a halo around the image).

Here's what we've tried so far:

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

Here's an example of what that does.

Original image:

original image

Image with the white background replaced with no background (or, for demonstration purposes here, a pink background):

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

Any ideas for getting rid of that halo? Tweaking things like LevelPenalty, I can only get the halo to go away at the expense of losing some of the image.

EDIT: So I can compare solutions for the bounty, please structure your solution like above, namely a self-contained function named unground-something that takes an image and returns an image with transparent background.

like image 690
dreeves Avatar asked Nov 07 '11 19:11

dreeves


People also ask

How do you make a white background transparent in a picture?

You can create a transparent area in most pictures. Select the picture that you want to create transparent areas in. Click Picture Tools > Recolor > Set Transparent Color. In the picture, click the color you want to make transparent.

Can you get rid of a white background on an image?

Select the picture that you want to remove the background from. Under Picture Tools, on the Format tab, in the Adjust group, select Remove Background.


2 Answers

This function implements the reverse blend 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 the background removal function. The threshold parameter is used for the initial binarization of the image, the minSizeCorrection is for tweaking the size limit of small junk components to 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]   ] 

Testing the function:

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

Brief explanation of how it works:

  1. Choose your favourite binarization method that produces relatively precise sharp edges

  2. Apply it to an up-scaled image, then downscale the obtained mask to the original size. This gives us antialiasing. Most of the work is done.

  3. For a small improvement, blend the image onto the background using the brightness of its negative as alpha, then blend the obtained image over the original in a thin region around the edges (edgemask) to reduce the visibility of white pixels on the edges. The alpha channel corresponding to these operations is calculated (the somewhat cryptic ImageMultiply/Add expression).

  4. Now we have an estimate of the alpha channel so we can do a reverse blend.

Steps 3 & 4 don't improve that much, but the difference is visible.

like image 133
16 revs Avatar answered Oct 06 '22 07:10

16 revs


Perhaps, depending on the edge quality you need:

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 halo and background imperfections 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

like image 27
Dr. belisarius Avatar answered Oct 06 '22 07:10

Dr. belisarius