{"cells":[{"metadata":{},"cell_type":"markdown","source":"**When we train object detection for custom datasets, we often get confused over how to choose hyperparameters for the Network. Anchor boxes (one of the hyperparameters) are very important to detect objects with different scales and aspect ratios. We will get improved detection results if we get the anchors right.**"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import numpy as np\nimport pydicom\nfrom pydicom.pixel_data_handlers.util import apply_voi_lut\nimport os\nfrom glob import glob\nimport pandas as pd\nfrom tqdm import tqdm\nimport time\nimport multiprocessing\nimport seaborn as sns\nimport collections\nfrom sklearn.cluster import KMeans\nimport matplotlib.pyplot as plt","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"def read_xray(path):\n    dicom = pydicom.read_file(path)\n    width = dicom.Rows\n    height = dicom.Columns\n    basename = os.path.basename(path).split('.')[0]\n    return basename, width, height","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Count CPU**"},{"metadata":{"trusted":true},"cell_type":"code","source":"cpu_count = multiprocessing.cpu_count()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train = pd.read_csv('../input/vinbigdata-chest-xray-abnormalities-detection/train.csv')\nimage_paths = \"../input/vinbigdata-chest-xray-abnormalities-detection/train\"\nimage_paths = glob(os.path.join(image_paths, '*.dicom'))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Load Shape Image with MultiProcessing**"},{"metadata":{"trusted":true},"cell_type":"code","source":"data = pd.DataFrame()\nimage_id = []\nwidths = []\nheights = []\nstart = time.time()\ntry:\n    pool = multiprocessing.Pool(processes = cpu_count)\n    for basename, width, height in pool.map(read_xray, image_paths):\n        image_id.append(basename)\n        widths.append(width)\n        heights.append(height)\nfinally:\n    pool.close()\n    pool.join()\ndata['image_id'] = image_id\ndata['heights'] = heights\ndata['widths'] = widths\nprint('Time Execution : {}'.format(time.time() - start))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Merge data contain shape Image with data origin**"},{"metadata":{"trusted":true},"cell_type":"code","source":"data = pd.merge(train, data, on='image_id', how='inner')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Filter Bounding Boxes in Data**"},{"metadata":{"trusted":true},"cell_type":"code","source":"data = data[data['class_name'] != 'No finding']","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Analysis of bounding boxes (Training data)**"},{"metadata":{},"cell_type":"markdown","source":"**Image resizing**"},{"metadata":{"trusted":true},"cell_type":"code","source":"def change_to_wh(data):\n    data['w'] = data['x_max'] - data['x_min'] + 1\n    data['h'] = data['y_max'] - data['y_min'] + 1\n    return data\n\nmin_dimension = 600\nmax_dimension = 1024\n\ndef _compute_new_static_size(width, height, min_dimension, max_dimension):\n    orig_height = height\n    orig_width = width\n    orig_min_dim = min(orig_height, orig_width)\n  \n    # Calculates the larger of the possible sizes\n    large_scale_factor = min_dimension / float(orig_min_dim)\n    large_height = int(round(orig_height * large_scale_factor))\n    large_width = int(round(orig_width * large_scale_factor))\n    large_size = [large_height, large_width]\n    if max_dimension:\n    # Calculates the smaller of the possible sizes, use that if the larger is too big.\n        orig_max_dim = max(orig_height, orig_width)\n        small_scale_factor = max_dimension / float(orig_max_dim)\n        small_height = int(round(orig_height * small_scale_factor))\n        small_width = int(round(orig_width * small_scale_factor))\n        small_size = [small_height, small_width]\n        new_size = large_size\n    if max(large_size) > max_dimension:\n        new_size = small_size\n    else:\n        new_size = large_size\n    \n    return new_size[1], new_size[0]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"data.describe()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Rescale Boxes**"},{"metadata":{"trusted":true},"cell_type":"code","source":"data = change_to_wh(data)\ndata['new_w'], data['new_h'] = np.vectorize(_compute_new_static_size)(data['widths'],  data['heights'], min_dimension, max_dimension)\ndata['b_w'] = data['new_w'] * data['w']/ data['widths']\ndata['b_h'] = data['new_h'] * data['h'] / data['heights']\ndata['b_ar'] = data['b_w'] / data['b_h']","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Distribution of Bounding Boxes!**"},{"metadata":{"trusted":true},"cell_type":"code","source":"sns.jointplot(x=\"b_w\", y=\"b_h\", data=data)\nsns.jointplot(x=\"b_w\", y=\"b_h\", data=data, kind='kde')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Calculate the base box size!****"},{"metadata":{"trusted":true},"cell_type":"code","source":"\ndef count_base_size(width, height, input_array=[64, 96, 128, 196, 212, 256, 512]):\n    result = {}\n    for ele in input_array:\n        result[str(ele)] = 0\n    result['rest'] = 0\n    \n    for w, h in zip(width, height):\n        done = False\n        for inp in input_array:\n            if w <= inp and h <= inp:\n                result[str(inp)] += 1\n                done = True\n        if done == False:\n            result['rest'] += 1\n            \n    return result\n    \nD = count_base_size(data[\"b_w\"].tolist(), data[\"b_h\"].tolist())\nOD = collections.OrderedDict(sorted(D.items()))\nplt.bar(range(len(OD)), OD.values(), align='center')\nplt.xticks(range(len(OD)), OD.keys())\n\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Cluster bbox (width, height) on eucledian distance metric**\n"},{"metadata":{"trusted":true},"cell_type":"code","source":"X = data[['b_w', 'b_h']].values\nK = KMeans(5, random_state=0)\nlabels = K.fit(X)\nplt.scatter(X[:, 0], X[:, 1], c=labels.labels_, s=50, cmap='viridis');","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"out = labels.cluster_centers_\n\nar = out[:,0] / out[:,1]\nscale = out[:,1] * np.sqrt(ar) / 256\n\nprint(\"Aspect Ratios: {}\".format(ar))\n\nprint(\"Scales: {}\".format(scale))\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**IOU based clusterring**"},{"metadata":{"trusted":true},"cell_type":"code","source":"import numpy as np\n\ndef iou(box, clusters):\n    \"\"\"\n    Calculates the Intersection over Union (IoU) between a box and k clusters.\n    :param box: tuple or array, shifted to the origin (i. e. width and height)\n    :param clusters: numpy array of shape (k, 2) where k is the number of clusters\n    :return: numpy array of shape (k, 0) where k is the number of clusters\n    \"\"\"\n    x = np.minimum(clusters[:, 0], box[0])\n    y = np.minimum(clusters[:, 1], box[1])\n    if np.count_nonzero(x == 0) > 0 or np.count_nonzero(y == 0) > 0:\n        raise ValueError(\"Box has no area\")\n\n    intersection = x * y\n    box_area = box[0] * box[1]\n    cluster_area = clusters[:, 0] * clusters[:, 1]\n\n    iou_ = intersection / (box_area + cluster_area - intersection)\n\n    return iou_\n\n\ndef avg_iou(boxes, clusters):\n    \"\"\"\n    Calculates the average Intersection over Union (IoU) between a numpy array of boxes and k clusters.\n    :param boxes: numpy array of shape (r, 2), where r is the number of rows\n    :param clusters: numpy array of shape (k, 2) where k is the number of clusters\n    :return: average IoU as a single float\n    \"\"\"\n    return np.mean([np.max(iou(boxes[i], clusters)) for i in range(boxes.shape[0])])\n\n\ndef translate_boxes(boxes):\n    \"\"\"\n    Translates all the boxes to the origin.\n    :param boxes: numpy array of shape (r, 4)\n    :return: numpy array of shape (r, 2)\n    \"\"\"\n    new_boxes = boxes.copy()\n    for row in range(new_boxes.shape[0]):\n        new_boxes[row][2] = np.abs(new_boxes[row][2] - new_boxes[row][0])\n        new_boxes[row][3] = np.abs(new_boxes[row][3] - new_boxes[row][1])\n    return np.delete(new_boxes, [0, 1], axis=1)\n\n\ndef kmeans(boxes, k, dist=np.median):\n    \"\"\"\n    Calculates k-means clustering with the Intersection over Union (IoU) metric.\n    :param boxes: numpy array of shape (r, 2), where r is the number of rows\n    :param k: number of clusters\n    :param dist: distance function\n    :return: numpy array of shape (k, 2)\n    \"\"\"\n    rows = boxes.shape[0]\n\n    distances = np.empty((rows, k))\n    last_clusters = np.zeros((rows,))\n\n    np.random.seed()\n\n    # the Forgy method will fail if the whole array contains the same rows\n    clusters = boxes[np.random.choice(rows, k, replace=False)]\n\n    while True:\n        for row in range(rows):\n            distances[row] = 1 - iou(boxes[row], clusters)\n\n        nearest_clusters = np.argmin(distances, axis=1)\n\n        if (last_clusters == nearest_clusters).all():\n            break\n\n        for cluster in range(k):\n            clusters[cluster] = dist(boxes[nearest_clusters == cluster], axis=0)\n\n        last_clusters = nearest_clusters\n\n    return clusters","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"X = data[['b_w', 'b_h']].values\n# Cluster with 5 centers\ncl = kmeans(X, 5)\nprint (cl)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"ar_iou = cl[:,0] / cl[:,1]\nprint(ar_iou)\nscale_iou = cl[:,1] * np.sqrt(ar_iou) / 256\nprint(scale_iou)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# **Don't forget upvote for me :))**"}],"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"}},"nbformat":4,"nbformat_minor":4}