{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport cv2\nimport tensorflow as tf\nfrom tqdm import tqdm\n\nfrom sklearn.model_selection import train_test_split\n\n%matplotlib inline","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","trusted":true},"cell_type":"code","source":"train_df = pd.read_csv('../input/aptos2019-blindness-detection/train.csv')\ntest_df = pd.read_csv('../input/aptos2019-blindness-detection/test.csv')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"IMG_SIZE = 224","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def load_img(path):\n    image = cv2.imread(path)\n    return image","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Let us take a look at some of the images."},{"metadata":{"trusted":true},"cell_type":"code","source":"fig, axes = plt.subplots(3,3,figsize=(10,10))\nselection = np.random.choice(train_df.index, size=9, replace=False)\nimages = '../input/aptos2019-blindness-detection/train_images/'+train_df.loc[selection]['id_code']+'.png'\nfor image, axis in zip(images, axes.ravel()):\n    img = load_img(image)\n    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n    axis.imshow(img)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"As we can see, there are extra black areas surrounding the eyes which can be removed. The lighting conditions also differ significantly from image to image."},{"metadata":{},"cell_type":"markdown","source":"Let's crop images so that the extra spaces surrounding eyes are removed."},{"metadata":{"trusted":true},"cell_type":"code","source":"def remove_unwanted_space(image, threshold=7):\n    gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)\n    mask = gray_image > threshold\n    return image[np.ix_(mask.any(1), mask.any(0))]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Ben Graham's preprocessing method (last competition) is used for fixing lighting conditions. Please refer to this kernel for more: https://www.kaggle.com/ratthachat/aptos-updatedv14-preprocessing-ben-s-cropping"},{"metadata":{"trusted":true},"cell_type":"code","source":"def preprocess_img(path):\n    image = load_img(path)\n    image = remove_unwanted_space(image, 5)\n    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n    image = cv2.resize(image, (IMG_SIZE, IMG_SIZE))\n    image = cv2.addWeighted(image,4, cv2.GaussianBlur(image, (0,0), 30), -4, 128)\n    return image","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig, axes = plt.subplots(3,3,figsize=(10,10))\nselection = np.random.choice(train_df.index, size=9, replace=False)\nimages = '../input/aptos2019-blindness-detection/train_images/'+train_df.loc[selection]['id_code']+'.png'\nfor image, axis in zip(images, axes.ravel()):\n    img = preprocess_img(image)\n    axis.imshow(img)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"There seems a correlation between the way images are cropped in the training data and the target variables. In order to avoid the model predicting targets based on the way images are cropped, we can circle crop the eyes ourself in preprocessing. Refer to this kernel for more info: https://www.kaggle.com/taindow/be-careful-what-you-train-on "},{"metadata":{"trusted":true},"cell_type":"code","source":"def circle_crop(img):\n    circle_img = np.zeros((IMG_SIZE, IMG_SIZE), np.uint8)\n    cv2.circle(circle_img, ((int)(IMG_SIZE/2),(int)(IMG_SIZE/2)), int(IMG_SIZE/2), 1, thickness=-1)\n    img = cv2.bitwise_and(img, img, mask=circle_img)\n    return img","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig, axes = plt.subplots(3,3,figsize=(10,10))\nselection = np.random.choice(train_df.index, size=9, replace=False)\nimages = '../input/aptos2019-blindness-detection/train_images/'+train_df.loc[selection]['id_code']+'.png'\nfor image, axis in zip(images, axes.ravel()):\n    img = circle_crop(preprocess_img(image))\n    axis.imshow(img)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"N = train_df.shape[0]\ntrain = np.empty((N, IMG_SIZE, IMG_SIZE, 3), dtype=np.uint8)\nfor i, image_id in enumerate(tqdm(train_df['id_code'])):\n    train[i,:,:,:] = circle_crop(preprocess_img('../input/aptos2019-blindness-detection/train_images/'+image_id+'.png'))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"N = test_df.shape[0]\ntest = np.empty((N, IMG_SIZE, IMG_SIZE, 3), dtype=np.uint8)\nfor i, image_id in enumerate(tqdm(test_df['id_code'])):\n    test[i,:,:,:] = circle_crop(preprocess_img('../input/aptos2019-blindness-detection/test_images/'+image_id+'.png'))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"X_train, X_val, y_train, y_val = train_test_split(train, train_df['diagnosis'], test_size=0.15, random_state=42)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"BATCH_SIZE = 32","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_data_generator = tf.keras.preprocessing.image.ImageDataGenerator(\n        zoom_range=[0.9, 1.0],\n        fill_mode='constant',\n        cval=0.,  # value used for fill_mode = \"constant\"\n        horizontal_flip=True,  # randomly flip images\n        vertical_flip=True,  # randomly flip images\n        rotation_range=120\n    )","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"val_data_generator = tf.keras.preprocessing.image.ImageDataGenerator()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_gen = train_data_generator.flow(X_train, y_train, batch_size=BATCH_SIZE)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"val_gen = val_data_generator.flow(X_val, y_val, batch_size=BATCH_SIZE)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"We can consider this problem as a regression problem instead of classification. Here targets 0,1,2,3,4 are different stages of diabetic retinopathy and are not just independent classes. Their magnitude is representative of the severity of the disease. So this is a regression problem."},{"metadata":{"trusted":true},"cell_type":"code","source":"resnet = tf.keras.applications.ResNet50(include_top=False, weights='../input/resnet50/resnet50_weights_tf_dim_ordering_tf_kernels_notop.h5', input_shape=(IMG_SIZE, IMG_SIZE, 3))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model = tf.keras.Sequential([\n    resnet,\n    tf.keras.layers.GlobalAveragePooling2D(),\n    tf.keras.layers.BatchNormalization(),\n    tf.keras.layers.Dropout(0.5),\n    tf.keras.layers.Dense(256, activation='relu'),\n    tf.keras.layers.BatchNormalization(),\n    tf.keras.layers.Dropout(0.5),\n    tf.keras.layers.Dense(1, activation='relu')\n])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"checkpoint = tf.keras.callbacks.ModelCheckpoint('model_weights.hdf5', monitor='val_loss', verbose=1, \n                             save_best_only=True, mode='min')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"optimizer = tf.keras.optimizers.Adam(lr=0.00005)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model.compile(optimizer=optimizer, loss='mse')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"steps_per_epoch = int(np.ceil(X_train.shape[0]/BATCH_SIZE))\nval_steps_per_epoch = int(np.ceil(X_val.shape[0]/BATCH_SIZE))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"history = model.fit(train_gen, validation_data=val_gen, steps_per_epoch=steps_per_epoch, \n                              validation_steps=val_steps_per_epoch, callbacks=[checkpoint], epochs=25)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model.load_weights('model_weights.hdf5')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"prediction = model.predict(test)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"for i, pred in enumerate(prediction):\n    if pred < 0.5:\n        prediction[i] = 0\n    elif pred < 1.5:\n        prediction[i] = 1\n    elif pred < 2.5:\n        prediction[i] = 2\n    elif pred < 3.5:\n        prediction[i] = 3\n    else:\n        prediction[i] = 4","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"prediction = np.squeeze(prediction.astype(np.int8))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sample = pd.read_csv(\"../input/aptos2019-blindness-detection/sample_submission.csv\")\nsample.diagnosis = prediction\nsample.to_csv(\"submission.csv\", index=False)","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.6.4","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat":4,"nbformat_minor":1}