{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Open Images Object Detection RVC 2020 edition\n### Detect objects in varied and complex images\n\nUsing FasterRCNN+InceptionResNet V2, an SSD-based object detection model trained on Open Images V4 with ImageNet pre-trained MobileNet V2 as image feature extractor.","execution_count":null},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import pdb\nimport math\nimport itertools\nimport time\nimport gc\nimport pickle\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\nimport tempfile\nimport tensorflow as tf\nimport tensorflow_hub as hub\n\nfrom PIL import Image, ImageColor, ImageDraw, ImageFont, ImageOps\n\nimport os\n\nimage_path = '/kaggle/input/open-images-object-detection-rvc-2020'\nmodule_handle = 'https://tfhub.dev/google/openimages_v4/ssd/mobilenet_v2/1'\n\nprint(tf.__version__)\ngpus = tf.config.experimental.list_physical_devices('GPU')\nif gpus:\n    # Restrict TensorFlow to only use the first GPU\n    try:\n        tf.config.experimental.set_visible_devices(gpus[0], 'GPU')\n        logical_gpus = tf.config.experimental.list_logical_devices('GPU')\n        print(len(gpus), \"Physical GPUs,\", len(logical_gpus), \"Logical GPU\")\n    except RuntimeError as e:\n        # Visible devices must be set before GPUs have been initialized\n        print(e)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Helper Functions","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"# Resizes image to new_width x new_height and returns PIL file\ndef resize_image(path, new_width=256, new_height=256):\n    _, filename = tempfile.mkstemp(suffix=\".jpg\")\n    pil_image = Image.open(path)\n    pil_image = ImageOps.fit(pil_image, (new_width, new_height), Image.ANTIALIAS)\n    pil_image_rgb = pil_image.convert(\"RGB\")\n    pil_image_rgb.save(filename, format=\"JPEG\", quality=90)\n    # print('Resized image saved as: {}'.format(filename))\n    return filename, pil_image","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Display a PIL image file\ndef display_image(image):\n    fig = plt.figure(figsize=(20, 15))\n    plt.grid(False)\n    plt.imshow(image)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Load image into TF\ndef load_img(path):\n    print('loading image: {}'.format(path))\n    img = tf.io.read_file(path)\n    img = tf.image.decode_jpeg(img, channels=3)\n    return img","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Adds a bounding box to an image\ndef draw_bounding_box_on_image(image, ymin, xmin, ymax,\n                               xmax, color, font,\n                               thickness=4, display_str_list=()):\n    draw = ImageDraw.Draw(image)\n    im_width, im_height = image.size\n    (left, right, top, bottom) = (xmin * im_width, xmax * im_width,\n                                ymin * im_height, ymax * im_height)\n    draw.line([(left, top), (left, bottom), (right, bottom), (right, top),\n               (left, top)],\n              width=thickness,\n              fill=color)\n\n    # If the total height of the display strings added to the top of the bounding\n    # box exceeds the top of the image, stack the strings below the bounding box\n    # instead of above.\n    display_str_heights = [font.getsize(ds)[1] for ds in display_str_list]\n    \n    # Each display_str has a top and bottom margin of 0.05x.\n    total_display_str_height = (1 + 2 * 0.05) * sum(display_str_heights)\n\n    if top > total_display_str_height:\n        text_bottom = top\n    else:\n        text_bottom = top + total_display_str_height\n    # Reverse list and print from bottom to top.\n    for display_str in display_str_list[::-1]:\n        text_width, text_height = font.getsize(display_str)\n        margin = np.ceil(0.05 * text_height)\n        draw.rectangle([(left, text_bottom - text_height - 2 * margin),\n                    (left + text_width, text_bottom)],\n                   fill=color)\n        draw.text((left + margin, text_bottom - text_height - margin),\n                  display_str,\n                  fill=\"black\",\n                  font=font)\n        text_bottom -= text_height - 2 * margin","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Draw object boxes for the images\ndef draw_boxes(image, boxes, class_names, scores, max_boxes=10, min_score=0.0):\n    print('in draw boxes')\n    colors = list(ImageColor.colormap.values())\n\n    try:\n        font = ImageFont.truetype(\"/usr/share/fonts/truetype/liberation/LiberationSansNarrow-Regular.ttf\",25)\n    except IOError:\n        print(\"Font not found, using default font.\")\n        font = ImageFont.load_default()\n\n    for i in range(min(boxes.shape[0], max_boxes)):\n        if scores[i] >= min_score:\n            ymin, xmin, ymax, xmax = tuple(boxes[i])\n            display_str = \"{}: {}%\".format(class_names[i].decode(\"ascii\"), int(100 * scores[i]))\n            color = colors[hash(class_names[i]) % len(colors)]\n            image_pil = Image.fromarray(np.uint8(image)).convert(\"RGB\")\n            draw_bounding_box_on_image(image_pil, ymin, xmin,\n                                       ymax, xmax, color, font,\n                                       display_str_list=[display_str])\n        np.copyto(image, np.array(image_pil))\n    return image","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Run the detector on an image\ndef run_detector(detector, path, b=True):\n    img = load_img(path)\n    converted_img  = tf.image.convert_image_dtype(img, tf.float32)[tf.newaxis, ...]\n    with tf.device('/GPU:0'):\n        result = detector(converted_img)\n        result = {key:value.numpy() for key,value in result.items()}\n        image_with_boxes = None\n        if b is True:\n            image_with_boxes = draw_boxes(\n                img.numpy(), result[\"detection_boxes\"],\n                result[\"detection_class_entities\"], result[\"detection_scores\"])\n        return image_with_boxes, result","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Get the file name from the image id\ndef filename_from_id(id):\n    return os.path.join(image_path, 'test/', '{}.jpg'.format(id) )","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Resize and Display Sample Image","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"sample_submission_df = pd.read_csv(f'{image_path}/sample_submission.csv')\nimage_ids = sample_submission_df['ImageId']\ndel sample_submission_df","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_img = 15\n# Build a list of images\n\nfilename = filename_from_id(image_ids[test_img])\n\n# Load, resize and display sample image\nfilename_r, pil_image = resize_image(filename)\ndisplay_image(pil_image)\ndel pil_image","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Run the Detector on a Single Image","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"print('Loading module...')\ndetector = hub.load(module_handle).signatures['default']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print('Processing image: {}'.format(filename))\nfilename_r, pil_image = resize_image(filename)\nimage_with_boxes, result = run_detector(detector, filename_r)\nprint('Found {} objects'.format(len(result['detection_scores'])))\ndisplay_image(image_with_boxes)\ndel image_with_boxes\ndel pil_image","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Create Submission","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"def index_marks(nrows, chunk_size):\n    return range(chunk_size, math.ceil(nrows / chunk_size) * chunk_size, chunk_size)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def split(df, chunk_size):\n    indices = index_marks(df.shape[0], chunk_size)\n    return np.split(df, indices)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Create the prediction results string\ndef make_prediction_string(result, idx):\n    class_name = result['detection_class_names'][idx].decode(\"utf-8\")\n    boxes = result['detection_boxes'][idx]\n    score = result['detection_scores'][idx]\n    return f\"{class_name} {score} \" + \" \".join(map(str, boxes))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Formats the prediction results\ndef format_prediction_string(image_id, result):\n    prediction_strings = [make_prediction_string(result, i) for i in range(len(result['detection_scores']))]\n    return  \" \".join(prediction_strings)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def chunk_inference(chunk, detector, predictions):\n    results = []\n    for img in chunk:\n        filename = filename_from_id(img)\n        filename_r, pil_image = resize_image(filename)\n        , result = run_detector(detector, filename_r, False)\n        results.append(result)\n    return results","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"chunk_size = 2500\nn_chunks = round(len(image_ids) / chunk_size)\nchunks = split(image_ids, chunk_size)\nprint('Chunks: {}'.format(n_chunks))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"predictions = []\nchunk_counter = 1\nfor c in chunks:\n    print('Processing chunk {}'.format(chunk_counter))\n    chunk_pred = chunk_inference(c, detector, predictions)\n    predictions.append(chunk_pred)\n    chunk_counter += 1\n    del chunk_pred\ndel detector","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"gc.collect()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"file = open('predictions_final.pkl', 'wb')\npickle.dump(predictions, file)\nfile.close()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"file = open('image_ids.pkl', 'wb')\npickle.dump(image_ids, file)\nfile.close()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"predictions = pickle.load(open( \"predictions_final.pkl\", \"rb\" ))\nimage_ids = pickle.load(open( \"image_ids.pkl\", \"rb\" ))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"preds_merged = list(itertools.chain.from_iterable(predictions))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"column_names = ['ImageID', 'RawPred','PredictionString']\noutput = pd.DataFrame(columns = column_names)\noutput['ImageID'] = image_ids\noutput['RawPred'] = preds_merged\nfor index, row in output.iterrows():\n    row['PredictionString'] = format_prediction_string(row['ImageID'], row['RawPred'])\noutput.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"output.drop('RawPred', axis=1, inplace=True)\noutput.to_csv('submission.csv', index=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]}],"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}