{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import os\nimport math\n\nimport numpy as np\nimport pandas as pd\nimport tensorflow as tf\nimport matplotlib.pyplot as plt\nfrom tensorflow.keras.applications import DenseNet121\nfrom tensorflow.keras.models import Sequential, Model\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nfrom tensorflow.keras import layers, optimizers\nfrom sklearn.model_selection import train_test_split\nimport cv2","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"load_dir = '/kaggle/input/bengaliai/256_train/256/'\n\ntrain = pd.read_csv('/kaggle/input/bengaliai-cv19/train.csv')\ntrain['filename'] = train.image_id.apply(lambda filename: load_dir + filename + '.png')\n\ntrain.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Cropping function\n\nA simple function from stack overflow that crops the image."},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_pad_width(im, new_shape, is_rgb=True):\n    pad_diff = new_shape - im.shape[0], new_shape - im.shape[1]\n    t, b = math.floor(pad_diff[0]/2), math.ceil(pad_diff[0]/2)\n    l, r = math.floor(pad_diff[1]/2), math.ceil(pad_diff[1]/2)\n    if is_rgb:\n        pad_width = ((t,b), (l,r), (0, 0))\n    else:\n        pad_width = ((t,b), (l,r))\n    return pad_width","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def crop_object(img, thresh=220, maxval=255, square=True):\n    \"\"\"\n    Source: https://stackoverflow.com/questions/49577973/how-to-crop-the-biggest-object-in-image-with-python-opencv\n    \"\"\"\n    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # convert to grayscale\n    # threshold to get just the signature (INVERTED)\n    retval, thresh_gray = cv2.threshold(gray, thresh=thresh, maxval=maxval, type=cv2.THRESH_BINARY_INV)\n\n    contours, hierarchy = cv2.findContours(thresh_gray,cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)\n\n    # Find object with the biggest bounding box\n    mx = (0,0,0,0)      # biggest bounding box so far\n    mx_area = 0\n    for cont in contours:\n        x,y,w,h = cv2.boundingRect(cont)\n        area = w*h\n        if area > mx_area:\n            mx = x,y,w,h\n            mx_area = area\n    x,y,w,h = mx\n    \n    crop = img[y:y+h, x:x+w]\n    \n    if square:\n        pad_width = get_pad_width(crop, max(crop.shape))\n        crop = np.pad(crop, pad_width=pad_width, mode='constant', constant_values=255)\n    \n    return crop","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Data Generator"},{"metadata":{"trusted":true},"cell_type":"code","source":"def data_generator(filenames, y, batch_size=64, shape=(128, 128, 1), random_state=2019):\n    y = y.copy()\n    np.random.seed(random_state)\n    indices = np.arange(len(filenames))\n    \n    while True:\n        np.random.shuffle(indices)\n        \n        for i in range(0, len(indices), batch_size):\n            batch_idx = indices[i:i+batch_size]\n            size = len(batch_idx)\n            \n            batch_files = filenames[batch_idx]\n            X_batch = np.zeros((size, *shape))\n            y_batch = y[batch_idx]\n            \n            for i, file in enumerate(batch_files):\n                img = cv2.imread(file)\n                img = crop_object(img, thresh=220)\n                img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)\n                img = cv2.resize(img, shape[:2])\n                X_batch[i, :, :, 0] = img / 255.\n            \n            yield X_batch, [y_batch[:, i] for i in range(y_batch.shape[1])]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Modelling"},{"metadata":{"trusted":true},"cell_type":"code","source":"def build_model(densenet):\n    x_in = layers.Input(shape=(128, 128, 1))\n    x = layers.Conv2D(3, (3, 3), padding='same')(x_in)\n    x = densenet(x)\n    \n    x = layers.GlobalAveragePooling2D()(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.Dropout(0.5)(x)\n    x = layers.Dense(256, activation='relu')(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.Dropout(0.5)(x)\n    \n    out_grapheme = layers.Dense(168, activation='softmax', name='grapheme')(x)\n    out_vowel = layers.Dense(11, activation='softmax', name='vowel')(x)\n    out_consonant = layers.Dense(7, activation='softmax', name='consonant')(x)\n    \n    model = Model(inputs=x_in, outputs=[out_grapheme, out_vowel, out_consonant])\n    \n    model.compile(\n        optimizers.Adam(lr=0.0001), \n        metrics=['accuracy'], \n        loss='sparse_categorical_crossentropy'\n    )\n    \n    return model","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"weights_path = '/kaggle/input/densenet-keras/DenseNet-BC-121-32-no-top.h5'\ndensenet = DenseNet121(include_top=False, weights=weights_path, input_shape=(128, 128, 3))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model = build_model(densenet)\nmodel.summary()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_files, valid_files, y_train, y_valid = train_test_split(\n    train.filename.values, \n    train[['grapheme_root','vowel_diacritic', 'consonant_diacritic']].values, \n    test_size=0.25, \n    random_state=2019\n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"batch_size = 128\n\ntrain_gen = data_generator(train_files, y_train)\nvalid_gen = data_generator(valid_files, y_valid)\n\ntrain_steps = round(len(train_files) / batch_size) + 1\nvalid_steps = round(len(valid_files) / batch_size) + 1","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"callbacks = [tf.keras.callbacks.ModelCheckpoint('model.h5', save_best_only=True)]\n\ntrain_history = model.fit_generator(\n    train_gen,\n    steps_per_epoch=train_steps,\n    epochs=15,\n    validation_data=valid_gen,\n    validation_steps=valid_steps,\n    callbacks=callbacks\n)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Save history"},{"metadata":{"trusted":true},"cell_type":"code","source":"pd.DataFrame(train_history.history).to_csv('history.csv', index=False)","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":1}