{"cells":[{"metadata":{"trusted":true},"cell_type":"code","source":"import sys\npt_models = \"../input/pretrained-models/pretrained-models.pytorch-master/\"\nsys.path.insert(0, pt_models)\nimport pretrainedmodels","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport torch\nimport albumentations\nimport gc\nfrom tqdm import tqdm\nimport cv2\nfrom PIL import Image\n# Input data files are available in the \"../input/\" directory.\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\nORIGINAL_HEIGHT = 137\nORIGINAL_WIDTH = 236\nPROCESSED_HEIGHT = 128\nPROCESSED_WIDTH = 128\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# Any results you write to the current directory are saved as output.","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def normalize_image(img):\n    return (img*(255.0/img.max())).astype(np.uint8)\n\ndef get_min_indices(img, min_writing_value=80):\n    min_value = img > min_writing_value\n    h_min, h_max = np.where(np.any(min_value, axis=0))[0][[0, -1]]\n    v_min, v_max = np.where(np.any(min_value, axis=1))[0][[0, -1]]\n    return (h_min, h_max, v_min, v_max)\n\ndef get_min_indices_with_border(img, min_writing_value=80, border=20):\n    h_min, h_max, v_min, v_max = get_min_indices(img[border:-border, border:-border], min_writing_value=min_writing_value)\n    return (h_min + border, h_max + border, v_min + border, v_max + border) #indices ignored border, is added again\n\ndef cut_and_denoise_image(img, border=20, min_writing_value=80, max_noise=28):\n    #cut minimum needed to encease image\n    h_min, h_max, v_min, v_max = get_min_indices_with_border(img, border=border, min_writing_value=min_writing_value)\n    #add tolerance around minium, making it dependend on border prevents missing part of the character\n    h_min= (h_min-border) if h_min>border else 0\n    v_min= (v_min-border) if v_min>border else 0\n    h_max= (h_max+border) if ORIGINAL_WIDTH-h_max>border else ORIGINAL_WIDTH\n    v_max= (v_max+border) if ORIGINAL_HEIGHT-v_max>border else ORIGINAL_HEIGHT\n    #cut image\n    img = img[v_min:v_max, h_min:h_max]\n    #denoise\n    img[img < max_noise] = 0\n    #add padding to image\n    longer_side_length = max(np.ma.size(img, axis=0), np.ma.size(img, axis=1))\n    padding = [((longer_side_length - np.ma.size(img, axis=0)) // 2,),\n               ((longer_side_length - np.ma.size(img, axis=1)) // 2,)]\n    img = np.pad(img, padding, mode=\"constant\")\n    #return resized image\n    return cv2.resize(img,(PROCESSED_HEIGHT, PROCESSED_WIDTH))","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"def load_data(file_name):\n    print(\"Dealing with \", file_name)\n    df = pd.read_parquet(file_name)\n    names = df.iloc[:,0].values\n    images = 255 - df.iloc[:,1:].values.reshape(-1, ORIGINAL_HEIGHT, ORIGINAL_WIDTH)\n    del df\n    print(\"Freed after loading: \", gc.collect())\n    for image_index, img in enumerate(images):\n            img = normalize_image(img)\n            img = cut_and_denoise_image(img)\n            images[image_index,0:PROCESSED_HEIGHT, 0:PROCESSED_WIDTH] = img#inplace to save RAM\n    images = images[:,0:PROCESSED_HEIGHT, 0:PROCESSED_WIDTH]#cut off unneeded part\n    #images = images.astype(np.float32) /255.0 #conversion before division important, halves the RAM usage as NumPy defaults to float64!\n    images = images.reshape(-1, PROCESSED_HEIGHT, PROCESSED_WIDTH)\n    print(\"Freed after processing: \", gc.collect())\n    return names, images","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#add header\nwith open(\"submission.csv\", \"a\") as submission_file:\n    submission_file.write(\"row_id,target\\n\")\n    \ndef write_predictions(names, prediction):\n    chosen_root = np.argmax(prediction[0].detach().numpy(), axis=1)\n    chosen_vowel = np.argmax(prediction[1].detach().numpy(), axis=1)\n    chosen_consonant = np.argmax(prediction[2].detach().numpy(), axis=1)\n    with open(\"submission.csv\", \"a\") as submission_file:\n        for index, name in enumerate(names):\n            submission_file.write(f\"{name}_consonant_diacritic,{chosen_consonant[index]}\\n\")\n            submission_file.write(f\"{name}_grapheme_root,{chosen_root[index]}\\n\")\n            submission_file.write(f\"{name}_vowel_diacritic,{chosen_vowel[index]}\\n\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class SubmissionDataSet():\n    transform = albumentations.Compose([albumentations.Normalize(always_apply=True)])\n    \n    def __init__(self, names, images):\n        self.names = names\n        self.images = images\n        \n    def __len__(self):\n        return len(self.names)\n\n    def __getitem__(self, index):\n        img = self.images[index]\n        img = Image.fromarray(img).convert(\"RGB\")\n        img = self.transform(image=np.array(img))[\"image\"]\n        img = np.transpose(img, (2,0,1))\n        return (self.names[index], torch.tensor(img))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class Net(torch.nn.Module):\n    \n    def __init__(self):\n        super(Net, self).__init__()\n        self.transfer_model = pretrainedmodels.__dict__[\"resnet101\"](pretrained=None)\n        \n        self.drop1 = torch.nn.Dropout(p=0.3)\n        self.l1 = torch.nn.Linear(2048, 1024)\n        self.drop2 = torch.nn.Dropout(p=0.3)\n        self.l2 = torch.nn.Linear(1024, 512)\n        \n        self.root = torch.nn.Linear(512, 168)\n        self.vowel = torch.nn.Linear(512, 11)\n        self.consonant = torch.nn.Linear(512, 7)\n        \n    def forward(self, x):\n        batch_size = x.shape[0]\n        x = self.transfer_model.features(x)\n        x = torch.nn.functional.adaptive_avg_pool2d(x, 1).reshape(batch_size, -1)\n        \n        x = self.drop1(x)\n        x = self.l1(x)\n        x = self.drop2(x)\n        x = self.l2(x)\n        \n        root = self.root(x)\n        vowel = self.vowel(x)\n        consonant = self.consonant(x)\n        return (root, vowel, consonant)\n        return (root, vowel, consonant)\n    \nmodel = Net()\nmodel.load_state_dict(torch.load(\"/kaggle/input/bengalipytorchmodels/ResNet101_30Epochs.pt\", map_location=torch.device('cpu')))\nmodel.eval()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"for file_index in range(0, 4):\n    names, images = load_data(f\"/kaggle/input/bengaliai-cv19/test_image_data_{file_index}.parquet\")\n    batch_data_set = SubmissionDataSet(names, images)\n    data_loader = torch.utils.data.DataLoader(batch_data_set,\n                                              batch_size=8,\n                                              shuffle=False,\n                                              num_workers=4)\n    for batch_index, (batch_names, batch_images) in enumerate(tqdm(data_loader, total=(len(batch_data_set)//data_loader.batch_size))):\n        prediction = model(batch_images)\n        write_predictions(batch_names, prediction)\n        \n    print(\"Collected after writing predictions: \", gc.collect())","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}