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 lous
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]
Brief explanation of how it works:
Choose your favourite binariaztion method that produces relatively precise sharp edges
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.
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).
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.
I'm going to speak generically, not specifically in reference to Mathematica. I have no idea whether these operations are difficult or trivial.
The first step is to estimate an alpha (transparency) level for the pixels on the edge of the image. Right now you're using a strict threshold, so the alpha is either 0% totally transparent or 100% totally opaque. You should define a range between the total white of the background and colors that are indisputably part of the image, and set an appropriate proportion - if it's closer in color to the background it's low alpha, and if it's closer to the darker cutoff then it's a high alpha. After that you can make adjustments based on the surrounding alpha values - the more a pixel is surrounded by transparency, the more likely it is to be transparent itself.
Once you have alpha values you need to do a reverse blend to get the proper color. When an image is displayed over a background it is blended 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 the unknown, so we reverse the formula: fc = (c - bc*(1-a))/a
. When a=0
the formula calls for a divide by zero, but the color doesn't matter anyway so just use black or white.
Just playing around as a beginner - it's 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]