Images -- is it possible to change selected colors?

Thanks for great presentation of Images.jl at JuliaCon 2023. I haven’t really used the package (I’m not doing much work on images), but I see that I can import raw files from several camera brands. Nice.

I’m curious about the following: is it possible to selectively change some color to another color? This can be done in, say, Adobe Lightroom. To illustrate my question, I had the following image created using Bing Image Creator, with command “grumpy professor with yellow bowtie drinking tequila in Santa Fe adobe bar in picasso style”.

Suppose I now am very happy with the picture (AI generated with some random seed, so impossible to exactly recreate). However, I regret that I specified “yellow bowtie” and wish I instead had written “orange bowtie”.

Question: Is it possible to specify that I want to change the yellow color shades in the image to shades of orange?

1 Like

You can play with this:

using TestImages, Plots

img = testimage("mandrill")
plot(img)

# Turn red nose yellow:
img[0.9 .≤ red.(img) .≤ 1] .= RGB(1, 1, 0)
plot(img)

# Turn yellow nose orange:
img[0.95 .≤ red.(img) .≤ 1 .&& 0.95 .≤ green.(img) .≤ 1] .= RGB(1, 165/256, 0)
plot(img)

PS:
In other examples you might need to constrain more using all 3 color channels, here it was intentionally made simpler.

2 Likes

Will check tomorrow. What I really need is to change a range of colors which are (e.g.) yellowish into a range of colors that are (e.g.) orangish.

Should this be done in an hsv (hsb) representation?

I think you can define a range of colors that are yellowish in the same way as above by including the blue channel as well:
(0.95 .≤ red.(img) .≤ 1) .&& (0.95 .≤ green.(img) .≤ 1) .&& (0 .≤ blue.(img) .≤ 0.05)

Better use the @. macro to simplify the dots:

@. img[(0.95 ≤ red(img) ≤ 1) && (0.95 ≤ green(img) ≤ 1) && (0 ≤ blue(img) ≤ 0.05)] = RGB(1, 165/256, 0)

If selecting pixels based on color alone does not give you what you want, consider starting with a segmented image (e.g., ImageSegmentation) and then algorithmically change the color. E.g.,

function more_orange(col::AbstractRGB)
    r, g, b = red(col), green(col), blue(col)
    r *= 1.2
    if r > 1
        r, g, b = r/r, g/g, b/b
    end
    return oftype(col, RGB(r, g, b))
end

and then use more_orange on the pixels of the segment corresponding to the bowtie.

(Untested)

2 Likes

OK – some progress, but no solution yet…

bowtie = convert.(HSL,copy(img[203:247,170:252]))

leads to

Seems like bowtie colors have hue value between 55 and 65 or so…:

julia > idx_yellow = 55 .< hue.(bowtie) .< 65
5×83 BitMatrix:
 0  0  0  1  0  1  0  0  0  0  0  0  0  …  0  0  0  0  0  0  0  0  0  0  1  0
 0  0  1  1  1  1  1  0  0  0  0  0  0     0  0  0  0  1  1  1  1  1  1  1  1
 0  0  1  1  1  1  1  1  1  1  1  0  0     1  1  1  1  1  1  1  1  1  1  1  0
 0  0  1  1  1  1  1  1  1  1  1  0  0     1  1  1  1  1  1  1  1  1  1  1  0
 0  0  1  1  1  1  1  1  1  1  1  1  1     1  1  1  1  1  1  1  1  1  1  1  0
 0  0  1  1  1  1  1  1  1  1  1  1  1  …  1  1  1  1  1  1  1  1  1  1  1  0
 0  0  1  1  1  1  1  1  1  1  1  1  1     1  1  1  1  1  1  1  1  1  1  1  0
 0  0  1  1  1  1  1  1  1  1  1  1  1     1  1  1  1  1  1  1  1  1  1  1  0
 0  0  1  1  1  1  1  1  1  1  1  1  1     1  1  1  1  1  1  1  1  1  1  1  0
 0  0  1  1  1  1  1  1  1  1  1  1  1     1  1  1  1  1  1  1  1  1  1  0  0
 ⋮              ⋮              ⋮        ⋱              ⋮              ⋮     
 0  0  0  0  0  0  1  1  1  1  1  1  1     1  1  1  1  1  0  0  0  0  0  0  0
 0  0  0  0  0  0  1  1  1  1  1  1  1     1  1  1  1  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  1  1  1  1  1  1     1  1  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  1  1  1  1  1  1     1  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  1  1  1  1  1  1  …  1  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  1  1  1  1  1  1     1  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  1  1  1  1  1     0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  1  0  0  0  0  0  0  0     0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  1  0  0  0  0  0     0  0  0  0  0  0  0  0  0  0  0  0

I think :orange has a hue value of around 38-40.

Question: how can I change the hue part of cells given by idx_bowtie from their current value to value ca. 40?

The copy is redundant because img[rngs...] copies already (use view or @view if you want a view instead).

how can I change the hue part of cells given by idx_bowtie from their current value to value ca. 40?

The more_orange function above is a prototype, but if you’re handling the decision-making externally, we can simplify it further:

more_orange(col::HSV) = HSV(40, col.s, col.v)

bowtie_new = [idx_yellow[i] ? more_orange(bowtie[i]) : bowtie[i] for i in CartesianIndices(bowtie)]

(untested)

1 Like

OK… now it seems to work:

bowtie = convert.(HSV,img[203:247,170:252])
idx_yellow = 55 .< hue.(bowtie) .< 65

new_color(pixel::HSV,new_hue) = HSV(new_hue,pixel.s,pixel.v)
bowtie[idx_yellow] .= new_color.(bowtie[idx_yellow],40)

converts the above bowtie image to the following:

1 Like