TomSmeets/FractalArt

adjusting "color temperature" of generated images

Opened this issue ยท 5 comments

hi! I've used FractalArt to make some desktop background images and love them during the daytime. at nighttime, though, I find them quite bright and usually switch to a different image.

can you imagine an easy way to programmatically alter the temperature of the image? perhaps another command line option which is fed down to the pixel generator (not sure how all that works).

I would be up for PR'ing such a change if you could point me in the right direction (and provided it wouldn't require too much reworking of things!).

thanks ๐Ÿ™‚

I also have a similar problem with the brightness of the images. I use ImageMagick's "convert" utility to change the brightness/saturation/contrast like so:
convert -brightness-contrast -40x-10 wallpaper.bmp wallpaperT.bmp
where -40 =brightness% and -10 =contrast%

There's probably better parameters to play with. Perhaps is it possible to create a shell script to do the conversion and set the wallpaper depending on the time of day?

I also have a similar problem with the brightness of the images. I use ImageMagick's "convert" utility to change the brightness/saturation/contrast

@livmackintosh thanks, that is clever, I have just been swapping to darker wallpapers in the evening. will give this a try ๐Ÿ‘

There's probably better parameters to play with.

I had a look in the code and it seems like the right place to add a "color temp tweak" might be here - perhaps between the composition of return and clampColor.

if we added a color temp cmdline arg, that could propagate info down to there.

I know very little about color temp (and computer images/color in general) but did find this blogpost which describes a transformation on RBG colors for a given color temp. an image is shown at the end of the post where one half is the original and the other half has a "color temp correction" applied. code is not given (I don't think) for the color temp correction, but perhaps it's simple to computer an RGB for a given color temp and then combine that with the original RBG of a given pixel?

Perhaps is it possible to create a shell script to do the conversion and set the wallpaper depending on the time of day?

good idea, I bet it'd be fairly straightforward to make a daemon with e.g. systemd to generate new wallpapers every hour or so, and feed in a color temperature parameter based on local clock time.

suggested implementation approach

(suggestion for me ๐Ÿ™‚)

  • add cmdline argument for color temperature. perform basic validation on it - ensure it is in range of the lookup table.
  • use a lookup table + interpolation to compute approx RGB value corresponding to the input color temp.
    • see here for inspiration - interpolation occurs inline at bottom of file.
  • perform "color blending" between the original color values and the color-temp-determined color values.
    • see here for inspiration - tho I don't understand the 0.5 coefficient added to the first color. due to order of arithmetic operations, the 0.5 should be added to the computed product, rather than to the ratio. so maybe it's some semi-negligible fudge factor.
  • apply the above-described function here, perhaps before clampColor
  • avoid any gotchas with differing representations for RGB values (0-255 vs 0-1). I don't expect this to be a problem for the above approach.

Hi, nice to see that you are using this program!

Note that the initial color is chosen randomly here:

FractalArt/src/Main.hs

Lines 108 to 111 in 45bb2ef

startColor = do
hue <- uniformR (0, 360) gen
--sat <- uniform gen
return $ hsv hue 0.6 1

The call hsv hue 0.6 1.0 creates an initial color with the random hue and a fixed saturation of 0.6 and value of 1.0 (HSV cylinder)

And then for each neighbor pixel the color is tweaked a bit by randomly changing the red, green and blue values:

FractalArt/src/Main.hs

Lines 124 to 139 in 45bb2ef

nextColor :: PrimMonad m => Grid (PrimState m) -> Size -> Gen (PrimState m) -> Position -> m Color
nextColor grid size gen pos = do
c <- colors
i <- uniformR (0, length c - 1) gen
m <- modColor (mkRange $ length c) (c !! i)
return . clampColor $ m
where
colors = liftM (map snd . filter isValid) . mapM (MV.read grid . toIndex size) . filter (isInside size) $ ringAt pos 1
mkRange l = 0.006 * 4 / fromIntegral l
modColor range (r, g, b) = do
mr <- uniformR (-range, range) gen
mg <- uniformR (-range, range) gen
mb <- uniformR (-range, range) gen
return (r + mr, g + mg, b + mb)

Changing the color temperature directly in the nextColor function would not work that well I expect, because each color depends on the previous colors.

I also tried changing the saturation and value in the initial color value, but that did not look very good:
combined-small

So I agree that we should do a second pass to change the color temperature, and make it configurable with a command line parameter. But I also like livmackintosh's solution, which is more flexible.

hi @TomSmeets, thanks for the reply and helpful information!

doing a second pass makes a lot of sense and sounds more reasonable than what I suggested above ๐Ÿ™‚

perhaps it is as simple as replacing bitmap here with something like adjustColorTemp inputColorTemp ratio <$> bitmap.

I will look into this more soon, and see about implementing a solution.