Haskell domain coloring with the 'hip' library using the HSI color space

45 Views Asked by At

The Hakell library hip allows to do 2D images processing and it provides several color spaces, such as the well known RGB space.

I'm using this hip library to do some 2D images.

For example, I did this image with the RGB color space using a custom color map (and an elliptic function)

enter image description here

Now I would like to use another color map with the HSI color space. I don't understand what happens: each time my color map calculates a H (hue) value, I get an error such as "*** Exception: HSI pixel is not properly scaled, Hue: 120.0". However the doc found on Google claims that the H value ranges from 0 to 360. So why a value such as 120.0 is not valid?

1

There are 1 best solutions below

6
Stéphane Laurent On

The doc of hip itself says nothing about the range but we can look at the code:

Here is the relevant source code. However not everyting is defined, such as getRGB, but after many trials-errors, I managed to understand.

It is clear from this code that the range of H is not [0, 360], but rather [0, 1] or [0, 2pi] (we will see).

instance Elevator e => ToRGB HSI e where
  toPixelRGB (fmap toDouble -> PixelHSI h' s i) = getRGB (h'*2*pi) where
    !is = i*s
    !second = i - is
    getFirst !a !b = i + is*cos a/cos b
    {-# INLINE getFirst #-}
    getThird !v1 !v2 = i + 2*is + v1 - v2
    {-# INLINE getThird #-}
    getRGB h
      | h < 0      = error ("HSI pixel is not properly scaled, Hue: "++show h')
      | h < 2*pi/3 = let !r = getFirst h (pi/3 - h)
                         !b = second
                         !g = getThird b r
                     in PixelRGB r g b  
      | h < 4*pi/3 = let !g = getFirst (h - 2*pi/3) (h + pi)
                         !r = second
                         !b = getThird r g
                     in PixelRGB r g b
      | h < 2*pi   = let !b = getFirst (h - 4*pi/3) (2*pi - pi/3 - h)
                         !g = second
                         !r = getThird g b
                     in PixelRGB r g b
      | otherwise  = error ("HSI pixel is not properly scaled, Hue: "++show h')
    {-# INLINE getRGB #-}
  {-# INLINE toPixelRGB #-}

Based on this code, I made some progress. First, the range is indeed not [0, 360], contrary to the Google claims.

Here is my color map:

colorMap2 :: Complex Double -> (Double, Double, Double)
colorMap2 z =
    let a = phase z
    in 
    let arg = if a < 0 
        then a + pi 
        else a
    in
    let h = min (arg/2/pi) 0.9999999
    in 
    let w = 2 * pi * log(1 + abs arg)
    in 
    let s = sqrt((1.0 + sin w ) / 2.0)
    in 
    let l = (1.0 + cos w ) / 2.0
    in 
    (h, s, l)

Observe this line: let h = min (arg/2/pi) 0.9999999. The rest is the stuff for my color map. The H value (denoted by h here) ranges from 0 to almost 1. I don't take exactly 1 because I encountered some NaN when the value was exactly 1.

Moreover, my experiments show that S and I must lie between 0 and 1 I think, not between 0 and 100 as claimed on Google.

Finally it works, but this color map is not pretty for this example:

enter image description here