56
$\begingroup$

Consider the following image:

Red ball in a non-red environment

How can I change all red colors in this image into (for example) blue.

$\endgroup$

4 Answers 4

44
$\begingroup$

Here's a version using Manipulate with a Locator to pick the colour to replace. There is also a tolerance control which determines how wide a range of hues to replace.

i=Import["https://i.sstatic.net/Qr7Tx.jpg"];

{h,s,b}=ColorSeparate[i,"HSB"];

colourchange[c_,from_,tol_,to_]:=Module[
{offset=Mod[c-from+0.5,1]-0.5},
If[Abs[offset]>tol,c,to]];

Manipulate[
ColorCombine[
{ImageApply[colourchange[#,ImageValue[h,pos],tol,ColorConvert[to,Hue][[1]]]&,h],s,b},
"HSB"],
{{to,Blue,"Change to"},Blue},
{{tol,-0.01,"Tolerance"},-0.01,0.5},
{{pos,{100,50}},Locator}]

enter image description here

$\endgroup$
4
  • 1
    $\begingroup$ You could also add a polygon defined by other locators, to define the mask of impact... $\endgroup$
    – P. Fonseca
    Commented Apr 27, 2012 at 10:11
  • 2
    $\begingroup$ Or use another locator as a brush $\endgroup$ Commented Apr 27, 2012 at 10:48
  • 4
    $\begingroup$ ...and I see a full image-processing graphical suit a la Photoshop implemented in Mathematica coming up soon... $\endgroup$ Commented Apr 27, 2012 at 13:25
  • 3
    $\begingroup$ @Simon Woods: nice approach! I like that one but still: see the two people on the left hand side of the ball? The went blue too. How one avoid that? $\endgroup$
    – John
    Commented Apr 27, 2012 at 18:44
59
$\begingroup$

I wanted to change only the color of the ball, leaving all other red objects untouched:

getReds[x_Image] := First@ColorSeparate[x, "Hue"]
isolateSphere[x_Image] := SelectComponents[Binarize[getReds[x], .9], Large]
makeMask[x_Image] := Image@Graphics[ Disk @@ (1 /. 
                        ComponentMeasurements[isolateSphere[x], {"Centroid","BoundingDiskRadius"}]), 
                        {PlotRange -> Thread[{1, #}], ImageSize -> #} &@ImageDimensions@x]
getAreaToChange[x_Image] := ImageMultiply[i, ColorNegate@makeMask[x]]
shiftColors[x_Image] := Image[ImageData[getAreaToChange[x]] /. 
                                                      p: {r_, g_, b_} /; r > .3 :> RotateLeft[p, 1]]
finishIt[x_Image] :=  ImageAdd[ImageMultiply[x, makeMask[x]], ColorConvert[shiftColors[x], "RGB"]]

{#, getReds@#, isolateSphere@#, makeMask@#, getAreaToChange@#, shiftColors@#, finishIt@#} &
                                                       @Import["https://i.sstatic.net/Qr7Tx.jpg"]

Mathematica graphics

Comparing side to side:

Mathematica graphics

$\endgroup$
13
  • $\begingroup$ Nice. What should one change to change the from and to colors? Or it only works for red and blue? $\endgroup$
    – Rojo
    Commented Apr 26, 2012 at 18:30
  • $\begingroup$ +1 Nice. What would you have done if there were other instances of red in the picture that you did not want to alter? $\endgroup$
    – DavidC
    Commented Apr 26, 2012 at 18:51
  • 2
    $\begingroup$ @Rojo In fact Red is the most difficult case, since it is at the beginning and at the end of the Hue[] table. Try Graphics[Raster[Table[{h, s, 1}, {s, 0, 1, .01}, {h, 0, 1, .01}], ColorFunction -> Hue]] $\endgroup$ Commented Apr 26, 2012 at 19:19
  • 6
    $\begingroup$ @DavidCarraher That happens usually in image processing. Things like "reddish" and "the big thingie in the middle" are not as easy to program as CylindricalDecomposition[] :) $\endgroup$ Commented Apr 26, 2012 at 21:28
  • 2
    $\begingroup$ Congrats on your (soon to be awarded) populist badge! :) $\endgroup$
    – rm -rf
    Commented Apr 30, 2012 at 2:30
35
$\begingroup$

A one-liner:

i = Import["https://i.sstatic.net/Qr7Tx.jpg"];

Image[ImageData[i] /. {r_, g_, b_} /; r > g && r > b -> {b, g, r}]

enter image description here

$\endgroup$
1
  • 2
    $\begingroup$ ImageApply[RotateLeft, i] have a same effect. :) $\endgroup$
    – yode
    Commented Jun 28, 2016 at 8:59
4
$\begingroup$

First Method

If you just want to get different color of your ball,but don't want to name the color.I will recommend you this solution,which makes your life easier.In the meantime,it will change other place's color,not just the ball,like CSP's answer

img = Import["https://i.sstatic.net/Qr7Tx.jpg"];
Select[ColorCombine /@ Tuples[ColorSeparate[img], {3}], 
 ImageMeasurements[ColorDistance[ColorConvert[#, "Grayscale"], #], 
    "Total"] > 8000 &]

You get 20 different color ball by a line code.


Second Method

If you just want to change your ball's color with a specified color and want maintain other place,like Dr. belisarius' answer,I make a custom function for you to do this

ChangeColor[img_, color_] := 
 Module[{mask = 
    Dilation[
     DeleteSmallComponents[
      ChanVeseBinarize[
       KarhunenLoeveDecomposition[
         ColorCombine /@ Permutations[ColorSeparate[img]]][[1, 2]]]], 
     9]}, ImageAdd[ImageSubtract[img, ImageMultiply[img, mask]], 
   ColorConvert[
    ColorCombine[
     Prepend[Rest[
       ColorSeparate[ColorConvert[ImageMultiply[img, mask], "HSB"]]], 
      First[ColorSeparate[
        ImageMultiply[
         ConstantImage[color, ImageDimensions[img], 
          ColorSpace -> "HSB"], mask]]]], "HSB"], "RGB"]]]

Usage:

ChangeColor[img, #] & /@ RandomColor[5]

$\endgroup$
3
  • $\begingroup$ It seems that the background change in the first solution, so I prefer the second one $\endgroup$
    – Wjx
    Commented Jun 28, 2016 at 9:07
  • $\begingroup$ @Wjx Thanks. :) $\endgroup$
    – yode
    Commented Jun 28, 2016 at 9:10
  • 1
    $\begingroup$ ChanVeseBinarize[] looks to be the key component here. $\endgroup$ Commented Jun 28, 2016 at 13:54

Not the answer you're looking for? Browse other questions tagged or ask your own question.