简体   繁体   中英

Reorganize matrix obtained from PNG image with R

Question similar but not the same as in Matrix reorganization

I have some PNG files and want to do some pixel analysis. Using the png library I can easily read the image:

myImage <- readPNG("4colorpattern_15.png",native=FALSE)
str(myImage)

The output is

##  num [1:483, 1:483, 1:3] 0 0 0 0 0 0 0 0 0 0 ...

I'd like to have it reorganized as something like

X Y R G B A
0 0 0 0 0 0 
1 0 0 0 0 0
...

X,Y being the coordinates, RGB being the values for red, green and blue for that pixel, A being alpha (if image has it).

I've been reading about reshape and melt, it does not seems that this is a case for it. I don't have the R skills to devise a mapply function to do that. I'd like to avoid creating nested fors which could work but be inefficient.

Edit array seems to do the trick:

nrow  <- dim(myImage)[1]
ncol  <- dim(myImage)[2]
nbands <- dim(myImage)[3]
array(myImage,dim=c(nrow*ncol,nbands))

I still have to check whether the order is correct, but I still think one of the apply functions could do. Also, this solution does not gives me the X and Y coordinates.

Edit 2

I've added a very small PNG 小圆点点点PNG -- sorry it is so hard to click on it! It is a 9x4 PNG with a 3x2 pattern of 3x2 pixels. On the top row colors are black, red, green, the bottom row colors are blue, yellow, magenta.

From that image I'd expect to get a data frame similar to

X Y R G B (no A in this case)
0 0 0 0 0
1 0 0 0 0
2 0 0 0 0
3 0 1 0 0
4 0 1 0 0
5 0 1 0 0
6 0 0 1 0
7 0 0 1 0
8 0 0 1 0
...
0 2 0 0 1
1 2 0 0 1
2 2 0 0 1
3 2 1 1 0
4 2 1 1 0
5 2 1 1 0
6 2 1 0 1
7 2 1 0 1
8 2 1 0 1

(many omitted)

Here is a larger version of the image for reference, but the results are based on the 9x4 one.

大一点

This gets you to a stucture vaguely like the desired data.frame (although notice that R matrices and factors are 1-origined rather than 0-origined:

> long_tbl <- as.data.frame.table(myImage)
> long_tbl[1:3] <- lapply(long_tbl[1:3], as.numeric)
> head(long_tbl)
  Var1 Var2 Var3 Freq
1    1    1    1    0
2    2    1    1    0
3    3    1    1    0
4    4    1    1    0
5    1    2    1    0
6    2    2    1    0
> tail(long_tbl)
    Var1 Var2 Var3 Freq
103    3    8    3    1
104    4    8    3    1
105    1    9    3    0
106    2    9    3    0
107    3    9    3    1
108    4    9    3    1

The remaining problem is that the RGB coding was implicitly encoded in the layers which were the third dimension of that array. I've got some other work I need to do at the moment but I'll come back and provide a more complete solution if you or someone else doesn't beat me to it.

This should deliver the desired structure except for leaving the X- and Y-columns labeled Var1 and Var2:. The Var3 is really an RGB marker:

long3tbl <- cbind(long_tbl[1:2],  #Use the X and Y columns unchanged
                    # Replace with zeros in the rows where not the desired color.
                  with(long_tbl, cbind( R=(Var3==1)*Freq, G=(Var3==2)*Freq, B=(Var3==3)*Freq)))

head(long3tbl)
#=========
  Var1 Var2 R G B
1    1    1 0 0 0
2    2    1 0 0 0
3    3    1 0 0 0
4    4    1 0 0 0
5    1    2 0 0 0
6    2    2 0 0 0
#========
tail(long3tbl)
##++++++++++++
    Var1 Var2 R G B
103    3    8 0 0 1
104    4    8 0 0 1
105    1    9 0 0 0
106    2    9 0 0 0
107    3    9 0 0 1
108    4    9 0 0 1
#======
with(long3tbl, plot(Var1, Var2, col=R))
with(long3tbl, plot(Var1, Var2, col=G))
with(long3tbl, plot(Var1, Var2, col=B))

This seems to do the trick:

matrix(c(unlist(expand.grid(seq_len(dim(myImage)[1]), seq_len(dim(myImage)[2]))),
         as.vector(myImage)),
       ncol=dim(myImage)[3]+2)

The key concept here is that in R, all multi-dimensional structures are sugar-coated vectors.

It's probably easier to understand the other way around - to create a matrix, R will take first rows items and put them in first column; then next rows items will make second column. This process is repeated until all columns are filled or vector is exhausted. It is repeated for remaining dimensions in arrays.

You can run array(1:108, c(4,9,3)) to help visualize this idea.

Now let's break down my proposed solution.

as.vector(myImage)

This will present array in underlining vector form. If you had cared only about data frame with R, G and B, you could get away with matrix(as.vector(myImage), ncol=3) .

seq_len(dim(myImage)[1])
seq_len(dim(myImage)[2])

These will create vectors of n consecutive numbers, where n is number of items in first and second dimension of array. They are not that interesting and act only as input data for:

expand.grid(...)

This one is interesting. It creates data frame with all possible combinations of all levels of factors given as arguments. The first factor varies fastest. It will give each value of first argument with first value from second argument; then each value of first argument with second value from second argument; and so on. And this, not coincidentally, corresponds with values that constitute first and second dimension of PNG array!

Since expand.grid gives data frame and we want vector, I pass it through unlist .

Then I concatenate vector of X and Y coordinates with vector of actual values.

Finally, I pass that one single vector as argument to matrix and force R to "wrap" it so I get 5 columns (three for R, G and B, plus two for X and Y). I assume that PNG with alpha channel will have 4 values in third dimension, so I refer to dim(myImage)[3] instead of hardcoding value.

The only thing left is changing names of columns (Y, X, R, G, B and possibly A) and, optionally, reordering rows. I leave them as exercises for reader.

Using Miroslav's tips, this works:

myImage <- readPNG("origs/small.png",native=FALSE)
arr  <- matrix(c(unlist(expand.grid(seq_len(dim(myImage)[1]), seq_len(dim(myImage)[2]))),
    as.vector(myImage)),
    ncol=dim(myImage)[3]+2)
imFrame <- as.data.frame(arr)
colnames(imFrame) <- c('X','Y','R','G','B')
str(imFrame)
imFrame

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM