How to get a pixel matrix from grayscale image in R? - r

When grayscale images are represented by matrices each element of the matrix determines the intensity of the corresponding pixel. For convenience, most of the current digital files use integer numbers between 0 (to indicate black, the color of minimal intensity) and 255 (to indicate white, maximum intensity), giving a total of 256 = 2^8 different levels of gray.
Is there a way to get a pixel matrix of graysale images in R whose pixel values will range from 0 to 255?
It will also be helpful to know if I can resize the images in preferred dimension (say, $28 \times 28$) in R and then convert them into a pixel matrix whose elements range from 0 to 255?
What happens if the original image is RGB but I want the matrix for grayscale?

The R package png offers the readPNG() function which can read raster graphics (consisting of "pixel matrices") in PNG format into R. It returns either a single matrix with gray values in [0, 1] or three matrices with the RGB values in [0, 1].
For transforming between [0, 1] and {0, ..., 255} simply multiply or divide with 255 and round, if desired.
For transforming between RGB and grayscale you can use for example the desaturate() function from the colorspace package.
As an example, let's download the image you suggested:
download.file("http://www.greenmountaindiapers.com/skin/common_files/modules/Socialize/images/twitter.png",
destfile = "twitter.png")
Then we load the packages mentioned above:
library("png")
library("colorspace")
First, we read the PNG image into an array x with dimension 28 x 28 x 4. Thus, the image has 28 x 28 pixels and four channels: red, green, blue and alpha (for semi-transparency).
x <- readPNG("twitter.png")
dim(x)
## [1] 28 28 4
Now we can transform this into various other formats: y is a vector of hex character strings, specifying colors in R. yg is the corresponding desaturated color (again as hex character) with grayscale only. yn is the numeric amount of gray. All three objects are arranged into 28 x 28 matrices at the end
y <- rgb(x[,,1], x[,,2], x[,,3], alpha = x[,,4])
yg <- desaturate(y)
yn <- col2rgb(yg)[1, ]/255
dim(y) <- dim(yg) <- dim(yn) <- dim(x)[1:2]
I hope that at least one of these versions is what you are looking for. To check the pixel matrices I have written a small convenience function for visualization:
pixmatplot <- function (x, ...) {
d <- dim(x)
xcoord <- t(expand.grid(1:d[1], 1:d[2]))
xcoord <- t(xcoord/d)
par(mar = rep(1, 4))
plot(0, 0, type = "n", xlab = "", ylab = "", axes = FALSE,
xlim = c(0, 1), ylim = c(0, 1), ...)
rect(xcoord[, 2L] - 1/d[2L], 1 - (xcoord[, 1L] - 1/d[1L]),
xcoord[, 2L], 1 - xcoord[, 1L], col = x, border = "transparent")
}
For illustration let's look at:
pixmatplot(y)
pixmatplot(yg)
If you have a larger image and want to bring it to 28 x 28, I would average the gray values from the corresponding rows/columns and insert the results into a matrix of the desired dimension.
Final note: While it is certainly possible to do all this in R, it might be more convenient to use an image manipulation software instead. Depending on what you aim at, it might be easier to just use ImageMagick's mogrify for example:
mogrify -resize 28 -type grayscale twitter.png

Here is an example of converting and drawing an image from a grayscale png. Please ensure installing the relevant packages first.
library(png)
library(RCurl)
myurl = "https://postgis.net/docs/manual-dev/images/apple_st_grayscale.png"
my_image = readPNG(getURLContent(myurl))
img_mat=my_image[,,1] # will hold the grayscale values divided by 255
img_mat=t(apply(img_mat, 2, rev)) # otherwise the image will be rotated
image(img_mat, col = gray((0:255)/255)) # plot in grayscale

Related

Simulate a matricies of data using R as bands of satellite imagery from scratch

