{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Bengali AI Grapheme Classification\n\nBengali is the 5th most spoken language in the world. It's the official language of Bangladesh and the second language of India. It is also spoken in other territories such as Pakistan and South Arabia, and having important comunities in United Kingdom and United States.\n\nThe Bengali alphabet is composed by 168 different grapheme roots– smallest piece of information in the writting system. Since it has also 11 different vowels and 7 consontants accents, the total number of different combinations increases to $168 \\times 11 \\times 7 \\approx 12936$.The objective of this competition is to classify the grapheme, the vowel accent and the consontant accent of a Bengali grapheme – given as an image.\n\nThis notebook gathers different exploratory analysis of processing of different notebooks (Check <a href='#References'>References</a>)."},{"metadata":{"trusted":true},"cell_type":"code","source":"# Import the different modules\nimport os\nimport cv2\nimport zipfile\nimport PIL.Image\nimport numpy as np\nimport pandas as pd\nimport tensorflow as tf\nimport matplotlib.pyplot as plt\nfrom tqdm.notebook import tqdm\nprint(tf.__version__)","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"","_uuid":"","trusted":true},"cell_type":"code","source":"train = pd.read_csv(\"../input/bengaliai-cv19/train.csv\")\ntest = pd.read_csv(\"../input/bengaliai-cv19/test.csv\")\nclass_map = pd.read_csv(\"../input/bengaliai-cv19/class_map.csv\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(\"Training set size: {}\".format(train.shape))\ntrain.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(f\"No. different graphemes: {len(train['grapheme_root'].unique())}\")\nprint(f\"No. different vowels: {len(train['vowel_diacritic'].unique())}\")\nprint(f\"No. different consonants: {len(train['consonant_diacritic'].unique())}\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(\"Test set size: {}\".format(test.shape))\ntest.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(\"Class map size: {}\".format(class_map.shape))\nclass_map.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"HEIGHT = 137\nWIDTH = 236\n\n# Load one parquet file to observe the different images\nFOLDER_DIRECTORY = '/kaggle/input/bengaliai-cv19/'\ntrain = pd.read_parquet(os.path.join(FOLDER_DIRECTORY, 'train_image_data_0.parquet'))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def display_image_from_data(df, size=5, height=HEIGHT, width=WIDTH):\n    \"\"\"\n    Displays grapheme images from data.\n    \n    Args:\n        df (pandas.dataframe):\n        size (int): Number of graphemes to display\n        height(int): Height of the images (num rows)\n        width(int): Width of the images (num cols)\n    \"\"\"\n    fig, ax = plt.subplots(size, size, figsize=(12, 12))\n    for i, idx in enumerate(df.index):\n        image_id = df.iloc[i]['image_id']\n        flattened_image = df.iloc[i].drop('image_id').values.astype(np.uint8)\n        unpacked_image = PIL.Image.fromarray(flattened_image.reshape(137, 236))\n        ax[i//size, i%size].imshow(unpacked_image)\n        ax[i//size, i%size].set_title(image_id)\n        ax[i//size, i%size].axis('on')\n        \ndisplay_image_from_data(train.sample(25))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Final size of images after preprocessing (size x size)\nSIZE = 224\n\ndef bounding_box(image):\n    \"\"\"\n    Defines the bounding box containing the grapheme\n    \n    Args:\n        image (numpy.ndarray): grapheme image\n    \"\"\"\n    rows = np.any(image, axis=1)\n    cols = np.any(image, axis=0)\n    rmin, rmax = np.where(rows)[0][[0, -1]]\n    cmin, cmax = np.where(cols)[0][[0, -1]]\n    return rmin, rmax, cmin, cmax\n\ndef crop_resize(image, size=SIZE, pad=16):\n    \"\"\"\n    Crops, pads and resize the grapheme\n    \n    Args:\n        image (numpy.ndarray): grapheme image\n        size (int): final size of the croped image\n        pad (int): Amount of padding to the image\n    \"\"\"\n    ymin, ymax, xmin, xmax = bounding_box(image[5:-5, 5:-5]>80)\n    xmin = xmin - 13 if (xmin > 13) else 0\n    ymin = ymin - 10 if (ymin > 10) else 0\n    xmax = xmax + 13 if (xmax < WIDTH - 13) else WIDTH\n    ymax = ymax + 10 if (ymax < HEIGHT - 10) else HEIGHT\n    image = image[ymin:ymax, xmin:xmax]\n    image[image < 28] = 0\n    lx, ly = xmax-xmin, ymax-ymin\n    l = max(lx, ly) + pad\n    image = np.pad(image, [((l-ly)//2, ), ((l-lx)//2, )], mode='constant')\n    return cv2.resize(image, (SIZE, SIZE))\n\nnum_images = 5\ntrain_sample = train.sample(num_images)\nfig, ax = plt.subplots(num_images, 2, figsize=(12, 12))\n\nfor idx in range(num_images):\n    image_nocrop = 255 - train_sample.iloc[idx, 1:].values.reshape(HEIGHT, WIDTH).astype(np.uint8)\n    image_crop = (image_nocrop*(255/image_nocrop.max())).astype(np.uint8)\n    image_crop = crop_resize(image_crop)\n    ax[idx, 0].imshow(image_nocrop)\n    ax[idx, 0].set_title('Original Image')\n    ax[idx, 0].axis('off')\n    ax[idx, 1].imshow(image_crop)\n    ax[idx, 1].set_title('Cropped + Resized Image')\n    ax[idx, 1].axis('off')\n    ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Save the train processed images in a zip\nTRAIN_FILES = ['train_image_data_0.parquet','train_image_data_1.parquet','train_image_data_2.parquet','train_image_data_3.parquet']\n\nwith zipfile.ZipFile('train.zip','w') as train_out:\n    for file in TRAIN_FILES:\n        file_df = pd.read_parquet(os.path.join(FOLDER_DIRECTORY, file))\n        data = 255 - file_df.iloc[:, 1:].values.reshape(-1, HEIGHT, WIDTH).astype(np.uint8)\n        for idx in tqdm(range(len(file_df))):\n            name = file_df.iloc[idx,0]\n            image = (data[idx]*(255/data[idx].max())).astype(np.uint8)\n            image = crop_resize(image)\n            train_out.writestr(name + '.png', cv2.imencode('.png', image)[1])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Save the test processed images in a zip\nTEST_FILES = ['test_image_data_0.parquet','test_image_data_1.parquet','test_image_data_2.parquet','test_image_data_3.parquet']\n\nwith zipfile.ZipFile('test.zip','w') as test_out:\n    for file in TEST_FILES:\n        file_df = pd.read_parquet(os.path.join(FOLDER_DIRECTORY, file))\n        data = 255 - file_df.iloc[:, 1:].values.reshape(-1, HEIGHT, WIDTH).astype(np.uint8)\n        for idx in tqdm(range(len(file_df))):\n            name = file_df.iloc[idx,0]\n            image = (data[idx]*(255/data[idx].max())).astype(np.uint8)\n            image = crop_resize(image)\n            test_out.writestr(name + '.png', cv2.imencode('.png', image)[1])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# References\n1. [Bengali.AI Handwritten Grapheme - Getting Started](https://www.kaggle.com/gpreda/bengali-ai-handwritten-grapheme-getting-started)\n2. [Image Preprocessing (128x128)](https://www.kaggle.com/iafoss/image-preprocessing-128x128)"}],"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}