{"cells":[{"metadata":{},"cell_type":"markdown","source":"Please check out Guido's excellent kernel [here](https://www.kaggle.com/gzuidhof/reference-model). In this kernel i show how to perform inference on test set using the trained model.\n\nYou can find the BEV of the test set [here](https://www.kaggle.com/meaninglesslives/lyft3d-test-dataset). \n\n## Updates:\n- Corrected yaw calculation\n- Used category height information \n"},{"metadata":{},"cell_type":"markdown","source":"# Install the Lyft SDK"},{"metadata":{"trusted":true},"cell_type":"code","source":"!pip install lyft-dataset-sdk -q","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from datetime import datetime\nfrom functools import partial\nimport glob\nfrom multiprocessing import Pool\n\n# Disable multiprocesing for numpy/opencv. We already multiprocess ourselves, this would mean every subprocess produces\n# even more threads which would lead to a lot of context switching, slowing things down a lot.\nimport os\nos.environ[\"OMP_NUM_THREADS\"] = \"1\"\n\nimport matplotlib.pyplot as plt\n%matplotlib inline\n\nimport pandas as pd\nimport cv2\nfrom PIL import Image\nimport numpy as np\nfrom tqdm import tqdm, tqdm_notebook\nimport scipy\nimport scipy.ndimage\nimport scipy.special\nfrom scipy.spatial.transform import Rotation as R\n\nfrom lyft_dataset_sdk.lyftdataset import LyftDataset\nfrom lyft_dataset_sdk.utils.data_classes import LidarPointCloud, Box, Quaternion\nfrom lyft_dataset_sdk.utils.geometry_utils import view_points, transform_matrix\n\nimport time\nfrom lyft_dataset_sdk.utils.map_mask import MapMask\nfrom pathlib import Path\nfrom lyft_dataset_sdk.lyftdataset import LyftDataset,LyftDatasetExplorer","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Lyft SDK requires creating a link to input folders"},{"metadata":{"trusted":true},"cell_type":"code","source":"!ln -s /kaggle/input/3d-object-detection-for-autonomous-vehicles/test_images images\n!ln -s /kaggle/input/3d-object-detection-for-autonomous-vehicles/test_maps maps\n!ln -s /kaggle/input/3d-object-detection-for-autonomous-vehicles/test_lidar lidar","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Creating a new test class\n### If you try to use original LyftDataset Class, you will get missing table error"},{"metadata":{"trusted":true},"cell_type":"code","source":"class LyftTestDataset(LyftDataset):\n    \"\"\"Database class for Lyft Dataset to help query and retrieve information from the database.\"\"\"\n\n    def __init__(self, data_path: str, json_path: str, verbose: bool = True, map_resolution: float = 0.1):\n        \"\"\"Loads database and creates reverse indexes and shortcuts.\n        Args:\n            data_path: Path to the tables and data.\n            json_path: Path to the folder with json files\n            verbose: Whether to print status messages during load.\n            map_resolution: Resolution of maps (meters).\n        \"\"\"\n\n        self.data_path = Path(data_path).expanduser().absolute()\n        self.json_path = Path(json_path)\n\n        self.table_names = [\n            \"category\",\n            \"attribute\",\n            \"sensor\",\n            \"calibrated_sensor\",\n            \"ego_pose\",\n            \"log\",\n            \"scene\",\n            \"sample\",\n            \"sample_data\",\n            \"map\",\n        ]\n\n        start_time = time.time()\n\n        # Explicitly assign tables to help the IDE determine valid class members.\n        self.category = self.__load_table__(\"category\")\n        self.attribute = self.__load_table__(\"attribute\")\n        \n        \n        self.sensor = self.__load_table__(\"sensor\")\n        self.calibrated_sensor = self.__load_table__(\"calibrated_sensor\")\n        self.ego_pose = self.__load_table__(\"ego_pose\")\n        self.log = self.__load_table__(\"log\")\n        self.scene = self.__load_table__(\"scene\")\n        self.sample = self.__load_table__(\"sample\")\n        self.sample_data = self.__load_table__(\"sample_data\")\n        \n        self.map = self.__load_table__(\"map\")\n\n        if verbose:\n            for table in self.table_names:\n                print(\"{} {},\".format(len(getattr(self, table)), table))\n            print(\"Done loading in {:.1f} seconds.\\n======\".format(time.time() - start_time))\n\n        # Initialize LyftDatasetExplorer class\n        self.explorer = LyftDatasetExplorer(self)\n        # Make reverse indexes for common lookups.\n        self.__make_reverse_index__(verbose)\n        \n    def __make_reverse_index__(self, verbose: bool) -> None:\n        \"\"\"De-normalizes database to create reverse indices for common cases.\n        Args:\n            verbose: Whether to print outputs.\n        \"\"\"\n\n        start_time = time.time()\n        if verbose:\n            print(\"Reverse indexing ...\")\n\n        # Store the mapping from token to table index for each table.\n        self._token2ind = dict()\n        for table in self.table_names:\n            self._token2ind[table] = dict()\n\n            for ind, member in enumerate(getattr(self, table)):\n                self._token2ind[table][member[\"token\"]] = ind\n\n        # Decorate (adds short-cut) sample_data with sensor information.\n        for record in self.sample_data:\n            cs_record = self.get(\"calibrated_sensor\", record[\"calibrated_sensor_token\"])\n            sensor_record = self.get(\"sensor\", cs_record[\"sensor_token\"])\n            record[\"sensor_modality\"] = sensor_record[\"modality\"]\n            record[\"channel\"] = sensor_record[\"channel\"]\n\n        # Reverse-index samples with sample_data and annotations.\n        for record in self.sample:\n            record[\"data\"] = {}\n            record[\"anns\"] = []\n\n        for record in self.sample_data:\n            if record[\"is_key_frame\"]:\n                sample_record = self.get(\"sample\", record[\"sample_token\"])\n                sample_record[\"data\"][record[\"channel\"]] = record[\"token\"]\n\n        if verbose:\n            print(\"Done reverse indexing in {:.1f} seconds.\\n======\".format(time.time() - start_time))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Unzip the Lyft3D BEV test data\n\n### I recommend creating it once and reusing it.. Saves computation."},{"metadata":{"trusted":true},"cell_type":"code","source":"!tar -xf ../input/lyft3d-test-dataset/lyft3d_bev_test_data.tar.gz","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"classes = [\"car\", \"motorcycle\", \"bus\", \"bicycle\", \"truck\", \"pedestrian\", \"other_vehicle\", \"animal\", \"emergency_vehicle\"]\ntrain_dataset = LyftDataset(data_path='.', json_path='../input/3d-object-detection-for-autonomous-vehicles/train_data', verbose=True)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Find the mean height of all categories\nWe can use the mean height instead of blindly using 1.75m for all categories"},{"metadata":{"trusted":true},"cell_type":"code","source":"train_dataset.list_categories()\ndel train_dataset;","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class_heights = {'animal':0.51,'bicycle':1.44,'bus':3.44,'car':1.72,'emergency_vehicle':2.39,'motorcycle':1.59,\n                'other_vehicle':3.23,'pedestrian':1.78,'truck':3.44}\nlevel5data = LyftTestDataset(data_path='.', json_path='../input/3d-object-detection-for-autonomous-vehicles/test_data', verbose=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def move_boxes_to_car_space(boxes, ego_pose):\n    \"\"\"\n    Move boxes from world space to car space.\n    Note: mutates input boxes.\n    \"\"\"\n    translation = -np.array(ego_pose['translation'])\n    rotation = Quaternion(ego_pose['rotation']).inverse\n    \n    for box in boxes:\n        # Bring box to car space\n        box.translate(translation)\n        box.rotate(rotation)\n        \ndef scale_boxes(boxes, factor):\n    \"\"\"\n    Note: mutates input boxes\n    \"\"\"\n    for box in boxes:\n        box.wlh = box.wlh * factor\n\ndef draw_boxes(im, voxel_size, boxes, classes, z_offset=0.0):\n    for box in boxes:\n        # We only care about the bottom corners\n        corners = box.bottom_corners()\n        corners_voxel = car_to_voxel_coords(corners, im.shape, voxel_size, z_offset).transpose(1,0)\n        corners_voxel = corners_voxel[:,:2] # Drop z coord\n\n        class_color = classes.index(box.name) + 1\n        \n        if class_color == 0:\n            raise Exception(\"Unknown class: {}\".format(box.name))\n\n        cv2.drawContours(im, np.int0([corners_voxel]), 0, (class_color, class_color, class_color), -1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Some hyperparameters we'll need to define for the system\nvoxel_size = (0.4, 0.4, 1.5)\nz_offset = -2.0\nbev_shape = (336, 336, 3)\n\n# We scale down each box so they are more separated when projected into our coarse voxel space.\nbox_scale = 0.8","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def visualize_lidar_of_sample(sample_token, axes_limit=80):\n    sample = level5data.get(\"sample\", sample_token)\n    sample_lidar_token = sample[\"data\"][\"LIDAR_TOP\"]\n    level5data.render_sample_data(sample_lidar_token, axes_limit=axes_limit)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torch.utils.data\n\nclass BEVImageDataset(torch.utils.data.Dataset):\n    def __init__(self, input_filepaths, map_filepaths=None):\n        self.input_filepaths = input_filepaths\n\n    def __len__(self):\n        return len(self.input_filepaths)\n\n    def __getitem__(self, idx):\n        input_filepath = self.input_filepaths[idx]\n        \n        sample_token = input_filepath.split(\"/\")[-1].replace(\"_input.png\",\"\")\n        \n        im = cv2.imread(input_filepath, cv2.IMREAD_UNCHANGED)        \n        \n        im = im.astype(np.float32)/255\n        \n        im = torch.from_numpy(im.transpose(2,0,1))\n        \n        return im, sample_token\n\n    \ntest_data_folder = './artifacts/'\ninput_filepaths = sorted(glob.glob(os.path.join(test_data_folder, \"*_input.png\")))\n\ntest_dataset = BEVImageDataset(input_filepaths)\n    \nim, sample_token = test_dataset[1]\nim = im.numpy()\n\nplt.figure(figsize=(16,8))\n\n# Transpose the input volume CXY to XYC order, which is what matplotlib requires.\n# plt.imshow(np.hstack((im.transpose(1,2,0)[...,:3], target_as_rgb)))\nplt.imshow(im.transpose(1,2,0)[...,:3])\nplt.title(sample_token)\nplt.show()\n\nvisualize_lidar_of_sample(sample_token)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Unet Model"},{"metadata":{"trusted":true},"cell_type":"code","source":"# This implementation was copied from https://github.com/jvanvugt/pytorch-unet, it is MIT licensed.\n\nclass UNet(nn.Module):\n    def __init__(\n        self,\n        in_channels=1,\n        n_classes=2,\n        depth=5,\n        wf=6,\n        padding=False,\n        batch_norm=False,\n        up_mode='upconv',\n    ):\n        \"\"\"\n        Implementation of\n        U-Net: Convolutional Networks for Biomedical Image Segmentation\n        (Ronneberger et al., 2015)\n        https://arxiv.org/abs/1505.04597\n        Using the default arguments will yield the exact version used\n        in the original paper\n        Args:\n            in_channels (int): number of input channels\n            n_classes (int): number of output channels\n            depth (int): depth of the network\n            wf (int): number of filters in the first layer is 2**wf\n            padding (bool): if True, apply padding such that the input shape\n                            is the same as the output.\n                            This may introduce artifacts\n            batch_norm (bool): Use BatchNorm after layers with an\n                               activation function\n            up_mode (str): one of 'upconv' or 'upsample'.\n                           'upconv' will use transposed convolutions for\n                           learned upsampling.\n                           'upsample' will use bilinear upsampling.\n        \"\"\"\n        super(UNet, self).__init__()\n        assert up_mode in ('upconv', 'upsample')\n        self.padding = padding\n        self.depth = depth\n        prev_channels = in_channels\n        self.down_path = nn.ModuleList()\n        for i in range(depth):\n            self.down_path.append(\n                UNetConvBlock(prev_channels, 2 ** (wf + i), padding, batch_norm)\n            )\n            prev_channels = 2 ** (wf + i)\n\n        self.up_path = nn.ModuleList()\n        for i in reversed(range(depth - 1)):\n            self.up_path.append(\n                UNetUpBlock(prev_channels, 2 ** (wf + i), up_mode, padding, batch_norm)\n            )\n            prev_channels = 2 ** (wf + i)\n\n        self.last = nn.Conv2d(prev_channels, n_classes, kernel_size=1)\n\n    def forward(self, x):\n        blocks = []\n        for i, down in enumerate(self.down_path):\n            x = down(x)\n            if i != len(self.down_path) - 1:\n                blocks.append(x)\n                x = F.max_pool2d(x, 2)\n\n        for i, up in enumerate(self.up_path):\n            x = up(x, blocks[-i - 1])\n\n        return self.last(x)\n\n\nclass UNetConvBlock(nn.Module):\n    def __init__(self, in_size, out_size, padding, batch_norm):\n        super(UNetConvBlock, self).__init__()\n        block = []\n\n        block.append(nn.Conv2d(in_size, out_size, kernel_size=3, padding=int(padding)))\n        block.append(nn.ReLU())\n        if batch_norm:\n            block.append(nn.BatchNorm2d(out_size))\n\n        block.append(nn.Conv2d(out_size, out_size, kernel_size=3, padding=int(padding)))\n        block.append(nn.ReLU())\n        if batch_norm:\n            block.append(nn.BatchNorm2d(out_size))\n\n        self.block = nn.Sequential(*block)\n\n    def forward(self, x):\n        out = self.block(x)\n        return out\n\n\nclass UNetUpBlock(nn.Module):\n    def __init__(self, in_size, out_size, up_mode, padding, batch_norm):\n        super(UNetUpBlock, self).__init__()\n        if up_mode == 'upconv':\n            self.up = nn.ConvTranspose2d(in_size, out_size, kernel_size=2, stride=2)\n        elif up_mode == 'upsample':\n            self.up = nn.Sequential(\n                nn.Upsample(mode='bilinear', scale_factor=2),\n                nn.Conv2d(in_size, out_size, kernel_size=1),\n            )\n\n        self.conv_block = UNetConvBlock(in_size, out_size, padding, batch_norm)\n\n    def center_crop(self, layer, target_size):\n        _, _, layer_height, layer_width = layer.size()\n        diff_y = (layer_height - target_size[0]) // 2\n        diff_x = (layer_width - target_size[1]) // 2\n        return layer[\n            :, :, diff_y : (diff_y + target_size[0]), diff_x : (diff_x + target_size[1])\n        ]\n\n    def forward(self, x, bridge):\n        up = self.up(x)\n        crop1 = self.center_crop(bridge, up.shape[2:])\n        out = torch.cat([up, crop1], 1)\n        out = self.conv_block(out)\n\n        return out","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"We train a U-net fully convolutional neural network, we create a network that is less deep and with only half the amount of filters compared to the original U-net paper implementation. We do this to keep training and inference time low."},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_unet_model(in_channels=3, num_output_classes=2):\n    model = UNet(in_channels=in_channels, n_classes=num_output_classes, wf=5, depth=4, padding=True, up_mode='upsample')\n    \n    # Optional, for multi GPU training and inference\n    model = nn.DataParallel(model)\n    return model\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def visualize_predictions(input_image, prediction, n_images=2, apply_softmax=True):\n    \"\"\"\n    Takes as input 3 PyTorch tensors, plots the input image, predictions and targets.\n    \"\"\"\n    # Only select the first n images\n    prediction = prediction[:n_images]\n\n    input_image = input_image[:n_images]\n\n    prediction = prediction.detach().cpu().numpy()\n    if apply_softmax:\n        prediction = scipy.special.softmax(prediction, axis=1)\n    class_one_preds = np.hstack(1-prediction[:,0])\n\n\n    class_rgb = np.repeat(class_one_preds[..., None], 3, axis=2)\n    class_rgb[...,2] = 0\n\n    \n    input_im = np.hstack(input_image.cpu().numpy().transpose(0,2,3,1))\n    \n    if input_im.shape[2] == 3:\n        input_im_grayscale = np.repeat(input_im.mean(axis=2)[..., None], 3, axis=2)\n        overlayed_im = (input_im_grayscale*0.6 + class_rgb*0.7).clip(0,1)\n    else:\n        input_map = input_im[...,3:]\n        overlayed_im = (input_map*0.6 + class_rgb*0.7).clip(0,1)\n\n    thresholded_pred = np.repeat(class_one_preds[..., None] > 0.5, 3, axis=2)\n\n    fig = plt.figure(figsize=(12,26))\n    plot_im = np.vstack([class_rgb, input_im[...,:3], overlayed_im, thresholded_pred]).clip(0,1).astype(np.float32)\n    plt.imshow(plot_im)\n    plt.axis(\"off\")\n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# We weigh the loss for the 0 class lower to account for (some of) the big class imbalance.\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\nclass_weights = torch.from_numpy(np.array([0.2] + [1.0]*len(classes), dtype=np.float32))\nclass_weights = class_weights.to(device)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Loading Guido's trained Unet model"},{"metadata":{"trusted":true},"cell_type":"code","source":"# del model\nbatch_size = 8\nepochs = 15 # Note: We may be able to train for longer and expect better results, the reason this number is low is to keep the runtime short.\n\nmodel = get_unet_model(num_output_classes=len(classes)+1)\n\nstate = torch.load('../input/reference-model/artifacts/unet_checkpoint_epoch_15.pth')\nmodel.load_state_dict(state)\nmodel = model.to(device)\nmodel.eval();\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def calc_detection_box(prediction_opened,class_probability):\n\n    sample_boxes = []\n    sample_detection_scores = []\n    sample_detection_classes = []\n    \n    contours, hierarchy = cv2.findContours(prediction_opened, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) \n    \n    for cnt in contours:\n        rect = cv2.minAreaRect(cnt)\n        box = cv2.boxPoints(rect)\n        \n        # Let's take the center pixel value as the confidence value\n        box_center_index = np.int0(np.mean(box, axis=0))\n        \n        for class_index in range(len(classes)):\n            box_center_value = class_probability[class_index+1, box_center_index[1], box_center_index[0]]\n            \n            # Let's remove candidates with very low probability\n            if box_center_value < 0.01:\n                continue\n            \n            box_center_class = classes[class_index]\n\n            box_detection_score = box_center_value\n            sample_detection_classes.append(box_center_class)\n            sample_detection_scores.append(box_detection_score)\n            sample_boxes.append(box)\n            \n    return np.array(sample_boxes),sample_detection_scores,sample_detection_classes","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# We perform an opening morphological operation to filter tiny detections\n# Note that this may be problematic for classes that are inherently small (e.g. pedestrians)..\nkernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(3,3))\n    \ndef open_preds(predictions_non_class0):\n\n    predictions_opened = np.zeros((predictions_non_class0.shape), dtype=np.uint8)\n\n    for i, p in enumerate(tqdm(predictions_non_class0)):\n        thresholded_p = (p > background_threshold).astype(np.uint8)\n        predictions_opened[i] = cv2.morphologyEx(thresholded_p, cv2.MORPH_OPEN, kernel)\n        \n    return predictions_opened","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Test Set Predictions"},{"metadata":{"trusted":true},"cell_type":"code","source":"import gc\ngc.collect()\ntest_loader = torch.utils.data.DataLoader(test_dataset, batch_size, shuffle=True, num_workers=os.cpu_count()*2)\nprogress_bar = tqdm_notebook(test_loader)\n\n# We quantize to uint8 here to conserve memory. We're allocating >20GB of memory otherwise.\n# predictions = np.zeros((len(test_loader), 1+len(classes), 336, 336), dtype=np.uint8)\n\nsample_tokens = []\nall_losses = []\n\ndetection_boxes = []\ndetection_scores = []\ndetection_classes = []\n\n# Arbitrary threshold in our system to create a binary image to fit boxes around.\nbackground_threshold = 200\n\nwith torch.no_grad():\n    model.eval()\n    for ii, (X, batch_sample_tokens) in enumerate(progress_bar):\n\n        sample_tokens.extend(batch_sample_tokens)\n        \n        X = X.to(device)  # [N, 1, H, W]\n        prediction = model(X)  # [N, 2, H, W]\n        \n        prediction = F.softmax(prediction, dim=1)\n        \n        prediction_cpu = prediction.cpu().numpy()\n        predictions = np.round(prediction_cpu*255).astype(np.uint8)\n        \n        # Get probabilities for non-background\n        predictions_non_class0 = 255 - predictions[:,0]\n        \n        predictions_opened = np.zeros((predictions_non_class0.shape), dtype=np.uint8)\n\n        for i, p in enumerate(predictions_non_class0):\n            thresholded_p = (p > background_threshold).astype(np.uint8)\n            predictions_opened[i] = cv2.morphologyEx(thresholded_p, cv2.MORPH_OPEN, kernel)\n    \n            sample_boxes,sample_detection_scores,sample_detection_classes = calc_detection_box(predictions_opened[i],\n                                                                                              predictions[i])\n        \n            detection_boxes.append(np.array(sample_boxes))\n            detection_scores.append(sample_detection_scores)\n            detection_classes.append(sample_detection_classes)\n        \n#         # Visualize the first prediction\n#         if ii == 0:\n#             visualize_predictions(X, prediction, apply_softmax=False)\n            ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(\"Total amount of boxes:\", np.sum([len(x) for x in detection_boxes]))\n    \n\n# Visualize the boxes in the first sample\nt = np.zeros_like(predictions_opened[0])\nfor sample_boxes in detection_boxes[0]:\n    box_pix = np.int0(sample_boxes)\n    cv2.drawContours(t,[box_pix],0,(255),2)\nplt.imshow(t)\nplt.show()\n\n# Visualize their probabilities\nplt.hist(detection_scores[0], bins=20)\nplt.xlabel(\"Detection Score\")\nplt.ylabel(\"Count\")\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Transform predicted boxes back into world space"},{"metadata":{"trusted":true},"cell_type":"code","source":"def create_transformation_matrix_to_voxel_space(shape, voxel_size, offset):\n    \"\"\"\n    Constructs a transformation matrix given an output voxel shape such that (0,0,0) ends up in the center.\n    Voxel_size defines how large every voxel is in world coordinate, (1,1,1) would be the same as Minecraft voxels.\n    \n    An offset per axis in world coordinates (metric) can be provided, this is useful for Z (up-down) in lidar points.\n    \"\"\"\n    \n    shape, voxel_size, offset = np.array(shape), np.array(voxel_size), np.array(offset)\n    \n    tm = np.eye(4, dtype=np.float32)\n    translation = shape/2 + offset/voxel_size\n    \n    tm = tm * np.array(np.hstack((1/voxel_size, [1])))\n    tm[:3, 3] = np.transpose(translation)\n    return tm\n\ndef transform_points(points, transf_matrix):\n    \"\"\"\n    Transform (3,N) or (4,N) points using transformation matrix.\n    \"\"\"\n    if points.shape[0] not in [3,4]:\n        raise Exception(\"Points input should be (3,N) or (4,N) shape, received {}\".format(points.shape))\n    return transf_matrix.dot(np.vstack((points[:3, :], np.ones(points.shape[1]))))[:3, :]\n\n\ndef car_to_voxel_coords(points, shape, voxel_size, z_offset=0):\n    if len(shape) != 3:\n        raise Exception(\"Voxel volume shape should be 3 dimensions (x,y,z)\")\n        \n    if len(points.shape) != 2 or points.shape[0] not in [3, 4]:\n        raise Exception(\"Input points should be (3,N) or (4,N) in shape, found {}\".format(points.shape))\n\n    tm = create_transformation_matrix_to_voxel_space(shape, voxel_size, (0, 0, z_offset))\n    p = transform_points(points, tm)\n    return p\n\ndef create_voxel_pointcloud(points, shape, voxel_size=(0.5,0.5,1), z_offset=0):\n\n    points_voxel_coords = car_to_voxel_coords(points.copy(), shape, voxel_size, z_offset)\n    points_voxel_coords = points_voxel_coords[:3].transpose(1,0)\n    points_voxel_coords = np.int0(points_voxel_coords)\n    \n    bev = np.zeros(shape, dtype=np.float32)\n    bev_shape = np.array(shape)\n\n    within_bounds = (np.all(points_voxel_coords >= 0, axis=1) * np.all(points_voxel_coords < bev_shape, axis=1))\n    \n    points_voxel_coords = points_voxel_coords[within_bounds]\n    coord, count = np.unique(points_voxel_coords, axis=0, return_counts=True)\n        \n    # Note X and Y are flipped:\n    bev[coord[:,1], coord[:,0], coord[:,2]] = count\n    \n    return bev\n\ndef normalize_voxel_intensities(bev, max_intensity=16):\n    return (bev/max_intensity).clip(0,1)\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from lyft_dataset_sdk.eval.detection.mAP_evaluation import Box3D, recall_precision\npred_box3ds = []\n\n# This could use some refactoring..\nfor (sample_token, sample_boxes, sample_detection_scores, sample_detection_class) in tqdm_notebook(zip(sample_tokens, detection_boxes, detection_scores, detection_classes), total=len(sample_tokens)):\n    sample_boxes = sample_boxes.reshape(-1, 2) # (N, 4, 2) -> (N*4, 2)\n    sample_boxes = sample_boxes.transpose(1,0) # (N*4, 2) -> (2, N*4)\n\n    # Add Z dimension\n    sample_boxes = np.vstack((sample_boxes, np.zeros(sample_boxes.shape[1]),)) # (2, N*4) -> (3, N*4)\n\n    sample = level5data.get(\"sample\", sample_token)\n    sample_lidar_token = sample[\"data\"][\"LIDAR_TOP\"]\n    lidar_data = level5data.get(\"sample_data\", sample_lidar_token)\n    lidar_filepath = level5data.get_sample_data_path(sample_lidar_token)\n    ego_pose = level5data.get(\"ego_pose\", lidar_data[\"ego_pose_token\"])\n    ego_translation = np.array(ego_pose['translation'])\n\n    global_from_car = transform_matrix(ego_pose['translation'],\n                                       Quaternion(ego_pose['rotation']), inverse=False)\n\n    car_from_voxel = np.linalg.inv(create_transformation_matrix_to_voxel_space(bev_shape, voxel_size, (0, 0, z_offset)))\n\n\n    global_from_voxel = np.dot(global_from_car, car_from_voxel)\n    sample_boxes = transform_points(sample_boxes, global_from_voxel)\n\n    # We don't know at where the boxes are in the scene on the z-axis (up-down), let's assume all of them are at\n    # the same height as the ego vehicle.\n    sample_boxes[2,:] = ego_pose[\"translation\"][2]\n\n\n    # (3, N*4) -> (N, 4, 3)\n    sample_boxes = sample_boxes.transpose(1,0).reshape(-1, 4, 3)\n\n#     box_height = 1.75\n    box_height = np.array([class_heights[cls] for cls in sample_detection_class])\n\n    # Note: Each of these boxes describes the ground corners of a 3D box.\n    # To get the center of the box in 3D, we'll have to add half the height to it.\n    sample_boxes_centers = sample_boxes.mean(axis=1)\n    sample_boxes_centers[:,2] += box_height/2\n\n    # Width and height is arbitrary - we don't know what way the vehicles are pointing from our prediction segmentation\n    # It doesn't matter for evaluation, so no need to worry about that here.\n    # Note: We scaled our targets to be 0.8 the actual size, we need to adjust for that\n    sample_lengths = np.linalg.norm(sample_boxes[:,0,:] - sample_boxes[:,1,:], axis=1) * 1/box_scale\n    sample_widths = np.linalg.norm(sample_boxes[:,1,:] - sample_boxes[:,2,:], axis=1) * 1/box_scale\n    \n    sample_boxes_dimensions = np.zeros_like(sample_boxes_centers) \n    sample_boxes_dimensions[:,0] = sample_widths\n    sample_boxes_dimensions[:,1] = sample_lengths\n    sample_boxes_dimensions[:,2] = box_height\n\n    for i in range(len(sample_boxes)):\n        translation = sample_boxes_centers[i]\n        size = sample_boxes_dimensions[i]\n        class_name = sample_detection_class[i]\n        ego_distance = float(np.linalg.norm(ego_translation - translation))\n    \n        \n        # Determine the rotation of the box\n        v = (sample_boxes[i,0] - sample_boxes[i,1])\n        v /= np.linalg.norm(v)\n        r = R.from_dcm([\n            [v[0], -v[1], 0],\n            [v[1],  v[0], 0],\n            [   0,     0, 1],\n        ])\n        quat = r.as_quat()\n        # XYZW -> WXYZ order of elements\n        quat = quat[[3,0,1,2]]\n        \n        detection_score = float(sample_detection_scores[i])\n\n        \n        box3d = Box3D(\n            sample_token=sample_token,\n            translation=list(translation),\n            size=list(size),\n            rotation=list(quat),\n            name=class_name,\n            score=detection_score\n        )\n        pred_box3ds.append(box3d)\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"pred_box3ds[0]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Creating Submission File"},{"metadata":{"trusted":false},"cell_type":"code","source":"sub = {}\nfor i in tqdm_notebook(range(len(pred_box3ds))):\n#     yaw = -np.arctan2(pred_box3ds[i].rotation[2], pred_box3ds[i].rotation[0])\n    yaw = 2*np.arccos(pred_box3ds[i].rotation[0]);\n    pred =  str(pred_box3ds[i].score/255) + ' ' + str(pred_box3ds[i].center_x)  + ' '  + \\\n    str(pred_box3ds[i].center_y) + ' '  + str(pred_box3ds[i].center_z) + ' '  + \\\n    str(pred_box3ds[i].width) + ' ' \\\n    + str(pred_box3ds[i].length) + ' '  + str(pred_box3ds[i].height) + ' ' + str(yaw) + ' ' \\\n    + str(pred_box3ds[i].name) + ' ' \n        \n    if pred_box3ds[i].sample_token in sub.keys():     \n        sub[pred_box3ds[i].sample_token] += pred\n    else:\n        sub[pred_box3ds[i].sample_token] = pred        \n    \nsample_sub = pd.read_csv('../input/3d-object-detection-for-autonomous-vehicles/sample_submission.csv')\nfor token in set(sample_sub.Id.values).difference(sub.keys()):\n    print(token)\n    sub[token] = ''","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"sub = pd.DataFrame(list(sub.items()))\nsub.columns = sample_sub.columns\nsub.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"sub.tail()","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"sub.to_csv('lyft3d_pred.csv',index=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"ls","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"!rm -r ./artifacts/","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"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.6.6"},"widgets":{"application/vnd.jupyter.widget-state+json":{"state":{"1353917927204111a265f2a2842eb8dc":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"HBoxModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HBoxModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HBoxView","box_style":"","children":["IPY_MODEL_90080080725947a79b9793fbcac23eac","IPY_MODEL_14163046484f41f4a9667b33eb2037cc"],"layout":"IPY_MODEL_75fe38dbe61446cfbeba5fce17ecb936"}},"14163046484f41f4a9667b33eb2037cc":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"HTMLModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_9ec83d6c663a4ef2a3c610b9f8c3552a","placeholder":"​","style":"IPY_MODEL_dfaa8644625344748f9cd1dcf3285c95","value":" 27468/27468 [04:49&lt;00:00, 94.98it/s]"}},"242baad8ff174ca69a84b7b11f0fb73d":{"model_module":"@jupyter-widgets/base","model_module_version":"1.2.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"2bb40c6b75144f36beda41ac128ba4e1":{"model_module":"@jupyter-widgets/base","model_module_version":"1.2.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"2e584b5d2131402e87b3865b5449518f":{"model_module":"@jupyter-widgets/base","model_module_version":"1.2.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"34a7fe08073b46259fd7ed647c4e3e4b":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"HTMLModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_2bb40c6b75144f36beda41ac128ba4e1","placeholder":"​","style":"IPY_MODEL_85bc59f8d3ad446399b10cf84cb9075d","value":" 724265/724265 [00:08&lt;00:00, 89810.36it/s]"}},"38d615126cab4386a6b140e0e2a55b1f":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"ProgressStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"ProgressStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","bar_color":null,"description_width":"initial"}},"3f99276097fb4b59adbd2d87a462221c":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"ProgressStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"ProgressStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","bar_color":null,"description_width":"initial"}},"3fb06ccf785242198015514bc8b70ddd":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"HBoxModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HBoxModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HBoxView","box_style":"","children":["IPY_MODEL_aaf72a261aa4407cb607209cd993ac8f","IPY_MODEL_34a7fe08073b46259fd7ed647c4e3e4b"],"layout":"IPY_MODEL_a5dd39c9846c44069d5f721cef9b5910"}},"548c7007122f450da3ac04b41be7e93c":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"IntProgressModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"IntProgressModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"ProgressView","bar_style":"success","description":"100%","description_tooltip":null,"layout":"IPY_MODEL_2e584b5d2131402e87b3865b5449518f","max":3434,"min":0,"orientation":"horizontal","style":"IPY_MODEL_38d615126cab4386a6b140e0e2a55b1f","value":3434}},"71b567bc52f745acb61ef5d0391ab864":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"HTMLModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_d8d3ba5512e449098c55de2c90454c55","placeholder":"​","style":"IPY_MODEL_b9038f216ceb4f5a92d7ab701eacb809","value":" 3434/3434 [07:31&lt;00:00,  7.61it/s]"}},"75fe38dbe61446cfbeba5fce17ecb936":{"model_module":"@jupyter-widgets/base","model_module_version":"1.2.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"764fefe0be8240e0aacee9ca11df6381":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"ProgressStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"ProgressStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","bar_color":null,"description_width":"initial"}},"85bc59f8d3ad446399b10cf84cb9075d":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"DescriptionStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"90080080725947a79b9793fbcac23eac":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"IntProgressModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"IntProgressModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"ProgressView","bar_style":"success","description":"100%","description_tooltip":null,"layout":"IPY_MODEL_f4e8007da46149b294dc14764b32e4da","max":27468,"min":0,"orientation":"horizontal","style":"IPY_MODEL_764fefe0be8240e0aacee9ca11df6381","value":27468}},"9ec83d6c663a4ef2a3c610b9f8c3552a":{"model_module":"@jupyter-widgets/base","model_module_version":"1.2.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"a5dd39c9846c44069d5f721cef9b5910":{"model_module":"@jupyter-widgets/base","model_module_version":"1.2.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"aaf72a261aa4407cb607209cd993ac8f":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"IntProgressModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"IntProgressModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"ProgressView","bar_style":"success","description":"100%","description_tooltip":null,"layout":"IPY_MODEL_af8574f5427c4b39bb951f1748a1e8c0","max":724265,"min":0,"orientation":"horizontal","style":"IPY_MODEL_3f99276097fb4b59adbd2d87a462221c","value":724265}},"af8574f5427c4b39bb951f1748a1e8c0":{"model_module":"@jupyter-widgets/base","model_module_version":"1.2.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"b9038f216ceb4f5a92d7ab701eacb809":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"DescriptionStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"b9a2e7adaf1e4bb39807174bfd2f72a4":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"HBoxModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HBoxModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HBoxView","box_style":"","children":["IPY_MODEL_548c7007122f450da3ac04b41be7e93c","IPY_MODEL_71b567bc52f745acb61ef5d0391ab864"],"layout":"IPY_MODEL_242baad8ff174ca69a84b7b11f0fb73d"}},"d8d3ba5512e449098c55de2c90454c55":{"model_module":"@jupyter-widgets/base","model_module_version":"1.2.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"dfaa8644625344748f9cd1dcf3285c95":{"model_module":"@jupyter-widgets/controls","model_module_version":"1.5.0","model_name":"DescriptionStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"f4e8007da46149b294dc14764b32e4da":{"model_module":"@jupyter-widgets/base","model_module_version":"1.2.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}}},"version_major":2,"version_minor":0}}},"nbformat":4,"nbformat_minor":1}