I am trying to
1) Simulate a matrix of data using R (in effect an image of numbers where each cell in the matrix has a number on the numerical scale of 0-255 (8 bit data))
2) Map the simulated data using mapping tools
3) Classify the image into 8-10 classes
The idea is to use a simple function to generate an image with 3 bands of Red Green and Blue imagery simulating multispectral imagery from satellite. So a composite of 3 different matricies. Like this.
Then classify the composite by colour into 8 or 10 classes
Any help would be much appreciated.
Based on your comments, here is an approach to sample as a gradient.
imagerows <- 100
imagecols <- 100
cuts <- 8
(imagecols * imagerows) %% cuts == 0 #Must be true
colorgroups <- as.integer(cut(0:255,cuts))
colors <- c("red","green","blue")
result <- lapply(colors,function(y){
unlist(
lapply(seq(1,cuts),function(x){sample((0:255)[colorgroups == x],
size = (imagerows*imagecols)/cuts,
replace = TRUE)})
)})
result is now a list of length 3, each element of which is a 100x100 matrix. The matrix contains 100 * 100 random samples between 0 and 255, but in cuts number of increasing groups.
We can then control the direction of the gradient using byrow = in matrix and by using rev() on the data.
red.matrix <- matrix((result[[1]]),nrow=imagerows,ncol=imagecols,byrow = TRUE)
green.matrix <- matrix((result[[2]]),nrow=imagerows,ncol=imagecols,byrow = FALSE)
blue.matrix <- matrix(rev(result[[3]]),nrow=imagerows,ncol=imagecols,byrow = FALSE)
Then we put the colors together with rgb() which outputs a vector. We can coerce it back into a matrix by assigning dimensions. Then just plot with grid.raster().
library(grid)
rgb.matrix <- rgb(red.matrix,green.matrix,blue.matrix,maxColorValue = 255)
dim(rgb.matrix) <- c(imagerows,imagecols)
grid.newpage()
grid.raster(rgb.matrix,interpolate = FALSE)

Transposing Images in R - Matrix transformation mirrors image

I have a data frame that I'm casting to a matrix. Every row represents an image in white, gray, and black colors.
The image corresponds to the number 5. I'm wondering what I am missing that the number 5 appears mirrored?
plotimage <- function(df,n, imageTitle) {
convertedImage <- df
matrx <- matrix(unlist(convertedImage[n,1:784]), byrow = T, nrow=28)
image(col = gray(0:255/255), z = matrx)
}
plotimage(image5, 1600,"Image of 5")
Output I want
Number 5 not mirrored.
Thanks,
That's because of the (unusual) way image works:
Notice that image interprets the z matrix as a table of f(x[i], y[j])
values, so that the x axis corresponds to row number and the y axis to
column number, with column 1 at the bottom, i.e. a 90 degree
counter-clockwise rotation of the conventional printed layout of a
matrix.
That's how your matrx is interpreted. Hence, writing
image(col = gray(0:255/255), z = matrx[nrow(matrx):1, ])
should fix it.

Color extraction, quantification and analysis from image in R

