如何将 2D 图像投影为 3D 散点图中的表面(在 R 中)?
How to project a 2D image as a surface in a 3D scatter plot (in R)?
我正在处理 3 维坐标数据,我在散点图中绘制它,我有大约 30.000 个数据点,我在这里包含了前 10 个,以便您可以重现它
library(rgl)
library(plot3D)
library(car)
df <- data.frame(meanX = c(147.34694,
173.89244,
135.73004,
121.93766,
109.72152,
92.53709,
165.46588,
169.77744,
127.01796,
99.34347),
meanY = c(140.40816,
110.99128,
134.56023,
164.18703,
166.04051,
155.97329,
105.29377,
104.42683,
130.17066,
155.99696),
avgDist = c(40.788118,
12.957329,
14.24348,
39.10424,
34.694258,
25.532335,
21.491695,
23.528944,
9.309201,
31.916879))
我一直在使用 scatter3d 函数绘制此图
scatter3d(x = df$meanX, y = df$meanY, z = df$avgDist, surface = FALSE)
现在我的 "problem" 是我想要一个二维表面,在 z=0 处叠加一个外部图像文件,作为奖励,如果我可以投影 heatmap/contours 来自该图像上的散点图数据(用于轮廓的 meanX 和 meanY),那会很棒。
这是我想在 z = 0 处覆盖的图像:
http://i.imgur.com/m6j4q3M.png
该图像是使用此 ggplot 制作的:
map.colors <- colorRampPalette(c("green","yellow","red"))
densityPlot <- ggplot(direData, aes(x = meanX, y = ,meanY)) +
stat_density2d(geom="tile", aes(fill=..density.., alpha=sqrt(sqrt(..density..))), contour=FALSE, n=100) +
scale_alpha(range = c(0, 1.0)) + scale_fill_gradientn(colours = map.colors(5)) +
xlim(70,185) + ylim(70,185)
minimap <- readPNG('~/yasp/minimap.png')
densityPlot + annotation_raster(minimap, ymin = 70 ,ymax=185 ,xmin = 70,xmax = 185) +
stat_density2d(geom="tile", aes(fill=..density.., alpha=10*sqrt(..density..)), contour=FALSE, n=100)
有什么办法吗?我在谷歌上搜索了很多解决方案,但没有找到真正的方法。我不介意首先在 ggplot2 中使用热图创建图像,保存它,然后将其用作表面覆盖的输入,但如果它可以在一次绘图调用中完成,那当然会很酷。
这个怎么样?
我将你的内衬图像文件存储在本地目录中的 png 中,可能有一种方法可以在没有中间文件的情况下做到这一点,但我会把它作为一个单独的问题来问。
请注意,这实际上是一个简单的纹理映射案例。纹理保存在您指定的 gameshot.png
文件中。您可以通过向几何体添加更多点并相应地调整纹理贴图坐标来围绕更复杂的对象扭曲文本。
虽然它们在这里不应该是绝对必要的,但我添加了纹理贴图坐标,因为它看起来像文件,默认情况下数据没有对齐 - 事实上 gameshot.png
文件显示是颠倒的。在我看来你指定的png文件与数据不太匹配,我认为在你保存之前某处发生了反转。
library(rgl)
library(plot3D)
library(car)
df <- data.frame(meanX = c(147.34694, 173.89244, 135.73004, 121.93766,
109.72152, 92.53709, 165.46588, 169.77744,
127.01796, 99.34347),
meanY = c(140.40816, 110.99128, 134.56023, 164.18703,
166.04051, 155.97329, 105.29377, 104.42683,
130.17066, 155.99696),
avgDist = c(40.788118, 12.957329, 14.24348, 39.10424,
34.694258, 25.532335, 21.491695,23.528944,
9.309201, 31.916879))
car::scatter3d(x = df$meanX, y = df$meanY, z = df$avgDist, surface = FALSE)
xvek <- c(0,1)
yvek <- c(0,1)
lnx <- length(xvek)
lny <- length(yvek)
zmat <- matrix(0,lnx,lny)
# Setup the Texture coordinates - defaults seem to invert image
# tms <- matrix(c(0,0,1,1),lnx,lny) # generic case (xy-maped texture looks like png file)
# tmt <- matrix(c(0,1,0,1),lnx,lny)
tmt <- matrix(c(1,1,0,0),lnx,lny) # "correct case" (ball density look more like picture)
tms <- matrix(c(1,0,1,0),lnx,lny) # I think the gameshot.png is in error
# Texture file specified in question was stored locally in "gameshot.png"
surface3d(xvek,yvek,zmat,coord=c(3,1),texture_s=tms,texture_t=tmt,
lit=F,fog=T,color="white",textype="rgb",texture="gameshot.png",add=T)
产生这个:
(第二次编辑)我尝试编写更好的代码并确认两个 xy 坐标相同。 ggplot2 theme with no axes or grid 帮我只绘制面板区域。
library(rgl); library(grid); library(gtable)
df <- data.frame(meanX = c(147.34694, 173.89244, 135.73004, 121.93766,
109.72152, 92.53709, 165.46588, 169.77744,
127.01796, 99.34347),
meanY = c(140.40816, 110.99128, 134.56023, 164.18703,
166.04051, 155.97329, 105.29377, 104.42683,
130.17066, 155.99696),
avgDist = c(40.788118, 12.957329, 14.24348, 39.10424,
34.694258, 25.532335, 21.491695,23.528944,
9.309201, 31.916879))
map.colors <- colorRampPalette(c("green","yellow","red"))
# set scale_*_continuous() to plot only the panel region. limits mean xlim (or ylim)
# change "tile" into "raster" because of making noise lines on my screen
densityPlot <- ggplot(df[,1:2], aes(x = meanX, y = ,meanY)) +
stat_density2d(geom="raster", aes(fill=..density.., alpha=sqrt(sqrt(..density..))), contour=FALSE, n=100) +
scale_alpha(range = c(0, 1.0)) + scale_fill_gradientn(colours = map.colors(5)) +
scale_x_continuous(limits=c(70,185), expand = c(0,0)) + scale_y_continuous(limits=c(70,185), expand = c(0,0)) +
geom_point(size=4) # to test XY-coordinate (black points on the map)
open3d()
plot3d( df, type="s", radius=1, col="red", axes=F,
xlim = c(70,185), ylim = c(70,185),
expand = 1 )
plot3d( df, type="h", col="blue", add=T ) # to test XY-coordinate (line segments from z = 0)
axes3d(c("x","y","z") )
show2d({ # show2d uses 2D plot function's output as a texture on a box.
grid.draw(gtable_filter(ggplotGrob(densityPlot), "panel"))
},
expand = 1 , texmipmap = F ) # texmipmap = F makes tone clear (not essential)
# I think this is clearly better than using a intermediate file,
# so I deleted related code. Thanks Mike !
我正在处理 3 维坐标数据,我在散点图中绘制它,我有大约 30.000 个数据点,我在这里包含了前 10 个,以便您可以重现它
library(rgl)
library(plot3D)
library(car)
df <- data.frame(meanX = c(147.34694,
173.89244,
135.73004,
121.93766,
109.72152,
92.53709,
165.46588,
169.77744,
127.01796,
99.34347),
meanY = c(140.40816,
110.99128,
134.56023,
164.18703,
166.04051,
155.97329,
105.29377,
104.42683,
130.17066,
155.99696),
avgDist = c(40.788118,
12.957329,
14.24348,
39.10424,
34.694258,
25.532335,
21.491695,
23.528944,
9.309201,
31.916879))
我一直在使用 scatter3d 函数绘制此图
scatter3d(x = df$meanX, y = df$meanY, z = df$avgDist, surface = FALSE)
现在我的 "problem" 是我想要一个二维表面,在 z=0 处叠加一个外部图像文件,作为奖励,如果我可以投影 heatmap/contours 来自该图像上的散点图数据(用于轮廓的 meanX 和 meanY),那会很棒。
这是我想在 z = 0 处覆盖的图像:
http://i.imgur.com/m6j4q3M.png
该图像是使用此 ggplot 制作的:
map.colors <- colorRampPalette(c("green","yellow","red"))
densityPlot <- ggplot(direData, aes(x = meanX, y = ,meanY)) +
stat_density2d(geom="tile", aes(fill=..density.., alpha=sqrt(sqrt(..density..))), contour=FALSE, n=100) +
scale_alpha(range = c(0, 1.0)) + scale_fill_gradientn(colours = map.colors(5)) +
xlim(70,185) + ylim(70,185)
minimap <- readPNG('~/yasp/minimap.png')
densityPlot + annotation_raster(minimap, ymin = 70 ,ymax=185 ,xmin = 70,xmax = 185) +
stat_density2d(geom="tile", aes(fill=..density.., alpha=10*sqrt(..density..)), contour=FALSE, n=100)
有什么办法吗?我在谷歌上搜索了很多解决方案,但没有找到真正的方法。我不介意首先在 ggplot2 中使用热图创建图像,保存它,然后将其用作表面覆盖的输入,但如果它可以在一次绘图调用中完成,那当然会很酷。
这个怎么样?
我将你的内衬图像文件存储在本地目录中的 png 中,可能有一种方法可以在没有中间文件的情况下做到这一点,但我会把它作为一个单独的问题来问。
请注意,这实际上是一个简单的纹理映射案例。纹理保存在您指定的 gameshot.png
文件中。您可以通过向几何体添加更多点并相应地调整纹理贴图坐标来围绕更复杂的对象扭曲文本。
虽然它们在这里不应该是绝对必要的,但我添加了纹理贴图坐标,因为它看起来像文件,默认情况下数据没有对齐 - 事实上 gameshot.png
文件显示是颠倒的。在我看来你指定的png文件与数据不太匹配,我认为在你保存之前某处发生了反转。
library(rgl)
library(plot3D)
library(car)
df <- data.frame(meanX = c(147.34694, 173.89244, 135.73004, 121.93766,
109.72152, 92.53709, 165.46588, 169.77744,
127.01796, 99.34347),
meanY = c(140.40816, 110.99128, 134.56023, 164.18703,
166.04051, 155.97329, 105.29377, 104.42683,
130.17066, 155.99696),
avgDist = c(40.788118, 12.957329, 14.24348, 39.10424,
34.694258, 25.532335, 21.491695,23.528944,
9.309201, 31.916879))
car::scatter3d(x = df$meanX, y = df$meanY, z = df$avgDist, surface = FALSE)
xvek <- c(0,1)
yvek <- c(0,1)
lnx <- length(xvek)
lny <- length(yvek)
zmat <- matrix(0,lnx,lny)
# Setup the Texture coordinates - defaults seem to invert image
# tms <- matrix(c(0,0,1,1),lnx,lny) # generic case (xy-maped texture looks like png file)
# tmt <- matrix(c(0,1,0,1),lnx,lny)
tmt <- matrix(c(1,1,0,0),lnx,lny) # "correct case" (ball density look more like picture)
tms <- matrix(c(1,0,1,0),lnx,lny) # I think the gameshot.png is in error
# Texture file specified in question was stored locally in "gameshot.png"
surface3d(xvek,yvek,zmat,coord=c(3,1),texture_s=tms,texture_t=tmt,
lit=F,fog=T,color="white",textype="rgb",texture="gameshot.png",add=T)
产生这个:
(第二次编辑)我尝试编写更好的代码并确认两个 xy 坐标相同。 ggplot2 theme with no axes or grid 帮我只绘制面板区域。
library(rgl); library(grid); library(gtable)
df <- data.frame(meanX = c(147.34694, 173.89244, 135.73004, 121.93766,
109.72152, 92.53709, 165.46588, 169.77744,
127.01796, 99.34347),
meanY = c(140.40816, 110.99128, 134.56023, 164.18703,
166.04051, 155.97329, 105.29377, 104.42683,
130.17066, 155.99696),
avgDist = c(40.788118, 12.957329, 14.24348, 39.10424,
34.694258, 25.532335, 21.491695,23.528944,
9.309201, 31.916879))
map.colors <- colorRampPalette(c("green","yellow","red"))
# set scale_*_continuous() to plot only the panel region. limits mean xlim (or ylim)
# change "tile" into "raster" because of making noise lines on my screen
densityPlot <- ggplot(df[,1:2], aes(x = meanX, y = ,meanY)) +
stat_density2d(geom="raster", aes(fill=..density.., alpha=sqrt(sqrt(..density..))), contour=FALSE, n=100) +
scale_alpha(range = c(0, 1.0)) + scale_fill_gradientn(colours = map.colors(5)) +
scale_x_continuous(limits=c(70,185), expand = c(0,0)) + scale_y_continuous(limits=c(70,185), expand = c(0,0)) +
geom_point(size=4) # to test XY-coordinate (black points on the map)
open3d()
plot3d( df, type="s", radius=1, col="red", axes=F,
xlim = c(70,185), ylim = c(70,185),
expand = 1 )
plot3d( df, type="h", col="blue", add=T ) # to test XY-coordinate (line segments from z = 0)
axes3d(c("x","y","z") )
show2d({ # show2d uses 2D plot function's output as a texture on a box.
grid.draw(gtable_filter(ggplotGrob(densityPlot), "panel"))
},
expand = 1 , texmipmap = F ) # texmipmap = F makes tone clear (not essential)
# I think this is clearly better than using a intermediate file,
# so I deleted related code. Thanks Mike !