42

For example, using ExampleData[{"TestImage", "Girl2"}]:

Mathematica graphics

what's a general way to make the background transparent? I've tried various combinations of EdgeDetect and Threshold and ImageAdd but can't figure it out. Thank you.

Philip Maymin
  • 1,163
  • 8
  • 12
  • Maybe something useful here: http://stackoverflow.com/questions/8041703/remove-white-background-from-an-image-and-make-it-transparent –  Aug 07 '12 at 20:40
  • @ChrisDegnen: that is a very useful post in general but simply running it on the girl image instead of the one given doesn't work because it's not a single colored background. Plus, parts of the girl herself have the same color as the background... –  Aug 07 '12 at 22:26
  • @Verde: I'd be just as happy with a white background, it's not about the transparency issues of that post per se, just how to remove the background without affecting the head of the girl. Thanks. –  Aug 07 '12 at 22:28
  • @Verbeia -- I, uh, um... don't know. That's certainly where I was aiming for. I guess I don't really know the difference between the two sites. Do you suggest I take it down from here and put it up there? PS that is great code at the link, but it also doesn't work immediately on the girl, even moving the locator around. –  Aug 08 '12 at 11:01
  • 1
    You can flag to have it migrated if you want. Your question is about programming so it is on topic on StackOverflow, but really any Mathematica-related question is better off on the Mathematica specific site now. – Verbeia Aug 08 '12 at 12:03
  • Allow me to remind you of three things we usually do here: 1) As you receive help, try to give it too, by answering questions in your area of expertise. 2) Read the FAQs! 3) When you see good Q&A, vote them up by clicking the gray triangles, because the credibility of the system is based on the reputation gained by users sharing their knowledge. ALSO, remember to accept the answer, if any, that solves your problem, by clicking the checkmark sign` – Dr. belisarius Aug 20 '12 at 06:42
  • This should now be do-able in Mathematica v10 with RemoveBackground http://reference.wolfram.com/language/ref/RemoveBackground.html?q=RemoveBackground – dr.blochwave Jul 23 '14 at 18:37

4 Answers4

41

============>> 2-liner <<============

Let's import the image and crop boundary artifacts:

img = ImageCrop[ExampleData[{"TestImage", "Girl2"}], {200, 200}]

enter image description here

We have quite uniform background. So with Mathematica functions such as RegionBinarize where you can specify background test pixel, your task is about just a few lines:

ImageAdd[img,ColorNegate@Blur[Erosion[Dilation[DeleteSmallComponents[
      ColorNegate@RegionBinarize[img, {{10, 190}}, 0.1]], 3], 3], 2]]

enter image description here

Below is just a deeper insight on how it all owrks on pixel value level.

============>> Deeper Insight <<============

Now lets get the ImageData and see how the RGB pixel values are distributed on a diagonal across the image:

data = ImageData[img];
ListPlot[Transpose[data[[#, #]] & /@ Range[200]]]

enter image description here

Because background is located above approximately 0 to 60, i'll pick number 30 (1st tuning parameter) as a test pixel and plot EuclideanDistance of all other pixels from that test pixel:

ListPlot[EuclideanDistance[#, data[[30, 30]]] & /@ (data[[#, #]] & 
/@Range[200]), Filling -> 0]

enter image description here

Based on this we can say that roughly background is everything below threshold 0.1 (2nd tuning parameter) and replace all those pixels with white {1,1,1} pixel:

ndata = data /. {x_, y_, z_} /; 
    EuclideanDistance[{x, y, z}, data[[30, 30]]] < .1 -> {1, 1, 1};

Based on that create mask and to avoid pixelation at the edge Blur it at some radius value (3rd tuning parameter)

nimg = Blur[ColorNegate@Erosion[DeleteSmallComponents[
     Dilation[ColorNegate@Binarize[Image[ndata], .99], 3]], 3], 5]

enter image description here

And finally mask the original image by adding the thresholded version of it:

ImageAdd[img, nimg]

enter image description here

In the same way you could add an alpha channel to background making it transparent. To play with the whole procedure make an app to tune in the 3 parameters described above. Note the little piece of background in the left bottom corner is removed now by tuning background pixel parameter.

Manipulate[ImageAdd[img, Blur[ColorNegate@Erosion[DeleteSmallComponents[
      Dilation[ColorNegate@Binarize[Image[data /. {x_, y_, z_} /; 
             EuclideanDistance[{x, y, z}, data[[px, px]]] < th -> {1, 
             1, 1}], .99], 3]], 3], bl]]
 , {{px, 30, "background"}, 1, 60, 1, Appearance -> "Labeled", ImageSize -> Small}
 , {{th, .1, "threshold"}, .01, .2, Appearance -> "Labeled", ImageSize -> Small}
 , {{bl, 2, "blur"}, 0, 10, 1, Appearance -> "Labeled", ImageSize -> Small}]

enter image description here

Additional parameters to add could be Binarize function ranges. Binarize[image,{Subscript[t, 1],Subscript[t, 2]}] creates a binary image by replacing all values in the range Subscript[t, 1] through Subscript[t, 2] with 1 and others with 0. ~ from Documentation.

Vitaliy Kaurov
  • 73,078
  • 9
  • 204
  • 355
  • There is a piece of background under your right armpit :) – Dr. belisarius Aug 15 '12 at 09:38
  • @Verde removed now by playing with background pixel parameter ;-) – Vitaliy Kaurov Aug 15 '12 at 09:46
  • you can make your two liner a one liner easily... – rm -rf Aug 15 '12 at 19:34
  • Great answer, Vitaliy - I'm working through it and appreciating and learning from it. But a question: what do you mean by "Because background is located above approximately 0 to 60"? – cormullion Aug 16 '12 at 17:41
  • @cormullion In the first ListPlot flat portion (background) of brown, red, blue graphs spans approximately numbers 0 to 60 on horizontal axes of the ListPlot. Those are pixel indexes of the image. Does this help? – Vitaliy Kaurov Aug 16 '12 at 17:54
  • @VitaliyKaurov Thanks - I think they're the x/y coordinates of the pixels running down the diagonal from top left to bottom right. Don't know why you said background is 'above' 60... – cormullion Aug 16 '12 at 18:13
  • @cormullion maybe more like 0 to 55. These are numbers on x-coordinate axes. Above 0 to 55 the plot lines are flat - it means it is background. – Vitaliy Kaurov Aug 16 '12 at 18:17
  • @VitaliyKaurov Thanks! I see it clearer now... :) – cormullion Aug 16 '12 at 19:19
20
f[im_, n_] := Nest[Erosion[Dilation[#, 2], 2] &, im, n];
(*Get your image and Crop Nuisances*)
i1 = ImageTake[ExampleData[{"TestImage", "Girl2"}], 2 {10, -10}, {10, -10}];
(*Get edges and extend edges to border*)
b3 = Erosion[ColorNegate@f[EdgeDetect[i1, 1], 10], 1];
(*identify area& Get background pixels*)
b5 = Position[#, SortBy[Tally@Flatten@#, -#[[2]] &][[1, 1]]] &@ MorphologicalComponents@b3;
(*make a mask*)
b6 = Array[{0, 0, 0} &, Reverse@ImageDimensions@i1];
(b6[[##]] = {1, 1, 1}) & @@@ b5;
(*ready*)
b7 = ColorNegate@ImageMultiply[ColorNegate@Blur@Dilation[Image@b6, 1], ColorNegate@i1]

Mathematica graphics

Mathematica graphics

Edit

It works quite well mostly

Mathematica graphics Mathematica graphics

The problems are due to the background not being connected or not being the biggest morphological component.

Edit Using RM's suggestion

Mathematica graphics

Not perfect, but better. (blondes still can't make it unharmed)

f[im_, n_] := Nest[Erosion[Dilation[#, 1], 1] &, im, n];
(*Get your image and Crop Nuisances*)
Table[(
  i1 = ImageTake[j, 2 {10, -10}, {10, -10}];
  (*Get edges and extend edges to border*)
  b3 = Erosion[ColorNegate@f[EdgeDetect[i1, 1], 1], 1];
  b4 = (MorphologicalComponents@b3);
  (*identify area& Get background pixels*)
  (*select the component with most pixels at the border*)
  bckg = SortBy[
     Tally@Flatten@Join[#[[{1, -1}]], (Transpose@#)[[{1, -1}]]] &@
      b4, -#[[2]] &][[1, 1]];
  (*Its mean color*)
  meanBkg = Mean@Extract[ImageData[i1], Position[#, bckg] &@b4];
  (*identify other bckgnd components*)
  bckgs = 
   Flatten@MapIndexed[
       If[#1 < .08, #2, Sequence @@ {}] &, #] &@(EuclideanDistance[
        meanBkg, #] & /@ (Mean@
          Extract[ImageData[i1], Position[b4, #]] & /@ 
        Range@Max@Flatten@b4));
  (*replace by main bckgnd color*)
  b4 = b4 /. (Rule[#, bckg] & /@ bckgs);
  (*get all bckgnds together*)
  b5 = Position[b4, bckg];
  (*make a mask*)
  b6 = Array[{0, 0, 0} &, Reverse@ImageDimensions@i1];
  (b6[[##]] = {1, 1, 1}) & @@@ b5;
  (*ready*)
  b7 = ColorNegate@
    ImageMultiply[ColorNegate@Blur@Dilation[Image@b6, 1], 
     ColorNegate@i1]), {j, h}]
Dr. belisarius
  • 115,881
  • 13
  • 203
  • 453
  • cool graphic - helps a lot to see how it's working! – cormullion Aug 16 '12 at 17:42
  • What is b4? Not defined. – Philip Maymin Aug 20 '12 at 05:06
  • @PhilipMaymin Sorry, it was a leftover of a previous version. Corrected. Thanks! – Dr. belisarius Aug 20 '12 at 05:11
  • your algorithm doesn't like blondes and golden hair? Looks like one more kicked into the CW bin... soon anyway :) – rm -rf Aug 20 '12 at 05:18
  • @R.M The algo doesn't do Evaluate[IQ] ... yet. Just in case it is not clear, I'm making two hypothesis: 1) The background is singly connected and 2) It is the biggest morphological component in the picture. Regrettably, some photographers don't appreciate the benefits of science. – Dr. belisarius Aug 20 '12 at 05:24
  • You could probably also add 3) it is also connected to the border pixels (if multiple components are present, pick the one with the most pixels in common with the border). BTW, I rejected your silly tag wikis today. That felt... good! :P – rm -rf Aug 20 '12 at 05:35
  • Hehe, should I expect a dead horse's head on my bed? Or worse... NKS? – rm -rf Aug 20 '12 at 05:55
  • That looks much better. It does look like it's going to be really hard. BTW, do you remember this one? There it was explicitly white bg only, but in any case, anything worth stealing from there? If I remember, the same issues were seen there too, in some of Szabolcs' additional pics – rm -rf Aug 20 '12 at 07:54
  • @R.M The whole thing there was based in the (almost) homogeneous background. (see the TargetColor ->{...} in my answer). Here, I am trying with some success to address a more general case almost without human intervention (which is why this answer is more tangled than Vitaliy's) - Note the inhomogeneous background for example at the first image in the set of six – Dr. belisarius Aug 20 '12 at 08:01
  • 1
    aha! I hadn't actually noticed that the figures in the first column have backgrounds with a radial lighting effect. Pretty impressive. – rm -rf Aug 20 '12 at 08:45
4

The other two excellent answers to this question were created before the arrival of version 10.0 and the RemoveBackground function. Out of curiosity I tried this new function on the test image, to see if it delivered on its promise.

g = ImagePad[ExampleData[{"TestImage", "Girl2"}], -10]

original

The function is set up to work without settings, presumably for use with images like this, with no obvious problem areas:

RemoveBackground[g]

remove background 1

but the result is a bit disappointing. An obvious option is try next is "Uniform", not her uniform, but an option which identifies "a region of almost uniform colour". By default, all settings define the background to be removed:

RemoveBackground[g, "Uniform"]

remove background 2

but it looks the same. Perhaps "Uniform" was the default after all... Let's try to specify a color. Gray looks a close match:

RemoveBackground[g, Gray]

remove background 3

but that's worse, if anything. How about "Dark" - "a darker background":

RemoveBackground[g, "Dark"]

remove background 4

which isn't very good, finding the hair and bow tie. I suppose "Bright" will do no better?

RemoveBackground[g, "Bright"]

remove background 5

Perhaps trying some markers to indicate where the background is will be the solution?

RemoveBackground[g, {"Background" , {{35,178},{35,128}}}]

remove background 6

It's not really helping.

There are a few intriguing options left to try. How about "Blurred", which finds in-focus and out-of-focus areas? Perhaps the background is blurred...

RemoveBackground[g, {"Blurred", 10}]

remove background 7

That's perhaps the best so far.

I don't know if I'm doing something wrong, or if this TestImage isn't a suitable test image. Or perhaps there are teething problems in version 10.0.0.0?

cormullion
  • 24,243
  • 4
  • 64
  • 133
4

RemoveBackground works well only when the foreground does not have the same (or very similar) color as the background. Here is an alternative approach using GrowCutComponents. Markers were generated using Mask Tool from Image Toolbar.

g = ImagePad[ExampleData[{"TestImage", "Girl2"}], -10];

Remove background using GrowCutComponents

Piotr Wendykier
  • 1,281
  • 9
  • 10