Search code examples
rrgbhsvhsl

Converting image array to RGB to HSL/HSV and back?


I read in colored jpg images using readJPEG() from the jpeg package. Now I have my images as three-dimensional arrays (width, height, channels) in R.

I want to convert these image arrays into the HSL or HSV color space, mutate the images and save them as JPGs in the RGB format again. However, as the images are quite large (5000 x 8000), it would be too time consuming to loop through every single cell. I found the package OpenImageRto convert the image to the HSV color space quickly, however, I am confused by large negative values in the "saturation" channel. Also, the package contains no functions to convert the image back.

Is there any package to perform fast conversions from RGB to HSL or HSV (and back)? Or is there any other way to perform the converison quickly?

These are my current attempts for converting into one direction, element-wise:

# load packages
library(jpeg)
library(plotwidgets)    

# load image
img <- readJPEG(img_path)
img <- img * 255
  
# new empty image
img_new <- array(NA, dim = dim(img))

# this takes way too long
for (img_row in 1:dim(img)[1]) {
  for (img_col in 1:dim(img)[2]) {
    img_new[img_row,img_col,] <- round(rgb2hsl(as.matrix(img[img_row,img_col,])))
  }
}

# this takes also way too long
for (img_row in 1:dim(img)[1]) {
  img_new[img_row,,] <- t(round(rgb2hsl(t(matrix(img[img_row,,], ncol = 3)))))
}

# this takes also ages
rgb_hsl_fun <- function(x) {
  as.numeric(rgb2hsl(matrix(x)))
}
img_hsl <- apply(X = img, MARGIN = c(1,2), FUN = rgb_hsl_fun)

Solution

  • The whole thing is quite simple to do. Use the colorspace library for this.

    Here is my original img.jpg file. enter image description here

    Here is the code.

    library(jpeg)
    library(colorspace)
    
    #Reading a jpg file
    img = readJPEG("img.jpg") * 255
    
    #Row-by-row conversion
    for(i in 1:dim(img)[1]){
      
      #Convert to HSV format
      hsv = RGB(img[i,,1], img[i,,2], img[i,,3]) |> as("HSV")
      
      #Mutation of H, S, V components
      attributes(hsv)$coords[,"H"] = attributes(hsv)$coords[,"H"]/2 
      attributes(hsv)$coords[,"S"] = attributes(hsv)$coords[,"S"]*.998 
      attributes(hsv)$coords[,"V"] = attributes(hsv)$coords[,"V"]-1 
      
      #Convert to RGB format and save to the current line.
      rgb = as(hsv, "RGB")
      img[i,,1] = attributes(rgb)$coords[,"R"]
      img[i,,2] = attributes(rgb)$coords[,"G"]
      img[i,,3] = attributes(rgb)$coords[,"B"]
    }
    
    #Save to JPG file
    writeJPEG(img / 255, "img_hsv.jpg")
    

    Just note that to get to the individual H, S, V (or R, G, B) components you have to use the coords attribute.

    As you can see, my mutation of the components H, S, V was as follows:

    • H = H / 2
    • S = S * 0.998
    • V = V-1

    After this mutation, the original file looks like this. enter image description here

    However, if you prefer to carry out the mutation on the HLS palette, it is possible.

    #Reading a jpg file
    img = readJPEG("img.jpg") * 255
    
    #Row-by-row conversion
    for(i in 1:dim(img)[1]){
      
      #Convert to HLS format
      hls = RGB(img[i,,1], img[i,,2], img[i,,3]) |> as("HLS")
      
      #Mutation of H, S, V components
      attributes(hls)$coords[,"H"] = attributes(hls)$coords[,"H"]/2 
      attributes(hls)$coords[,"L"] = attributes(hls)$coords[,"L"]/2 
      attributes(hls)$coords[,"S"] = attributes(hls)$coords[,"S"]/2 
      
      #Convert to RGB format and save to the current line.
      rgb = as(hls, "RGB")
      img[i,,1] = attributes(rgb)$coords[,"R"]
      img[i,,2] = attributes(rgb)$coords[,"G"]
      img[i,,3] = attributes(rgb)$coords[,"B"]
    }
    
    #Save to JPG file
    writeJPEG(img / 255, "img_hls.jpg")
    

    Here is the image with H/2, L/2 and S/2 conversion. enter image description here

    Hope this is what you were looking for.