{"cells":[{"metadata":{"trusted":true},"cell_type":"code","source":"DATA_PATH = '../input/aptos2019-blindness-detection/'","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"f13313d4-8734-40e1-a805-74efcf8fa9a7","_cell_guid":"b6f64525-eb68-4332-b5bb-82e4430717ae","trusted":true},"cell_type":"code","source":"import numpy as np\nimport tensorflow as tf\nimport pandas as pd\nimport matplotlib.pyplot as plt\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nfrom tensorflow.keras.utils import plot_model\nfrom tensorflow.keras.optimizers import Adam\nfrom tensorflow.keras.models import load_model\n\nimport os\nimport numpy as np\nimport pandas as pd\nfrom PIL import Image\nfrom tqdm import tqdm\nimport cv2\nimport keras.backend as K\n\n\n\ndef preprocess_all(input_dir, output_dir, label_path=None, limit=np.inf, skip=0):\n    if label_path is not None:\n        labels = pd.read_csv(label_path)\n        label_dict = dict(zip(labels['id_code'], labels['diagnosis']))\n        del labels\n\n    count = 0\n    for filepath in tqdm(os.listdir(input_dir)[skip:]):\n        if count >= limit:\n            break\n\n        input_path = os.path.join(input_dir, filepath)\n        if not os.path.isfile(input_path):\n            continue\n\n        img = standard_crop(input_path, IMG_DIM=(512, 512), contrast_fnc=contrast_enhance, gray=True)\n        img.save(os.path.join(output_dir, str(label_dict[filepath[:-4]]) if label_path else '', filepath))\n\n        count += 1\n\n\nimport os\nfrom PIL import Image\nimport cv2\nimport numpy as np\nimport matplotlib.pyplot as plt\n\nSTANDARDIZE_CROP_RATIO = 0.792\nOVERCROP_THRESHOLD = 25\nZERO_TOLERANCE = 2\nBLUE_LAYER_IDX = 2\nRED_LAYER_IDX = 0\n\n\ndef autocrop_scale(path, IMG_DIM=(512, 512), EPSILON=7, standardize_crop=False, return_crop_only=False):\n    '''\n    Loads the image file and automatically crops based on dark regions and rescales\n        to a specified dimension ignoring aspect ratio\n\n    :param path: str - path to file\n    :param IMG_DIM: tuple(int,int)/None - final dimension of image (no rescale if None)\n    :param EPSILON: int - minimum threshold for mean row/col pixel value\n    :param standardize_crop: bool - if True, intentionally over-crop perfect circles to standardize to\n                                    the images that are not perfect circles\n    :param return_crop_only: bool - if True, does not resize and return numpy array\n    :return: PIL.Image/np.ndarray\n    '''\n\n    # loads and convert images\n#     img = Image.open(path)\n#     data = np.asarray(img)\n    data = path\n    gray_data = data.mean(axis=2)\n\n    # dynamically determine non-dark regions based on grayscale mean of row/col values\n    limit_h = np.where(gray_data.mean(axis=0) >= EPSILON)[0]\n    horizontal = (limit_h[0], limit_h[-1])\n    limit_v = np.where(gray_data.mean(axis=1) >= EPSILON)[0]\n\n    # over-cropping\n    if standardize_crop and (abs((limit_h[-1] - limit_h[0]) - (limit_v[-1] - limit_v[0])) <= OVERCROP_THRESHOLD):\n        # executes only if the limits are approximately square\n        crop_v = STANDARDIZE_CROP_RATIO * (limit_v[-1] - limit_v[0]) / 2\n        center = (limit_v[-1] + limit_v[0]) / 2\n        vertical = (int(center - crop_v), int(center + crop_v))\n    else:\n        vertical = (limit_v[0], limit_v[-1])\n\n    # crops array representation\n    new_data = data[vertical[0]:vertical[1] + 1, horizontal[0]:horizontal[1] + 1, :]\n\n    # reverts to original if too much cropped\n    if new_data.shape[0] < 100 or new_data.shape[1] < 100:\n        new_data = data\n\n    if return_crop_only:\n        return new_data\n\n    # converts and resize to PIL image\n    processed_img = Image.fromarray(new_data)\n    if IMG_DIM is not None:\n        processed_img = processed_img.resize(IMG_DIM)\n\n    return processed_img\n\n\ndef standard_crop(path, IMG_DIM=(512, 512), ratio=4 / 3, cratio=592 / 386, contrast_fnc=None, **kwargs):\n    '''\n    Crops an image to an approximate form found in majority of the images in the test dataset.\n\n    The values found here is derived from manual measurements with trigonometry.\n\n    :param path: str - path of image\n    :param IMG_DIM: tuple(int, int)/None - final dimension of image (no rescale if None)\n    :param ratio: float - final aspect ratio of output image\n    :param cratio: flaot - ratio of chord at the bottom to the radius\n    :param contrast_fnc: None/function - contrast function\n    :return: PIL.Image\n    '''\n    data = autocrop_scale(path, return_crop_only=True)  # crops away all excess background\n    h, l = data.shape[0], data.shape[1]\n\n    # dynamically determine the radius of the circle\n    sample_column = data[:, 1, :]  # second column of data\n    # first non-background pixel\n    edge = np.where((data.mean(axis=2)[:, 0] > sample_column.mean() + 5 / sample_column.mean(axis=1).std()))\n    edge = edge[0][edge[0] > h / 8]  # accepts if not near the corner\n    if len(edge) > 0:\n        r = np.sqrt((edge[0] - h / 2) ** 2 + (l / 2) ** 2)\n    else:\n        r = l / 2  # assumes length is the diameter\n\n    delta_h = r - np.sqrt(r ** 2 - (cratio * r / 2) ** 2)  # height to be cropped away at both the top and bottom\n    # max(..., 0) is needed to handle the cases when the specified image is a perfect circle and cropped circle\n    data = data[max(int(delta_h - (2 * r - h) / 2), 0):h - max(int(delta_h - (2 * r - h) / 2), 0), ...]\n\n    h, l = data.shape[0], data.shape[1]\n    delta_l = l - h * ratio  # length to be cropped away at both the left and right\n\n    data = data[:, int(delta_l / 2):l - int(delta_l / 2), :]\n\n    data = cv2.resize(data, dsize=IMG_DIM, interpolation=cv2.INTER_AREA)\n\n    if contrast_fnc is not None:\n        data = contrast_fnc(data, **kwargs)\n\n#     processed_img = Image.fromarray(data)\n\n    return data.reshape(*data.shape)\n\n\ndef cropscale_all(input_dir, output_dir, cropping_fnc='standard', limit=np.inf, **kwargs):\n    '''\n    Calls autocrop_scale on all files in input_dir. This is non-recursive.\n\n    :param input_dir: str - path to files to be processed\n    :param output_dir: str - path to location for processed images to be stored\n    :param cropping_fnc: \"standard\"/\"autocrop\" - type of cropping function to use\n    :param limit: int - number of images to process\n    :param kwargs: dict - arguments for cropping function\n    :return: None\n    '''\n    if cropping_fnc == 'standard':\n        cropping_fnc = lambda x: standard_crop(x, **kwargs)\n    elif cropping_fnc == 'autocrop':\n        cropping_fnc = lambda x: autocrop_scale(x, **kwargs)\n    else:\n        raise ValueError('Incorrect argument for cropping_fnc')\n\n    count = 0\n    for filepath in os.listdir(input_dir):\n        if count >= limit:\n            break\n\n        input_path = os.path.join(input_dir, filepath)\n        if not os.path.isfile(input_path):\n            continue\n\n        img = cropping_fnc(input_path)\n        img.save(os.path.join(output_dir, filepath))\n        count += 1\n\n\ndef contrast_enhance(img, sigma=10, gray=False):\n    '''\n    Contrast technique based on Kaggle notebook\n\n    :param img: np.ndarray - image\n    :param sigma: int - degree of contrast (affects efficiency of code)\n    :param gray: bool - returns grayscale (i.e. 1 channel)\n    :return: nd.ndarray\n    '''\n    if gray:\n        img = img.mean(axis=2)\n        img = np.dstack((img, img, img)).astype(np.uint8)\n\n    return cv2.addWeighted(img, 4, cv2.GaussianBlur(img, (0, 0), sigma), -4, 128)\n\nclass FixedDropout(tf.keras.layers.Dropout):\n        def _get_noise_shape(self, inputs):\n            if self.noise_shape is None:\n                return self.noise_shape\n\n            symbolic_shape = K.shape(inputs)\n            noise_shape = [symbolic_shape[axis] if shape is None else shape\n                           for axis, shape in enumerate(self.noise_shape)]\n            return tuple(noise_shape)\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model = load_model('../input/model21jack/model21_Jack_EfficientNetB5OP.h5', custom_objects={'FixedDropout': FixedDropout})","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission_df = pd.read_csv(DATA_PATH + '/test.csv')\nsubmission_df['filename'] = submission_df['id_code'].astype(str)+'.png'\n\nsubmission = ImageDataGenerator(preprocessing_function=lambda x: standard_crop(x,IMG_DIM=(256, 256), contrast_fnc=contrast_enhance, gray=False),\n                               rescale=1/255.)\ngen = submission.flow_from_dataframe(dataframe = submission_df,\n                                       directory= DATA_PATH + \"test_images\",\n                                       x_col=\"filename\", \n                                       batch_size = 16,\n                                       shuffle=False,\n                                       class_mode=None, \n                                       target_size=(256, 256), validate_filenames=False)\n\npred = model.predict_generator(gen, verbose=1)\n\n\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"pred = np.argmax(pred, axis=1)\n\nresults = dict(zip(gen.filenames, pred))\nlabels = list(map(lambda x: results[x], submission_df['filename']))\n\nsubmission_df.drop(columns=['filename'], inplace= True)\nsubmission_df['diagnosis'] = labels\nsubmission_df.to_csv('submission.csv', index=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# submission_df.to_csv('../submission.csv', index=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission_df","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission_df.shape","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from collections import Counter\ncnt = Counter(labels)\ncnt","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}