diff --git a/.gitignore b/.gitignore
index 8937b78..174e541 100644
--- a/.gitignore
+++ b/.gitignore
@@ -48,3 +48,5 @@ docs/_build
# virtualenv
venv/
ENV/
+*.obj
+*.pth
diff --git a/Eval.ipynb b/Eval.ipynb
new file mode 100644
index 0000000..89a8041
--- /dev/null
+++ b/Eval.ipynb
@@ -0,0 +1,410 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "!nvidia-smi"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "%matplotlib inline\n",
+ "%reload_ext autoreload\n",
+ "%autoreload 2"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ ""
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "import logging\n",
+ "import numpy as np\n",
+ "import torch, os\n",
+ "from sicgan.utils.metrics import compare_meshes\n",
+ "from tqdm import tqdm\n",
+ "\n",
+ "from IPython.core.display import display, HTML\n",
+ "from sicgan.config import Config\n",
+ "from sicgan.models import Pixel2MeshHead\n",
+ "from sicgan.models import GraphConvClf\n",
+ "from mpl_toolkits.mplot3d import Axes3D\n",
+ "from sicgan.models import MeshLoss\n",
+ "from pytorch3d.io import load_obj, save_obj\n",
+ "import matplotlib.pyplot as plt\n",
+ "import matplotlib as mpl\n",
+ "import matplotlib.animation #import FuncAnimation\n",
+ "from matplotlib.animation import FuncAnimation\n",
+ "\n",
+ "\n",
+ "from sicgan.data.build_data_loader import build_data_loader\n",
+ "display(HTML(\"\"))\n",
+ "device = torch.device(\"cuda:0\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from PyGEL3D import gel\n",
+ "from PyGEL3D import js\n",
+ "def plot_mesh(mesh):\n",
+ " save_obj('mesh.obj', mesh.verts_packed(), mesh.faces_packed())\n",
+ " js.set_export_mode()\n",
+ " m = gel.obj_load('mesh.obj')\n",
+ " js.display(m, smooth=False)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 20,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "_C = Config('config/train_p2m.yml',[])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 21,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "1500"
+ ]
+ },
+ "execution_count": 21,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "data_loader = build_data_loader(_C, \"MeshVox\", split_name='test', multigpu=False)\n",
+ "len(data_loader)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "cp = torch.load('pixel2mesh_R50.pth')[\"best_states\"][\"model\"]\n",
+ "from collections import OrderedDict\n",
+ "new_cp = OrderedDict()\n",
+ "for k, v in cp.items():\n",
+ " name = k[7:] # remove `module.`\n",
+ " if name != 'K':\n",
+ " new_cp[name] = v"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 9,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "model = Pixel2MeshHead(_C).cuda()\n",
+ "model.load_state_dict(new_cp)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 22,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ " 0%| | 0/1500 [00:01, ?it/s]\n"
+ ]
+ }
+ ],
+ "source": [
+ "for batch in tqdm(data_loader):\n",
+ " batch = data_loader.postprocess(batch, device)\n",
+ " imgs, meshes_gt = batch[0], batch[1]\n",
+ " break"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 23,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "torch.Size([32, 3, 137, 137])"
+ ]
+ },
+ "execution_count": 23,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "imgs.shape"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 24,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "_,m = model(imgs)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 25,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "[,\n",
+ " ,\n",
+ " ]"
+ ]
+ },
+ "execution_count": 25,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "m"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "scrolled": false
+ },
+ "outputs": [],
+ "source": [
+ "plot_mesh(meshes_gt)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "plot_mesh(meshes_pred[-1])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Evaluation - Change this"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from pytorch3d.ops import sample_points_from_meshes\n",
+ "from pytorch3d.loss import (\n",
+ " chamfer_distance, \n",
+ " mesh_edge_loss, \n",
+ " mesh_laplacian_smoothing, \n",
+ " mesh_normal_consistency,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "device = torch.device(\"cuda:0\")\n",
+ "class_names = {\n",
+ " \"03001627\": \"chair\",\n",
+ " \"04379243\": \"table\",\n",
+ "}\n",
+ "\n",
+ "num_instances = {i: 0 for i in class_names}\n",
+ "chamfer = {i: 0 for i in class_names}\n",
+ "# normal = {i: 0 for i in class_names}\n",
+ "f1_1e_4 = {i: 0 for i in class_names}\n",
+ "f1_2e_4 = {i: 0 for i in class_names}"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "loss_fn_kwargs = {\n",
+ " \"chamfer_weight\": 1,\n",
+ " \"normal_weight\": 0,\n",
+ " \"edge_weight\": 0,\n",
+ " \"gt_num_samples\": 5000,\n",
+ " \"pred_num_samples\": 5000,\n",
+ "}\n",
+ "\n",
+ "mesh_loss = MeshLoss(**loss_fn_kwargs).cuda()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sample_trg = sample_points_from_meshes(meshes_gt, 5000)\n",
+ "sample_src = sample_points_from_meshes(meshes_pred[-1], 5000)\n",
+ "chamfer_distance(sample_trg, sample_src)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "num_batch_evaluated = 0\n",
+ "cd_chair = []\n",
+ "cd_table = []\n",
+ "for batch in tqdm(data_loader):\n",
+ " batch = data_loader.postprocess(batch, device)\n",
+ " imgs, meshes_gt, _, _, id_strs = batch\n",
+ "# sids = [id_str.split(\"-\")[0] for id_str in id_strs]\n",
+ " sid = id_strs[0].split(\"-\")[0]\n",
+ "# for sid in sids:\n",
+ "# num_instances[sid] += 1\n",
+ "\n",
+ " # with inference_context(model):\n",
+ " \n",
+ " # Change this\n",
+ " _,meshes_pred = model(imgs) #Removed Voxels\n",
+ " \n",
+ " loss, _ = mesh_loss(meshes_gt, meshes_pred[-1])\n",
+ " if sid == '03001627':\n",
+ " cd_chair.append(loss.item())\n",
+ " elif sid == '04379243':\n",
+ " cd_table.append(loss.item())\n",
+ "\n",
+ "\n",
+ "# cur_metrics = compare_meshes(\n",
+ "# meshes_pred[-1], meshes_gt, scale=0.57, thresholds=[0.01, 0.014142], reduce=False\n",
+ "# )\n",
+ "# cur_metrics[\"verts_per_mesh\"] = meshes_pred[-1].num_verts_per_mesh().cpu()\n",
+ "# cur_metrics[\"faces_per_mesh\"] = meshes_pred[-1].num_faces_per_mesh().cpu()\n",
+ "\n",
+ "# for i, sid in enumerate(sids):\n",
+ "# chamfer[sid] += cur_metrics[\"Chamfer-L2\"][i].item()\n",
+ "# # normal[sid] += cur_metrics[\"AbsNormalConsistency\"][i].item()\n",
+ "# f1_1e_4[sid] += cur_metrics[\"F1@%f\" % 0.01][i].item()\n",
+ "# f1_2e_4[sid] += cur_metrics[\"F1@%f\" % 0.014142][i].item()\n",
+ "\n",
+ "# num_batch_evaluated += 1\n",
+ "# print(\"Evaluated %d / %d batches\" % (num_batch_evaluated, len(data_loader)))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "{\"chamfer\": chamfer, \"f1_1e_4\": f1_1e_4, \"f1_2e_4\": f1_2e_4}"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import numpy as np"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "scrolled": false
+ },
+ "outputs": [],
+ "source": [
+ "np.mean(cd_chair), np.mean(cd_table)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "dasyed",
+ "language": "python",
+ "name": "dasyed"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.8.1"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/Untitled.ipynb b/Untitled.ipynb
deleted file mode 100644
index 479a491..0000000
--- a/Untitled.ipynb
+++ /dev/null
@@ -1,395 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": 1,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/html": [
- ""
- ],
- "text/plain": [
- ""
- ]
- },
- "metadata": {},
- "output_type": "display_data"
- }
- ],
- "source": [
- "from IPython.core.display import display, HTML\n",
- "display(HTML(\"\"))\n",
- "%load_ext autoreload\n",
- "%autoreload 2 "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {},
- "outputs": [],
- "source": [
- "from sicgan.models import MeshLoss"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "metadata": {},
- "outputs": [],
- "source": [
- "from PyGEL3D import gel\n",
- "from PyGEL3D import js\n",
- "import re\n",
- "from pytorch3d.io import load_obj, save_obj"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "metadata": {},
- "outputs": [],
- "source": [
- "from sicgan.models import Pixel2MeshHead\n",
- "from sicgan.config import Config\n",
- "\n",
- "\n",
- "_C = Config('./config/sicgan_train.yml', [])\n",
- "G = Pixel2MeshHead(_C).cuda()"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "metadata": {},
- "outputs": [],
- "source": [
- "from sicgan.data.build_data_loader import build_data_loader\n",
- "train = build_data_loader(_C, \"MeshVox\", split_name='train')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "metadata": {},
- "outputs": [],
- "source": [
- "import matplotlib.pyplot as plt\n",
- "%matplotlib inline"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 20,
- "metadata": {},
- "outputs": [],
- "source": [
- "from tqdm import tqdm"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 21,
- "metadata": {},
- "outputs": [
- {
- "name": "stderr",
- "output_type": "stream",
- "text": [
- " 0%| | 0/20634 [00:01, ?it/s]\n"
- ]
- }
- ],
- "source": [
- "for data in tqdm(train):\n",
- " imgs = data[0]\n",
- " meshes = data[1]\n",
- " break"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 19,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- ""
- ]
- },
- "execution_count": 19,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "meshes"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 8,
- "metadata": {
- "scrolled": true
- },
- "outputs": [
- {
- "name": "stderr",
- "output_type": "stream",
- "text": [
- "Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).\n"
- ]
- },
- {
- "data": {
- "text/plain": [
- ""
- ]
- },
- "execution_count": 8,
- "metadata": {},
- "output_type": "execute_result"
- },
- {
- "data": {
- "image/png": "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\n",
- "text/plain": [
- "