Extract Rgb Channels from a Jpeg Image in R

extract RGB channels from a jpeg image in R

You have several package to read in JPEG. Here I use package jpeg:

library(jpeg)
img <- readJPEG("Rlogo.jpg")

dim(img)
[1] 76 100 3

As you can see, there is 3 layers: they correspond to your R, G and B values. In each layer, each cell is a pixel.

img[35:39,50:54,]
, , 1

[,1] [,2] [,3] [,4] [,5]
[1,] 0.5098039 0.5921569 0.4549020 0.3372549 0.1921569
[2,] 0.5098039 0.6000000 0.4549020 0.3372549 0.1921569
[3,] 0.5137255 0.6000000 0.4549020 0.3450980 0.1921569
[4,] 0.5215686 0.6039216 0.4627451 0.3450980 0.1921569
[5,] 0.5215686 0.6039216 0.4627451 0.3450980 0.1882353

, , 2

[,1] [,2] [,3] [,4] [,5]
[1,] 0.5882353 0.6666667 0.5098039 0.3803922 0.2156863
[2,] 0.5882353 0.6627451 0.5098039 0.3803922 0.2156863
[3,] 0.5843137 0.6627451 0.5098039 0.3764706 0.2156863
[4,] 0.5843137 0.6627451 0.5058824 0.3764706 0.2117647
[5,] 0.5843137 0.6627451 0.5058824 0.3764706 0.2156863

, , 3

[,1] [,2] [,3] [,4] [,5]
[1,] 0.7254902 0.7921569 0.6156863 0.4588235 0.2705882
[2,] 0.7254902 0.7921569 0.6156863 0.4588235 0.2784314
[3,] 0.7254902 0.7921569 0.6156863 0.4588235 0.2784314
[4,] 0.7176471 0.7921569 0.6156863 0.4666667 0.2862745
[5,] 0.7176471 0.7921569 0.6156863 0.4666667 0.2862745

R - Getting Color Values

readJPEG returns a 3-D array that is height x width x channels. You can access individual color values using standard indexing. For example, y[,,1] will give you a height x width matrix of red intensities. You can convert these to color values using the rgb() function:

val <- rgb( y[,,1], y[,,2], y[,,3] )
myImg <- matrix( val, dim(y)[1], dim(y)[2] )

How to extract individual channels from an RGB image

The extracted red channel may look like a grayscale image but it is correct. It is simply a 2D array with values in the range [0,255]. To visualize a specific channel, you need to set the other channels to zero. So to show the red channel, the blue and green channels need to be set to zero.

import cv2

img = cv2.imread('1.jpg')

# Set blue and green channels to 0
img[:,:,0] = 0
img[:,:,1] = 0

cv2.imshow('red_img', img)
cv2.waitKey()

Sample Image

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

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

Here is my original img.jpg file.
Sample Image

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.
Sample Image

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.
Sample Image

Hope this is what you were looking for.

How to get pixel data from an image using R

First I generate an example png image :

png("/tmp/test.png")
plot(rnorm(100))
dev.off()

Then I convert it to a format readable by pixmap : here I convert the png to a ppm file as I want to keep colors information. I use ImageMagick from the command line, but you can use whatever program you want :

$ convert /tmp/test.png /tmp/test.ppm

Next, you can read the image with the read.pnm function from the pixmap package :

x <- read.pnm("/tmp/test.ppm")

And then, you can use the x@red, x@blue, x@green slots (or x@grey for a greyscale image) to get the pixels value for each channel as a matrix. You can check that the dimensions of the matrices are the same as the size of your picture :

dim(x@red)
[1] 480 480

How to extract coordinates of colored dots from a jpeg image?

Maybe there is some library that can do this already, but here are some utility functions that I wrote to help:

# What are the cartesian coordinates of pixels within the tolerance?
extract.coord<-function(channel,tolerance=0.99){
positions<-which(img[,,channel]>=tolerance)
row<-nrow(img) - (positions %% nrow(img))
col<-floor(positions / nrow(img)) +1
data.frame(x=col,y=row)
}

# Do these two pixels touch? (Diagonal touch returns TRUE)
touches<-function(coord1,coord2)
coord2$x <= (coord1$x+1) & coord2$x >= (coord1$x-1) & coord2$y <= (coord1$y+1) & coord2$y >= (coord1$y-1)

# Does this pixel touch any pixel in this list?
touches.list<-function(coord1,coord.list)
any(sapply(1:nrow(coord.list),function(x)touches(coord.list[x,],coord1)))

# Given a data.frame of pixel coordinates, give me a list of data frames
# that contain the "blobs" of pixels that all touch.
extract.pixel.blobs<-function(coords){
blob.list<-list()
for(row in 1:nrow(coords)){
coord<-coords[row,]
matched.blobs<-sapply(blob.list,touches.list,coord1=coord)
if(!any(matched.blobs)){
blob.list[[length(blob.list)+1]]<-coords[row,,drop=FALSE]
} else {
if(length(which(matched.blobs))==1) {
blob.list[[which(matched.blobs)]]<-rbind(blob.list[[which(matched.blobs)]],coords[row,,drop=FALSE])
} else { # Pixel touches two blobs
touched.blobs<-blobs[which(matched.blobs)]
blobs<-blobs[-which(matched.blobs)]
combined.blobs<-do.call(rbind,touched.blobs)
combined.blobs<-rbind(combined.blobs,coords[row,,drop=FALSE])
blobs[[length(blob.list)+1]]<-combined.blobs
}
}
}
blob.list
}

# Not exact center, but maybe good enough?
extract.center<-function(coords){
round(c(mean(coords$x),mean(coords$y))) # Good enough?
}

Use the functions like this:

coord.list<-lapply(1:3,extract.coord)
names(coord.list)<-c('red','green','blue')
pixel.blobs<-lapply(coord.list,extract.pixel.blobs)
pixel.centers<-lapply(pixel.blobs,function(x) do.call(rbind,lapply(x,extract.center)))

# $red
# [,1] [,2]
# [1,] 56 60
# [2,] 62 65
# [3,] 117 123
# [4,] 154 158
#
# $green
# [,1] [,2]
# [1,] 72 30
# [2,] 95 15
#
# $blue
# [,1] [,2]
# [1,] 44 45


Related Topics



Leave a reply



Submit