用R重组从PNG图像中获得的矩阵
Reorganize matrix obtained from PNG image with R
与Matrix reorganization
中的问题相似但不相同
我有一些 PNG 文件,想做一些像素分析。使用 png 库我可以轻松阅读图像:
myImage <- readPNG("4colorpattern_15.png",native=FALSE)
str(myImage)
输出为
## num [1:483, 1:483, 1:3] 0 0 0 0 0 0 0 0 0 0 ...
我想将其重新组织为
X Y R G B A
0 0 0 0 0 0
1 0 0 0 0 0
...
X,Y 是坐标,RGB 是该像素的红色、绿色和蓝色的值,A 是 alpha(如果图像有)。
我一直在阅读有关重塑和熔化的内容,但似乎并非如此。我没有 R 技能来设计一个 mapply 函数来做到这一点。我想避免创建可以工作但效率低下的嵌套 for。
编辑
array 似乎可以解决问题:
nrow <- dim(myImage)[1]
ncol <- dim(myImage)[2]
nbands <- dim(myImage)[3]
array(myImage,dim=c(nrow*ncol,nbands))
我仍然需要检查顺序是否正确,但我仍然认为其中一个应用函数可以做到。另外,这个解决方案没有给我 X 和 Y 坐标。
编辑 2
我添加了一个非常小的 PNG -- 抱歉很难点击它!它是具有 3x2 像素的 3x2 图案的 9x4 PNG。顶行颜色为黑色、红色、绿色,底行颜色为蓝色、黄色、洋红色。
从该图像中我希望得到类似于
的数据框
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
(省略很多)
这里有放大版的图片供参考,但结果是基于 9x4 的。
这会让你得到一个模糊的结构,就像你想要的data.frame(尽管注意 R 矩阵和因子是从 1 开始的而不是从 0 开始的:
> 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
剩下的问题是 RGB 编码是在该数组的第三维层中隐式编码的。我现在还有一些其他工作需要做,但如果您或其他人没有赶我走,我会回来提供更完整的解决方案。
除了留下标记为 Var1 和 Var2: 的 X 和 Y 列外,这应该会提供所需的结构。 Var3 实际上是一个 RGB 标记:
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))
这似乎可以解决问题:
matrix(c(unlist(expand.grid(seq_len(dim(myImage)[1]), seq_len(dim(myImage)[2]))),
as.vector(myImage)),
ncol=dim(myImage)[3]+2)
这里的关键概念是,在R中,所有的多维结构都是糖衣向量。
相反的方式可能更容易理解 - 创建矩阵,R 将首先取 rows
项并将它们放在第一列;然后下 rows
项将成为第二列。重复此过程,直到所有列都被填满或向量用尽。重复数组中的剩余维度。
您可以 运行 array(1:108, c(4,9,3))
帮助形象化这个想法。
现在让我们分解一下我提出的解决方案。
as.vector(myImage)
这将以带下划线的向量形式呈现数组。如果您只关心带有 R、G 和 B 的数据框,您可以使用 matrix(as.vector(myImage), ncol=3)
.
seq_len(dim(myImage)[1])
seq_len(dim(myImage)[2])
这些将创建 n
个连续数字的向量,其中 n
是数组第一维和第二维中的项目数。它们不是那么有趣,仅作为输入数据用于:
expand.grid(...)
这个很有趣。它创建数据框,其中包含作为参数给出的所有级别因素的所有可能组合。第一个因素变化最快。它将为第一个参数的每个值提供第二个参数的第一个值;然后第一个参数的每个值和第二个参数的第二个值;等等。这并非巧合,对应于构成 PNG 数组的第一维和第二维的值!
由于expand.grid
给出了数据框,而我们想要矢量,我通过unlist
传递它。
然后我将 X 和 Y 坐标向量与实际值向量连接起来。
最后,我将那个单一向量作为参数传递给 matrix
并强制 R 为 "wrap" 它所以我得到 5 列(R、G 和 B 三列,X 和两列Y).我假设带有 alpha 通道的 PNG 在三维中有 4 个值,所以我指的是 dim(myImage)[3]
而不是硬编码值。
唯一剩下的就是更改列的名称(Y、X、R、G、B 和可能的 A),并且可以选择对行重新排序。我把它们留作 reader.
的练习
使用 Miroslav 的提示,这有效:
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
与Matrix reorganization
中的问题相似但不相同我有一些 PNG 文件,想做一些像素分析。使用 png 库我可以轻松阅读图像:
myImage <- readPNG("4colorpattern_15.png",native=FALSE)
str(myImage)
输出为
## num [1:483, 1:483, 1:3] 0 0 0 0 0 0 0 0 0 0 ...
我想将其重新组织为
X Y R G B A
0 0 0 0 0 0
1 0 0 0 0 0
...
X,Y 是坐标,RGB 是该像素的红色、绿色和蓝色的值,A 是 alpha(如果图像有)。
我一直在阅读有关重塑和熔化的内容,但似乎并非如此。我没有 R 技能来设计一个 mapply 函数来做到这一点。我想避免创建可以工作但效率低下的嵌套 for。
编辑 array 似乎可以解决问题:
nrow <- dim(myImage)[1]
ncol <- dim(myImage)[2]
nbands <- dim(myImage)[3]
array(myImage,dim=c(nrow*ncol,nbands))
我仍然需要检查顺序是否正确,但我仍然认为其中一个应用函数可以做到。另外,这个解决方案没有给我 X 和 Y 坐标。
编辑 2
我添加了一个非常小的 PNG
从该图像中我希望得到类似于
的数据框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
(省略很多)
这里有放大版的图片供参考,但结果是基于 9x4 的。
这会让你得到一个模糊的结构,就像你想要的data.frame(尽管注意 R 矩阵和因子是从 1 开始的而不是从 0 开始的:
> 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
剩下的问题是 RGB 编码是在该数组的第三维层中隐式编码的。我现在还有一些其他工作需要做,但如果您或其他人没有赶我走,我会回来提供更完整的解决方案。
除了留下标记为 Var1 和 Var2: 的 X 和 Y 列外,这应该会提供所需的结构。 Var3 实际上是一个 RGB 标记:
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))
这似乎可以解决问题:
matrix(c(unlist(expand.grid(seq_len(dim(myImage)[1]), seq_len(dim(myImage)[2]))),
as.vector(myImage)),
ncol=dim(myImage)[3]+2)
这里的关键概念是,在R中,所有的多维结构都是糖衣向量。
相反的方式可能更容易理解 - 创建矩阵,R 将首先取 rows
项并将它们放在第一列;然后下 rows
项将成为第二列。重复此过程,直到所有列都被填满或向量用尽。重复数组中的剩余维度。
您可以 运行 array(1:108, c(4,9,3))
帮助形象化这个想法。
现在让我们分解一下我提出的解决方案。
as.vector(myImage)
这将以带下划线的向量形式呈现数组。如果您只关心带有 R、G 和 B 的数据框,您可以使用 matrix(as.vector(myImage), ncol=3)
.
seq_len(dim(myImage)[1])
seq_len(dim(myImage)[2])
这些将创建 n
个连续数字的向量,其中 n
是数组第一维和第二维中的项目数。它们不是那么有趣,仅作为输入数据用于:
expand.grid(...)
这个很有趣。它创建数据框,其中包含作为参数给出的所有级别因素的所有可能组合。第一个因素变化最快。它将为第一个参数的每个值提供第二个参数的第一个值;然后第一个参数的每个值和第二个参数的第二个值;等等。这并非巧合,对应于构成 PNG 数组的第一维和第二维的值!
由于expand.grid
给出了数据框,而我们想要矢量,我通过unlist
传递它。
然后我将 X 和 Y 坐标向量与实际值向量连接起来。
最后,我将那个单一向量作为参数传递给 matrix
并强制 R 为 "wrap" 它所以我得到 5 列(R、G 和 B 三列,X 和两列Y).我假设带有 alpha 通道的 PNG 在三维中有 4 个值,所以我指的是 dim(myImage)[3]
而不是硬编码值。
唯一剩下的就是更改列的名称(Y、X、R、G、B 和可能的 A),并且可以选择对行重新排序。我把它们留作 reader.
的练习使用 Miroslav 的提示,这有效:
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