{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":77752,"databundleVersionId":8538126,"sourceType":"competition"}],"dockerImageVersionId":30698,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# **_preliminaries: Install Libraries_**\n> **외부 라이브러리 설치**","metadata":{}},{"cell_type":"code","source":"%%capture\n!yes | pip install trimesh==3.21.6\n!yes | pip install open3d==0.17.0\n!yes | pip install natsort==8.3.1","metadata":{"execution":{"iopub.status.busy":"2024-05-13T17:02:48.448533Z","iopub.execute_input":"2024-05-13T17:02:48.449107Z","iopub.status.idle":"2024-05-13T17:03:54.853831Z","shell.execute_reply.started":"2024-05-13T17:02:48.449078Z","shell.execute_reply":"2024-05-13T17:03:54.851825Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **_preliminaries: Import Libraries_**\n> **필요한 라이브러리를 임포팅**","metadata":{}},{"cell_type":"code","source":"from typing import Tuple\nfrom natsort import natsorted\nfrom tqdm import tqdm\n\nimport os\nimport json\nimport errno\nimport random\n\nimport numpy as np\nimport pandas as pd\n\nimport matplotlib.pyplot as plt\n%matplotlib inline\nimport IPython.display as IPd\n\nimport plotly.express as px\nimport plotly.graph_objs as go\n\nimport torch\nfrom torch.utils.data import Dataset, DataLoader\n\nimport trimesh\nimport open3d as o3d\n\nfrom sklearn.decomposition import PCA\n\nfrom sklearn.svm import SVC","metadata":{"execution":{"iopub.status.busy":"2024-05-13T17:03:54.856814Z","iopub.execute_input":"2024-05-13T17:03:54.857285Z","iopub.status.idle":"2024-05-13T17:04:01.657092Z","shell.execute_reply.started":"2024-05-13T17:03:54.857236Z","shell.execute_reply":"2024-05-13T17:04:01.656135Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **_preliminaries: Set Random seed_**\n> **랜덤 시드 고정**\n\n- **Baseline 성능 원복을 위해, 랜덤 시드를 임의로 변경을 금합니다.**","metadata":{}},{"cell_type":"code","source":"seed = 42\n\nrandom.seed(seed)\nnp.random.seed(seed)\nos.environ[\"PYTHONHASHSEED\"] = str(seed)\n\no3d.utility.random.seed(seed)\n\ntorch.manual_seed(seed)","metadata":{"execution":{"iopub.status.busy":"2024-05-13T17:04:01.658434Z","iopub.execute_input":"2024-05-13T17:04:01.659029Z","iopub.status.idle":"2024-05-13T17:04:01.671935Z","shell.execute_reply.started":"2024-05-13T17:04:01.659002Z","shell.execute_reply":"2024-05-13T17:04:01.670890Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **_preliminaries: Set Input Path_**\n> **데이터 디렉토리 지정**\n>\n> **제출 시 Label은 0, 1, 2 등의 정수형으로 제출해야합니다. class2Label 파일은 \"클래스 명:클래스에 해당하는 정수형 라벨\"로 구성되어 있습니다**","metadata":{}},{"cell_type":"code","source":"BASE_PATH = '/kaggle/input/2024-ml-project-3d/modelnet10'\n\nDATA_PATH   = os.path.join(BASE_PATH, 'dataset')\nLABEL_PATH  = os.path.join(BASE_PATH, 'class2Label.json')\nSUBMIT_PATH = os.path.join(BASE_PATH, 'sample_submit.csv')","metadata":{"execution":{"iopub.status.busy":"2024-05-13T17:06:31.623811Z","iopub.execute_input":"2024-05-13T17:06:31.624218Z","iopub.status.idle":"2024-05-13T17:06:31.631259Z","shell.execute_reply.started":"2024-05-13T17:06:31.624190Z","shell.execute_reply":"2024-05-13T17:06:31.629851Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# __ModelNet10__\n\n> **[ModelNet10 Dataset](https://modelnet.cs.princeton.edu/)은 3D 모델 분류를 위한 데이터 셋 중 하나로, 다양한 형태와 크기를 가진 10 종류의 클래스 (bathtub, bed, chair, desk, dresser, monitor, night_stand, sofa, table, toilet)에 속하는 총 4,899개의 3D CAD 모델로 구성되어 있습니다.**\n> \n> **ModelNet10은 다양한 각도에서 촬영된 3D CAD 모델로, 이를 통해 물체의 다양한 시점에서의 모습을 포착할 수 있습니다.**\n\n- **이번 텀프로젝트에서는 ModelNet10 데이터 셋을 통해 3D 데이터로부터 Hand-crafted Feature를 기술하는 다양한 방법을 알아봅니다.**\n- **참고 사항으로, 현재 제공된 데이터 셋은 코드 실행 시간의 단축을 위해 ModelNet10을 샘플링 하여 제공하고 있습니다.**","metadata":{}},{"cell_type":"markdown","source":"<h3><center>ModelNet10 Preview</center></h3>\n<center><img src=\"https://www.researchgate.net/publication/330933095/figure/fig4/AS:960275410857998@1605958907799/The-some-samples-retrieval-result-on-ModelNet-10.png\" width=\"600\" height=\"500\"/></center>\n<h4></h4>\n<h4><center><a href=\"https://arxiv.org/pdf/1406.5670.pdf\">[source paper] 3D ShapeNets: A Deep Representation for Volumetric Shapes [Zhirong et al.]</a></center></h4>","metadata":{}},{"cell_type":"markdown","source":"# __*Visualization*__\n> **3D 데이터를 로드 및 시각화**\n\n- **이번 텀프로젝트에서는 3D 데이터를 시각화하는 코드를 제공합니다. 마우스로 3D 모델을 돌려보며 시각화 과정을 살펴보시길 바랍니다.**","metadata":{}},{"cell_type":"code","source":"class_name_list = sorted(os.listdir(os.path.join(DATA_PATH, 'train')))\n\nscene_list = list()\n\nfor class_name in class_name_list:\n    off = random.choice(os.listdir(os.path.join(DATA_PATH, 'train', class_name)))\n    \n    mesh = trimesh.load(os.path.join(DATA_PATH, 'train', class_name, off))\n    \n    scene = trimesh.Scene()\n    \n    scene.add_geometry(mesh)\n    \n    scene_list.append(scene)","metadata":{"execution":{"iopub.status.busy":"2024-05-13T17:06:38.599669Z","iopub.execute_input":"2024-05-13T17:06:38.600120Z","iopub.status.idle":"2024-05-13T17:06:39.152528Z","shell.execute_reply.started":"2024-05-13T17:06:38.600090Z","shell.execute_reply":"2024-05-13T17:06:39.151445Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for class_name, scene in zip(class_name_list, scene_list):\n    IPd.display(IPd.HTML(f\"<h4 style='text-align:center;'>{class_name}</h4>\"))\n    IPd.display(scene.show())","metadata":{"execution":{"iopub.status.busy":"2024-05-13T17:06:41.162741Z","iopub.execute_input":"2024-05-13T17:06:41.163774Z","iopub.status.idle":"2024-05-13T17:06:41.382794Z","shell.execute_reply.started":"2024-05-13T17:06:41.163724Z","shell.execute_reply":"2024-05-13T17:06:41.381346Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **_Dataset_**\n> **아래는 사용자 정의 데이터 셋을 생성하는 과정입니다. 해당 과정은 기계학습 수업의 범위를 벗어나는 내용이므로, 코드 전체가 제공되어 있습니다.**\n>\n> **그러므로 코드를 이해하지 않아도 되나 사용 시 편리함을 위해 아래 train data loader 및 test data loader의 출력 예시를 참고해주시길 바랍니다.**\n\n- **상단 셀의 _preliminaries: Set Input Path_ 에서 설명했던 바처럼, <span style='background-color:yellow'>class2Label 파일은 class ModelNet10() 내 self.le 변수에서 사용되며 데이터 셋 로드 시 클래스 명을 문자열이 아닌 클래스 명에 해당하는 정수형 라벨로 불러오도록 합니다.</span>**","metadata":{}},{"cell_type":"code","source":"class ModelNet10(Dataset):\n    def __init__(self, root: str, split: str, class2label:dict):\n        self.root = root\n        self.split = split.lower()\n        assert split in ['train', 'test']\n        \n        if class2label is not None:\n            self.class2label = class2label\n        \n        self.path, self.label = list(), list()\n        \n        if self.split == 'train':\n            self.classes = sorted(os.listdir(os.path.join(self.root, self.split)))\n            self.le = {self.classes[i]: self.class2label[self.classes[i]] for i in range(len(self.classes))}\n        \n            for class_name in self.classes:\n                class_path = os.path.join(self.root, self.split, class_name)\n\n                for off in os.listdir(class_path):\n                    if off.endswith('off'):\n                        self.path.append(os.path.join(class_path, off))\n                        self.label.append(self.le[class_name])\n                    else:\n                        raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), off)\n                        \n        elif self.split == 'test':\n            fname_list = natsorted(os.listdir(os.path.join(self.root, self.split)))\n            for fname in fname_list:\n                self.path.append(os.path.join(self.root, self.split, fname))\n            \n    def __getitem__(self, index):\n        if self.split == 'train':\n            return self.path[index], self.label[index]\n        elif self.split == 'test':\n            return self.path[index]\n    \n    def __len__(self):\n        return len(self.path)","metadata":{"execution":{"iopub.status.busy":"2024-05-13T17:07:00.632796Z","iopub.execute_input":"2024-05-13T17:07:00.633191Z","iopub.status.idle":"2024-05-13T17:07:00.649313Z","shell.execute_reply.started":"2024-05-13T17:07:00.633154Z","shell.execute_reply":"2024-05-13T17:07:00.647963Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# class2Label 파일 로드\nwith open(LABEL_PATH, 'r') as j:\n    class2label = json.load(j)\n    \n# 학습 데이터 셋(train_dataset) 및 학습 데이터로더(train_dataloader) 생성\ntrain_dataset = ModelNet10(root=DATA_PATH, split='train', class2label=class2label)\ntrain_dataloader = DataLoader(train_dataset, batch_size=1, shuffle=True)\n\n# 평가 데이터 셋(test_dataset) 및 평가 데이터로더(test_dataloader) 생성\ntest_dataset = ModelNet10(root=DATA_PATH, split='test', class2label=None)\ntest_dataloader = DataLoader(test_dataset, batch_size=1, shuffle=False)","metadata":{"execution":{"iopub.status.busy":"2024-05-13T17:07:04.645563Z","iopub.execute_input":"2024-05-13T17:07:04.645992Z","iopub.status.idle":"2024-05-13T17:07:04.671634Z","shell.execute_reply.started":"2024-05-13T17:07:04.645961Z","shell.execute_reply":"2024-05-13T17:07:04.670530Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"> **하단의 셀은 학습 데이터로더(train_dataloader)와 평가 데이터로더(test_dataloader)로부터 데이터를 로드하는 예시 코드입니다.**\n>\n> **<span style=\"color:red\">학습 데이터로더(train_dataloader)는 데이터 파일의 디렉토리 및 정수형 라벨을 로드할 수 있는 반면, 평가 데이터로드(test_dataloader)는 데이터 파일의 디렉토리만 로드할 수 있습니다.</span>**\n>\n> - **특히, 출력 예시를 통해 학습 데이터로더를 통해 <span style='background-color:yellow'>로드된 label을 label.item()을 통해 정수형으로 변환해야함</span>을 알면 좋습니다.**","metadata":{}},{"cell_type":"code","source":"# 반복문을 통해 학습 데이터로더로부터 데이터 디렉토리(off)와 정수형 라벨(label)을 받음\nfor off, label in train_dataloader:\n    print(f'TRAIN DATALOADER\\npath: {off}\\nlabel: {label}\\n')\n    print(f'TRAIN DATALOADER\\npath: {off[0]}\\nlabel: {label.item()}'); break\n    \n# 반복문을 통해 평가 데이터로더로부터 데이터 디렉토리(off)를 받음\nfor off in test_dataloader:\n    print(f'\\nTEST  DATALODER\\npath: {off[0]}'); break","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **_Feature Extraction_**\n> **3D 데이터로부터 Hand-crafted Feature를 기술하기 위해서는 3D 데이터를 표현하는 방법을 알아야합니다.**\n>\n> **<span style=\"color:red\">다음은 3D 데이터를 표현하는 세 방법(Point Cloud, Voxel, Mesh)에 대한 설명입니다.</span>**","metadata":{}},{"cell_type":"markdown","source":"### **<span style=\"color:CornflowerBlue\">I. Point Cloud</span>**\n<center><img src=\"https://upload.wikimedia.org/wikipedia/commons/4/4c/Point_cloud_torus.gif\"></center>\n\n> **Point Cloud는 3D 공간 상의 점(Point)들의 집합을 의미합니다. <span style='background-color:yellow'>3D 데이터는 일반적으로 3차원 상의 (x, y, z) 점들을 이용하여 표현하는데, 이들의 집합을 Point Cloud라고 명합니다.</span>**\n>\n> **Point Cloud는 점(Point)들의 위치와 색상 정보를 담고 있으며, 이를 이용하여 3D 객체를 구성하고 객체의 특징을 추출하여 분석할 수 있는 지표로 활용할 수 있습니다.**","metadata":{}},{"cell_type":"markdown","source":"### **<span style=\"color:CornflowerBlue\">II. Voxel</span>**\n\n<center><img src=\"https://www.ifp.uni-stuttgart.de/img/forschung/computer_vision/ALS-fig2.JPG\" width=\"500\" height=\"400\"></center>\n\n> **Voxel은 Volume + Pixel의 합성어로, 3차원 공간에서의 픽셀 형식으로 표현한 방법입니다.**\n>\n> **<span style='background-color:yellow'>3차원 공간에서 일정한 크기의 정육면체를 이용해 데이터를 표현</span>하며, 각 Voxel에는 해당 위치의 밀도, 질량, 색상 등의 정보를 담고 있습니다.**\n>\n> **해당 정보를 통해 전체 3D 모델을 표현할 수 있으며, 이산적인 공간을 가지는 Voxel의 특성 상 다른 3D 모델링 방식과는 표현의 차이가 있습니다.**","metadata":{}},{"cell_type":"markdown","source":"### **<span style=\"color:CornflowerBlue\">III. Mesh</span>**\n\n<center><img src=\"https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99BCFB4C5C360FD426\" width=\"600\" height=\"400\"></center>\n\n> **Mesh는 3D 모델링에서 사용되는 격자 구조의 표현 방법으로, 정점(Vertex), 선(Edge), 면(Polygon, Face)을 토대로 물체를 표현하고 인식합니다.**\n>\n> **그 중 <span style='background-color:yellow'>정점(Vertex)은 Point Cloud의 한 Point와 동일하게 3차원 공간 상의 한 지점의 좌표 (x, y, z)로 표현됩니다.</span>**\n>\n> **선(Edge)은 정점 간의 연결을 나타내며, 하나의 선은 두 정점을 필요로하므로 두 좌표 (x1, y1, z1), (x2, y2, z2)로 표현됩니다.**\n>\n> **<span style='background-color:yellow'>면(Polygon, Face)은 정점과 선을 이용하여 구성된 평면 도형으로, 최소 셋 이상의 정점(삼각형)을 필요로 합니다.</span>**","metadata":{"execution":{"iopub.status.busy":"2023-04-17T17:13:23.029892Z","iopub.execute_input":"2023-04-17T17:13:23.030670Z","iopub.status.idle":"2023-04-17T17:13:23.036319Z","shell.execute_reply.started":"2023-04-17T17:13:23.030625Z","shell.execute_reply":"2023-04-17T17:13:23.034595Z"}}},{"cell_type":"markdown","source":"- **아래는 Voxel, Point cloud, Mesh(Polygon mesh)의 그림 및 데이터의 특성을 나타내는 표입니다.**\n<center><img src=\"https://i.ytimg.com/vi/vziVsrCaMHY/maxresdefault.jpg\" width=\"700\" height=\"700\"></center>","metadata":{"execution":{"iopub.status.busy":"2023-04-17T17:15:08.478255Z","iopub.execute_input":"2023-04-17T17:15:08.478721Z","iopub.status.idle":"2023-04-17T17:15:08.487509Z","shell.execute_reply.started":"2023-04-17T17:15:08.478681Z","shell.execute_reply":"2023-04-17T17:15:08.485859Z"}}},{"cell_type":"markdown","source":"# **_Read File_**\n> **해당 3D 데이터 셋은 OFF 확장자 형식의 파일(.off)로 구성되어 있습니다.**\n>\n> **<span style='background-color:yellow'>OFF 파일은 3D 모델링에서 자주 사용되는 파일 형식 중 하나로, 다각형 Mesh를 포함하는 3D 객체를 표현하는데 사용됩니다.</span>**\n>\n> **따라서 OFF 파일로부터 정점(Vertex)과 면(Face)의 정보를 얻을 수 있으며, 아래는 해당 형식의 데이터 파일에 대한 예시와 설명입니다.**\n\n- **OFF 확장자 형식 데이터 파일 예시**\n\n```HTML\nOFF\n3 1 0\n0.0 0.0 0.0\n1.0 0.0 0.0\n0.0 1.0 0.0\n3 0 1 2\n```\n\n- **위 예시는 세 정점(Vertex)과 하나의 면(Face)으로 구성된 객체를 표현합니다.**\n    - **첫 번째 줄의 OFF는 해당 파일이 .off 확장자 형식임을 나타내는 구분자 역할을 합니다.**\n    - **두 번째 줄은 각각 객체의 Vertex, Face, Edge의 총 개수를 나타냅니다.**\n        - **<span style=\"color:gray\">이 때 .off 파일 형식은 Edge 정보를 포함하지 않으므로, Edge 개수는 항상 0이 됩니다.</span>**\n    - **다음의 세 번째 줄부터 Vertex 개수 만큼의 줄 (3-5줄)은 3차원 상의 Vertex의 좌표 (x, y, z)를 나타냅니다.** \n        - **<span style=\"color:gray\">위 예시에서의 Vertex 좌표의 집합은 Float형 좌표 (x, y, z) = (0.0, 0.0, 0.0), (1.0, 0.0, 0.0), (0.0, 1.0, 0.0)을 포함합니다.</span>**\n    - **마지막으로, 여섯 번째 줄부터는 Face를 구성하는 다각형과 Vertex의 인덱스를 나타냅니다.**\n        - **<span style=\"color:gray\">위 예시에서의 Face는 (3 0 1 2)로, 차례대로 3은 Face의 형태(삼각형)을, 0, 1, 2는 Vertex의 0 번째 인덱스 (0.0, 0.0, 0.0)와 1 번째 인덱스 (1.0, 0.0, 0.0), 2 번째 인덱스 (0.0, 1.0, 0.0)로 구성되어 있음을 의미합니다.</span>**\n\n\n### **<span style='background-color:LightSkyBlue'>Empty Module #1: OFF 확장자 형식의 데이터 파일을 읽어 Vertex를 담은 배열과 Face를 담은 배열을 반환</span>**\n- **<span style='background-color:yellow'>Empty Module #1에서 사용되는 함수: readline(), strip(), split()</span>**\n\n> **[1-1] <span style=\"color:RoyalBlue\">예외 처리</span>**\n>\n> - **readline() 함수와 strip() 함수를 이용하여 첫 번째 줄을 읽은 다음, 해당 파일이 OFF 확장자 형식의 파일인지 확인합니다. 만약 첫 번째 줄이 OFF로 명시되어 있지 않다면 에러: raise ValueError('Not a valid OFF header')를 발생하도록 합니다.** \n>\n> **[1-2] <span style=\"color:RoyalBlue\">Vertex, Face, Edge의 수</span>**\n>\n> **동일한 방법으로 다음 줄을 읽어 Vertex, Face, Edge의 수를 정수형 변수(예: n_vertex, n_face, n_edge)에 저장합니다.**\n>\n> **[1-3] <span style=\"color:RoyalBlue\">리스트에 Vertex 정보 추가</span>**\n>\n> - **다음으로, [1-2]에서 구한 Vertex 수 만큼 줄을 읽으며 해당 줄의 Vertex 정보를 담은 리스트를 다음에 선언된 리스트(vertices)에 추가합니다.**\n>> **이 때 Vertex의 좌표 (x, y, z)는 Float형으로 변환해야 합니다.**\n>\n> **[1-4] <span style=\"color:RoyalBlue\">리스트에 Face 정보 추가</span>** \n>\n> - **다음으로, [1-2]에서 구한 Face 수 만큼 줄을 읽으며 해당 줄의 Face 정보를 담은 리스트를 다음에 선언된 리스트(faces)에 추가합니다.**\n>> **이 때 Face의 정보를 담은 리스트는 Face의 형태를 명시하는 첫 번째 문자에 대해서는 제외한 채 구성하며(예: (3 0 1 2)에서 Face의 형태가 삼각형임을 명시하는 3은 제외합니다), Face를 구성하는 Vertex의 인덱스는 Int형으로 변환해야합니다.**\n>\n> **<span style='background-color:yellow'>코드 구현이 마친 후, 함수를 실행하여 반환된 vertices와 faces의 shape이 다음과 같은지 확인해보시길 바랍니다. vertices.shape = (vertex의 수, 3), faces.shape = (face의 수, 3)</span>**","metadata":{}},{"cell_type":"code","source":"def read_off(off: str) -> Tuple[np.array, np.array]:\n    with open(off, 'r') as f:\n        # [1-1] 예외 처리\n        \n        # [1-2] Vertex, Face, Edge의 수\n        \n        # [1-3] 리스트에 Vertex 정보 추가\n        vertices = list()\n        \n        # [1-4] 리스트에 Face 정보 추가\n        faces = list()\n        \n        # Vertex들의 정보를 담은 리스트(vertices)와 Face들의 정보를 담은 리스트(faces)를 각각 numpy array로 변환하여 튜플 형식으로 반환\n        return np.array(vertices), np.array(faces)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **_Select Method_**\n> **앞서 명시했던 바와 같이, 이번 텀프로젝트에서는 3D 데이터를 표현하는 세 방법을 활용하여 각각 다른 방식의 Hand-crafted Feature를 기술하는 방법을 알아봅니다.**\n>\n> **그러므로 이번 텀프로젝트에서는 아래의 변수 <span style='background-color:yellow'>method에 3D 데이터를 표현하는 방법(point_cloud, voxel, mesh)을 선택하여, 각각의 Baseline을 달성해야 합니다.</span>**","metadata":{}},{"cell_type":"code","source":"# 3D 데이터 표현 방법 선택: 'point_cloud', 'voxel', 'mesh' 중 하나를 선택\nmethod = ''","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **<span style=\"color:CornflowerBlue\">I. Point Cloud</span>**\n\n## **_Visualization_**\n> **Train data를 Point Cloud로 시각화**\n\n- **아래는 Train data 중 한 클래스의 OFF 파일 중 하나를 선택하여, Point Cloud로 시각화하는 과정입니다.**\n    - **<span style='background-color:yellow'>해당 과정은 직접 구현할 필요는 없으나, 앞서 구현한 Empty Module #1의 read_off() 함수로부터 Point Cloud(vertices)를 읽는 과정을 살펴보시길 바랍니다.<span>**\n    - **Empty Method #1이 구현 성공 여부를 알아볼 수 있습니다. (비정상 구현 시, 에러 반환)**\n    - **class_name 변수를 직접 변경하며 Point Cloud 시각화 과정을 살펴보시길 바랍니다.**\n        - **<span style=\"color:gray\">class_name 리스트: ['bathtub', 'bed', 'chair', 'desk', 'dresser', 'monitor', 'night_stand', 'sofa', 'table', 'toilet']</span>**","metadata":{}},{"cell_type":"code","source":"class_name = 'sofa'\n\noff = random.choice(os.listdir(os.path.join(DATA_PATH, 'train', class_name)))\n\nvertices, faces = read_off(os.path.join(DATA_PATH, 'train', class_name, off))\n\nlayout = go.Layout(title={'text':off, 'xanchor':'center', 'yanchor':'top'})\nplot_points = go.Scatter3d(x=vertices[:, 0], y=vertices[:, 1], z=vertices[:, 2], \n                           mode='markers', marker=dict(size=3))\n\nfig = go.Figure(data=[plot_points], layout=layout)\n\nfig.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## **_Visualization_**\n> **Test data를 Point Cloud로 시각화**\n\n- **아래는 Test data 중 하나의 OFF 파일 Point Cloud로 시각화하는 과정입니다.**\n    - **해당 과정은 상단 셀과 동일합니다.**\n    - **<span style='background-color:yellow'>이는 Test data의 Point Cloud 시각화 과정으로부터 직접적인 분류를 진행하고자 함이 아니라, Test data는 Train data에 비해 비교적 Point Cloud의 수가 적음을 보이기 위함입니다.</span>**","metadata":{}},{"cell_type":"code","source":"off = random.choice(os.listdir(os.path.join(DATA_PATH, 'test')))\n\nvertices, _ = read_off(os.path.join(DATA_PATH, 'test', off))\n\nlayout = go.Layout(title={'text':off, 'xanchor':'center', 'yanchor':'top'})\nplot_points = go.Scatter3d(x=vertices[:, 0], y=vertices[:, 1], z=vertices[:, 2], \n                           mode='markers', marker=dict(size=3))\n\nfig = go.Figure(data=[plot_points], layout=layout)\n\nfig.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **_Feature Extraction (Point Cloud)_**\n> **Empty Module #2 - #5는 Point Cloud로부터 Hand-Crafted Feature를 기술하고, 이를 학습 및 평가 데이터로 가공하는 과정을 다룹니다.**\n>\n> **Point Cloud로부터 Hand-Crafted Feature를 기술하는 전체 파이프라인(Empty Module #2 - #3)은 다음과 같습니다.**\n\n- **<span style=\"color:Red\">Empty Module #2: Estimate Normal Vector from Point Clouds</span>**\n> - **우선, <span style='background-color:yellow'>Point Cloud로부터 Normal Vector(법선 벡터)를 추정합니다. Point Cloud는 3차원 상 한 점을 의미하므로, Normal Vector는 Point Cloud 근처의 점들을 활용하여 추정할 수 있습니다.</span> 다음은 Normal Vector 추정 파이프라인을 설명합니다.**\n> - **Normal Vector 추론 파이프라인**\n>>\n>> **1. Point Cloud의 한 점 P의 Nearest Neigbor(최근접 이웃점, P_K) 획득합니다.**\n>>\n>> **2. P와 P_K 간의 Covariance Matrix(공분산 행렬, C)를 계산합니다.**\n>>\n>> **3. C로부터 EigenValues(고유값)와 EigenVectors(고유벡터)를 추정합니다.**\n>>\n>> **4. EigenValues를 기준으로 EigenVectors를 내림차순으로 정렬합니다.**\n>>\n>> **5. EigenValues 중 최소 EigenValue에 해당하는 EigenVector를 Normal Vector로 추정합니다.**\n>> - **<span style=\"color:gray\">EigenValue는 Point Cloud의 집합인 P와 P_K로 구성된 평면의 기울기를 의미하며, 최소 EigenValue는 평면의 기울기가 가장 작은 방향으로, 해당 EigenValue에 해당하는 EigenVector를 P의 Normal Vector로 추정할 수 있습니다.</span>**\n>\n> - **위의 Point Cloud로부터 Normal Vector를 추정하는 과정은 open3d 라이브러리의 함수를 통해 구할 수 있습니다.**\n\n- **<span style=\"color:Red\">Empty Module #3: Compute Histogram of Normal Vector along each axis of Point Cloud</span>**\n> **<span style='background-color:yellow'>Normal Vector의 x, y, z 각 축의 데이터를 히스토그램을 이용하여 Feature를 추출합니다.</span>**\n> - **Histogram(히스토그램)**\n>> **히스토그램이란 데이터의 빈도를 그래프로 나타내는 방법입니다. 히스토그램은 전체 데이터를 일정한 간격으로 나누고, 각 구간에 속하는 데이터의 빈도를 막대 그래프로 나타냅니다.**\n>>\n>> **아래의 Example of Histogram of Normal Vector에서 오른쪽 그래프는 히스토그램을 의미합니다. 히스토그램은 데이터를 일정 구간으로 정하여 표현할 수 있는 장점으로 인해, Feature Extraction을 위해 자주 사용되는 방법입니다.**\n\n\n- **<span style='background-color:yellow'>Point Cloud로부터 Normal Vector를 추정하고, 추정된 Normal Vector의 축 별 히스토그램을 쌓아 데이터를 표현하는 방식을 통해 Point Cloud의 Geometric한 Feature를 기술할 수 있습니다.</span>**\n\n<center><img src=\"https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fu51mL%2FbtqPPCiAG3h%2FpbxiYSKY4i9iqJjwaElEf1%2Fimg.png\" width=\"500\" height=\"500\"></center>\n<h5><center>Example of Normal Vector of Point Cloud</center></h5>\n\n<center><img src=\"https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd9Nghi%2FbtqPYjhHHNI%2F4xDT51NNLhQzNHwMKu6GXK%2Fimg.png\" width=\"600\" height=\"600\"></center>\n<h5><center>Example of Histogram of Normal Vector</center></h5>\n<h4><center><a href=\"https://ieeexplore.ieee.org/stamp/stamp.jsp?arnumber=990988\">[source paper] 3D Object Recognition from Range Images using Local Feature Histograms [Hetzel et al., 2001]</a></center></h4>","metadata":{}},{"cell_type":"markdown","source":"### **<span style='background-color:LightSkyBlue'>Empty Module #2: Estiamte Normal Vector from Point Clouds</span>**\n- **Point Cloud로부터 Normal Vector(법선 벡터)를 추정합니다.**\n- **<span style='background-color:yellow'>Empty Module #2에서 사용되는 함수: o3d.utility.Vector3dVector(), estimate_normals()</span>**\n\n> **[2-1] <span style=\"color:RoyalBlue\">Point Cloud를 받아 인스턴스 변수에 저장</span>** \n>\n> - **인스턴스의 points에 함수 인자로 받은 point_cloud를 3D Point Cloud Vector 형태로 저장합니다.**\n>> **<span style=\"color:Red\">o3d.utility.Vector3dVector() 함수를 사용합니다.</span>**\n>> - **해당 함수는 인자로 받은 point_cloud를 Open3D에서 사용하는 3D Vector Point Cloud 형태로 변환하여 Open3D형 인스턴스의 함수를 사용할 수 있도록 합니다.**\n>> - **<span style='background-color:yellow'>해당 코드의 작동 방식은 함수로부터 반환하여 대입하는 방식이 아닌, Open3D형 인스턴스로부터 내장된 함수를 사용하는 방식입니다. 해당 방식이 익숙치 않아 어렵다면, 구글링을 통해 해결하실 수 있습니다.</span>**\n>\n> **[2-2] <span style=\"color:RoyalBlue\">Open3D 인스턴스로부터 Normal Vector 추정</span>**\n>\n> - **인스턴스 변수로부터 Open3D의 내장 함수를 사용하여 법선 벡터를 추정합니다.**\n>> **<span style=\"color:Red\">Open3D 인스턴스 내장 함수인 estimate_normals() 함수를 사용합니다.</span>** \n>> - **해당 함수가 정상적으로 작동되려면 [2-1]에서의 구현이 정상적으로 작동되어, 인스턴스의 points 속성에 Open3D형 Point Cloud 데이터가 포함되어 있어야합니다.**\n>>\n>> **estimate_normals() 함수의 파라미터로는 다음이 사용됩니다.**\n>> - **search_param: 법선 추정을 위해 사용되는 검색 알고리즘입니다. 이번에는 KD-Tree와 Ball-Tree 검색 알고리즘을 혼합한 방식이 사용됩니다. (o3d.geometry.KDTreeSearchParamHybrid())**\n>> - **다시, KDTreeSearchParamHybrid()함수는 radius와 max_nn 파라미터를 받아 사용합니다.**\n>>     - **<span style=\"color:gray\">radius는 주변 이웃 점(Point)을 검색하기 위한 반경을, max_nn은 최대 이웃 점의 개수를 의미합니다.</span>**\n>\n> **[2-3] <span style=\"color:RoyalBlue\">추정된 Normal Vector를 반환</span>**\n>\n> - **추정된 Normal Vector는 Open3D 인스턴스 내 normals에 저장되어 있습니다. 이를 numpy array 형식으로 변환하여 반환합니다.**\n\n- **참고: [Open3D Gemotry Documentation](http://www.open3d.org/docs/release/python_api/open3d.geometry.html#)**","metadata":{}},{"cell_type":"code","source":"def estimate_normal_vector_from_point_cloud(point_cloud, radius: int = None, max_nn: int = None) -> np.array:\n    # Open3D 라이브러리에서 제공하는 Point Cloud 데이터를 다루는 함수를 활용해 인스턴스 변수 pcd를 반환받음\n    pcd = o3d.geometry.PointCloud()\n    \n    # [2-1] Point Cloud 인자(point_cloud)로부터 3D Vector인 Point Cloud 형으로 인스턴스의 points(pcd.points)에 저장 \n    \n    # [2-2] Open3D 인스턴스로부터 Normal Vector 추정\n    \n    # [2-3] Open3D 인스턴스로부터 추정된 Normal Vector를 numpy array로 변환하여 반환\n    return","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### **<span style='background-color:LightSkyBlue'>Empty Module #3: Compute Histogram of Normal Vector along each axis of Point Cloud</span>**\n- **Normal Vector (x, y, z)의 각 축 (x축, y축, z축)의 데이터를 히스토그램을 이용하여 Feature를 추출합니다.**\n- **<span style='background-color:yellow'>Empty Module #3에서 사용되는 함수: np.histogram(), np.concatenate(), np.sum()</span>**\n> **[3-1] <span style=\"color:RoyalBlue\">Normal Vector의 축 별 히스토그램 생성</span>** \n>\n> - **인자로 받은 normal_vector의 각 축 (x, y, z) 별로 히스토그램을 생성합니다.**\n>> **<span style=\"color:Red\">np.histogram() 함수를 사용합니다.</span>** \n>>\n>> **인자로 받은 n_bins와 n_range를 활용하여 Normal Vector의 축 별 히스토그램을 생성하여 변수에 각각 저장합니다**\n>\n> **[3-2] <span style=\"color:RoyalBlue\">생성된 1차원 히스토그램 배열 결합</span>**\n>\n> - **위에서 생성한 1차원 히스토그램 배열을 결합합니다. <span style='background-color:yellow'>결합한 배열을 Feature로 사용합니다.</span>**\n>> **<span style=\"color:Red\">np.concatenate() 함수를 사용합니다.</span>**\n>\n> **[3-3] <span style=\"color:RoyalBlue\">결합한 배열 정규화</span>**\n>\n> - **위에서 결합한 배열을 배열의 전체 합으로 나누어 정규화합니다.**\n>> **<span style=\"color:Red\">np.sum() 함수를 사용합니다.</span>**","metadata":{}},{"cell_type":"code","source":"def compute_histogram_of_normal_vector(normal_vector, n_bins: int = 10, n_range: tuple = (-1,1)):\n    # [3-1] Normal Vector의 축 별 히스토그램 생성\n    \n    # [3-2] 생성된 1차원 히스토그램 배열 결합\n    \n    # [3-3] 결합한 배열 정규화\n     \n    # [3-3]에서 정규화한 배열을 반환\n    return ","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### **<span style='background-color:LightSkyBlue'>Empty Module #4: Train Feature Extraction (Point Cloud)</span>**\n- **Empty Module #2-#3을 토대로 학습 Feature (x_train_point_cloud)를 구성합니다.**\n- **해당 과정은 Voxel, Mesh 표현 방법을 통한 Feature Extraction에서도 전반적으로 동일하게 사용됩니다.**\n\n> **[4-1] <span style=\"color:RoyalBlue\">데이터 파일을 읽어 Point Cloud 추출</span>** \n>\n> - **read_off() 함수를 이용하여 Point Cloud(np.array(vertices))를 추출합니다.**\n>\n> **[4-2] <span style=\"color:RoyalBlue\">추출한 Point Cloud로부터 Normal Vector 추정</span>**\n>\n> - **estimate_normal_vector_from_point_cloud() 함수를 사용하여 Normal Vector를 추정합니다.**\n>> **이 때, 함수의 인자는 다음과 같이 설정합니다. (Baseline)**\n>>\n>> **<span style='background-color:yellow'>radius=5, max_nn=30</span>**\n>\n> **[4-3] <span style=\"color:RoyalBlue\">추정된 Normal Vector로부터 히스토그램 기반 Train Feature 생성</span>**\n>\n> - **compute_histogram_of_normal_vector() 함수를 사용하여 히스토그램 기반의 학습 Feature를 생성합니다.**\n>> **이 때, 함수의 인자는 다음과 같이 설정합니다. (Baseline)**\n>>\n>> **<span style='background-color:yellow'>n_bins=32, n_range=(-1,1)</span>**","metadata":{}},{"cell_type":"code","source":"if method == 'point_cloud':\n    # 학습 데이터 (x_train_point_cloud), 학습 라벨 (y_train_point_cloud)을 담을 리스트 생성\n    x_train_point_cloud, y_train_point_cloud = list(), list()\n    \n    # pbar 변수는 tqdm(반복문의 진행 상황을 시각화하여 보여주는 라이브러리)을 담음\n    pbar = tqdm(enumerate(train_dataloader, start=1))\n    for i, (off_, label_) in pbar:\n        # off: OFF 확장자 파일 디렉토리, label: off 파일에 해당하는 정수형 라벨\n        off, label = off_[0], label_.item()\n        \n        # [4-1] 데이터 파일을 읽어 Point Cloud를 추출\n        \n        # [4-2] 추출한 Point Cloud로부터 Normal Vector 추정 (estimate_normal_vector_from_point_cloud() 함수 사용)\n        \n        # [4-3] 추정된 Normal Vector로부터 히스토그램 기반 학습 Feature 생성 (compute_histogram_of_normal_vector() 함수 사용)\n        \n        # [4-3]에서 생성한 학습 Feature를 학습 데이터(x_train_point_cloud)에 추가\n        x_train_point_cloud.append()\n        y_train_point_cloud.append(label)\n\n        pbar.set_description(f'Processing: {os.path.basename(off)}\\tPercentage: {i / len(train_dataset) * 100:.1f}%')\n\n    # 학습 데이터와 학습 라벨을 numpy array 형식으로 변환\n    x_train_point_cloud = np.asarray(x_train_point_cloud)\n    x_train_point_cloud = np.asarray(x_train_point_cloud)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### **<span style='background-color:LightSkyBlue'>Empty Module #5: Test Feature Extraction (Point Cloud)</span>**\n- **Empty Module #2-#3을 토대로 평가 Feature (x_test_point_cloud)를 구성합니다.**\n- **해당 과정은 Voxel, Mesh 표현 방법을 통한 Feature Extraction에서도 전반적으로 동일하게 사용됩니다.**\n\n> **[5-1] <span style=\"color:RoyalBlue\">데이터 파일을 읽어 Point Cloud 추출</span>** \n>\n> - **해당 과정은 [4-1]과 동일합니다.**\n>\n> **[5-2] <span style=\"color:RoyalBlue\">추출한 Point Cloud로부터 Normal Vector 추정</span>**\n>\n> - **해당 과정은 [4-2]와 동일합니다.**\n>\n> **[5-3] <span style=\"color:RoyalBlue\">추정된 Normal Vector로부터 히스토그램 기반 평가 Feature 생성</span>**\n>\n> - **해당 과정은 [3-3]과 동일합니다.**","metadata":{}},{"cell_type":"code","source":"if method == 'point_cloud':\n    # 평가 데이터 (x_test_point_cloud)을 담을 리스트 생성\n    x_test_point_cloud = list()\n    \n    # 상단 셀에서의 예시와 같이, tqdm은 test_dataloader를 받습니다.\n    pbar = tqdm(enumerate(test_dataloader, start=1))\n    for i, (off_) in pbar:\n        # off: OFF 확장자 파일 디렉토리, test_dataloader는 라벨이 없이 off 파일만 존재\n        off = off_[0]\n        \n        # [5-1] 데이터 파일을 읽어 Point Cloud를 추출\n        \n        # [5-2] 추출한 Point Cloud로부터 Normal Vector 추정 (estimate_normal_vector_from_point_cloud() 함수 사용)\n        \n        # [5-3] 추정된 Normal Vector로부터 히스토그램 기반 평가 Feature 생성 (compute_histogram_of_normal_vector() 함수 사용)\n        \n        # [5-3]에서 생성한 학습 Feature를 평가 데이터(x_test_point_cloud)에 추가\n        x_test_point_cloud.append()\n        \n        pbar.set_description(f'Processing: {os.path.basename(off)}\\tPercentage: {i / len(test_dataset) * 100:.1f}%')\n\n    # 평가 데이터를 numpy array 형식으로 변환\n    x_test_point_cloud = np.asarray(x_test_point_cloud)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **<span style=\"color:CornflowerBlue\">II. Voxel</span>**\n\n# **_Feature Extraction (Voxel)_**\n> **Empty Module #6 - #9는 Voxel로부터 Hand-Crafted Feature를 기술하고, 이를 학습 및 평가 데이터로 가공하는 과정을 다룹니다.**\n>\n> **Voxel로부터 Hand-Crafted Feature를 기술하는 전체 파이프라인(Empty Module #6 - #7)은 다음과 같습니다.**\n>\n- **<span style=\"color:Red\">Empty Module #6: Voxelization</span>**\n> **<span style='background-color:yellow'>Voxelization이란 3D 모델을 표현하는 Point Cloud를 일정한 Grid 형태의 3D Voxel Grid로 변환하는 과정입니다.</span>** \n>\n> **Voxelization을 통해 3D 모델을 Voxel 단위로 분할하여 3D 데이터를 보다 쉽게 다룰 수 있는 장점이 있습니다.**\n>\n- **<span style=\"color:Red\">Empty Module #7: Compute Occupancy in Voxel Grid</span>**\n> **Empty Module #7은 <span style='background-color:yellow'> 일정한 간격의 Voxel Grid 내 Point Cloud (x, y, z)가 차지하는 비율을 구합니다.</span>** \n>\n> **총 Voxel 수, Point Cloud가 있는 Voxel 수, Voxel Grid 내 Point Cloud가 차지한 비율을 구하여 Feature로 가공합니다.**\n\n- **Empty Module #7의 Occupancy 기반 Feature Extraction은 Naive한 접근 방식으로, 특히 로봇 공학 분야에서 많이 사용되는 3D 표현 방식 중 하나입니다.**\n    - **<span style=\"color:gray\">아래 source paper는 Voxelization과 Occupancy Grid를 활용한 Deep Learning 방식으로, 이번 텀프로젝트에서는 기계학습의 범위에 맞게 Occupancy Grid의 아이디어를 차용해 Feature를 기술합니다.</span>**\n\n\n<center><img src=\"https://davidstutz.de/wordpress/wp-content/uploads/2018/04/voxelization-screenshot.jpg\" width=\"600\" height=\"400\"></center>\n<h5><center>Example of Voxelization and Occupancy Grid</center></h5>\n<h4><center><a href=\"http://graphics.stanford.edu/courses/cs233-21-spring/ReferencedPapers/voxnet_07353481.pdf\">[source paper] Voxnet: A 3d convolutional neural network for real-time object recognition [Maturana et al., 2015]</a></center></h4>","metadata":{}},{"cell_type":"markdown","source":"### **<span style='background-color:LightSkyBlue'>Empty Module #6: Voxelization</span>**\n\n- **Point Cloud로부터 Voxel Grid를 생성합니다.**\n- **<span style='background-color:yellow'>Empty Module #6에서 사용되는 함수: o3d.utility.Vector3dVector(), o3d.geometry.VoxelGrid.create_from_point_cloud(), np.stack()</span>**\n\n> **[6-1] <span style=\"color:RoyalBlue\">Point Cloud를 받아 인스턴스 변수에 저장</span>** \n>\n> - **해당 과정은 [2-1]과 동일합니다.**\n>\n> **[6-2] <span style=\"color:RoyalBlue\">Point Cloud를 Voxel Grid로 변환</span>**\n>\n> - **Open3D의 함수를 사용하여 Point Cloud를 Voxel Grid로 변환합니다.**\n>> **<span style=\"color:Red\">o3d.geometry.VoxelGrid.create_from_point_cloud() 함수를 사용합니다.</span>**\n>>\n>> - **해당 함수는 point_cloud를 입력 인자로 받으며, voxel_size, min_bound, max_bound의 파라미터를 가집니다. Baseline에서는 입력 point_cloud 외의 파라미터를 디폴트로 설정하여 진행합니다.**\n>>\n>> - **해당 함수는 Voxelization 과정을 직접 구현하여 실행 시, 데이터 양으로 인한 Kaggle RAM이 부족하여 세션이 종료되는 이슈를 극복하고자 사용되었습니다.**\n>>\n>> - **그러므로, 이번 텀프로젝트에서 직접 구현할 필요는 없지만, 아래의 코드를 참고하여 Voxelization 과정을 직접 살펴보시면 해당 파이프라인의 이해에 도움이 될 수 있습니다. 아래는 예시 코드입니다.**\n\n\n```python\ndef create_voxel_grid(point_cloud, voxel_size):\n    min_bounds = np.min(point_cloud, axis=0)\n    max_bounds = np.max(point_cloud, axis=0)\n\n    voxel_counts = np.ceil((max_bounds - min_bounds) / voxel_size).astype(int)\n    x_count, y_count, z_count = voxel_counts\n\n    voxel_grid = np.zeros((x_count, y_count, z_count), dtype=bool)\n\n    voxel_indices = ((point_cloud - min_bounds) / voxel_size).astype(int)\n\n    voxel_grid[voxel_indices[:, 0], voxel_indices[:, 1], voxel_indices[:, 2]] = True\n\n    return voxel_grid\n```\n\n> **[6-3] <span style=\"color:RoyalBlue\">Voxel의 인덱스 정보 추출</span>**\n>\n> - **[6-2] 에서 구한 Voxel Grid로부터 인덱스 정보를 추출하여 stack합니다.**\n>\n> - **인덱스 정보는 Point Cloud가 있는 Voxel Grid의 인덱스를 의미합니다.**\n>> **<span style=\"color:Red\">np.stack() 함수를 사용합니다.</span>**","metadata":{"execution":{"iopub.status.busy":"2023-04-18T15:03:57.888819Z","iopub.execute_input":"2023-04-18T15:03:57.889579Z","iopub.status.idle":"2023-04-18T15:03:57.905481Z","shell.execute_reply.started":"2023-04-18T15:03:57.889511Z","shell.execute_reply":"2023-04-18T15:03:57.903960Z"}}},{"cell_type":"code","source":"def create_voxel_grid(point_cloud, voxel_size: int) -> np.array:\n    # Open3D 라이브러리에서 제공하는 Point Cloud 데이터를 다루는 함수를 활용해 인스턴스 변수 pcd를 반환받음\n    pcd = o3d.geometry.PointCloud()\n    \n    # [6-1] Point Cloud 인자(point_cloud)로부터 3D Vector인 Point Cloud 형으로 인스턴스의 points(pcd.points)에 저장 \n    \n    # [6-2] Point Cloud를 Voxel Grid로 변환하여 변수에 저장\n    \n    # 모든 Voxel의 정보(Voxel의 좌표(center), 크기 (extent), 인덱스(grid_index))를 담은 리스트를 추출\n    voxels = voxel_grid.get_voxels()\n    \n    # [6-3] 모든 voxels의 인덱스 정보 (grid_index)를 쌓은 numpy 배열을 생성\n    \n    # [6-3]에서 생성한 numpy 배열을 반환\n    return ","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### **<span style='background-color:LightSkyBlue'>Empty Module #7: Compute Occupancy in Voxel Grid</span>**\n\n- **Voxel Grid 내 Occupancy(점유율)을 구합니다.**\n- **<span style='background-color:yellow'>Empty Module #7에서 사용되는 함수: np.count_nonzero(), np.sum(), np.column_stack()</span>**\n\n> - **Voxel Grid의 크기 (n_voxels), Voxel Grid 내 Point Cloud가 존재하는 Grid의 수 (n_occupied_voxels), 점유율 (occupancy)를 구합니다.**\n>\n> - **<span style='background-color:yellow'>n_occupied_voxels를 구하는 과정 (코드 내 주석)을 주목해주시길 바랍니다.</span>**\n>\n> - **이 때, 점유율 (occupancy)은 Point Cloud가 존재하는 Grid의 수를 Voxel Grid의 크기로 나누어 구합니다.**\n>\n> **[7-1] <span style=\"color:RoyalBlue\">Voxel Grid의 각 축 (x, y, z)에서 점유율을 구함</span>** \n>> **<span style=\"color:Red\">np.count_nonzero(), np.sum(), np.column_stack() 함수를 사용합니다.</span>**\n>\n> - **x축에서의 점유율을 구하는 파이프라인 예시는 다음과 같습니다.**\n>> **1. Voxel Grid에서 y축과 z축에 해당하는 열을 모은 2차원 배열을 만듭니다 <span style=\"color:Red\">(np.column_stack())</span>**\n>>\n>> **2. 2차원 배열의 합을 통해 x축에 대한 Voxel의 분포를 구합니다. <span style=\"color:Red\">(np.sum(axis=1))</span>**\n>>\n>> **2-1. 2차원 배열의 합은 y축, z축의 인덱스 값을 더한 것을 나타내며, 이를 통해 x축에 대한 Voxel의 분포를 알 수 있습니다.**\n>>\n>> **3. x축의 분포 중, 0이 아닌 요소의 수를 구합니다. 이는 Point Cloud의 x축 방향으로의 분포 정도를 의미합니다. <span style=\"color:Red\">(np.count_nonzero())</span>**\n>\n> **[7-2] <span style=\"color:RoyalBlue\">축 별 점유율을 numpy array로 column 방향으로 쌓은 후 정규화</span>** \n>\n> - **[7-1]에서 각 축 (x, y, z) 별로 구한 점유율을 열 방향으로 쌓은 이후, Voxel Grid의 크기로 나누어 정규화합니다.**\n>\n> - **정규화한 배열을 점유율 (axis)과 결합하여 학습 Feature로 사용합니다.**\n>> **<span style=\"color:Red\">np.array([list, list, list]) 혹은 np.column_stack() 함수를 사용합니다.</span>**","metadata":{}},{"cell_type":"code","source":"def compute_occupancy(voxel_grid) -> Tuple[np.array, np.array]:\n    # Voxel Grid의 크기 (n_voxels), Voxel Grid 내 Point Cloud가 존재하는 Voxel의 수 (n_occupied_voxels), 점유율 (occupancy)을 구함\n    # n_occupied_voxels는 Voxel Grid 내 z축의 인덱스 (voxel_grid[:, -1])가 0이 아닌 수를 세어 구함\n    n_voxels = voxel_grid.shape[0]\n    n_occupied_voxels = np.count_nonzero(voxel_grid[:, -1])\n    occupancy = n_occupied_voxels / n_voxels\n    \n    # [7-1] Voxel Grid의 각 축 (x, y, z)에서의 점유율을 구합니다.\n    \n    \n    # [7-2] 축 별 점유율을 numpy array로 column 방향으로 쌓은 후 정규화\n    \n    # [7-2]에서 구한 numpy array 변수를 axis_occupancy라고 명함\n    return np.array([occupancy]), axis_occupancy","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### **<span style='background-color:LightSkyBlue'>Empty Module #8: Train Feature Extraction (Voxel)</span>**\n- **Empty Module #6-#7을 토대로 학습 Feature (x_train_voxel)를 구성합니다.**\n- **해당 과정은 이전 Point Cloud로부터 Feature를 구성하는 Empty Moudle #4와 동일한 방식으로 작동합니다. 따라서 Empty Module #4를 작성하였다면, 변경된 부분에 대해서만 작성하시면 됩니다.**\n\n> **[8-1] <span style=\"color:RoyalBlue\">데이터 파일을 읽어 Point Cloud 추출</span>** \n>\n> - **해당 과정은 [4-1]과 동일합니다.**\n>\n> **[8-2] <span style=\"color:RoyalBlue\">Voxelization 이후 Voxel Grid 내 차지한 인덱스 반환</span>**\n>\n> - **create_voxel_grid() 함수를 사용하여 Point Cloud가 차지한 Voxel Grid의 인덱스를 반환합니다.**\n>> **이 때, 함수의 인자는 다음과 같이 설정합니다. (Baseline)**\n>>\n>> **<span style='background-color:yellow'>voxel_size=8</span>**\n>\n> **[8-3] <span style=\"color:RoyalBlue\">Voxel Grid 내 Point Cloud의 점유율과 각 축 별 점유율을 구함</span>**\n>\n> - **compute_occupancy() 함수를 사용하여 Point Cloud의 Voxel Grid 내 전체 점유율과 각 축 (x, y, z)별 점유율을 구합니다.**\n>\n> **[8-4] <span style=\"color:RoyalBlue\">점유율과 축 별 점유율을 순서대로 결합하여 학습 Feature 생성</span>**\n>\n> - **[8-3]에서 구한 점유율 및 각 축 별 점유율을 순서대로 결합하여 (axis=0), 학습 Feature를 생성합니다.**","metadata":{"execution":{"iopub.status.busy":"2023-04-19T03:13:59.206621Z","iopub.execute_input":"2023-04-19T03:13:59.207066Z","iopub.status.idle":"2023-04-19T03:13:59.218188Z","shell.execute_reply.started":"2023-04-19T03:13:59.207024Z","shell.execute_reply":"2023-04-19T03:13:59.216581Z"}}},{"cell_type":"code","source":"if method == 'voxel':\n    # 학습 데이터 (x_train_voxel), 학습 라벨 (y_train_voxel)을 담을 리스트 생성\n    x_train_voxel  = list()\n    y_train_voxel = list()\n    \n    pbar = tqdm(enumerate(train_dataloader, start=1))\n    for i, (off_, label_) in pbar:\n        off, label = off_[0], label_.item()\n        \n        # [8-1] 데이터 파일을 읽어 Point Cloud를 추출\n        \n        # [8-2] Voxelization 이후 Voxel Grid 내 차지한 인덱스 반환 (create_voxel_grid() 함수 사용)\n        \n        # [8-3] Voxel Grid 내 Point Cloud의 점유율 (occupancy)과 각 축 별 점유율 (axis_occupancy)을 구함 (compute_occupancy() 함수 사용)\n        \n        # [8-4] 이전 단계에서 구한 점유율과 축 별 점유율을 순서대로 결합하여 학습 Feature 생성\n        \n        # [8-4]에서 생성한 학습 Feature를 학습 데이터(x_train_voxel)에 추가\n        x_train_voxel.append()\n        y_train_voxel.append(label)\n\n        pbar.set_description(f'Processing: {os.path.basename(off)}\\tPercentage: {i / len(train_dataset) * 100:.1f}%')\n    \n    # 학습 데이터와 학습 라벨을 numpy array 형식으로 변환\n    x_train_voxel = np.array(x_train_voxel)\n    y_train_voxel = np.array(y_train_voxel)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### **<span style='background-color:LightSkyBlue'>Empty Module #9: Test Feature Extraction (Voxel)</span>**\n- **Empty Module #6-#7을 토대로 평가 Feature (x_test_voxel)를 구성합니다.**\n- **해당 과정은 Voxel로부터 Feature를 구성하는 Empty Moudle #8과 동일한 방식으로 작동합니다. 따라서 Empty Module #8을 작성하였다면, 변경된 부분에 대해서만 작성하시면 됩니다.**\n\n> **[9-1] <span style=\"color:RoyalBlue\">데이터 파일을 읽어 Point Cloud 추출</span>** \n>\n> - **해당 과정은 [8-1]과 동일합니다.**\n>\n> **[9-2] <span style=\"color:RoyalBlue\">Voxelization 이후 Voxel Grid 내 차지한 인덱스 반환</span>**\n>\n> - **해당 과정은 [8-2]와 동일합니다.**\n>\n> **[9-3] <span style=\"color:RoyalBlue\">Voxel Grid 내 Point Cloud의 점유율과 각 축 별 점유율을 구함</span>**\n>\n> - **해당 과정은 [8-3]과 동일합니다.**\n>\n> **[9-4] <span style=\"color:RoyalBlue\">점유율과 축 별 점유율을 결합하여 학습 Feature 생성</span>**\n>\n> - **해당 과정은 [8-4]와 동일합니다.**","metadata":{}},{"cell_type":"code","source":"if method == 'voxel':\n    # 평가 데이터 (x_test_voxel) 담을 리스트 생성\n    x_test_voxel = list()\n    \n    pbar = tqdm(enumerate(test_dataloader, start=1))\n    for i, (off_) in pbar:\n        off = off_[0]\n        \n        # [9-1] 데이터 파일을 읽어 Point Cloud를 추출\n        \n        # [9-2] Voxelization 이후 Voxel Grid 내 차지한 인덱스 반환 (create_voxel_grid() 함수 사용)\n        \n        # [9-3] Voxel Grid 내 Point Cloud의 점유율 (occupancy)과 각 축 별 점유율 (axis_occupancy)을 구함 (compute_occupancy() 함수 사용)\n        \n        # [9-4] 이전 단계에서 구한 점유율과 축 별 점유율을 결합하여 평가 Feature 생성\n        \n        # [9-4]에서 생성한 평가 Feature를 평가 데이터(x_test_voxel)에 추가\n        x_test_voxel.append()\n        \n        pbar.set_description(f'Processing: {os.path.basename(off)}\\tPercentage: {i / len(test_dataset) * 100:.1f}%')\n    \n    # 평가 데이터를 numpy array 형식으로 변환\n    x_test_voxel = np.array(x_test_voxel)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **<span style=\"color:CornflowerBlue\">III. Mesh</span>**\n\n# **_Feature Extraction (Mesh)_**\n> **Empty Module #10 - #13은 Mesh로부터 Hand-Crafted Feature를 기술하고, 이를 학습 및 평가 데이터로 가공하는 과정을 다룹니다.**\n>\n> **Mesh로부터 Hand-Crafted Feature를 기술하는 전체 파이프라인(Empty Module #10 - #11)은 다음과 같습니다.**\n>\n- **<span style=\"color:Red\">Empty Module #10: Calculate Normal Vector from Mesh</span>**\n> **<span style='background-color:yellow'>Mesh (Polygon, Face)로부터 Normal Vector(법선 벡터)를 구합니다.</span>** \n>\n> - **Feature Extraction 방식은 Empty Module #2의 Point Cloud로부터 Normal Vector를 추정하는 과정과 비슷하나, 다음의 차이점이 있습니다.**\n>     - **Point Cloud는 한 Point (x,y,z)의 최근접 이웃점을 이용하여 해당 Point가 가질 수 있는 Normal Vector를 추정합니다.**\n>     - **Mesh는 다각형 (Face, 이번 텀프로젝트에서 사용되는 ModelNet10은 삼각형으로 구성되어 있습니다)로부터 면의 실제 법선벡터를 계산합니다.**\n>     - **<span style='background-color:yellow'>그러므로 Point Cloud는 Normal Vector를 추정 (Estimate), Mesh는 Normal Vector를 계산 (Calculate)한다는 점에서 차이가 있습니다.</span>**\n>\n\n- **<span style=\"color:Red\">Empty Module #11: PCA on Normal Vectors for Each Axis</span>**\n> **Normal Vector에 PCA를 적용하여 3차원 공간 상에서 법선 벡터를 표현하는 새로운 벡터를 생성하여 Feature로 가공합니다.** \n>\n> **PCA는 OFF 확장자 형식의 데이터 파일마다 Face의 수가 달라, Normal Vector를 계산 시 차원이 달라지는 것을 학습 Feature로 가공하기 위해 사용합니다.**\n\n- **Empty Module #10-#11의 Normal Vector 기반 Feature Extraction은 Naive한 접근 방식으로, 특히 얼굴 인식 분야에서 많이 사용되는 방식입니다.**\n    - **<span style=\"color:gray\">아래 source paper는 얼굴 정보를 담은 Mesh 데이터로부터 Normal Vector와 Curvature (Mesh의 곡률)를 결합한 Feature에 PCA를 적용한 학습 Feature를 생성하여 SVM으로 얼굴 표정을 분류하는 방식을 사용합니다.</span>**\n    - **<span style=\"color:gray\">이번 텀프로젝트에서는 구현의 간편함을 위하여 논문의 아이디어를 차용하여 Normal Vector로부터 PCA를 적용하여 학습 Feature를 가공하는 과정을 기술합니다.</span>**\n\n<center><img src=\"https://upload.wikimedia.org/wikipedia/commons/thumb/c/cc/Surface_normals.svg/512px-Surface_normals.svg.png\" width=\"600\" height=\"500\"></center>\n<h5><center>Example of Normal Vector from Mesh</center></h5>\n<h4><center><a href=\"https://dl.acm.org/doi/abs/10.1145/3446999.3447022\">[source paper] Fusing Normal Vector and Curvature Features on the Mesh for 3D Facial Expression Recognition [Yu Gu et al., 2021]</a></center></h4>","metadata":{}},{"cell_type":"markdown","source":"### **<span style='background-color:LightSkyBlue'>Empty Module #10: Calculate Normal Vector from Mesh</span>**\n\n- **Mesh (Face)로부터 Normal Vector를 계산합니다.**\n- **<span style='background-color:yellow'>Empty Module #10에서 사용되는 함수: np.cross()</span>**\n\n> **[10-1] <span style=\"color:RoyalBlue\">Mesh (Face)로부터 Face를 구성하는 Point를 구함</span>** \n>\n> - **read_off()로부터 Face (Mesh)에 해당하는 Vertex (Point) 데이터를 읽어 변수(예: P, Q, R)에 저장합니다.**\n>     - **OFF 파일에서 추출한 두 인자 (vertices, faces)의 연관성을 생각하면 됩니다.**\n>\n> **[10-2] <span style=\"color:RoyalBlue\">Point로부터 Face의 Normal Vector를 계산</span>** \n>> **<span style=\"color:Red\">np.cross() 함수를 사용합니다.</span>**\n>\n> - **Point들이 이루는 평면의 Normal Vector를 계산합니다. 아래는 Normal Vector를 계산하는 전체 파이프라인 예시입니다.**\n>> **1. Face를 구성하는 세 Point를 P, Q, R이라고 명할때, P, Q, R으로 구성된 도형은 △PQR 입니다.**\n>>\n>> **2. 평면 △PQR의 법선 벡터는 PQ 벡터와 PR 벡터에 동시에 수직입니다.**\n>>\n>> **3. 결과적으로 △PQR의 법선 벡터는 PQ 벡터와 PR 벡터의 외적으로 표현할 수 있습니다. (<span style=\"color:Red\">np.cross()</span>)**\n>\n> **[10-3] <span style=\"color:RoyalBlue\">계산한 Normal Vector를 리스트에 담은 후, 반환</span>** \n>\n> - **전체 Face에 대해, 하나의 Face에 대한 Normal Vector를 계산하여 리스트에 담습니다. 반복이 마친 후, numpy array로 변환하여 반환합니다.**","metadata":{}},{"cell_type":"code","source":"def calculate_normal_vector_from_mesh(vertices, faces) -> np.array:\n    # Face들의 법선 벡터를 담을 리스트 생성\n    normal_vector = list()\n    \n    # 전체 Face (faces)에 대해 반복\n    for face in faces:\n        # [10-1] Face를 구성하는 Point를 구함 \n\n        # [10-2] Point로부터 Face의 Normal Vector를 계산\n        \n        # [10-3] 계산한 Normal Vector를 리스트 (normal_vector)에 담은 후, 반환\n\n    return ","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### **<span style='background-color:LightSkyBlue'>Empty Module #11: PCA on Normal Vectors for Each Axis</span>**\n\n- **PCA를 통해 Normal Vector를 주성분 개수가 3개인 3차원 공간에서 표현하는 새로운 벡터를 생성합니다.**\n- **<span style='background-color:yellow'>Empty Module #11에서 사용되는 함수: PCA(), PCA.fit_transform(), np.concatenate</span>**\n\n> **[11-1] <span style=\"color:RoyalBlue\">주성분 개수가 3개인 PCA 객체를 생성 및 적용</span>** \n>\n> - **주성분 개수가 3개인 PCA 객체를 생성한 다음, Normal Vector에 적용합니다.**\n>\n> **[11-2] <span style=\"color:RoyalBlue\">주성분 추출</span>** \n>> **<span style=\"color:Red\">PCA.components_를 사용합니다.</span>**\n> - **Normal Vector에 적용한 PC의 주성분 벡터를 추출하여, 각 축 (x, y, z)에 해당하는 변수에 할당합니다.** \n> - **해당 벡터는 Normal Vector를 3차원 공간에서 표현하는 새로운 벡터입니다.**\n>\n> **[11-3] <span style=\"color:RoyalBlue\">벡터의 축 별로 결합하여 학습 Feature 기술</span>**\n> - **각 축 별로 결합하여 학습 Feature를 구성합니다.**","metadata":{"execution":{"iopub.status.busy":"2023-04-19T05:31:40.752344Z","iopub.execute_input":"2023-04-19T05:31:40.752797Z","iopub.status.idle":"2023-04-19T05:31:40.767744Z","shell.execute_reply.started":"2023-04-19T05:31:40.752750Z","shell.execute_reply":"2023-04-19T05:31:40.765976Z"}}},{"cell_type":"code","source":"def pca_normal_vector(normal_vector) -> np.array:\n    # [11-1] 주성분 개수가 3개인 PCA 객체를 생성 및 적용\n    \n    \n    # [11-2] 주성분 추출 (PCA.components_)\n\n    # [11-3] 주성분을 numpy array로 결합 (axis=0)하여 학습 Feature 기술 (np.concatenate() 함수 사용)\n\n    # [11-3]에서 기술한 학습 Feature 반환\n    return","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### **<span style='background-color:LightSkyBlue'>Empty Module #12: Train Feature Extraction (Mesh)</span>**\n- **Empty Module #10-#11을 토대로 학습 Feature (x_train_mesh)를 구성합니다.**\n- **해당 과정은 Mesh로부터 Feature를 구성하는 Empty Moudle #11과 동일한 방식으로 작동합니다. 따라서 Empty Module #11을 작성하였다면, 변경된 부분에 대해서만 작성하시면 됩니다.**\n\n> **[12-1] <span style=\"color:RoyalBlue\">데이터 파일을 읽어 Point Cloud와 Face 추출</span>** \n>\n> - **해당 과정은 [4-1]과 동일합니다.**\n>\n> **[12-2] <span style=\"color:RoyalBlue\">Face를 구성하는 Point Cloud 데이터를 활용하여 Normal Vector 계산</span>**\n>\n> - **calculate_normal_vector_from_mesh() 함수를 사용하여 Face의 Normal Vector를 계산합니다.**\n>\n> **[12-3] <span style=\"color:RoyalBlue\">PCA를 통해 Normal Vector로부터 평가 Feature를 생성</span>**\n>\n> - **pca_normal_vector() 함수를 사용하여 평 Feature를 생성합니다.**","metadata":{}},{"cell_type":"code","source":"if method == 'mesh':\n    # 학습 데이터 (x_train_mesh) 담을 리스트 생성\n    x_train_mesh, y_train_mesh = list(), list()\n\n    pbar = tqdm(enumerate(train_dataloader, start=1))\n    for i, (off_, label_) in pbar:\n        off, label = off_[0], label_.item()\n        \n        # [12-1] 데이터 파일을 읽어 Point Cloud와 Face 추출\n        \n        # [12-2] Face를 구성하는 Point Cloud 데이터를 활용하여 Normal Vector 계산\n        \n        # [12-3] PCA를 통해 Normal Vector로부터 학습 Feature를 생성\n\n        x_train_mesh.append()\n        y_train_mesh.append(label)\n\n        pbar.set_description(f'Processing: {os.path.basename(off)}\\tPercentage: {i / len(train_dataset) * 100:.1f}%')\n\n    # 학습 데이터와 학습 라벨을 numpy array 형식으로 변환\n    x_train_mesh = np.array(x_train_mesh)\n    y_train_mesh = np.array(y_train_mesh)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### **<span style='background-color:LightSkyBlue'>Empty Module #13: Test Feature Extraction (Mesh)</span>**\n- **Empty Module #10-#11을 토대로 평가 Feature (x_test_mesh)를 구성합니다.**\n- **해당 과정은 이전 Point Cloud로부터 Feature를 구성하는 Empty Moudle #4와 동일한 방식으로 작동합니다. 따라서 Empty Module #4를 작성하였다면, 변경된 부분에 대해서만 작성하시면 됩니다.**\n\n> **[13-1] <span style=\"color:RoyalBlue\">데이터 파일을 읽어 Point Cloud와 Face 추출</span>** \n>\n> - **해당 과정은 [4-1]과 동일합니다.**\n>\n> **[13-2] <span style=\"color:RoyalBlue\">Face를 구성하는 Point Cloud 데이터를 활용하여 Normal Vector 계산</span>**\n>\n> - **calculate_normal_vector_from_mesh() 함수를 사용하여 Face의 Normal Vector를 계산합니다.**\n>\n> **[13-3] <span style=\"color:RoyalBlue\">PCA를 통해 Normal Vector로부터 학습 Feature를 생성</span>**\n>\n> - **pca_normal_vector() 함수를 사용하여 학습 Feature를 생성합니다.**","metadata":{}},{"cell_type":"code","source":"if method == 'mesh':\n    # 평가 데이터 (x_test_mesh) 담을 리스트 생성\n    x_test_mesh = list()\n\n    pbar = tqdm(enumerate(test_dataloader, start=1))\n    for i, (off_) in pbar:\n        off = off_[0]\n        \n        # [13-1] 데이터 파일을 읽어 Point Cloud와 Face 추출\n        \n        # [13-2] Face를 구성하는 Point Cloud 데이터를 활용하여 Normal Vector 계산\n        \n        # [13-3] PCA를 통해 Normal Vector로부터 평가 Feature를 생성\n        \n        x_test_mesh.append()\n\n        pbar.set_description(f'Processing: {os.path.basename(off)}\\tPercentage: {i / len(test_dataset) * 100:.1f}%')\n\n    # 평가 데이터를 numpy array 형식으로 변환\n    x_test_mesh = np.array(x_test_mesh)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **_Model Train (SVM)_**\n> **3D 모델 데이터를 표현하는 세 방법 (Point Cloud, Voxel, Mesh)를 통해 Hand-crafted Feature를 기술한 이후, 기계학습 분류 모델을 사용하여 분류합니다.**\n>\n> **Baseline에서는 Support Vector Machine의 분류 모델인 SVC를 사용하였으며, 파라미터는 다음과 같이 설정하였습니다.**\n> - **<span style='background-color:yellow'>C=10, random_state=seed</span>**","metadata":{}},{"cell_type":"markdown","source":"### **<span style='background-color:LightSkyBlue'>Empty Module #14: Classification with SVM</span>**\n- **3D 모델 데이터를 표현하는 세 방법 (method)에 대해, 학습 데이터 및 학습 라벨 (x_train, y_train)과 평가 데이터 (x_test)를 구성하여 학습합니다.**\n\n> **[14-1] <span style=\"color:RoyalBlue\">학습 데이터 및 학습 라벨을 통한 모델 학습</span>**\n>\n> **[14-2] <span style=\"color:RoyalBlue\">평가 데이터를 통한 모델 분류 예측</span>**","metadata":{}},{"cell_type":"code","source":"# method는 상단 셀의 Select Method에서 정한 값으로 설정됩니다.\nprint(f'Select Method is {method}')\n\nif method == 'point_cloud':\n    x_train, y_train = x_train_point_cloud, y_train_point_cloud\n    x_test = x_test_point_cloud\n\nelif method == 'voxel':\n    x_train, y_train = x_train_voxel, y_train_voxel\n    x_test = x_test_voxel\n\nelif method == 'mesh':\n    x_train, y_train = x_train_mesh, y_train_mesh\n    x_test = x_test_mesh\n    \n# [14-1] 학습 데이터 및 학습 라벨을 통한 모델 학습\n\n# [14-2] 평가 데이터를 통한 모델 분류 예측\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **_Submit to CSV_**\n- **마지막으로, 예측한 분류 데이터를 CSV 형식의 파일로 저장합니다.**","metadata":{}},{"cell_type":"markdown","source":"### **<span style='background-color:LightSkyBlue'>Empty Module #15: Submit to CSV</span>**\n- **sample_submit.csv에 형식을 지정하여 저장합니다.**\n\n> **[15-1] <span style=\"color:RoyalBlue\">sample_submit.csv에 분류 예측 값을 대입</span>**","metadata":{"execution":{"iopub.status.busy":"2023-04-19T06:28:29.941641Z","iopub.execute_input":"2023-04-19T06:28:29.942207Z","iopub.status.idle":"2023-04-19T06:28:29.950183Z","shell.execute_reply.started":"2023-04-19T06:28:29.942163Z","shell.execute_reply":"2023-04-19T06:28:29.948702Z"}}},{"cell_type":"code","source":"# [15-1] sample_submit.csv (SUBMIT_PATH)에 분류 예측 값을 대입\n\n# submit 파일 저장\nsubmit.to_csv(f\"{method}_baseline.csv\", index=False)","metadata":{"trusted":true},"execution_count":null,"outputs":[]}]}