I would like to quantifying colors in an image.
I work on the iridescence of nacre (mother of pearl), and I want to quantifying three colors (red, yellow and green) on this shell (for example on the right picture on the link above).
Iridescence of nacre
So, I had test some packages (imager, ImageMagick, EBImage...), but I don't really find something that help me.
Well, I would like to make color quantification on R, with circles. The area of ​​the primitive in pixel may be expressed as that of a circle of equivalent surface area. The primitive is a contiguous area of neighboring pixels of similar color. The center of the circle can be the anchor pixel.
So, there is the equation which I think it's ok to do this:
DeltaI = square root[(Ranchor - Ri)² - (Ganchor - Gi)² - (Banchor -
Bi)²]
Where R,G and B are color components of a pixel, ranging from 0 to 255, anchor is the anchor pixel and i is any pixel around the anchor pixel which are the same equivalent color.
There is a image link to the expectation results (from Alçiçek & Balaban 2012):
Shrimp resulting equivalent circles
So there is my (bootable worked) code, but I have really no idea how to continue.. May be try to create a package ?
library(png)
nacre <- readPNG("test.png")
nacre
dim(nacre)
# show the full RGB image
grid.raster(nacre)
# show the 3 channels in separate images
nacre.R = nacre
nacre.G = nacre
nacre.B = nacre
# zero out the non-contributing channels for each image copy
nacre.R[,,2:3] = 0
nacre.G[,,1]=0
nacre.G[,,3]=0
nacre.B[,,1:2]=0
# build the image grid
img1 = rasterGrob(nacre.R)
img2 = rasterGrob(nacre.G)
img3 = rasterGrob(nacre.B)
grid.arrange(img1, img2, img3, nrow=1)
# Now let’s segment this image. First, we need to reshape the array into a data frame with one row for each pixel and three columns for the RGB channels:
# reshape image into a data frame
df = data.frame(
red = matrix(nacre[,,1], ncol=1),
green = matrix(nacre[,,2], ncol=1),
blue = matrix(nacre[,,3], ncol=1)
)
### compute the k-means clustering
K = kmeans(df,4)
df$label = K$cluster
### Replace the color of each pixel in the image with the mean
### R,G, and B values of the cluster in which the pixel resides:
# get the coloring
colors = data.frame(
label = 1:nrow(K$centers),
R = K$centers[,"red"],
G = K$centers[,"green"],
B = K$centers[,"blue"]
)
# merge color codes on to df
df$order = 1:nrow(df)
df = merge(df, colors)
df = df[order(df$order),]
df$order = NULL
# get mean color channel values for each row of the df.
R = matrix(df$R, nrow=dim(nacre)[1])
G = matrix(df$G, nrow=dim(nacre)[1])
B = matrix(df$B, nrow=dim(nacre)[1])
# reconstitute the segmented image in the same shape as the input image
nacre.segmented = array(dim=dim(nacre))
nacre.segmented[,,1] = R
nacre.segmented[,,2] = G
nacre.segmented[,,3] = B
# View the result
grid.raster(nacre.segmented)
Someone have a track or any idea ?
Thanks for any help..
Well I've find a other way to answer my question:
I upload my image with load.imagefrom imager package.
I extract the RGB channels with this code:
# Assign RGB channels to data frame
nacreRGB <- data.frame(
x = rep(1:nacreDm[2], each = nacreDm[1]),
y = rep(nacreDm[1]:1, nacreDm[2]),
R = as.vector(nacre[,,1]),
G = as.vector(nacre[,,2]),
B = as.vector(nacre[,,3])
)
# head(nacreRGB)
# Assign RGB channels to data frame without pixel coordinates
nacreRGB2 <- data.frame(
R = as.vector(nacre[,,1]),
G = as.vector(nacre[,,2]),
B = as.vector(nacre[,,3])
After I convert this into HEX code with rgbSVG2rgbCSS fonction.
I put this into a matrix which I call RGB0 to create histogramms and show the different colors with the pixels frequencies.
After I perform a PCA to show the distribution of these colors:
require("ggplot2")
RGB0 <- as.data.frame(RGB0)
# perform PCA on the nacre data and add the uv coordinates to the
dataframe
PCA = prcomp(RGB0[,c("R","G","B")], center=TRUE, scale=TRUE)
RGB0$u = PCA$x[,1]
RGB0$v = PCA$x[,2]
I show this PCA with ggplot2.
After this I translate the RGB code into HSV code with rgb2hsv and I can have a value for the hue, for saturation (hue to white) and value (hue to dark), so I can obtain qualities and quantities datas about colors.
Edit : All codes are now publishing into CRAN in ImaginR package :
https://cran.r-project.org/web/packages/ImaginR/ImaginR.pdf
Or on GitHub :
https://github.com/PLStenger/ImaginR
This version not really quantifying the color, but it will appears soon in the next version.

How to change rgb image into matrix using EBImage?

I want to change an image into matrix of numbers in R using EBImage package. I have tried this code but it only output all 1's:
library(EBImage)
img<-readImage("test.jpg")
imageData(img)[1:50,1:60,1]
this is the image
The following example illustrates how to load a grayscale image containing an alpha channel, convert it to single-channel grayscale image, and do some post-processing: crop the border and resize.
library(EBImage)
img <- readImage("http://i.stack.imgur.com/9VTWx.png")
# grayscale images containing an alpha channel are represented in EBImage as
# RGBA images by replicating the grayscale intensities over the red, green and
# blue channels
print(img, short=TRUE)
## Image
## colorMode : Color
## storage.mode : double
## dim : 819 460 4
## frames.total : 4
## frames.render: 1
# convert to grayscale
img <- channel(img, "gray")
# collect matrix indices of non-white pixles
ind <- which(img < 1, arr.ind=TRUE)
# find min/max indices across rows/columns
ind <- apply(ind, 2L, range)
rownames(ind) <- c("min", "max")
ind
## row col
## min 17 7
## max 819 413
# crop the image
img <- img[ind["min","row"]:ind["max","row"], ind["min","col"]:ind["max","col"]]
# resize to specific width and height
img <- resize(img, w=128, h=128)
display(img)
To extract the underlying matrix use imageData(img).
First of all, that's not a JPEG image, but PNG.
Second, it's not RGB, but greyscale + alpha (according to ImageMagick at least), although the alpha channel is completely opaque, so it doesn't hold any actual data.
Third, the reason you're getting all ones is because the section of the image you are choosing is all white, i.e. maximum intensity, which is represented by the value 1.
Try something like imageData(img)[51:100,1:60,1] and see if that doesn't give a different result.

Draw a circle in a bitmap image and crop pixels outside circle in R

I'm loading bitmap images into R with dimensions that are roughly 17,000 X 17,000 pixels. I'd like to find a way to draw a circle with a radius (in pixels) of my choosing around the center of the picture and convert all pixels outside of the circle into NA's.
For example, if the radius desired was 500 pixels, all pixels within that distance (500) from the centroid would be kept as is. Any pixel farther than that distance (>= 501) from the centroid would be converted to an NA.
The bitmap images are made up entirely of 1's and 0's so here's a smaller example of what these images look like.
img=matrix(sample(c(1,0),1000000,replace=TRUE),ncol=1000,nrow=1000)
image(0:1000,0:1000,img)
This is a slight variation of the solution by eipi10. It does not use the "melt" function of the reshape package and rather uses subsetting the matrix directly:
# Number of rows and columns in image
nr = 200
nc = 100
# Create image values
set.seed(78)
img <- matrix(sample(c(1,0), nr*nc, prob=c(0.8, 1-0.8), replace=TRUE), ncol=nc, nrow=nr)
center <- c(median(1:nr), median(1:nc)) # center of image
r <- 40 # radius
# setting the matrix element inside the circle to value -1
img[(row(img) - center[1])^2 + (col(img) - center[2])^2 < r^2] <- -1
# plot image
par(mar = c(0, 0, 0, 0))
image(img, useRaster=TRUE, axes=FALSE)
I've created a fake image that's smaller than yours so that the code will run more quickly:
library(plotrix) # To draw a circle
library(reshape2) # For "melt" function
Create a fake image:
# Number of rows and columns in image
nr = 200
nc = 100
# Create image values
set.seed(78)
img = matrix(sample(c(1,0), nr*nc, prob=c(0.8, 1-0.8), replace=TRUE), ncol=nc, nrow=nr)
Now that we have our image, remove points outside the desired circle:
# melt matrix into "long" format
img = melt(id.var=1:nrow(img), img)
names(img) = c("rows","cols","z")
# Find center of image
center=c(median(1:nr), median(1:nc))
# Set desired radial distance from center
r=40
# Set values outside radius to -1 (or some value that can't otherwise appear in
# the matrix). You can set the value to NA, but then you won't be able to
# control the color of the excluded region (it will just be white).
img$z[sqrt((img$rows - center[1])^2 + (img$cols - center[2])^2) > r] = -1
# Plot image. Colors ordered from lowest (-1) to highest (1) value
image(1:nr, 1:nc, matrix(img$z, nrow=nr, byrow=FALSE), col=c("gray80", "green","red"))
# Draw a circle around the selected points
draw.circle(center[1], center[2], r, lwd=2)

Resources