How to apply ConvertColor function to RGB values listed in a data.table in R

107 views Asked by At

I'm running an experiment where I need to compare the consistency of RGB values selected from two different time points. To do this, I need to convert RGB to XYZ values which I know I can do using ConvertColor.

However! I can't figure out an efficient way of being able to convert multiple RGB values stored in data.table into XYZ using ConvertColor. I have a feeling the problem lies in the fact that I can't figure out how to store RGB values as numeric values rather than characters.

My data.table named syn_RGB will be set out as follows:

ID Grapheme RGB_1 RGB_2
S01 A (37, 13, 219) (39, 23, 211)
S01 B (50, 3, 19) (40, 23, 11)
S02 A (43, 57, 89) (50, 50, 78)
S02 B (48, 59, 199) (50, 63, 178)
S03 A (100, 123, 209) (89, 112, 200)
S03 B (89, 120, 199) (87, 119, 175)

RGB_1 is the RGB given for the first time, RGB_2 the RGB given the second time.

This is what I've tried so far:

syn_XYZ <- synRGB(,. (s_xyz = convertColor(RGB_1, from = "sRGB", to = "XYZ", clip = TRUE)), by =. (ID, Grapheme)]

Here I'm trying to convert all the RGB values listed in RGB_1 into XYZ values for each participant and each grapheme and store them in syn_XYZ.

I've tried converting the RGB values from characters to numeric using as.numeric and strtoi but it doesn't work as you can imagine.

I'm still relatively new to programming so if there's a far more efficient way than trying to do it like this, I would be very appreciative since this is the only way I can think of how to do it.

Thanks

1

There are 1 answers

5
r2evans On

I don't think data.table has anything "efficient" for this, it's just a handling of a string vector. As clunky as it looks, this works:

do.call(rgb, c(asplit(do.call(rbind, lapply(regmatches(syn_RGB$RGB_1, gregexpr("\\d+", syn_RGB$RGB_1)), as.integer)), 2), list(maxColorValue = 255)))
# [1] "#250DDB" "#320313" "#2B3959" "#303BC7" "#647BD1" "#5978C7"

Walk through, mostly inside-out:

  • regmatches(., gregexpr("\\d+", .)) extracts things that look like numbers into individual numbers; i.e., "(37, 13, 219)" is converted into c("37", "13", "219"); this returns a list with the same length as nrow(syn_RGB)
  • lapply(., as.integer) converts the list of strings into a list of integer; e.g., list(c("37", "13", "219"), c("52", "3", "19"), ...) into list(c(37, 13, 219), c(52, 3, 19), ...).
  • We start with (effectively) a list of "R, G, B" values, but to programmatically use that with another function, it would help to have a list with all Rs, all Gs, and all Bs in respective vectors; to do that, do.call(rbind, .) converts that list of vectors into a matrix, and then asplit(., 2) converts it to a list of columns as we need.
  • do.call(rgb, .) calls the rgb function (once) with each of the three values of the asplit(., 2) list as its arguments, adding the argument of list(maxColorValue=255) (since rgb normally wants its values to be on [0,1]).