diff --git a/docs/tutorials/render_colored_points.ipynb b/docs/tutorials/render_colored_points.ipynb index 92f5f535..8eaa939f 100644 --- a/docs/tutorials/render_colored_points.ipynb +++ b/docs/tutorials/render_colored_points.ipynb @@ -67,6 +67,7 @@ "\n", "# Data structures and functions for rendering\n", "from pytorch3d.structures import Pointclouds\n", + "from pytorch3d.visualization import AxisArgs, plot_pointclouds\n", "from pytorch3d.renderer import (\n", " look_at_view_transform,\n", " FoVOrthographicCameras, \n", @@ -286,6 +287,86 @@ "plt.grid(\"off\")\n", "plt.axis(\"off\");" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### View pointclouds in Plotly figures\n", + "\n", + "Here we use the PyTorch3D function `plot_pointclouds` to render the Pointcloud in a Plotly figure. `plot_pointclouds` returns a plotly figure with a trace for each pointcloud." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plot_pointclouds(point_cloud)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We will now render a batch of pointclouds. The first pointcloud is the same as above, and the second is all-black and offset by 2 in all dimensions so we can see them on the same plot. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "point_cloud_batch = Pointclouds(points=[verts, verts + 2], features=[rgb, torch.zeros_like(rgb)])\n", + "# render both in the same plot\n", + "fig = plot_pointclouds(point_cloud_batch)\n", + "fig.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# render both in 1 row in different subplots\n", + "fig2 = plot_pointclouds(point_cloud_batch, in_subplots=True, ncols=2)\n", + "fig2.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# modify the plotly figure height and width\n", + "fig2.update_layout(height=500, width=500)\n", + "fig2.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can also modify the axis arguments and axis backgrounds, and title our plots." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "fig3 = plot_pointclouds(point_cloud_batch, xaxis={\"backgroundcolor\":\"rgb(200, 200, 230)\"},\n", + " yaxis={\"backgroundcolor\":\"rgb(230, 200, 200)\"},\n", + " zaxis={\"backgroundcolor\":\"rgb(200, 230, 200)\"}, \n", + " subplot_titles=[\"2 pointclouds\"], # this should have a title for each subplot, titles can be \"\"\n", + " axis_args=AxisArgs(showgrid=True))\n", + "fig3.show()" + ] } ], "metadata": { @@ -298,9 +379,9 @@ "bento/extensions/theme/main.css": true }, "kernelspec": { - "display_name": "pytorch3d (local)", + "display_name": "pytorch3d_etc (local)", "language": "python", - "name": "pytorch3d_local" + "name": "pytorch3d_etc_local" }, "language_info": { "codemirror_mode": { diff --git a/docs/tutorials/render_textured_meshes.ipynb b/docs/tutorials/render_textured_meshes.ipynb index 1e3033cc..678a37df 100644 --- a/docs/tutorials/render_textured_meshes.ipynb +++ b/docs/tutorials/render_textured_meshes.ipynb @@ -93,6 +93,7 @@ "\n", "# Data structures and functions for rendering\n", "from pytorch3d.structures import Meshes\n", + "from pytorch3d.visualization import AxisArgs, plot_meshes\n", "from pytorch3d.renderer import (\n", " look_at_view_transform,\n", " FoVPerspectiveCameras, \n", @@ -103,7 +104,8 @@ " MeshRenderer, \n", " MeshRasterizer, \n", " SoftPhongShader,\n", - " TexturesUV\n", + " TexturesUV,\n", + " TexturesVertex\n", ")\n", "\n", "# add path for demo utils functions \n", @@ -555,6 +557,111 @@ "image_grid(images.cpu().numpy(), rows=4, cols=5, rgb=True)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 7. Plotly visualization \n", + "If you only want to visualize a mesh, you don't really need to use a differentiable renderer - instead we support plotting of Meshes with plotly. For these Meshes, we use TexturesVertex to define a texture for the rendering.\n", + "`plot_meshes` creates a Plotly figure with a trace for each Meshes object. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "verts, faces_idx, _ = load_obj(obj_filename)\n", + "faces = faces_idx.verts_idx\n", + "\n", + "# Initialize each vertex to be white in color.\n", + "verts_rgb = torch.ones_like(verts)[None] # (1, V, 3)\n", + "textures = TexturesVertex(verts_features=verts_rgb.to(device))\n", + "\n", + "# Create a Meshes object\n", + "mesh = Meshes(\n", + " verts=[verts.to(device)], \n", + " faces=[faces.to(device)],\n", + " textures=textures\n", + ")\n", + "\n", + "# Render the plotly figure\n", + "fig = plot_meshes(mesh)\n", + "fig.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# use Plotly's default colors (no texture)\n", + "mesh = Meshes(\n", + " verts=[verts.to(device)], \n", + " faces=[faces.to(device)]\n", + ")\n", + "\n", + "# Render the plotly figure\n", + "plot_meshes(mesh)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# create a batch of meshes, and offset one to prevent overlap\n", + "mesh_batch = Meshes(\n", + " verts=[verts.to(device), (verts + 2).to(device)], \n", + " faces=[faces.to(device), faces.to(device)]\n", + ")\n", + "\n", + "plot_meshes(mesh_batch)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# extend the batch to have 4 meshes\n", + "mesh = mesh_batch.extend(2)\n", + "\n", + "# visualize the batch in different subplots, 2 per row\n", + "# title the subplots\n", + "fig = plot_meshes(mesh, in_subplots=True, ncols=2,\n", + " subplot_titles = [\"cow\" + str(i) for i in range(1,5)] # this should have a title for each subplot, titles can be \"\"\n", + " )\n", + "# we can update the figure height and width\n", + "fig.update_layout(height=500, width=500)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can also modify the axis arguments and axis backgrounds." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "fig2 = plot_meshes(mesh_batch, xaxis={\"backgroundcolor\":\"rgb(200, 200, 230)\"},\n", + " yaxis={\"backgroundcolor\":\"rgb(230, 200, 200)\"},\n", + " zaxis={\"backgroundcolor\":\"rgb(200, 230, 200)\"}, \n", + " subplot_titles=[\"2 meshes\"],\n", + " axis_args=AxisArgs(showgrid=True))\n", + "fig2.show()" + ] + }, { "cell_type": "markdown", "metadata": { @@ -562,15 +669,15 @@ "id": "t3qphI1ElUb5" }, "source": [ - "## 7. Conclusion\n", - "In this tutorial we learnt how to **load** a textured mesh from an obj file, initialize a PyTorch3D datastructure called **Meshes**, set up an **Renderer** consisting of a **Rasterizer** and a **Shader**, and modify several components of the rendering pipeline. " + "## 8. Conclusion\n", + "In this tutorial we learnt how to **load** a textured mesh from an obj file, initialize a PyTorch3D datastructure called **Meshes**, set up an **Renderer** consisting of a **Rasterizer** and a **Shader**, and modify several components of the rendering pipeline. We also learned how to render Meshes in Plotly figures." ] } ], "metadata": { "accelerator": "GPU", "anp_metadata": { - "path": "fbsource/fbcode/vision/fair/pytorch3d/docs/tutorials/render_textured_meshes.ipynb" + "path": "notebooks/render_textured_meshes.ipynb" }, "bento_stylesheets": { "bento/extensions/flow/main.css": true, @@ -588,9 +695,9 @@ "backup_notebook_id": "569222367081034" }, "kernelspec": { - "display_name": "intro_to_cv", + "display_name": "pytorch3d_etc (local)", "language": "python", - "name": "bento_kernel_intro_to_cv" + "name": "pytorch3d_etc_local" }, "language_info": { "codemirror_mode": {