{"cells":[{"metadata":{},"cell_type":"markdown","source":"This is a component of a larger project [Cat-A-Logger](https://github.com/screamatthewind/cat-a-logger) on github   \nSee this [Short Slide Presentation](https://github.com/screamatthewind/cat-a-logger/blob/main/Slide%20Presentation%20-%20Short.pdf)"},{"metadata":{"trusted":true},"cell_type":"code","source":"# are we running locally or in kaggle?\n\nimport os\n\nif os.environ.get('KAGGLE_KERNEL_RUN_TYPE','') == '':\n    print(\"We are running code on Localhost\")\n    isLocalhost = True\n\nelse:\n    print(\"We are running in Kaggle\")\n    isLocalhost = False","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"OUTPUT_DATASET_ID = 'cropped-cats-and-dogs'\nOUTPUT_DATASET_NAME = 'Cropped Cats and Dogs'\nOUTPUT_PATH = './output/cropped-cats-and-dogs'\n\nif isLocalhost:\n    YOLO_V3_PATH = './input/dogs-vs-cats-redux-kernels-edition/yolov3-files'\n    INPUT_FILES = './input/dogs-vs-cats-redux-kernels-edition/train/*.jpg'\nelse:\n    YOLO_V3_PATH = '../input/yolov3-files'\n    INPUT_FILES = 'train/*.jpg'\n\n    from kaggle_secrets import UserSecretsClient\n    user_secrets = UserSecretsClient()\n\n    USER_ID = user_secrets.get_secret(\"user-id\")\n    API_TOKEN = user_secrets.get_secret(\"api-token\")\n\n# same size is used in Augment Cats and Dogs\nX_SIZE = 224\nY_SIZE = 224\n\nuse_gpu = True\nshow = False\nsave = True\n\nconfidence = 0.5\nthreshold = 0.3\nmax_size = 128\n\nrun_limit = 10","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# unzip data\n\nimport os\nfrom os import path\nimport zipfile\n\nif isLocalhost:\n\n    if path.exists(\"./input/dogs-vs-cats-redux-kernels-edition/train/cat.0.jpg\") == False:\n\n        print('Downloading files')\n\n        ! kaggle competitions download -c dogs-vs-cats-redux-kernels-edition\n        ! kaggle datasets download -d screamatthewind/yolov3-files\n\n        os.makedirs(\"./input\", exist_ok=True)    \n\n        ! mv dogs-vs-cats-redux-kernels-edition.zip input\n        ! mv yolov3-files.zip input\n\n        print('Extracting files')\n\n        os.makedirs(\"./input/dogs-vs-cats-redux-kernels-edition\", exist_ok=True)    \n        os.makedirs(\"./input/dogs-vs-cats-redux-kernels-edition/yolov3-files\", exist_ok=True)    \n\n        with zipfile.ZipFile(\"./input/dogs-vs-cats-redux-kernels-edition.zip\",\"r\") as z:\n            z.extractall(\"./input/dogs-vs-cats-redux-kernels-edition\")\n\n        with zipfile.ZipFile(\"./input/dogs-vs-cats-redux-kernels-edition/train.zip\",\"r\") as z:\n            z.extractall(\"./input/dogs-vs-cats-redux-kernels-edition\")\n\n        with zipfile.ZipFile(\"./input/yolov3-files.zip\",\"r\") as z:\n            z.extractall(\"./input/dogs-vs-cats-redux-kernels-edition/yolov3-files\")\n\n        print('Cleaning up files')\n\n        ! rm ./input/dogs-vs-cats-redux-kernels-edition.zip\n        ! rm ./input/yolov3-files.zip\n        ! rm ./input/dogs-vs-cats-redux-kernels-edition/train.zip\n        ! rm ./input/dogs-vs-cats-redux-kernels-edition/test.zip\n        ! rm ./input/dogs-vs-cats-redux-kernels-edition/sample_submission.csv\n    \n        print(\"Done extracting files\")\n\n    else:\n        print(\"Files already exist locally\")\n    \nelse:\n    print('Extracting files')\n    \n    with zipfile.ZipFile(\"../input/dogs-vs-cats-redux-kernels-edition/train.zip\",\"r\") as z:\n        z.extractall(\".\")\n    \n    with zipfile.ZipFile(\"../input/dogs-vs-cats-redux-kernels-edition/test.zip\",\"r\") as z:\n        z.extractall(\".\")\n        \n    print(\"Done extracting files\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"This section was inspired by: [YOLO Object Detection with OpenCV](https://gilberttanner.com/blog/yolo-object-detection-with-opencv) by [Gilbert Tanner](https://gilberttanner.com/)"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Crop Images\n\nimport os\nimport sys\nimport cv2\nimport glob\nimport time\nimport numpy as np\nimport matplotlib.pyplot as plt\nfrom PIL import Image\n\nweights_path = YOLO_V3_PATH + '/yolov3.weights'\nconfig_path = YOLO_V3_PATH + '/yolov3.cfg'\nlabels_path = YOLO_V3_PATH + '/coco.names'    \n\ndef extract_boxes_confidences_classids(outputs, confidence, width, height):\n    \n    boxes = []\n    confidences = []\n    classIDs = []\n\n    for output in outputs:\n        \n        for detection in output: \n            \n            # Extract the scores, classid, and the confidence of the prediction\n            scores = detection[5:]\n            classID = np.argmax(scores)\n            conf = scores[classID]\n            \n            # Consider only the predictions that are above the confidence threshold\n            if conf > confidence:\n                \n                # Scale the bounding box back to the size of the image\n                box = detection[0:4] * np.array([width, height, width, height])\n                centerX, centerY, w, h = box.astype('int')\n\n                # Use the center coordinates, width and height to get the coordinates of the top left corner\n                x = int(centerX - (w / 2))\n                y = int(centerY - (h / 2))\n\n                boxes.append([x, y, int(w), int(h)])\n                confidences.append(float(conf))\n                classIDs.append(classID)\n\n    return boxes, confidences, classIDs\n\n\ndef crop_image(image, boxes, confidences, classIDs, idxs, colors, filename, scale):\n    \n    wasProcessed = False\n    \n    if len(idxs) > 0:\n        \n        rgba_image = cv2.cvtColor(image, cv2.COLOR_RGB2RGBA)\n        \n        for i in idxs.flatten():\n            \n            if labels[classIDs[i]] == 'cat':\n                \n                # extract bounding box coordinates\n                x, y = boxes[i][0], boxes[i][1]\n                w, h = boxes[i][2], boxes[i][3]\n\n                if w < scale or h < scale or x < 0 or y < 0:\n                    continue;\n                \n                wasProcessed = True\n                \n                img_cropped = rgba_image[y:y+h, x:x+w]\n                \n                # show the output image\n                if show:\n                    %matplotlib inline\n                    plt.rcParams['figure.figsize'] = (10.0, 10.0)\n                    plt.imshow(cv2.cvtColor(img_cropped, cv2.COLOR_BGR2RGB))\n                    plt.show()\n\n                fname, ext = os.path.splitext(filename)                   \n                cv2.imwrite(OUTPUT_PATH + '/' + fname + '-' + i.astype(str) + ext, img_cropped)\n\n                # scale natively\n                img_resized = cv2.resize(img_cropped, (X_SIZE, Y_SIZE), interpolation=cv2.INTER_AREA)\n                cv2.imwrite(OUTPUT_PATH + '/' + fname + '-' + i.astype(str) + '-resized' + ext, img_resized)\n\n                # also write black and white and inverted (negative color) images\n                gray = cv2.cvtColor(img_resized, cv2.COLOR_BGR2GRAY)\n                negative_image = cv2.cvtColor(1 - gray, cv2.COLOR_GRAY2BGR)\n\n                cv2.imwrite(OUTPUT_PATH + '/' + fname + '-' + i.astype(str) + '-resized-neg' + ext, negative_image)\n                cv2.imwrite(OUTPUT_PATH + '/' + fname + '-' + i.astype(str) + '-resized-bw' + ext, cv2.cvtColor(gray, cv2.COLOR_GRAY2BGR))\n\n                # also write black and white and inverted (negative color) images\n                gray = cv2.cvtColor(img_cropped, cv2.COLOR_BGR2GRAY)\n                negative_image = cv2.cvtColor(1 - gray, cv2.COLOR_GRAY2BGR)\n                \n                cv2.imwrite(OUTPUT_PATH + '/' + fname + '-' + i.astype(str) + '-neg' + ext, negative_image)\n                cv2.imwrite(OUTPUT_PATH + '/' + fname + '-' + i.astype(str) + '-bw' + ext, cv2.cvtColor(gray, cv2.COLOR_GRAY2BGR))\n                \n                return wasProcessed\n\n            \ndef make_prediction(net, layer_names, labels, image, confidence, threshold):\n    \n    height, width = image.shape[:2]\n    \n    # Create a blob and pass it through the model\n    blob = cv2.dnn.blobFromImage(image, 1 / 255.0, (416, 416), swapRB=True, crop=False)\n    net.setInput(blob)\n    outputs = net.forward(layer_names)\n\n    # Extract bounding boxes, confidences and classIDs\n    boxes, confidences, classIDs = extract_boxes_confidences_classids(outputs, confidence, width, height)\n\n    # Apply Non-Max Suppression\n    idxs = cv2.dnn.NMSBoxes(boxes, confidences, confidence, threshold)\n\n    return boxes, confidences, classIDs, idxs\n\n\nif __name__ == '__main__':\n\n    print(\"Cropping Images\")\n    start_time = time.time()\n\n    i = 0\n    \n    # Get the labels\n    labels = open(labels_path).read().strip().split('\\n')\n\n    # Create a list of colors for the labels\n    colors = np.random.randint(0, 255, size=(len(labels), 3), dtype='uint8')\n\n    # Load weights using OpenCV\n    net = cv2.dnn.readNetFromDarknet(config_path, weights_path)\n\n    if use_gpu:\n        net.setPreferableBackend(cv2.dnn.DNN_BACKEND_CUDA)\n        net.setPreferableTarget(cv2.dnn.DNN_TARGET_CUDA)\n        print('Using GPU')\n        \n    if save:\n        os.makedirs(OUTPUT_PATH, exist_ok=True)\n\n    # Get the ouput layer names\n    layer_names = net.getLayerNames()\n    layer_names = [layer_names[i[0] - 1] for i in net.getUnconnectedOutLayers()]\n\n    for filepath in glob.iglob(INPUT_FILES):\n\n        image = cv2.imread(filepath)\n\n        boxes, confidences, classIDs, idxs = make_prediction(net, layer_names, labels, image, confidence, threshold)\n\n        # print(filepath)\n        filename = os.path.basename(filepath)\n        wasProcessed = crop_image(image, boxes, confidences, classIDs, idxs, colors, filename, max_size)\n\n        if wasProcessed:\n            i += 1\n            \n            if i > run_limit:\n                break\n            \n            print(str(i) + ': ' + filename)\n\n    run_time = time.time()-start_time\n    print('Done Cropping Images - Elapsed Time: {:.1f}'.format(run_time) + ' Secs')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Google Cloud Plaform for Kaggle(Beta) does not support /usr/lib modules at this time \n# Save Output Dataset\n\nif isLocalhost == False:\n\n    ! python -m pip install --index-url https://test.pypi.org/simple/ --no-deps kaggle_uploader-screamatthewind\n    \n    import time\n    import os\n\n    from kaggle_uploader import kaggle_uploader \n\n    print(\"Saving Images to Kaggle\")\n    start_time = time.time()\n\n    # kaggle_secrets are not supported by Google Cloud Platform for Kaggle(Beta) at this time\n    # from kaggle_secrets import UserSecretsClient\n    # user_secrets = UserSecretsClient()\n    # api_secret = user_secrets.get_secret(\"Crop Cats and Cogs YOLOv3\")\n\n    kaggle_uploader.resources = []\n    kaggle_uploader.init_on_kaggle(USER_ID, API_TOKEN)\n    kaggle_uploader.base_path = OUTPUT_PATH\n    kaggle_uploader.title = OUTPUT_DATASET_NAME\n    kaggle_uploader.dataset_id = OUTPUT_DATASET_ID\n    kaggle_uploader.user_id = USER_ID\n\n    for filename in os.listdir(kaggle_uploader.base_path):\n        print(filename)\n        kaggle_uploader.add_resource(filename, filename)\n\n    kaggle_uploader.update(\"new version\")\n\n    run_time = time.time()-start_time\n    print('Done Saving Images - Total Time: {:.1f}'.format(run_time) + ' Secs')\n\n    # If you get an error during update, it is typically because of an invalid api key, bad username, \n    # or the dataset does not exist.  This code does not create datasets.  It updates existing ones\n\nelse:\n    print(\"Done\")","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}