thomasp85 / farver

High Performance Colourspace Manipulation in R
https://farver.data-imaginist.com
Other
128 stars 14 forks source link

Negative rgb values #3

Closed EmilHvitfeldt closed 4 years ago

EmilHvitfeldt commented 6 years ago

Sometimes going from rgb to yxy and back gives negative values for rgb.

library(farver)

spectrum1 <- t(col2rgb(rainbow(10)))

spectrum2 <- farver::convert_colour(spectrum1, "rgb", "yxy")
spectrum3 <- farver::convert_colour(spectrum2, "yxy", "rgb")

plot(as.numeric(spectrum1 - spectrum3))

spectrum1
#>       red green blue
#>  [1,] 255     0    0
#>  [2,] 255   153    0
#>  [3,] 204   255    0
#>  [4,]  51   255    0
#>  [5,]   0   255  102
#>  [6,]   0   255  255
#>  [7,]   0   102  255
#>  [8,]  51     0  255
#>  [9,] 204     0  255
#> [10,] 255     0  153
spectrum3
#>               [,1]          [,2]          [,3]
#>  [1,] 2.550000e+02  4.342722e-04 -5.629177e-05
#>  [2,] 2.550000e+02  1.530000e+02 -9.327647e-05
#>  [3,] 2.040000e+02  2.550000e+02 -1.500950e-04
#>  [4,] 5.100003e+01  2.550000e+02 -1.179680e-04
#>  [5,] 1.552075e-04  2.550000e+02  1.020000e+02
#>  [6,] 2.982680e-04  2.550000e+02  2.550000e+02
#>  [7,] 1.826908e-04  1.020000e+02  2.550000e+02
#>  [8,] 5.100004e+01 -4.230843e-05  2.550000e+02
#>  [9,] 2.040000e+02  2.055405e-04  2.550000e+02
#> [10,] 2.550000e+02  4.162154e-04  1.530000e+02

It is fairly expected to get a little bit of noise, but it become troublesome when the noise leads to invalid numbers. Is this something you think farver should handle internally or should be adjusted for by the individual user?

thomasp85 commented 6 years ago

Yes, farver should def handle this, and I thought it did. Will get fixed

EmilHvitfeldt commented 6 years ago

Okay cool!

furthermore after playing around a little bit more, it seems that some of the conversions it also seems to work regardless of the input being valid

library(farver)

spectrum1 <- t(col2rgb(rainbow(3))) - 1000

spectrum2 <- farver::convert_colour(spectrum1, "rgb", "yxy")
spectrum3 <- farver::convert_colour(spectrum2, "yxy", "rgb")

spectrum1
#>        red green  blue
#> [1,]  -745 -1000 -1000
#> [2,] -1000  -745 -1000
#> [3,] -1000 -1000  -745
spectrum3
#>           [,1]       [,2]  [,3]
#> [1,] -745.0000 -1000.0001 -1000
#> [2,] -999.9999  -745.0001 -1000
#> [3,] -999.9999 -1000.0002  -745

spectrum2 <- farver::convert_colour(spectrum1, "rgb", "hsl")
spectrum3 <- farver::convert_colour(spectrum2, "hsl", "rgb")

spectrum1
#>        red green  blue
#> [1,]  -745 -1000 -1000
#> [2,] -1000  -745 -1000
#> [3,] -1000 -1000  -745
spectrum3
#>       [,1]  [,2]  [,3]
#> [1,]  -745 -1000 -1000
#> [2,] -1000  -745 -1000
#> [3,] -1000 -1000  -745

spectrum2 <- farver::convert_colour(spectrum1, "rgb", "hsv")
spectrum3 <- farver::convert_colour(spectrum2, "hsv", "rgb")

spectrum1
#>        red green  blue
#> [1,]  -745 -1000 -1000
#> [2,] -1000  -745 -1000
#> [3,] -1000 -1000  -745
spectrum3
#>       [,1]  [,2]  [,3]
#> [1,]  -745 -745 -745
#> [2,] -745  -745 -745
#> [3,] -745 -745  -745

with the rgb->hsl->rgb being exact. you might want to validate input too.

Using a very bruteforce approach I got to the following

library(magrittr)
all_colors <- expand.grid(red = 0:255, blue = 0:255, green = 0:255) %>%
  as.matrix()

check <- function(colors, space) {
  all_colors_out <- farver::convert_colour(colors, "rgb", space)
  all_colors_back <- farver::convert_colour(all_colors_out, space, "rgb")

  data.frame(
    space = space,
    n_na = sum(is.na(all_colors_back)),
    n_under_0 = sum(all_colors_back < 0, na.rm = TRUE),
    n_over_255 = sum(all_colors_back > 255, na.rm = TRUE), 
    stringsAsFactors = FALSE
  )
}

all_spaces <- c("cmy", "cmyk", "hsl", "hsb", "hsv", "lab", "hunterlab", "lch", 
                "luv", "rgb", "xyz", "yxy")
purrr::map_df(all_spaces, ~ check(all_colors, .x))
#>        space n_na n_under_0 n_over_255
#> 1        cmy    0         0          0
#> 2       cmyk    0         0          0
#> 3        hsl    0         0       5238
#> 4        hsb    0         0          0
#> 5        hsv    0         0          0
#> 6        lab    0     77711     119907
#> 7  hunterlab    0     77711     119907
#> 8        lch    0     77711     119907
#> 9        luv    3     77711     119907
#> 10       rgb    0         0          0
#> 11       xyz    0     77711     119907
#> 12       yxy    3     77711     119907

And a little exploration regarding rgb->hsl->rgb

library(magrittr)
all_colors <- expand.grid(red = 0:255, blue = 0:255, green = 0:255) %>%
  as.matrix()

all_colors_out <- farver::convert_colour(all_colors, "rgb", "hsl")
all_colors_back <- farver::convert_colour(all_colors_out, "hsl", "rgb")

hsl_errors <- as.data.frame(cbind(all_colors, all_colors_back)) %>%
  dplyr::filter(V4 > 255 | V5 > 255 | V6 > 255)

nrow(hsl_errors)
#> [1] 4729

hsl_errors %>%
  dplyr::summarise(same = sum(red == blue & blue == green),
                   red_n = sum(red == 255),
                   blue_n = sum(red != 255 & blue == 255),
                   green_n = sum(red != 255 & blue != 255 & green == 255)
                   ) %>%
  print() %>%
  sum()
#>   same red_n blue_n green_n
#> 1  253  1497   1492    1488
#> [1] 4730

gives us that we get errors when