{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Hamming loss"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "from io import StringIO\n",
    "from sklearn.metrics import hamming_loss"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "def hammingLoss(y_true, y_pred):\n",
    "    \"\"\"\n",
    "    Computes the hamming loss. Used for multiclass and multilabel classification.\n",
    "    \"\"\"\n",
    "    from sklearn import preprocessing\n",
    "    from sklearn.metrics import hamming_loss\n",
    "    import numpy as np\n",
    "    from itertools import chain\n",
    "\n",
    "    def to_2d_array(df):\n",
    "        MULTI_CLASS_CONDITION=True\n",
    "        _dict = {}\n",
    "        for [index,value] in df.as_matrix():\n",
    "            if index in _dict.keys():\n",
    "                _dict[index].append(value)\n",
    "                MULTI_CLASS_CONDITION = False\n",
    "            else:\n",
    "                _dict[index]=[value]\n",
    "        return list(_dict.keys()), list(_dict.values()), MULTI_CLASS_CONDITION\n",
    "    \n",
    "    (y_true_keys, y_true_mat, b_multiclass) = to_2d_array(y_true)\n",
    "    (y_pred_keys, y_pred_mat, b_multiclass) = to_2d_array(y_pred)\n",
    "    \n",
    "    assert y_true_keys==y_pred_keys\n",
    "    \n",
    "    if b_multiclass:\n",
    "#         print('this is a multiclass case')\n",
    "        y_true_label_encoded = np.array(y_true_mat).ravel()\n",
    "        y_pred_label_encoded = np.array(y_pred_mat).ravel()\n",
    "    else: # MULTI_LABEL_CONDITION\n",
    "#         print('this is a multilabel case')\n",
    "        y_true_classes=(set(list(chain.from_iterable(y_true_mat))))\n",
    "        y_pred_classes=(set(list(chain.from_iterable(y_pred_mat))))\n",
    "        all_classes = list(y_true_classes.union(y_pred_classes))\n",
    "        lb = preprocessing.MultiLabelBinarizer(classes=all_classes)\n",
    "        y_true_label_encoded = lb.fit_transform(y_true_mat)\n",
    "        y_pred_label_encoded = lb.transform(y_pred_mat)\n",
    "    return hamming_loss(y_true_label_encoded, y_pred_label_encoded)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/svattam/miniconda3/envs/automl/lib/python3.6/site-packages/ipykernel_launcher.py:13: FutureWarning: Method .as_matrix will be removed in a future version. Use .values instead.\n",
      "  del sys.path[0]\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "0.26666666666666666"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Testcase 1: MultiLabel, typical\n",
    "y_true = pd.read_csv(StringIO(\"\"\"\n",
    "d3mIndex,class_label\n",
    "3,happy-pleased\n",
    "3,relaxing-calm\n",
    "7,amazed-suprised\n",
    "7,happy-pleased\n",
    "13,quiet-still\n",
    "13,sad-lonely\n",
    "\"\"\"))\n",
    "\n",
    "y_pred = pd.read_csv(StringIO(\"\"\"\n",
    "d3mIndex,class_label\n",
    "3,happy-pleased\n",
    "3,sad-lonely\n",
    "7,amazed-suprised\n",
    "7,happy-pleased\n",
    "13,quiet-still\n",
    "13,happy-pleased\n",
    "\"\"\"))\n",
    "\n",
    "hammingLoss(y_true, y_pred)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/svattam/miniconda3/envs/automl/lib/python3.6/site-packages/ipykernel_launcher.py:13: FutureWarning: Method .as_matrix will be removed in a future version. Use .values instead.\n",
      "  del sys.path[0]\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "0.0"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Testcase 2: MultiLabel, Zero loss\n",
    "y_true = pd.read_csv(StringIO(\"\"\"\n",
    "d3mIndex,class_label\n",
    "3,happy-pleased\n",
    "3,relaxing-calm\n",
    "7,amazed-suprised\n",
    "7,happy-pleased\n",
    "13,quiet-still\n",
    "13,sad-lonely\n",
    "\"\"\"))\n",
    "\n",
    "y_pred = pd.read_csv(StringIO(\"\"\"\n",
    "d3mIndex,class_label\n",
    "3,happy-pleased\n",
    "3,relaxing-calm\n",
    "7,amazed-suprised\n",
    "7,happy-pleased\n",
    "13,quiet-still\n",
    "13,sad-lonely\n",
    "\"\"\"))\n",
    "\n",
    "hammingLoss(y_true, y_pred)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/svattam/miniconda3/envs/automl/lib/python3.6/site-packages/ipykernel_launcher.py:13: FutureWarning: Method .as_matrix will be removed in a future version. Use .values instead.\n",
      "  del sys.path[0]\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "1.0"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Testcase 3: MultiLabel, Complete loss\n",
    "y_true = pd.read_csv(StringIO(\"\"\"\n",
    "d3mIndex,class_label\n",
    "3,happy-pleased\n",
    "3,relaxing-calm\n",
    "7,amazed-suprised\n",
    "7,happy-pleased\n",
    "13,quiet-still\n",
    "13,sad-lonely\n",
    "\"\"\"))\n",
    "\n",
    "y_pred = pd.read_csv(StringIO(\"\"\"\n",
    "d3mIndex,class_label\n",
    "3,ecstatic\n",
    "3,sad-lonely\n",
    "3,quiet-still\n",
    "3,amazed-suprised\n",
    "7,ecstatic\n",
    "7,sad-lonely\n",
    "7,relaxing-calm\n",
    "7,quiet-still\n",
    "13,ecstatic\n",
    "13,happy-pleased\n",
    "13,relaxing-calm\n",
    "13,amazed-suprised\n",
    "\"\"\"))\n",
    "\n",
    "hammingLoss(y_true, y_pred)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/svattam/miniconda3/envs/automl/lib/python3.6/site-packages/ipykernel_launcher.py:13: FutureWarning: Method .as_matrix will be removed in a future version. Use .values instead.\n",
      "  del sys.path[0]\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "0.2"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Testcase 4: Multiclass, Typical\n",
    "y_true = pd.read_csv(StringIO(\"\"\"\n",
    "d3mIndex,species\n",
    "1,versicolor\n",
    "2,versicolor\n",
    "16,virginica\n",
    "17,setosa\n",
    "22,versicolor\n",
    "26,versicolor\n",
    "30,versicolor\n",
    "31,virginica\n",
    "33,versicolor\n",
    "37,virginica\n",
    "\"\"\"))\n",
    "\n",
    "y_pred = pd.read_csv(StringIO(\"\"\"\n",
    "d3mIndex,species\n",
    "1,setosa\n",
    "2,versicolor\n",
    "16,virginica\n",
    "17,setosa\n",
    "22,versicolor\n",
    "26,virginica\n",
    "30,versicolor\n",
    "31,virginica\n",
    "33,versicolor\n",
    "37,virginica\n",
    "\"\"\"))\n",
    "\n",
    "hammingLoss(y_true, y_pred)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/svattam/miniconda3/envs/automl/lib/python3.6/site-packages/ipykernel_launcher.py:13: FutureWarning: Method .as_matrix will be removed in a future version. Use .values instead.\n",
      "  del sys.path[0]\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "0.0"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Testcase 5: Multiclass, Zero loss\n",
    "y_true = pd.read_csv(StringIO(\"\"\"\n",
    "d3mIndex,species\n",
    "1,versicolor\n",
    "2,versicolor\n",
    "16,virginica\n",
    "17,setosa\n",
    "22,versicolor\n",
    "26,versicolor\n",
    "30,versicolor\n",
    "31,virginica\n",
    "33,versicolor\n",
    "37,virginica\n",
    "\"\"\"))\n",
    "\n",
    "y_pred = pd.read_csv(StringIO(\"\"\"\n",
    "d3mIndex,species\n",
    "1,versicolor\n",
    "2,versicolor\n",
    "16,virginica\n",
    "17,setosa\n",
    "22,versicolor\n",
    "26,versicolor\n",
    "30,versicolor\n",
    "31,virginica\n",
    "33,versicolor\n",
    "37,virginica\n",
    "\"\"\"))\n",
    "\n",
    "hammingLoss(y_true, y_pred)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/svattam/miniconda3/envs/automl/lib/python3.6/site-packages/ipykernel_launcher.py:13: FutureWarning: Method .as_matrix will be removed in a future version. Use .values instead.\n",
      "  del sys.path[0]\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "1.0"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Testcase 6: Multiclass, Complete loss\n",
    "y_true = pd.read_csv(StringIO(\"\"\"\n",
    "d3mIndex,species\n",
    "1,versicolor\n",
    "2,versicolor\n",
    "16,versicolor\n",
    "17,virginica\n",
    "22,versicolor\n",
    "26,versicolor\n",
    "30,versicolor\n",
    "31,virginica\n",
    "33,versicolor\n",
    "37,virginica\n",
    "\"\"\"))\n",
    "\n",
    "y_pred = pd.read_csv(StringIO(\"\"\"\n",
    "d3mIndex,species\n",
    "1,setosa\n",
    "2,setosa\n",
    "16,setosa\n",
    "17,setosa\n",
    "22,setosa\n",
    "26,setosa\n",
    "30,setosa\n",
    "31,setosa\n",
    "33,setosa\n",
    "37,setosa\n",
    "\"\"\"))\n",
    "\n",
    "hammingLoss(y_true, y_pred)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# RMSE"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "def rootMeanSquaredError(y_true, y_pred):\n",
    "    \"\"\"\n",
    "    Computes the root mean squared error, for both univariate and multivariate case\n",
    "    \"\"\"\n",
    "    import numpy as np\n",
    "    from sklearn.metrics import mean_squared_error\n",
    "    from math import sqrt\n",
    "    \n",
    "    rmse = None\n",
    "    \n",
    "    # perform some checks\n",
    "    assert 'd3mIndex' in y_true.columns\n",
    "    assert 'd3mIndex' in y_pred.columns\n",
    "    assert y_true.shape == y_pred.shape\n",
    "    \n",
    "    # preprocessing\n",
    "    y_true.set_index('d3mIndex', inplace=True)\n",
    "    y_pred.set_index('d3mIndex', inplace=True)\n",
    "    \n",
    "    # determine the dimension\n",
    "    y_true_dim=y_true.shape[1]\n",
    "         \n",
    "    # univariate case\n",
    "    if y_true_dim == 1: \n",
    "        y_true_array = y_true.as_matrix().ravel()\n",
    "        y_pred_array = y_pred.as_matrix().ravel()\n",
    "        mse = mean_squared_error(y_true, y_pred)\n",
    "        rmse = sqrt(mse)\n",
    "    \n",
    "    # multivariate case\n",
    "    elif y_true_dim > 1:\n",
    "        y_true_array = y_true.as_matrix()\n",
    "        y_pred_array = y_pred.as_matrix()\n",
    "        mse = mean_squared_error(y_true_array, y_pred_array, multioutput='uniform_average')\n",
    "        rmse = sqrt(mse)\n",
    "    \n",
    "    return rmse"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/svattam/miniconda3/envs/automl/lib/python3.6/site-packages/ipykernel_launcher.py:25: FutureWarning: Method .as_matrix will be removed in a future version. Use .values instead.\n",
      "/home/svattam/miniconda3/envs/automl/lib/python3.6/site-packages/ipykernel_launcher.py:26: FutureWarning: Method .as_matrix will be removed in a future version. Use .values instead.\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "0.8381527307120105"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# test case 1\n",
    "# y_true_uni=[3, -1., 2, 7]\n",
    "# y_pred_uni=[2.1, 0.0, 2, 8]\n",
    "# expected rmse = 0.8381527307120105\n",
    "\n",
    "y_true = pd.read_csv(StringIO(\"\"\"\n",
    "d3mIndex,value\n",
    "1,3\n",
    "2,-1.0\n",
    "16,2\n",
    "17,7\n",
    "\"\"\"))\n",
    "y_pred = pd.read_csv(StringIO(\"\"\"\n",
    "d3mIndex,value\n",
    "1,2.1\n",
    "2,0.0\n",
    "16,2\n",
    "17,8\n",
    "\"\"\"))\n",
    "rootMeanSquaredError(y_true, y_pred)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/svattam/miniconda3/envs/automl/lib/python3.6/site-packages/ipykernel_launcher.py:32: FutureWarning: Method .as_matrix will be removed in a future version. Use .values instead.\n",
      "/home/svattam/miniconda3/envs/automl/lib/python3.6/site-packages/ipykernel_launcher.py:33: FutureWarning: Method .as_matrix will be removed in a future version. Use .values instead.\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "0.8416254115301732"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# test case 2\n",
    "# y_true_multi=[[0.5, 1],[-1, 1],[7, -6]]\n",
    "# y_pred_multi=[[0, 2],[-1, 2],[8, -5]]\n",
    "# expected rmse = 0.8416254115301732\n",
    "\n",
    "y_true = pd.read_csv(StringIO(\"\"\"\n",
    "d3mIndex,value1, value2\n",
    "1,0.5,1\n",
    "2,-1,1\n",
    "16,7,-6\n",
    "\"\"\"))\n",
    "y_pred = pd.read_csv(StringIO(\"\"\"\n",
    "d3mIndex,value1,value2\n",
    "1,0,2\n",
    "2,-1,2\n",
    "16,8,-5\n",
    "\"\"\"))\n",
    "rootMeanSquaredError(y_true, y_pred)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Object detection average precision"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "TEST CASE 1 --- AP:  0.6666666666666666\n",
      "TEST CASE 2 --- AP:  0.125\n",
      "TEST CASE 3 --- AP:  0.4444444444444444\n",
      "TEST CASE 4 --- AP:  0.4444444444444444\n"
     ]
    }
   ],
   "source": [
    "def group_gt_boxes_by_image_name(gt_boxes):\n",
    "    gt_dict: typing.Dict = {}\n",
    "\n",
    "    for box in gt_boxes:\n",
    "        image_name = box[0]\n",
    "        bounding_polygon = box[1:]\n",
    "        bbox = convert_bouding_polygon_to_box_coords(bounding_polygon)\n",
    "\n",
    "        if image_name not in gt_dict.keys():\n",
    "            gt_dict[image_name] = []\n",
    "\n",
    "        gt_dict[image_name].append({'bbox': bbox})\n",
    "\n",
    "    return gt_dict\n",
    "\n",
    "\n",
    "def convert_bouding_polygon_to_box_coords(bounding_polygon):\n",
    "    # box_coords = [x_min, y_min, x_max, y_max]\n",
    "    box_coords = [bounding_polygon[0], bounding_polygon[1],\n",
    "                  bounding_polygon[4], bounding_polygon[5]]\n",
    "    return box_coords\n",
    "\n",
    "\n",
    "def voc_ap(rec, prec):\n",
    "    import numpy\n",
    "\n",
    "    # First append sentinel values at the end.\n",
    "    mrec = numpy.concatenate(([0.], rec, [1.]))\n",
    "    mpre = numpy.concatenate(([0.], prec, [0.]))\n",
    "\n",
    "    # Compute the precision envelope.\n",
    "    for i in range(mpre.size - 1, 0, -1):\n",
    "        mpre[i - 1] = numpy.maximum(mpre[i - 1], mpre[i])\n",
    "\n",
    "    # To calculate area under PR curve, look for points\n",
    "    # where X axis (recall) changes value.\n",
    "    i = numpy.where(mrec[1:] != mrec[:-1])[0]\n",
    "\n",
    "    # And sum (\\Delta recall) * prec.\n",
    "    ap = numpy.sum((mrec[i + 1] - mrec[i]) * mpre[i + 1])\n",
    "\n",
    "    return float(ap)\n",
    "\n",
    "\n",
    "def object_detection_average_precision(y_true, y_pred):\n",
    "    \"\"\"\n",
    "    This function takes a list of ground truth bounding polygons (rectangles in this case)\n",
    "    and a list of detected bounding polygons (also rectangles) for a given class and\n",
    "    computes the average precision of the detections with respect to the ground truth polygons.\n",
    "    Parameters:\n",
    "    -----------\n",
    "    y_true: list\n",
    "     List of ground truth polygons. Each polygon is represented as a list of\n",
    "     vertices, starting in the upper-left corner going counter-clockwise.\n",
    "     Since in this case, the polygons are rectangles, they will have the\n",
    "     following format:\n",
    "        [image_name, x_min, y_min, x_min, y_max, x_max, y_max, x_max, y_min].\n",
    "    y_pred: list\n",
    "     List of bounding box polygons with their corresponding confidence scores. Each\n",
    "     polygon is represented as a list of vertices, starting in the upper-left corner\n",
    "     going counter-clockwise. Since in this case, the polygons are rectangles, they\n",
    "     will have the following format:\n",
    "        [image_name, x_min, y_min, x_min, y_max, x_max, y_max, x_max, y_min, confidence_score].\n",
    "    Returns:\n",
    "    --------\n",
    "    ap: float\n",
    "     Average precision between detected polygons (rectangles) and the ground truth polylgons (rectangles).\n",
    "     (it is also the area under the precision-recall curve).\n",
    "    Example 1:\n",
    "    >> predictions_list_1 = [['img_00001.png', 110, 110, 110, 210, 210, 210, 210, 110, 0.6],\n",
    "                             ['img_00002.png', 5, 10, 5, 20, 20, 20, 20, 10, 0.9],\n",
    "                             ['img_00002.png', 120, 130, 120, 200, 200, 200, 200, 130, 0.6]]\n",
    "    >> ground_truth_list_1 = [['img_00001.png', 100, 100, 100, 200, 200, 200, 200, 100],\n",
    "                              ['img_00002.png', 10, 10, 10, 20, 20, 20, 20, 10],\n",
    "                              ['img_00002.png', 70, 80, 70, 150, 140, 150, 140, 80]]\n",
    "    >> ap_1 = object_detection_average_precision(ground_truth_list_1, predictions_list_1)\n",
    "    >> print(ap_1)\n",
    "    0.667\n",
    "    Example 2:\n",
    "    >> predictions_list_2 = [['img_00285.png', 330, 463, 330, 505, 387, 505, 387, 463, 0.0739],\n",
    "                             ['img_00285.png', 420, 433, 420, 498, 451, 498, 451, 433, 0.0910],\n",
    "                             ['img_00285.png', 328, 465, 328, 540, 403, 540, 403, 465, 0.1008],\n",
    "                             ['img_00285.png', 480, 477, 480, 522, 508, 522, 508, 477, 0.1012],\n",
    "                             ['img_00285.png', 357, 460, 357, 537, 417, 537, 417, 460, 0.1058],\n",
    "                             ['img_00285.png', 356, 456, 356, 521, 391, 521, 391, 456, 0.0843],\n",
    "                             ['img_00225.png', 345, 460, 345, 547, 415, 547, 415, 460, 0.0539],\n",
    "                             ['img_00225.png', 381, 362, 381, 513, 455, 513, 455, 362, 0.0542],\n",
    "                             ['img_00225.png', 382, 366, 382, 422, 416, 422, 416, 366, 0.0559],\n",
    "                             ['img_00225.png', 730, 463, 730, 583, 763, 583, 763, 463, 0.0588]]\n",
    "    >> ground_truth_list_2 = [['img_00285.png', 480, 457, 480, 529, 515, 529, 515, 457],\n",
    "                              ['img_00285.png', 480, 457, 480, 529, 515, 529, 515, 457],\n",
    "                              ['img_00225.png', 522, 540, 522, 660, 576, 660, 576, 540],\n",
    "                              ['img_00225.png', 739, 460, 739, 545, 768, 545, 768, 460]]\n",
    "    >> ap_2 = object_detection_average_precision(ground_truth_list_2, predictions_list_2)\n",
    "    >> print(ap_2)\n",
    "    0.125\n",
    "    Example 3:\n",
    "    >> predictions_list_3 = [['img_00001.png', 110, 110, 110, 210, 210, 210, 210, 110, 0.6],\n",
    "                             ['img_00002.png', 120, 130, 120, 200, 200, 200, 200, 130, 0.6],\n",
    "                             ['img_00002.png', 5, 8, 5, 16, 15, 16, 15, 8, 0.9],\n",
    "                             ['img_00002.png', 11, 12, 11, 18, 21, 18, 21, 12, 0.9]]\n",
    "    >> ground_truth_list_3 = [['img_00001.png', 100, 100, 100, 200, 200, 200, 200, 100],\n",
    "                              ['img_00002.png', 10, 10, 10, 20, 20, 20, 20, 10],\n",
    "                              ['img_00002.png', 70, 80, 70, 150, 140, 150, 140, 80]]\n",
    "    >> ap_3 = object_detection_average_precision(ground_truth_list_3, predictions_list_3)\n",
    "    >> print(ap_3)\n",
    "    0.444\n",
    "    Example 4:\n",
    "    (Same as example 3 except the last two box predictions in img_00002.png are switched)\n",
    "    >> predictions_list_4 = [['img_00001.png', 110, 110, 110, 210, 210, 210, 210, 110, 0.6],\n",
    "                             ['img_00002.png', 120, 130, 120, 200, 200, 200, 200, 130, 0.6],\n",
    "                             ['img_00002.png', 11, 12, 11, 18, 21, 18, 21, 12, 0.9],\n",
    "                             ['img_00002.png', 5, 8, 5, 16, 15, 16, 15, 8, 0.9]]\n",
    "    >> ground_truth_list_4 = [['img_00001.png', 100, 100, 100, 200, 200, 200, 200, 100],\n",
    "                              ['img_00002.png', 10, 10, 10, 20, 20, 20, 20, 10],\n",
    "                              ['img_00002.png', 70, 80, 70, 150, 140, 150, 140, 80]]\n",
    "    >> ap_4 = object_detection_average_precision(ground_truth_list_4, predictions_list_4)\n",
    "    >> print(ap_4)\n",
    "    0.444\n",
    "    \"\"\"\n",
    "\n",
    "    \"\"\"\n",
    "    This function is different from others because ``y_true`` and ``y_pred`` are not vectors but arrays.\n",
    "    \"\"\"\n",
    "    import numpy\n",
    "    ovthresh = 0.5\n",
    "\n",
    "    # y_true = typing.cast(Truth, unvectorize(y_true))\n",
    "    # y_pred = typing.cast(Predictions, unvectorize(y_pred))\n",
    "\n",
    "    # Load ground truth.\n",
    "    gt_dict = group_gt_boxes_by_image_name(y_true)\n",
    "\n",
    "    # Extract gt objects for this class.\n",
    "    recs = {}\n",
    "    npos = 0\n",
    "\n",
    "    imagenames = sorted(gt_dict.keys())\n",
    "    for imagename in imagenames:\n",
    "        Rlist = [obj for obj in gt_dict[imagename]]\n",
    "        bbox = numpy.array([x['bbox'] for x in Rlist])\n",
    "        det = [False] * len(Rlist)\n",
    "        npos = npos + len(Rlist)\n",
    "        recs[imagename] = {'bbox': bbox, 'det': det}\n",
    "\n",
    "    # Load detections.\n",
    "    det_length = len(y_pred[0])\n",
    "\n",
    "    # Check that all boxes are the same size.\n",
    "    for det in y_pred:\n",
    "        assert len(det) == det_length, 'Not all boxes have the same dimensions.'\n",
    "\n",
    "    image_ids = [x[0] for x in y_pred]\n",
    "    BP = numpy.array([[float(z) for z in x[1:-1]] for x in y_pred])\n",
    "    BB = numpy.array([convert_bouding_polygon_to_box_coords(x) for x in BP])\n",
    "\n",
    "    confidence = numpy.array([float(x[-1]) for x in y_pred])\n",
    "    boxes_w_confidences_list = numpy.hstack((BB, -1 * confidence[:, None]))\n",
    "    boxes_w_confidences = numpy.empty((boxes_w_confidences_list.shape[0],),\n",
    "                                     dtype=[('x_min', float), ('y_min', float),\n",
    "                                            ('x_max', float), ('y_max', float),\n",
    "                                            ('confidence', float)])\n",
    "    boxes_w_confidences[:] = [tuple(i) for i in boxes_w_confidences_list]\n",
    "\n",
    "    # Sort by confidence.\n",
    "    #sorted_ind = numpy.argsort(-confidence)\n",
    "    sorted_ind = numpy.argsort(\n",
    "        boxes_w_confidences, kind='mergesort',\n",
    "        order=('confidence', 'x_min', 'y_min'))\n",
    "    BB = BB[sorted_ind, :]\n",
    "    image_ids = [image_ids[x] for x in sorted_ind]\n",
    "\n",
    "    # Go down y_pred and mark TPs and FPs.\n",
    "    nd = len(image_ids)\n",
    "    tp = numpy.zeros(nd)\n",
    "    fp = numpy.zeros(nd)\n",
    "    for d in range(nd):\n",
    "        R = recs[image_ids[d]]\n",
    "        bb = BB[d, :].astype(float)\n",
    "        ovmax = -numpy.inf\n",
    "        BBGT = R['bbox'].astype(float)\n",
    "\n",
    "        if BBGT.size > 0:\n",
    "            # Compute overlaps.\n",
    "            # Intersection.\n",
    "            ixmin = numpy.maximum(BBGT[:, 0], bb[0])\n",
    "            iymin = numpy.maximum(BBGT[:, 1], bb[1])\n",
    "            ixmax = numpy.minimum(BBGT[:, 2], bb[2])\n",
    "            iymax = numpy.minimum(BBGT[:, 3], bb[3])\n",
    "            iw = numpy.maximum(ixmax - ixmin + 1., 0.)\n",
    "            ih = numpy.maximum(iymax - iymin + 1., 0.)\n",
    "            inters = iw * ih\n",
    "\n",
    "            # Union.\n",
    "            uni = ((bb[2] - bb[0] + 1.) * (bb[3] - bb[1] + 1.) +\n",
    "                   (BBGT[:, 2] - BBGT[:, 0] + 1.) *\n",
    "                   (BBGT[:, 3] - BBGT[:, 1] + 1.) - inters)\n",
    "\n",
    "            overlaps = inters / uni\n",
    "            ovmax = numpy.max(overlaps)\n",
    "            jmax = numpy.argmax(overlaps)\n",
    "\n",
    "        if ovmax > ovthresh:\n",
    "            if not R['det'][jmax]:\n",
    "                tp[d] = 1.\n",
    "                R['det'][jmax] = 1\n",
    "            else:\n",
    "                fp[d] = 1.\n",
    "        else:\n",
    "            fp[d] = 1.\n",
    "\n",
    "    # Compute precision recall.\n",
    "    fp = numpy.cumsum(fp)\n",
    "    tp = numpy.cumsum(tp)\n",
    "    rec = tp / float(npos)\n",
    "    # Avoid divide by zero in case the first detection matches a difficult ground truth.\n",
    "    prec = tp / numpy.maximum(tp + fp, numpy.finfo(numpy.float64).eps)\n",
    "    ap = voc_ap(rec, prec)\n",
    "\n",
    "    return ap\n",
    "\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    predictions_list_1 = [\n",
    "        ['img_00001.png', 110, 110, 110, 210, 210, 210, 210, 110, 0.6],\n",
    "        ['img_00002.png', 5, 10, 5, 20, 20, 20, 20, 10, 0.9],\n",
    "        ['img_00002.png', 120, 130, 120, 200, 200, 200, 200, 130, 0.6]\n",
    "    ]\n",
    "    ground_truth_list_1 = [\n",
    "        ['img_00001.png', 100, 100, 100, 200, 200, 200, 200, 100],\n",
    "        ['img_00002.png', 10, 10, 10, 20, 20, 20, 20, 10],\n",
    "        ['img_00002.png', 70, 80, 70, 150, 140, 150, 140, 80]\n",
    "    ]\n",
    "    ap_1 = object_detection_average_precision(\n",
    "        ground_truth_list_1, predictions_list_1)\n",
    "    print('TEST CASE 1 --- AP: ', ap_1)\n",
    "\n",
    "    predictions_list_2 = [\n",
    "        ['img_00285.png', 330, 463, 330, 505, 387, 505, 387, 463, 0.0739],\n",
    "        ['img_00285.png', 420, 433, 420, 498, 451, 498, 451, 433, 0.0910],\n",
    "        ['img_00285.png', 328, 465, 328, 540, 403, 540, 403, 465, 0.1008],\n",
    "        ['img_00285.png', 480, 477, 480, 522, 508, 522, 508, 477, 0.1012],\n",
    "        ['img_00285.png', 357, 460, 357, 537, 417, 537, 417, 460, 0.1058],\n",
    "        ['img_00285.png', 356, 456, 356, 521, 391, 521, 391, 456, 0.0843],\n",
    "        ['img_00225.png', 345, 460, 345, 547, 415, 547, 415, 460, 0.0539],\n",
    "        ['img_00225.png', 381, 362, 381, 513, 455, 513, 455, 362, 0.0542],\n",
    "        ['img_00225.png', 382, 366, 382, 422, 416, 422, 416, 366, 0.0559],\n",
    "        ['img_00225.png', 730, 463, 730, 583, 763, 583, 763, 463, 0.0588],\n",
    "    ]\n",
    "    ground_truth_list_2 = [\n",
    "        ['img_00285.png', 480, 457, 480, 529, 515, 529, 515, 457],\n",
    "        ['img_00285.png', 480, 457, 480, 529, 515, 529, 515, 457],\n",
    "        ['img_00225.png', 522, 540, 522, 660, 576, 660, 576, 540],\n",
    "        ['img_00225.png', 739, 460, 739, 545, 768, 545, 768, 460],\n",
    "    ]\n",
    "    ap_2 = object_detection_average_precision(\n",
    "        ground_truth_list_2, predictions_list_2)\n",
    "    print('TEST CASE 2 --- AP: ', ap_2)\n",
    "\n",
    "    predictions_list_3 = [\n",
    "        ['img_00001.png', 110, 110, 110, 210, 210, 210, 210, 110, 0.6],\n",
    "        ['img_00002.png', 120, 130, 120, 200, 200, 200, 200, 130, 0.6],\n",
    "        ['img_00002.png', 5, 8, 5, 16, 15, 16, 15, 8, 0.9],\n",
    "        ['img_00002.png', 11, 12, 11, 18, 21, 18, 21, 12, 0.9]\n",
    "    ]\n",
    "    ground_truth_list_3 = [\n",
    "        ['img_00001.png', 100, 100, 100, 200, 200, 200, 200, 100],\n",
    "        ['img_00002.png', 10, 10, 10, 20, 20, 20, 20, 10],\n",
    "        ['img_00002.png', 70, 80, 70, 150, 140, 150, 140, 80]\n",
    "    ]\n",
    "    ap_3 = object_detection_average_precision(\n",
    "        ground_truth_list_3, predictions_list_3)\n",
    "    print('TEST CASE 3 --- AP: ', ap_3)\n",
    "\n",
    "    predictions_list_4 = [\n",
    "        ['img_00001.png', 110, 110, 110, 210, 210, 210, 210, 110, 0.6],\n",
    "        ['img_00002.png', 120, 130, 120, 200, 200, 200, 200, 130, 0.6],\n",
    "        ['img_00002.png', 11, 12, 11, 18, 21, 18, 21, 12, 0.9],\n",
    "        ['img_00002.png', 5, 8, 5, 16, 15, 16, 15, 8, 0.9]\n",
    "    ]\n",
    "    ground_truth_list_4 = [\n",
    "        ['img_00001.png', 100, 100, 100, 200, 200, 200, 200, 100],\n",
    "        ['img_00002.png', 10, 10, 10, 20, 20, 20, 20, 10],\n",
    "        ['img_00002.png', 70, 80, 70, 150, 140, 150, 140, 80]\n",
    "    ]\n",
    "    ap_4 = object_detection_average_precision(\n",
    "        ground_truth_list_4, predictions_list_4)\n",
    "    print('TEST CASE 4 --- AP: ', ap_4)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "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.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
