├── IAM_dataset.py ├── LICENSE ├── OCR_Training_synthetic.ipynb ├── OCR_training_handwritten.ipynb ├── README.md ├── evaluation.py ├── fake_texts ├── background_generator.py ├── computer_text_generator.py ├── data_generator.py ├── distorsion_generator.py ├── imgaug_transformations.py ├── pytorch_dataset_fake.py ├── pytorch_dataset_fake_2.py └── string_generator.py ├── fonts ├── cn │ └── SourceHanSans-Normal.ttf └── latin │ ├── AllerDisplay.ttf │ ├── Aller_Bd.ttf │ ├── Aller_BdIt.ttf │ ├── Aller_It.ttf │ ├── Aller_Lt.ttf │ ├── Aller_LtIt.ttf │ ├── Aller_Rg.ttf │ ├── Amatic-Bold.ttf │ ├── AmaticSC-Regular.ttf │ ├── BEBAS___.ttf │ ├── Capture_it.ttf │ ├── Capture_it_2.ttf │ ├── CaviarDreams.ttf │ ├── CaviarDreams_BoldItalic.ttf │ ├── CaviarDreams_Italic.ttf │ ├── Caviar_Dreams_Bold.ttf │ ├── DroidSans-Bold.ttf │ ├── DroidSans.ttf │ ├── FFF_Tusj.ttf │ ├── Lato-Black.ttf │ ├── Lato-BlackItalic.ttf │ ├── Lato-Bold.ttf │ ├── Lato-BoldItalic.ttf │ ├── Lato-Hairline.ttf │ ├── Lato-HairlineItalic.ttf │ ├── Lato-Heavy.ttf │ ├── Lato-HeavyItalic.ttf │ ├── Lato-Italic.ttf │ ├── Lato-Light.ttf │ ├── Lato-LightItalic.ttf │ ├── Lato-Medium.ttf │ ├── Lato-MediumItalic.ttf │ ├── Lato-Regular.ttf │ ├── Lato-Semibold.ttf │ ├── Lato-SemiboldItalic.ttf │ ├── Lato-Thin.ttf │ ├── Lato-ThinItalic.ttf │ ├── OpenSans-Bold.ttf │ ├── OpenSans-BoldItalic.ttf │ ├── OpenSans-ExtraBold.ttf │ ├── OpenSans-ExtraBoldItalic.ttf │ ├── OpenSans-Italic.ttf │ ├── OpenSans-Light.ttf │ ├── OpenSans-LightItalic.ttf │ ├── OpenSans-Regular.ttf │ ├── OpenSans-Semibold.ttf │ ├── OpenSans-SemiboldItalic.ttf │ ├── Pacifico.ttf │ ├── Raleway-Black.ttf │ ├── Raleway-BlackItalic.ttf │ ├── Raleway-Bold.ttf │ ├── Raleway-BoldItalic.ttf │ ├── Raleway-ExtraBold.ttf │ ├── Raleway-ExtraBoldItalic.ttf │ ├── Raleway-ExtraLight.ttf │ ├── Raleway-ExtraLightItalic.ttf │ ├── Raleway-Italic.ttf │ ├── Raleway-Light.ttf │ ├── Raleway-LightItalic.ttf │ ├── Raleway-Medium.ttf │ ├── Raleway-MediumItalic.ttf │ ├── Raleway-Regular.ttf │ ├── Raleway-SemiBold.ttf │ ├── Raleway-SemiBoldItalic.ttf │ ├── Raleway-Thin.ttf │ ├── Raleway-ThinItalic.ttf │ ├── Roboto-Black.ttf │ ├── Roboto-BlackItalic.ttf │ ├── Roboto-Bold.ttf │ ├── Roboto-BoldItalic.ttf │ ├── Roboto-Italic.ttf │ ├── Roboto-Light.ttf │ ├── Roboto-LightItalic.ttf │ ├── Roboto-Medium.ttf │ ├── Roboto-MediumItalic.ttf │ ├── Roboto-Regular.ttf │ ├── Roboto-Thin.ttf │ ├── Roboto-ThinItalic.ttf │ ├── RobotoCondensed-Bold.ttf │ ├── RobotoCondensed-BoldItalic.ttf │ ├── RobotoCondensed-Italic.ttf │ ├── RobotoCondensed-Light.ttf │ ├── RobotoCondensed-LightItalic.ttf │ ├── RobotoCondensed-Regular.ttf │ ├── SEASRN__.ttf │ ├── Sansation-Bold.ttf │ ├── Sansation-BoldItalic.ttf │ ├── Sansation-Italic.ttf │ ├── Sansation-Light.ttf │ ├── Sansation-LightItalic.ttf │ ├── Sansation-Regular.ttf │ ├── Walkway_Black.ttf │ ├── Walkway_Bold.ttf │ ├── Walkway_Oblique.ttf │ ├── Walkway_Oblique_Black.ttf │ ├── Walkway_Oblique_Bold.ttf │ ├── Walkway_Oblique_SemiBold.ttf │ ├── Walkway_Oblique_UltraBold.ttf │ ├── Walkway_SemiBold.ttf │ └── Walkway_UltraBold.ttf ├── fully_conv_model.py ├── inference_demo.ipynb └── requirements.txt /IAM_dataset.py: -------------------------------------------------------------------------------- 1 | #We might even be able to use the dataset from another guy 2 | import random 3 | from glob import glob 4 | from os.path import basename 5 | from skimage.transform import resize 6 | 7 | import numpy as np 8 | import pandas as pd 9 | import torch 10 | import torchvision.transforms as transforms 11 | from PIL import Image 12 | from torch.utils.data import Dataset 13 | from torch.utils.data import sampler 14 | import re 15 | import string 16 | import cv2 17 | from fake_texts.imgaug_transformations import augmentations 18 | 19 | class hwrDataset(Dataset): 20 | def __init__(self, input_folder="/home/leander/AI/data/OCR_handwritten/",width=32): 21 | self.width=width 22 | train_threshold = 0.75 23 | 24 | pool = '' 25 | pool += string.ascii_letters 26 | pool += "0123456789" 27 | pool += "!\"#$%&'()*+,-./:;?@[\\]^_`{|}~" 28 | pool += ' ' 29 | self.keys = list(pool) 30 | self.values = np.array(range(1,len(pool)+1)) 31 | self.dictionary = dict(zip(self.keys, self.values)) 32 | 33 | self.decode_dict=dict((v,k) for k,v in self.dictionary.items()) 34 | self.decode_dict.update({93 : "OOK"}) 35 | 36 | 37 | files=glob(input_folder+"img/*/*/*.png") 38 | name_to_file = {} 39 | rows = [] 40 | 41 | for file_name in files: 42 | name_to_file[basename(file_name).rstrip(".png")] = file_name 43 | 44 | # Reduce the time it takes for this if needed. 45 | for line in open(input_folder+"txt/sentences.txt", "r").readlines(): 46 | parts = line.split(" ") 47 | if parts[0] in name_to_file: 48 | gt = " ".join(parts[9:]).rstrip("\n") 49 | 50 | # Filters out characters not in the alphabet. 51 | processed_gt = "".join([k for k in gt if k in pool]).replace("|" ," ") 52 | processed_gt=[self.dictionary[x] if x in self.keys else 93 for x in processed_gt ] 53 | if len(processed_gt) > 0: 54 | rows.append([parts[0], name_to_file[parts[0]], processed_gt]) 55 | self.data = pd.DataFrame(rows[:int(len(rows) * train_threshold)], columns=["name", "path", "groundtruth"]) 56 | 57 | def __len__(self): 58 | return int(self.data.__len__()) 59 | 60 | def __getitem__(self, index): 61 | 62 | try: 63 | img = Image.open(list(self.data.iloc[[index]].path)[0]).convert('L') 64 | except IOError: 65 | print('Corrupted image for %d' % index) 66 | return self[index + 1] 67 | except Exception: 68 | print(index) 69 | img=cv2.cvtColor(np.array(img),cv2.COLOR_GRAY2RGB) 70 | 71 | 72 | 73 | img=augmentations.augment_image(np.array(img))/255 74 | label = self.data.iloc[[index]].groundtruth 75 | label=np.array(label.values[0]) 76 | 77 | 78 | resize_shape=(self.width,int(self.width*img.shape[1]/img.shape[0])) 79 | 80 | img = resize(img,resize_shape,mode="constant") 81 | img = np.expand_dims(img,0) 82 | 83 | #We also need to downscale the image 84 | 85 | #how to give out as strings 86 | #[self.decode_dict[x] for x in strings ] 87 | return img, label,img.shape[2],len(label) 88 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 LeLoew 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /OCR_Training_synthetic.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "collapsed": true 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "#General Imports\n", 12 | "import torch\n", 13 | "import torch.nn as nn\n", 14 | "import torch.optim as optim\n", 15 | "import numpy as np\n", 16 | "from torch.utils.data import DataLoader\n", 17 | "import random\n", 18 | "\n", 19 | "import matplotlib.pyplot as plt\n", 20 | "from tensorboardX import SummaryWriter\n", 21 | "\n", 22 | "#Load fake, non handwritten generator \n", 23 | "from fake_texts.pytorch_dataset_fake_2 import Dataset\n", 24 | "\n", 25 | "#Import the loss from baidu \n", 26 | "from torch.nn import CTCLoss\n", 27 | "\n", 28 | "#Import the model \n", 29 | "from fully_conv_model import cnn_attention_ocr\n", 30 | "\n", 31 | "#Helper to count params\n", 32 | "def count_parameters(model):\n", 33 | " return sum(p.numel() for p in model.parameters() if p.requires_grad)\n", 34 | "\n", 35 | "#Evaluation function preds_to_integer\n", 36 | "from evaluation import wer_eval,preds_to_integer,show,my_collate,AverageMeter" 37 | ] 38 | }, 39 | { 40 | "cell_type": "code", 41 | "execution_count": 2, 42 | "metadata": {}, 43 | "outputs": [], 44 | "source": [ 45 | "#Set up Tensorboard writer for current test\n", 46 | "writer = SummaryWriter(log_dir=\"/home/leander/AI/repos/OCR-CNN/logs2/correct_cosine_2\")" 47 | ] 48 | }, 49 | { 50 | "cell_type": "code", 51 | "execution_count": 3, 52 | "metadata": {}, 53 | "outputs": [ 54 | { 55 | "data": { 56 | "text/plain": [ 57 | "3530194" 58 | ] 59 | }, 60 | "execution_count": 3, 61 | "metadata": {}, 62 | "output_type": "execute_result" 63 | } 64 | ], 65 | "source": [ 66 | "###Set up model. \n", 67 | "cnn=cnn_attention_ocr(model_dim=64,nclasses=67,n_layers=8)\n", 68 | "cnn=cnn.cuda().train()\n", 69 | "count_parameters(cnn)" 70 | ] 71 | }, 72 | { 73 | "cell_type": "code", 74 | "execution_count": 4, 75 | "metadata": { 76 | "collapsed": true 77 | }, 78 | "outputs": [], 79 | "source": [ 80 | "#cnn=cnn.eval()" 81 | ] 82 | }, 83 | { 84 | "cell_type": "code", 85 | "execution_count": 5, 86 | "metadata": { 87 | "collapsed": true 88 | }, 89 | "outputs": [], 90 | "source": [ 91 | "#CTC Loss ,average_frames=True\n", 92 | "ctc_loss = CTCLoss(reduction=\"mean\")\n", 93 | "#Optimizer: Good initial is 5e5 \n", 94 | "optimizer = optim.Adam(cnn.parameters(), lr=5e-4)" 95 | ] 96 | }, 97 | { 98 | "cell_type": "code", 99 | "execution_count": 6, 100 | "metadata": { 101 | "collapsed": true 102 | }, 103 | "outputs": [], 104 | "source": [ 105 | "#We keep track of the Average loss and CER \n", 106 | "ave_total_loss = AverageMeter()\n", 107 | "CER_total= AverageMeter()\n", 108 | "\n" 109 | ] 110 | }, 111 | { 112 | "cell_type": "code", 113 | "execution_count": 7, 114 | "metadata": { 115 | "collapsed": true 116 | }, 117 | "outputs": [], 118 | "source": [ 119 | "n_iter=0\n", 120 | "batch_size=4" 121 | ] 122 | }, 123 | { 124 | "cell_type": "code", 125 | "execution_count": 8, 126 | "metadata": { 127 | "collapsed": true 128 | }, 129 | "outputs": [], 130 | "source": [ 131 | "#torch.save(cnn.state_dict(), \"400ksteps_augment_new_gen_e15.pt\")" 132 | ] 133 | }, 134 | { 135 | "cell_type": "code", 136 | "execution_count": 9, 137 | "metadata": { 138 | "collapsed": true 139 | }, 140 | "outputs": [], 141 | "source": [ 142 | "torch.save(cnn.state_dict(), \"415ksteps_augment_new_gen_e56.pt\")" 143 | ] 144 | }, 145 | { 146 | "cell_type": "code", 147 | "execution_count": 8, 148 | "metadata": { 149 | "collapsed": true 150 | }, 151 | "outputs": [], 152 | "source": [ 153 | "#\n", 154 | "cnn.load_state_dict(torch.load(\"415ksteps_augment_new_gen_e56.pt\"))" 155 | ] 156 | }, 157 | { 158 | "cell_type": "code", 159 | "execution_count": 8, 160 | "metadata": { 161 | "collapsed": true 162 | }, 163 | "outputs": [], 164 | "source": [ 165 | "ds=Dataset()\n" 166 | ] 167 | }, 168 | { 169 | "cell_type": "code", 170 | "execution_count": 9, 171 | "metadata": { 172 | "collapsed": true 173 | }, 174 | "outputs": [], 175 | "source": [ 176 | "trainset = DataLoader(dataset=ds,\n", 177 | " batch_size=batch_size,\n", 178 | " shuffle=False,\n", 179 | " num_workers=6,\n", 180 | " pin_memory=True,\n", 181 | " \n", 182 | " collate_fn=my_collate)\n", 183 | "gen = iter(trainset)" 184 | ] 185 | }, 186 | { 187 | "cell_type": "code", 188 | "execution_count": 10, 189 | "metadata": { 190 | "collapsed": true 191 | }, 192 | "outputs": [], 193 | "source": [ 194 | "from torch.optim.lr_scheduler import CosineAnnealingLR" 195 | ] 196 | }, 197 | { 198 | "cell_type": "code", 199 | "execution_count": 11, 200 | "metadata": { 201 | "collapsed": true 202 | }, 203 | "outputs": [], 204 | "source": [ 205 | "cs=CosineAnnealingLR(optimizer=optimizer,T_max=250000,eta_min=1e-6)" 206 | ] 207 | }, 208 | { 209 | "cell_type": "code", 210 | "execution_count": 12, 211 | "metadata": {}, 212 | "outputs": [ 213 | { 214 | "name": "stdout", 215 | "output_type": "stream", 216 | "text": [ 217 | "train start\n" 218 | ] 219 | }, 220 | { 221 | "name": "stderr", 222 | "output_type": "stream", 223 | "text": [ 224 | "/home/leander/AI/repos/Pytorch-OCR-Fully-Conv/evaluation.py:42: RuntimeWarning: divide by zero encountered in true_divide\n", 225 | " return we/len(preds)\n" 226 | ] 227 | }, 228 | { 229 | "name": "stdout", 230 | "output_type": "stream", 231 | "text": [ 232 | "train start\n", 233 | "train start\n", 234 | "train start\n", 235 | "train start\n", 236 | "train start\n", 237 | "train start\n", 238 | "train start\n", 239 | "train start\n", 240 | "train start\n", 241 | "train start\n", 242 | "train start\n", 243 | "train start\n", 244 | "train start\n", 245 | "train start\n", 246 | "train start\n", 247 | "train start\n", 248 | "train start\n", 249 | "train start\n", 250 | "train start\n", 251 | "train start\n", 252 | "train start\n", 253 | "train start\n", 254 | "train start\n", 255 | "train start\n", 256 | "train start\n", 257 | "train start\n", 258 | "train start\n", 259 | "train start\n", 260 | "train start\n", 261 | "train start\n", 262 | "train start\n", 263 | "train start\n", 264 | "train start\n", 265 | "train start\n", 266 | "train start\n", 267 | "train start\n", 268 | "train start\n", 269 | "train start\n", 270 | "train start\n", 271 | "train start\n", 272 | "train start\n", 273 | "train start\n", 274 | "train start\n", 275 | "train start\n", 276 | "train start\n", 277 | "train start\n" 278 | ] 279 | }, 280 | { 281 | "ename": "KeyboardInterrupt", 282 | "evalue": "", 283 | "output_type": "error", 284 | "traceback": [ 285 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 286 | "\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)", 287 | "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 27\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 28\u001b[0m \u001b[0;31m#Then backward and step\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 29\u001b[0;31m \u001b[0mloss\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbackward\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 30\u001b[0m \u001b[0moptimizer\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstep\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 31\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", 288 | "\u001b[0;32m~/anaconda3/envs/pytorch/lib/python3.6/site-packages/torch/tensor.py\u001b[0m in \u001b[0;36mbackward\u001b[0;34m(self, gradient, retain_graph, create_graph)\u001b[0m\n\u001b[1;32m 100\u001b[0m \u001b[0mproducts\u001b[0m\u001b[0;34m.\u001b[0m \u001b[0mDefaults\u001b[0m \u001b[0mto\u001b[0m\u001b[0;31m \u001b[0m\u001b[0;31m`\u001b[0m\u001b[0;31m`\u001b[0m\u001b[0;32mFalse\u001b[0m\u001b[0;31m`\u001b[0m\u001b[0;31m`\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 101\u001b[0m \"\"\"\n\u001b[0;32m--> 102\u001b[0;31m \u001b[0mtorch\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mautograd\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbackward\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mgradient\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mretain_graph\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcreate_graph\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 103\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 104\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mregister_hook\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mhook\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 289 | "\u001b[0;32m~/anaconda3/envs/pytorch/lib/python3.6/site-packages/torch/autograd/__init__.py\u001b[0m in \u001b[0;36mbackward\u001b[0;34m(tensors, grad_tensors, retain_graph, create_graph, grad_variables)\u001b[0m\n\u001b[1;32m 88\u001b[0m Variable._execution_engine.run_backward(\n\u001b[1;32m 89\u001b[0m \u001b[0mtensors\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mgrad_tensors\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mretain_graph\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcreate_graph\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 90\u001b[0;31m allow_unreachable=True) # allow_unreachable flag\n\u001b[0m\u001b[1;32m 91\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 92\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", 290 | "\u001b[0;31mKeyboardInterrupt\u001b[0m: " 291 | ] 292 | } 293 | ], 294 | "source": [ 295 | "npa=1\n", 296 | "\n", 297 | "for epochs in range(10000):\n", 298 | "\n", 299 | " gen = iter(trainset)\n", 300 | " print(\"train start\")\n", 301 | " for i,ge in enumerate(gen):\n", 302 | " \n", 303 | " #to avoid OOM \n", 304 | " if ge[0].shape[3]<=800:\n", 305 | "\n", 306 | " #DONT FORGET THE ZERO GRAD!!!!\n", 307 | " optimizer.zero_grad()\n", 308 | " \n", 309 | " #Get Predictions, permuted for CTC loss \n", 310 | " log_probs = cnn(ge[0].cuda()).permute((2,0,1))\n", 311 | "\n", 312 | " #Targets have to be CPU for baidu loss \n", 313 | " targets = ge[1]#.cpu()\n", 314 | "\n", 315 | " #Get the Lengths/2 becase this is how much we downsample the width\n", 316 | " input_lengths = ge[2]/2\n", 317 | " target_lengths = ge[3]\n", 318 | " \n", 319 | " #Get the CTC Loss \n", 320 | " loss = ctc_loss(log_probs, targets, input_lengths, target_lengths)\n", 321 | " \n", 322 | " #Then backward and step \n", 323 | " loss.backward()\n", 324 | " optimizer.step()\n", 325 | " \n", 326 | " #Save Loss in averagemeter and write to tensorboard \n", 327 | " ave_total_loss.update(loss.data.item())\n", 328 | " writer.add_scalar(\"total_loss\", ave_total_loss.average(), n_iter) \n", 329 | " \n", 330 | " \n", 331 | " #Here we Calculate the Character error rate\n", 332 | " cum_len=np.cumsum(target_lengths)\n", 333 | " targets=np.split(ge[1].cpu(),cum_len[:-1])\n", 334 | " wer_list=[]\n", 335 | " for j in range(log_probs.shape[1]):\n", 336 | " wer_list.append(wer_eval(log_probs[:,j,:][0:input_lengths[j],:],targets[j]))\n", 337 | " \n", 338 | " #Here we save an example together with its decoding and truth\n", 339 | " #Only if it is positive \n", 340 | " \n", 341 | " if np.average(wer_list)>0.1:\n", 342 | "\n", 343 | " max_elem=np.argmax(wer_list)\n", 344 | " max_value=np.max(wer_list)\n", 345 | " max_image=ge[0][max_elem].cpu()\n", 346 | " max_target=targets[max_elem]\n", 347 | " \n", 348 | " max_target=[ds.decode_dict[x] for x in max_target.tolist()]\n", 349 | " max_target=\"\".join(max_target)\n", 350 | "\n", 351 | " ou=preds_to_integer(log_probs[:,max_elem,:])\n", 352 | " max_preds=[ds.decode_dict[x] for x in ou]\n", 353 | " max_preds=\"\".join(max_preds)\n", 354 | " \n", 355 | " writer.add_text(\"label\",max_target,n_iter)\n", 356 | " writer.add_text(\"pred\",max_preds,n_iter)\n", 357 | " writer.add_image(\"img\",ge[0][max_elem].detach().cpu().numpy(),n_iter)\n", 358 | " \n", 359 | " #gen.close()\n", 360 | " #break\n", 361 | " \n", 362 | " #Might become infinite \n", 363 | " if np.average(wer_list)< 10: \n", 364 | " CER_total.update(np.average(wer_list))\n", 365 | " writer.add_scalar(\"CER\", CER_total.average(), n_iter)\n", 366 | " \n", 367 | " #We save when the new avereage CR is beloew the NPA \n", 368 | " if npa>CER_total.average() and CER_total.average()>0 and CER_total.average()<1:\n", 369 | " \n", 370 | " torch.save(cnn.state_dict(), \"autosave.pt\")\n", 371 | " npa=CER_total.average()\n", 372 | " \n", 373 | " \n", 374 | " n_iter=n_iter+1\n", 375 | " cs.step()\n", 376 | " lr=optimizer.param_groups[0][\"lr\"]\n", 377 | " writer.add_scalar(\"lr\",lr,n_iter)\n", 378 | " " 379 | ] 380 | }, 381 | { 382 | "cell_type": "code", 383 | "execution_count": 14, 384 | "metadata": {}, 385 | "outputs": [ 386 | { 387 | "data": { 388 | "text/plain": [ 389 | "2.6976676417278758" 390 | ] 391 | }, 392 | "execution_count": 14, 393 | "metadata": {}, 394 | "output_type": "execute_result" 395 | } 396 | ], 397 | "source": [ 398 | "CER_total.average()" 399 | ] 400 | }, 401 | { 402 | "cell_type": "code", 403 | "execution_count": null, 404 | "metadata": { 405 | "collapsed": true 406 | }, 407 | "outputs": [], 408 | "source": [ 409 | "save_checkpoint({\n", 410 | " 'epoch': epoch + 1,\n", 411 | " 'arch': args.arch,\n", 412 | " 'state_dict': model.state_dict(),\n", 413 | " 'optimizer' : optimizer.state_dict(),\n", 414 | "})" 415 | ] 416 | }, 417 | { 418 | "cell_type": "code", 419 | "execution_count": 15, 420 | "metadata": { 421 | "collapsed": true 422 | }, 423 | "outputs": [], 424 | "source": [ 425 | "cnn.load_state_dict(torch.load(\"autosave.pt\"))" 426 | ] 427 | }, 428 | { 429 | "cell_type": "code", 430 | "execution_count": 16, 431 | "metadata": { 432 | "collapsed": true 433 | }, 434 | "outputs": [], 435 | "source": [ 436 | "optimizer.load_state_dict(torch.load(\"autosave_optimizer.pt\"))" 437 | ] 438 | }, 439 | { 440 | "cell_type": "code", 441 | "execution_count": null, 442 | "metadata": { 443 | "collapsed": true 444 | }, 445 | "outputs": [], 446 | "source": [] 447 | } 448 | ], 449 | "metadata": { 450 | "kernelspec": { 451 | "display_name": "Python (pytorchenv)", 452 | "language": "python", 453 | "name": "pytorch" 454 | }, 455 | "language_info": { 456 | "codemirror_mode": { 457 | "name": "ipython", 458 | "version": 3 459 | }, 460 | "file_extension": ".py", 461 | "mimetype": "text/x-python", 462 | "name": "python", 463 | "nbconvert_exporter": "python", 464 | "pygments_lexer": "ipython3", 465 | "version": "3.6.3" 466 | } 467 | }, 468 | "nbformat": 4, 469 | "nbformat_minor": 2 470 | } 471 | -------------------------------------------------------------------------------- /OCR_training_handwritten.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "collapsed": true 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "#General Imports\n", 12 | "import torch\n", 13 | "import torch.nn as nn\n", 14 | "import torch.optim as optim\n", 15 | "import numpy as np\n", 16 | "from torch.utils.data import DataLoader\n", 17 | "import random\n", 18 | "\n", 19 | "import matplotlib.pyplot as plt\n", 20 | "from tensorboardX import SummaryWriter\n", 21 | "\n", 22 | "#Load fake, non handwritten generator \n", 23 | "from IAM_dataset import hwrDataset as Dataset\n", 24 | "#Import the loss from baidu \n", 25 | "from torch_baidu_ctc import CTCLoss\n", 26 | "\n", 27 | "#Import the model \n", 28 | "from fully_conv_model import cnn_attention_ocr\n", 29 | "\n", 30 | "#Helper to count params\n", 31 | "def count_parameters(model):\n", 32 | " return sum(p.numel() for p in model.parameters() if p.requires_grad)\n", 33 | "\n", 34 | "\n", 35 | "#Evaluation function preds_to_integer\n", 36 | "from evaluation import wer_eval,preds_to_integer,show,AverageMeter,my_collate" 37 | ] 38 | }, 39 | { 40 | "cell_type": "code", 41 | "execution_count": 2, 42 | "metadata": { 43 | "collapsed": true 44 | }, 45 | "outputs": [], 46 | "source": [ 47 | "#Set up Tensorboard writer for current test\n", 48 | "writer = SummaryWriter(log_dir=\"/home/leander/AI/repos/Pytorch-OCR-Fully-Conv//logs2/handwritten_test_4\")" 49 | ] 50 | }, 51 | { 52 | "cell_type": "code", 53 | "execution_count": 3, 54 | "metadata": {}, 55 | "outputs": [ 56 | { 57 | "data": { 58 | "text/plain": [ 59 | "3543584" 60 | ] 61 | }, 62 | "execution_count": 3, 63 | "metadata": {}, 64 | "output_type": "execute_result" 65 | } 66 | ], 67 | "source": [ 68 | "###Set up model. \n", 69 | "cnn=cnn_attention_ocr(model_dim=64,nclasses=93,n_layers=8)\n", 70 | "cnn=cnn.cuda().train()\n", 71 | "count_parameters(cnn)" 72 | ] 73 | }, 74 | { 75 | "cell_type": "code", 76 | "execution_count": 4, 77 | "metadata": { 78 | "collapsed": true 79 | }, 80 | "outputs": [], 81 | "source": [ 82 | "#CTC Loss \n", 83 | "ctc_loss = CTCLoss(reduction=\"mean\",average_frames=True)\n", 84 | "#Optimizer: Good initial is 5e5 \n", 85 | "optimizer = optim.Adam(cnn.parameters(), lr=5e-5)" 86 | ] 87 | }, 88 | { 89 | "cell_type": "code", 90 | "execution_count": 5, 91 | "metadata": { 92 | "collapsed": true 93 | }, 94 | "outputs": [], 95 | "source": [ 96 | "#We keep track of the Average loss and CER \n", 97 | "ave_total_loss = AverageMeter()\n", 98 | "CER_total= AverageMeter()\n", 99 | "\n", 100 | "n_iter=0\n", 101 | "batch_size=4" 102 | ] 103 | }, 104 | { 105 | "cell_type": "code", 106 | "execution_count": 6, 107 | "metadata": { 108 | "collapsed": true 109 | }, 110 | "outputs": [], 111 | "source": [ 112 | "cnn.load_state_dict(torch.load(\"8_layers_continued_on_blanks_340k.pt\"))" 113 | ] 114 | }, 115 | { 116 | "cell_type": "code", 117 | "execution_count": 7, 118 | "metadata": { 119 | "collapsed": true 120 | }, 121 | "outputs": [], 122 | "source": [ 123 | "ds=Dataset()\n", 124 | "trainset = DataLoader(dataset=ds,\n", 125 | " batch_size=batch_size,\n", 126 | " shuffle=True,\n", 127 | " collate_fn=my_collate)" 128 | ] 129 | }, 130 | { 131 | "cell_type": "code", 132 | "execution_count": 8, 133 | "metadata": {}, 134 | "outputs": [ 135 | { 136 | "name": "stdout", 137 | "output_type": "stream", 138 | "text": [ 139 | "train start\n" 140 | ] 141 | }, 142 | { 143 | "name": "stderr", 144 | "output_type": "stream", 145 | "text": [ 146 | "/home/leander/AI/repos/Pytorch-OCR-Fully-Conv/evaluation.py:42: RuntimeWarning: divide by zero encountered in true_divide\n", 147 | " return we/len(preds)\n" 148 | ] 149 | }, 150 | { 151 | "name": "stdout", 152 | "output_type": "stream", 153 | "text": [ 154 | "train start\n", 155 | "train start\n", 156 | "train start\n", 157 | "train start\n", 158 | "train start\n", 159 | "train start\n", 160 | "train start\n", 161 | "train start\n", 162 | "train start\n", 163 | "train start\n", 164 | "train start\n", 165 | "train start\n", 166 | "train start\n", 167 | "train start\n", 168 | "train start\n", 169 | "train start\n", 170 | "train start\n", 171 | "train start\n", 172 | "train start\n", 173 | "train start\n", 174 | "train start\n", 175 | "train start\n", 176 | "train start\n" 177 | ] 178 | }, 179 | { 180 | "ename": "KeyboardInterrupt", 181 | "evalue": "", 182 | "output_type": "error", 183 | "traceback": [ 184 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 185 | "\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)", 186 | "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 30\u001b[0m \u001b[0;31m#Then backward and step\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 31\u001b[0m \u001b[0mloss\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbackward\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 32\u001b[0;31m \u001b[0moptimizer\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstep\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 33\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 34\u001b[0m \u001b[0;31m#Save Loss in averagemeter and write to tensorboard\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 187 | "\u001b[0;32m~/anaconda3/envs/pytorch/lib/python3.6/site-packages/torch/optim/adam.py\u001b[0m in \u001b[0;36mstep\u001b[0;34m(self, closure)\u001b[0m\n\u001b[1;32m 92\u001b[0m \u001b[0;31m# Decay the first and second moment running average coefficient\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 93\u001b[0m \u001b[0mexp_avg\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmul_\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mbeta1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madd_\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0mbeta1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mgrad\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 94\u001b[0;31m \u001b[0mexp_avg_sq\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmul_\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mbeta2\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0maddcmul_\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0mbeta2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mgrad\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mgrad\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 95\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mamsgrad\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 96\u001b[0m \u001b[0;31m# Maintains the maximum of all 2nd moment running avg. till now\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 188 | "\u001b[0;31mKeyboardInterrupt\u001b[0m: " 189 | ] 190 | } 191 | ], 192 | "source": [ 193 | "for epochs in range(10000):\n", 194 | "\n", 195 | " #Then we set up our own custom dataloade500r, with a custom collate, which packs the data\n", 196 | " #(does the padding) Should work with variable number of widths. \n", 197 | " \n", 198 | " #Multiple worker leads to crash with CTC loss \n", 199 | "\n", 200 | " gen = iter(trainset)\n", 201 | " print(\"train start\")\n", 202 | " for i,ge in enumerate(gen):\n", 203 | " \n", 204 | " if ge[0].shape[3]<=800:\n", 205 | "\n", 206 | " #DONT FORGET THE ZERO GRAD!!!!\n", 207 | " optimizer.zero_grad()\n", 208 | " \n", 209 | " #Get Predictions, permuted for CTC loss \n", 210 | " log_probs = cnn(ge[0]).permute((2,0,1))\n", 211 | "\n", 212 | " #Targets have to be CPU for baidu loss \n", 213 | " targets = ge[1].cpu()\n", 214 | "\n", 215 | " #Get the Lengths/2 becase this is how much we downsample the width\n", 216 | " input_lengths = ge[2]/2\n", 217 | " target_lengths = ge[3]\n", 218 | " \n", 219 | " #Get the CTC Loss \n", 220 | " loss = ctc_loss(log_probs, targets, input_lengths, target_lengths)\n", 221 | " \n", 222 | " #Then backward and step \n", 223 | " loss.backward()\n", 224 | " optimizer.step()\n", 225 | " \n", 226 | " #Save Loss in averagemeter and write to tensorboard \n", 227 | " ave_total_loss.update(loss.data.item())\n", 228 | " writer.add_scalar(\"total_loss\", ave_total_loss.average(), n_iter) \n", 229 | " \n", 230 | " \n", 231 | " #Here we Calculate the Character error rate\n", 232 | " cum_len=np.cumsum(target_lengths)\n", 233 | " targets=np.split(ge[1].cpu(),cum_len[:-1])\n", 234 | " wer_list=[]\n", 235 | " for j in range(log_probs.shape[1]):\n", 236 | " wer_list.append(wer_eval(log_probs[:,j,:][0:input_lengths[j],:],targets[j]))\n", 237 | " \n", 238 | " #Here we save an example together with its decoding and truth\n", 239 | " #Only if it is positive \n", 240 | " \n", 241 | " if np.average(wer_list)>0.1 and n_iter> 10000:\n", 242 | "\n", 243 | " max_elem=np.argmax(wer_list)\n", 244 | " max_value=np.max(wer_list)\n", 245 | " max_image=ge[0][max_elem].cpu()\n", 246 | " max_target=targets[max_elem]\n", 247 | " \n", 248 | " max_target=[ds.decode_dict[x] for x in max_target.tolist()]\n", 249 | " max_target=\"\".join(max_target)\n", 250 | "\n", 251 | " ou=preds_to_integer(log_probs[:,max_elem,:])\n", 252 | " max_preds=[ds.decode_dict[x] for x in ou]\n", 253 | " max_preds=\"\".join(max_preds)\n", 254 | " \n", 255 | " writer.add_text(\"label\",max_target,n_iter)\n", 256 | " writer.add_text(\"pred\",max_preds,n_iter)\n", 257 | " writer.add_image(\"img\",ge[0][max_elem].detach().cpu().numpy(),n_iter)\n", 258 | " \n", 259 | " #gen.close()\n", 260 | " #break\n", 261 | " \n", 262 | " #Might become infinite \n", 263 | " if np.average(wer_list)< 10: \n", 264 | " CER_total.update(np.average(wer_list))\n", 265 | " writer.add_scalar(\"CER\", CER_total.average(), n_iter)\n", 266 | " \n", 267 | " n_iter=n_iter+1" 268 | ] 269 | }, 270 | { 271 | "cell_type": "code", 272 | "execution_count": 9, 273 | "metadata": { 274 | "collapsed": true 275 | }, 276 | "outputs": [], 277 | "source": [ 278 | "torch.save(cnn.state_dict(), \"67ksteps_on_handwritten_CER012.pt\")" 279 | ] 280 | }, 281 | { 282 | "cell_type": "code", 283 | "execution_count": 10, 284 | "metadata": { 285 | "collapsed": true 286 | }, 287 | "outputs": [], 288 | "source": [ 289 | "##Checkpointing" 290 | ] 291 | }, 292 | { 293 | "cell_type": "code", 294 | "execution_count": 12, 295 | "metadata": {}, 296 | "outputs": [], 297 | "source": [ 298 | "torch.save({\n", 299 | " 'epoch': n_iter,\n", 300 | " 'model_state_dict': cnn.state_dict(),\n", 301 | " 'optimizer_state_dict': optimizer.state_dict(),\n", 302 | " 'loss': loss,\n", 303 | " }, \"67ksteps_on_handwritten_CER012_whole_cp.pt\")" 304 | ] 305 | }, 306 | { 307 | "cell_type": "code", 308 | "execution_count": null, 309 | "metadata": { 310 | "collapsed": true 311 | }, 312 | "outputs": [], 313 | "source": [ 314 | "#get it back llike: \n", 315 | "\n", 316 | "checkpoint = torch.load(PATH)\n", 317 | "model.load_state_dict(checkpoint['model_state_dict'])\n", 318 | "optimizer.load_state_dict(checkpoint['optimizer_state_dict'])\n", 319 | "epoch = checkpoint['epoch']\n", 320 | "loss = checkpoint['loss']\n" 321 | ] 322 | } 323 | ], 324 | "metadata": { 325 | "kernelspec": { 326 | "display_name": "Python (pytorchenv)", 327 | "language": "python", 328 | "name": "pytorch" 329 | }, 330 | "language_info": { 331 | "codemirror_mode": { 332 | "name": "ipython", 333 | "version": 3 334 | }, 335 | "file_extension": ".py", 336 | "mimetype": "text/x-python", 337 | "name": "python", 338 | "nbconvert_exporter": "python", 339 | "pygments_lexer": "ipython3", 340 | "version": "3.6.3" 341 | } 342 | }, 343 | "nbformat": 4, 344 | "nbformat_minor": 2 345 | } 346 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pytorch-OCR-Fully-Convolutional 2 | 3 | This repository is a work in progress implementation of: 4 | [Accurate, Data-Efficient, Unconstrained Text Recognition with Convolutional Neural Networks](https://arxiv.org/abs/1812.11894) I am not affiliated with the authors of the paper. 5 | 6 | I also implemented the excellent data generator of [@Belval](https://github.com/Belval/TextRecognitionDataGenerator) as a pytorch dataset. I rely on the pytorch implementation of baidus Warp-CTC loss from [@jpuigcerver](https://github.com/jpuigcerver/pytorch-baidu-ctc). I found the performance from warp-CTC to be better than the ctc loss native to pytorch. 7 | 8 | ## code organization 9 | ### Pytorch Datasets: 10 | - fake_texts: 11 | A folder that includes python files to set up a pytroch dataset using @Belval synthetic data generator. The pytorch dataset is defined in 'pytorch_dataset_fake.py' 12 | I load the data during the init of the dataset, with a particular set of parameters. The generation has many parameters most of them are hard set inside of the init. 13 | - IAM_dataset: 14 | Contains a simple Pytorch dataset to load the IAM handwritten offline dataset, on a line by line basis. 15 | 16 | ### Notebooks: 17 | - OCR_Training_synthetic.ipynb 18 | Trains a model on the synthetic dataset 19 | - OCR_training_handwritten.ipynb 20 | Trains a model on the IAM offline handwritten line segment dataset. 21 | - inference_demo.ipynb 22 | An example of how to go from images to text predictions. 23 | 24 | ## Progress: 25 | Implement performance on benchmark datasets. (IAM etc) 26 | Implement Batch-Renorm for Pytorch 27 | 28 | - [x] Implement Model in Pytorch 29 | - [x] Implement CTC Loss 30 | - [x] Implement Synthetic Data Generator 31 | - [x] Implement synthetic training script 32 | - [x] Implement IAM dataset 33 | - [x] Implement IAM training script 34 | - [ ] Compare performance on benchmark dataset 35 | - [ ] Implement Batch-Renorm for Pytorch 36 | -------------------------------------------------------------------------------- /evaluation.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import numpy as np 3 | import matplotlib.pyplot as plt 4 | 5 | 6 | def wer(r, h): 7 | d = np.zeros((len(r)+1)*(len(h)+1), dtype=np.uint8) 8 | d = d.reshape((len(r)+1, len(h)+1)) 9 | for i in range(len(r)+1): 10 | for j in range(len(h)+1): 11 | if i == 0: 12 | d[0][j] = j 13 | elif j == 0: 14 | d[i][0] = i 15 | 16 | # computation 17 | for i in range(1, len(r)+1): 18 | for j in range(1, len(h)+1): 19 | if r[i-1] == h[j-1]: 20 | d[i][j] = d[i-1][j-1] 21 | else: 22 | substitution = d[i-1][j-1] + 1 23 | insertion = d[i][j-1] + 1 24 | deletion = d[i-1][j] + 1 25 | d[i][j] = min(substitution, insertion, deletion) 26 | 27 | return d[len(r)][len(h)] 28 | 29 | def preds_to_integer(preds): 30 | preds=torch.argmax(preds,dim=1).detach().cpu().numpy() 31 | preds=preds.tolist() 32 | 33 | out=[] 34 | for i in range(len(preds)): 35 | if preds[i] != 0 and preds[i] != preds[i - 1]: 36 | out.append(preds[i]) 37 | return out 38 | 39 | def wer_eval(preds,labels): 40 | preds=preds_to_integer(preds) 41 | we=wer(preds,labels) 42 | return we/len(preds) 43 | 44 | def show(img): 45 | npimg = img.numpy() 46 | plt.figure(figsize=(20, 20)) 47 | plt.imshow(np.transpose(npimg, (1, 2, 0)), interpolation='nearest') 48 | 49 | class AverageMeter(object): 50 | """Computes and stores the average and current value""" 51 | def __init__(self): 52 | self.initialized = False 53 | self.val = None 54 | self.avg = None 55 | self.sum = None 56 | self.count = None 57 | 58 | def initialize(self, val, weight): 59 | self.val = val 60 | self.avg = val 61 | self.sum = val * weight 62 | self.count = weight 63 | self.initialized = True 64 | 65 | def update(self, val, weight=1): 66 | if not self.initialized: 67 | self.initialize(val, weight) 68 | else: 69 | self.add(val, weight) 70 | 71 | def add(self, val, weight): 72 | self.val = val 73 | self.sum += val * weight 74 | self.count += weight 75 | self.avg = self.sum / self.count 76 | 77 | def value(self): 78 | return self.val 79 | 80 | def average(self): 81 | return self.avg 82 | 83 | def my_collate(batch): 84 | #some shapes 85 | one,height,wi,channels=batch[0][0].shape 86 | #batch size 87 | batch_size=len(batch) 88 | #get hte max witdth for padding 89 | widths=np.array([x[2] for x in batch]) 90 | max_width=np.max(widths) 91 | #get label in a long array 92 | label_stack=np.concatenate([x[1] for x in batch]) 93 | #get all the lengths 94 | length_stack_y=np.array([x[3] for x in batch]) 95 | #Here we will inster images, aka we pad them 96 | img_stack=np.zeros(shape=(batch_size,height,max_width,channels)) 97 | 98 | #We loop over the batch once 99 | for enu,img in enumerate(batch): 100 | shape=img[2] 101 | img_stack[enu,:,0:shape,:]=np.squeeze(img[0]) 102 | 103 | img_stack=torch.tensor(img_stack).float().permute((0,3,1,2))#.cuda() 104 | label_stack=torch.tensor(label_stack, dtype=torch.int32)#.cuda() 105 | widths=torch.tensor(widths,dtype=torch.int32) 106 | length_stack_y=torch.tensor(length_stack_y,dtype=torch.int32) 107 | 108 | return img_stack,label_stack,widths,length_stack_y 109 | -------------------------------------------------------------------------------- /fake_texts/background_generator.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import math 3 | import os 4 | import random 5 | import numpy as np 6 | 7 | from PIL import Image, ImageDraw, ImageFilter 8 | 9 | class BackgroundGenerator(object): 10 | @classmethod 11 | def gaussian_noise(cls, height, width): 12 | """ 13 | Create a background with Gaussian noise (to mimic paper) 14 | """ 15 | 16 | # We create an all white image 17 | image = np.ones((height, width)) * 255 18 | 19 | # We add gaussian noise 20 | cv2.randn(image, 235, 10) 21 | 22 | return Image.fromarray(image).convert('RGB') 23 | 24 | @classmethod 25 | def plain_white(cls, height, width): 26 | """ 27 | Create a plain white background 28 | """ 29 | 30 | return Image.new("L", (width, height), 255).convert('RGB') 31 | 32 | @classmethod 33 | def quasicrystal(cls, height, width): 34 | """ 35 | Create a background with quasicrystal (https://en.wikipedia.org/wiki/Quasicrystal) 36 | """ 37 | 38 | image = Image.new("L", (width, height)) 39 | pixels = image.load() 40 | 41 | frequency = random.random() * 30 + 20 # frequency 42 | phase = random.random() * 2 * math.pi # phase 43 | rotation_count = random.randint(10, 20) # of rotations 44 | 45 | for kw in range(width): 46 | y = float(kw) / (width - 1) * 4 * math.pi - 2 * math.pi 47 | for kh in range(height): 48 | x = float(kh) / (height - 1) * 4 * math.pi - 2 * math.pi 49 | z = 0.0 50 | for i in range(rotation_count): 51 | r = math.hypot(x, y) 52 | a = math.atan2(y, x) + i * math.pi * 2.0 / rotation_count 53 | z += math.cos(r * math.sin(a) * frequency + phase) 54 | c = int(255 - round(255 * z / rotation_count)) 55 | pixels[kw, kh] = c # grayscale 56 | return image.convert('RGB') 57 | 58 | @classmethod 59 | def picture(cls, height, width): 60 | """ 61 | Create a background with a picture 62 | """ 63 | 64 | pictures = os.listdir('./pictures') 65 | 66 | if len(pictures) > 0: 67 | picture = Image.open('./pictures/' + pictures[random.randint(0, len(pictures) - 1)]) 68 | 69 | if picture.size[0] < width: 70 | picture = picture.resize([width, int(picture.size[1] * (width / picture.size[0]))], Image.ANTIALIAS) 71 | elif picture.size[1] < height: 72 | picture.thumbnail([int(picture.size[0] * (height / picture.size[1])), height], Image.ANTIALIAS) 73 | 74 | if (picture.size[0] == width): 75 | x = 0 76 | else: 77 | x = random.randint(0, picture.size[0] - width) 78 | if (picture.size[1] == height): 79 | y = 0 80 | else: 81 | y = random.randint(0, picture.size[1] - height) 82 | 83 | return picture.crop( 84 | ( 85 | x, 86 | y, 87 | x + width, 88 | y + height, 89 | ) 90 | ) 91 | else: 92 | raise Exception('No images where found in the pictures folder!') 93 | -------------------------------------------------------------------------------- /fake_texts/computer_text_generator.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | from PIL import Image, ImageColor, ImageFont, ImageDraw, ImageFilter 4 | 5 | class ComputerTextGenerator(object): 6 | @classmethod 7 | def generate(cls, text, font, text_color, font_size, orientation, space_width): 8 | if orientation == 0: 9 | return cls.__generate_horizontal_text(text, font, text_color, font_size, space_width) 10 | elif orientation == 1: 11 | return cls.__generate_vertical_text(text, font, text_color, font_size, space_width) 12 | else: 13 | raise ValueError("Unknown orientation " + str(orientation)) 14 | 15 | @classmethod 16 | def __generate_horizontal_text(cls, text, font, text_color, font_size, space_width): 17 | image_font = ImageFont.truetype(font=font, size=font_size) 18 | words = text.split(' ') 19 | space_width = image_font.getsize(' ')[0] * space_width 20 | 21 | words_width = [image_font.getsize(w)[0] for w in words] 22 | text_width = sum(words_width) + int(space_width) * (len(words) - 1) 23 | text_height = max([image_font.getsize(w)[1] for w in words]) 24 | 25 | txt_img = Image.new('RGBA', (text_width, text_height), (0, 0, 0, 0)) 26 | 27 | txt_draw = ImageDraw.Draw(txt_img) 28 | 29 | colors = [ImageColor.getrgb(c) for c in text_color.split(',')] 30 | c1, c2 = colors[0], colors[-1] 31 | 32 | fill = ( 33 | random.randint(c1[0], c2[0]), 34 | random.randint(c1[1], c2[1]), 35 | random.randint(c1[2], c2[2]) 36 | ) 37 | for i, w in enumerate(words): 38 | txt_draw.text((sum(words_width[0:i]) + i * int(space_width), 0), w, fill=fill, font=image_font) 39 | 40 | return txt_img 41 | 42 | @classmethod 43 | def __generate_vertical_text(cls, text, font, text_color, font_size, space_width): 44 | image_font = ImageFont.truetype(font=font, size=font_size) 45 | 46 | space_height = int(image_font.getsize(' ')[1] * space_width) 47 | 48 | char_heights = [image_font.getsize(c)[1] if c != ' ' else space_height for c in text] 49 | text_width = max([image_font.getsize(c)[0] for c in text]) 50 | text_height = sum(char_heights) 51 | 52 | txt_img = Image.new('RGBA', (text_width, text_height), (0, 0, 0, 0)) 53 | 54 | txt_draw = ImageDraw.Draw(txt_img) 55 | 56 | colors = [ImageColor.getrgb(c) for c in text_color.split(',')] 57 | c1, c2 = colors[0], colors[-1] 58 | 59 | fill = ( 60 | random.randint(c1[0], c2[0]), 61 | random.randint(c1[1], c2[1]), 62 | random.randint(c1[2], c2[2]) 63 | ) 64 | 65 | for i, c in enumerate(text): 66 | txt_draw.text((0, sum(char_heights[0:i])), c, fill=fill, font=image_font) 67 | 68 | return txt_img 69 | -------------------------------------------------------------------------------- /fake_texts/data_generator.py: -------------------------------------------------------------------------------- 1 | import os 2 | import random 3 | 4 | from PIL import Image, ImageFilter 5 | 6 | from computer_text_generator import ComputerTextGenerator 7 | from background_generator import BackgroundGenerator 8 | from distorsion_generator import DistorsionGenerator 9 | 10 | class FakeTextDataGenerator(object): 11 | @classmethod 12 | def generate_from_tuple(cls, t): 13 | """ 14 | Same as generate, but takes all parameters as one tuple 15 | """ 16 | 17 | cls.generate(*t) 18 | 19 | @classmethod 20 | def generate(cls, index, text, font, size, extension, skewing_angle, random_skew, blur, random_blur, background_type, distorsion_type, distorsion_orientation, is_handwritten, name_format, width, alignment, text_color, orientation, space_width): 21 | image = None 22 | 23 | ########################## 24 | # Create picture of text # 25 | ########################## 26 | 27 | image = ComputerTextGenerator.generate(text, font, text_color, size, orientation, space_width) 28 | 29 | random_angle = random.randint(0-skewing_angle, skewing_angle) 30 | 31 | rotated_img = image.rotate(skewing_angle if not random_skew else random_angle, expand=1) 32 | 33 | ############################# 34 | # Apply distorsion to image # 35 | ############################# 36 | if distorsion_type == 0: 37 | distorted_img = rotated_img # Mind = blown 38 | elif distorsion_type == 1: 39 | distorted_img = DistorsionGenerator.sin( 40 | rotated_img, 41 | vertical=(distorsion_orientation == 0 or distorsion_orientation == 2), 42 | horizontal=(distorsion_orientation == 1 or distorsion_orientation == 2) 43 | ) 44 | elif distorsion_type == 2: 45 | distorted_img = DistorsionGenerator.cos( 46 | rotated_img, 47 | vertical=(distorsion_orientation == 0 or distorsion_orientation == 2), 48 | horizontal=(distorsion_orientation == 1 or distorsion_orientation == 2) 49 | ) 50 | else: 51 | distorted_img = DistorsionGenerator.random( 52 | rotated_img, 53 | vertical=(distorsion_orientation == 0 or distorsion_orientation == 2), 54 | horizontal=(distorsion_orientation == 1 or distorsion_orientation == 2) 55 | ) 56 | 57 | ################################## 58 | # Resize image to desired format # 59 | ################################## 60 | 61 | # Horizontal text 62 | if orientation == 0: 63 | new_width = int(float(distorted_img.size[0] + 10) * (float(size) / float(distorted_img.size[1] + 10))) 64 | resized_img = distorted_img.resize((new_width, size - 10), Image.ANTIALIAS) 65 | background_width = width if width > 0 else new_width + 10 66 | background_height = size 67 | # Vertical text 68 | elif orientation == 1: 69 | new_height = int(float(distorted_img.size[1] + 10) * (float(size) / float(distorted_img.size[0] + 10))) 70 | resized_img = distorted_img.resize((size - 10, new_height), Image.ANTIALIAS) 71 | background_width = size 72 | background_height = new_height + 10 73 | else: 74 | raise ValueError("Invalid orientation") 75 | 76 | ############################# 77 | # Generate background image # 78 | ############################# 79 | if background_type == 0: 80 | background = BackgroundGenerator.gaussian_noise(background_height, background_width) 81 | elif background_type == 1: 82 | background = BackgroundGenerator.plain_white(background_height, background_width) 83 | elif background_type == 2: 84 | background = BackgroundGenerator.quasicrystal(background_height, background_width) 85 | else: 86 | background = BackgroundGenerator.picture(background_height, background_width) 87 | 88 | ############################# 89 | # Place text with alignment # 90 | ############################# 91 | 92 | new_text_width, _ = resized_img.size 93 | 94 | if alignment == 0: 95 | background.paste(resized_img, (5, 5), resized_img) 96 | elif alignment == 1: 97 | background.paste(resized_img, (int(background_width / 2 - new_text_width / 2), 5), resized_img) 98 | else: 99 | background.paste(resized_img, (background_width - new_text_width - 5, 5), resized_img) 100 | 101 | ################################## 102 | # Apply gaussian blur # 103 | ################################## 104 | 105 | final_image = background.filter( 106 | ImageFilter.GaussianBlur( 107 | radius=(blur if not random_blur else random.randint(0, blur)) 108 | ) 109 | ) 110 | 111 | ##################################### 112 | # Generate name for resulting image # 113 | ##################################### 114 | if name_format == 0: 115 | image_name = '{}_{}.{}'.format(text, str(index), extension) 116 | elif name_format == 1: 117 | image_name = '{}_{}.{}'.format(str(index), text, extension) 118 | elif name_format == 2: 119 | image_name = '{}.{}'.format(str(index),extension) 120 | else: 121 | print('{} is not a valid name format. Using default.'.format(name_format)) 122 | image_name = '{}_{}.{}'.format(text, str(index), extension) 123 | # Save the image 124 | return final_image.convert('RGB')#.save(os.path.join(out_dir, image_name)) 125 | -------------------------------------------------------------------------------- /fake_texts/distorsion_generator.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import math 3 | import os 4 | import random 5 | import numpy as np 6 | 7 | from PIL import Image, ImageDraw, ImageFilter 8 | 9 | class DistorsionGenerator(object): 10 | @classmethod 11 | def apply_func_distorsion(cls, image, vertical, horizontal, max_offset, func): 12 | """ 13 | """ 14 | 15 | # Nothing to do! 16 | if not vertical and not horizontal: 17 | return image 18 | 19 | rgb_image = image.convert('RGBA') 20 | 21 | img_arr = np.array(rgb_image) 22 | 23 | vertical_offsets = [func(i) for i in range(img_arr.shape[1])] 24 | horizontal_offsets = [ 25 | func(i) 26 | for i in range( 27 | img_arr.shape[0] + ( 28 | (max(vertical_offsets) - min(min(vertical_offsets), 0)) if vertical else 0 29 | ) 30 | ) 31 | ] 32 | 33 | new_img_arr = np.zeros(( 34 | img_arr.shape[0] + (2 * max_offset if vertical else 0), 35 | img_arr.shape[1] + (2 * max_offset if horizontal else 0), 36 | 4 37 | )) 38 | 39 | new_img_arr_copy = np.copy(new_img_arr) 40 | 41 | if vertical: 42 | column_height = img_arr.shape[0] 43 | for i, o in enumerate(vertical_offsets): 44 | column_pos = (i + max_offset) if horizontal else i 45 | new_img_arr[max_offset+o:column_height+max_offset+o, column_pos, :] = img_arr[:, i, :] 46 | 47 | if horizontal: 48 | row_width = img_arr.shape[1] 49 | for i, o in enumerate(horizontal_offsets): 50 | if vertical: 51 | new_img_arr_copy[i, max_offset+o:row_width+max_offset+o,:] = new_img_arr[i, max_offset:row_width+max_offset, :] 52 | else: 53 | new_img_arr[i, max_offset+o:row_width+max_offset+o,:] = img_arr[i, :, :] 54 | 55 | return Image.fromarray(np.uint8(new_img_arr_copy if horizontal and vertical else new_img_arr)).convert('RGBA') 56 | 57 | @classmethod 58 | def sin(cls, image, vertical=False, horizontal=False): 59 | """ 60 | Apply a sine distorsion on one or both of the specified axis 61 | """ 62 | 63 | max_offset = int(image.height ** 0.5) 64 | 65 | return cls.apply_func_distorsion(image, vertical, horizontal, max_offset, (lambda x: int(math.sin(math.radians(x)) * max_offset))) 66 | 67 | @classmethod 68 | def cos(cls, image, vertical=False, horizontal=False): 69 | """ 70 | Apply a cosine distorsion on one or both of the specified axis 71 | """ 72 | 73 | max_offset = int(image.height ** 0.5) 74 | 75 | return cls.apply_func_distorsion(image, vertical, horizontal, max_offset, (lambda x: int(math.cos(math.radians(x)) * max_offset))) 76 | 77 | @classmethod 78 | def random(cls, image, vertical=False, horizontal=False): 79 | """ 80 | Apply a random distorsion on one or both of the specified axis 81 | """ 82 | 83 | max_offset = int(image.height ** 0.4) 84 | 85 | return cls.apply_func_distorsion(image, vertical, horizontal, max_offset, (lambda x: random.randint(0, max_offset))) 86 | -------------------------------------------------------------------------------- /fake_texts/imgaug_transformations.py: -------------------------------------------------------------------------------- 1 | import imgaug as ia 2 | from imgaug import augmenters as iaa 3 | sometimes_5 = lambda aug: iaa.Sometimes(0.5, aug) 4 | sometimes_1 = lambda aug: iaa.Sometimes(0.1, aug) 5 | sometimes_8 = lambda aug: iaa.Sometimes(0.8, aug) 6 | sometimes_2 = lambda aug: iaa.Sometimes(0.2, aug) 7 | 8 | augmentations=iaa.Sequential([ 9 | sometimes_2(iaa.CropAndPad( 10 | percent=(-0.02, 0.02), 11 | pad_mode=["edge"], 12 | pad_cval=(0, 255) 13 | )), 14 | iaa.Sequential([iaa.size.Scale(0.6),iaa.size.Scale(1/0.6)]), 15 | #sometimes_8(iaa.OneOf([iaa.Sequential([iaa.size.Scale(0.6),iaa.size.Scale(1/0.6)]), 16 | # iaa.Sequential([iaa.size.Scale(0.8),iaa.size.Scale(1/0.8)]), 17 | # iaa.Sequential([iaa.size.Scale(0.9),iaa.size.Scale(1/0.9)]),])), 18 | 19 | #This inverts completely inverts the text ( make black white etc.) 20 | sometimes_1(iaa.Invert(1, per_channel=True)), 21 | 22 | #This does some affine transformations 23 | sometimes_2(iaa.Affine( 24 | scale={"x": (0.8, 1), "y": (0.8, 1)}, # scale images to 80-120% of their size, individually per axis 25 | translate_percent={"x": (-0, 0), "y": (-0.1, 0.1)}, # translate by -20 to +20 percent (per axis) 26 | rotate=(-2, 2), # rotate by -45 to +45 degrees 27 | shear=(-2, 2), # shear by -16 to +16 degrees 28 | order=[0, 1], # use nearest neighbour or bilinear interpolation (fast) 29 | cval=(0, 255), # if mode is constant, use a cval between 0 and 255 30 | mode=["edge"]# use any of scikit-image's warping modes (see 2nd image from the top for examples) 31 | )), 32 | sometimes_2(iaa.OneOf([ 33 | iaa.GaussianBlur((0, 1.0)), # blur images with a sigma between 0 and 3.0 34 | iaa.AverageBlur(k=(2, 3)), # blur image using local means with kernel sizes between 2 and 7 35 | #iaa.MedianBlur(k=(3, 5)), # blur image using local medians with kernel sizes between 2 and 7 36 | ])), 37 | sometimes_2(iaa.Add((-10, 10), per_channel=0.5)), # change brightness of images (by -10 to 10 of original value) 38 | sometimes_8(iaa.AddToHueAndSaturation((-200, 200))), # change hue and saturation 39 | sometimes_5(iaa.ContrastNormalization((0.8, 5), per_channel=0.5)), # improve or worsen the contrast 40 | sometimes_1(iaa.ElasticTransformation(alpha=(0, 0.5), sigma=0.2)), # move pixels locally around (with random strengths) 41 | #sometimes_1(iaa.PiecewiseAffine(scale=(0.001, 0.005))), 42 | sometimes_1(iaa.PiecewiseAffine(scale=(0.004, 0.008))), 43 | 44 | 45 | ]) -------------------------------------------------------------------------------- /fake_texts/pytorch_dataset_fake.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path.append("fake_texts/") 3 | #sys.path.append("/home/leander/AI/repos/gen_text/TextRecognitionDataGenerator/fonts") 4 | import argparse 5 | import os, errno 6 | import random 7 | import string 8 | from tqdm import tqdm 9 | 10 | from string_generator import ( 11 | create_strings_from_dict, 12 | create_strings_from_file, 13 | create_strings_from_wikipedia, 14 | create_strings_randomly 15 | ) 16 | 17 | from data_generator import FakeTextDataGenerator 18 | 19 | import torch 20 | from torch.utils.data import DataLoader 21 | from torchvision import transforms 22 | import torchvision.datasets as datasets 23 | import matplotlib.pyplot as plt 24 | import imgaug as ia 25 | from imgaug import augmenters as iaa 26 | from imgaug_transformations import augmentations 27 | from skimage.transform import resize 28 | import cv2 29 | 30 | 31 | #Basically we want to add the 32 | 33 | def load_dict(lang): 34 | """ 35 | Read the dictionnary file and returns all words in it. 36 | """ 37 | 38 | lang_dict = [] 39 | with open(os.path.join('dicts', lang + '.txt'), 'r', encoding="utf8", errors='ignore') as d: 40 | lang_dict = d.readlines() 41 | return lang_dict 42 | 43 | 44 | def load_fonts(lang): 45 | """ 46 | Load all fonts in the fonts directories 47 | """ 48 | 49 | if lang == 'cn': 50 | return [os.path.join('fonts/cn', font) for font in os.listdir('fonts/cn')] 51 | else: 52 | return [os.path.join("fonts/latin/", font) for font in os.listdir("fonts/latin/")] 53 | 54 | 55 | import numpy as np 56 | from nltk import word_tokenize 57 | import torch 58 | 59 | import torch 60 | from torch.utils import data 61 | 62 | class Dataset(data.Dataset): 63 | 'Characterizes a dataset for PyTorch' 64 | def __init__(self,batch_size,epoch_size=10,random_strings=True,num_words=5,transform=False,width=-1,alignment=1,height=32): 65 | 'Initialization' 66 | #General args: 67 | self.transform=transform 68 | self.random_strings=random_strings 69 | self.num_words=num_words 70 | #How much data we want to generate in a single epoch 71 | self.epoch_size=epoch_size 72 | self.batch_size=batch_size 73 | 74 | def take_10(x): 75 | 76 | if len(x)>self.num_words: 77 | return x[0:self.num_words] 78 | else: 79 | return x 80 | 81 | #Text gen specific stuff 82 | self.thread_count=6 83 | self.language="en" 84 | self.count=32 85 | #If we want to use random seq, alternatively we use Wikipedia ( needs internet) 86 | self.random_sequences=False 87 | self.include_letters=True 88 | self.include_numbers=True 89 | self.include_symbols=True 90 | #When source wikipedia, how many words we want to include 91 | self.length=10 92 | #If we want to have variable word lengths, "length is maximum) 93 | self.random=True 94 | #The height of the image 95 | self.format=height 96 | #Skeqing angle 97 | self.skew_angle=0 98 | self.random_skew=True 99 | self.use_wikipedia=True 100 | self.blur=0 101 | self.random_blur=True 102 | 103 | """Define what kind of background to use. 0: Gaussian Noise, 1: Plain white, 2: Quasicrystal, 3: Pictures""", 104 | self.background=1 105 | 106 | """Define a distorsion applied to the resulting image. 0: None (Default), 1: Sine wave, 2: Cosine wave, 3: Random""", 107 | 108 | self.distorsion=0 109 | """Define the distorsion's orientation. Only used if -d is specified. 0: Vertical (Up and down), 1: Horizontal (Left and Right), 2: Both""" 110 | self.distorsion_orientation=0 111 | self.width=width 112 | self.orientation=0 113 | self.text_color='#282828' 114 | self.space_width=0.1 115 | self.extension="jpg" 116 | self.handwritten=False 117 | self.name_format=0 118 | self.alignment=alignment 119 | 120 | #This shoule be done on init We also do string generation in the init of the dataset 121 | pool = '' 122 | pool += "abcdefghijklmnopqrstuvwxyz" 123 | pool += "0123456789" 124 | pool += "!\"#$%&'()*+,-./:;?@[\\]^_`{|}~" 125 | pool += ' ' 126 | self.keys = list(pool) 127 | self.values = np.array(range(1,len(pool)+1)) 128 | self.dictionary = dict(zip(self.keys, self.values)) 129 | self.fonts = load_fonts("en") 130 | 131 | self.decode_dict=dict((v,k) for k,v in self.dictionary.items()) 132 | self.decode_dict.update({67 : "OOK"}) 133 | 134 | ###Get strings 135 | strings = [] 136 | #We try to load strings from wikipedia, if not we return random strings. 137 | if self.random_strings==False: 138 | try: 139 | #Num words just doesnt work here I think it takes one sentence always 140 | strings = create_strings_from_wikipedia(1, self.batch_size*self.epoch_size, "en") 141 | except: 142 | print("Connection issues") 143 | strings = create_strings_randomly(self.num_words, False, batch_size*epoch_size, 144 | True, True,True, "en") 145 | else: 146 | strings = create_strings_randomly(self.num_words, False, batch_size*epoch_size, 147 | True, True,True, "en") 148 | 149 | ###Get Images 150 | #Here we actually take up to n words, by word tokenizing and then taking n 151 | strings =[" ".join(take_10(word_tokenize(x))) for x in strings] 152 | #Next we split into cahracter 153 | strings_=[list(x)for x in strings] 154 | #self.strings=strings 155 | #self.strings_=strings_ 156 | #Then we convert to interger, 93 for symbols we dont know 157 | self.strings_int=[[self.dictionary[x.lower() ] if x.lower() in self.keys else 67 for x in m ] for m in strings_] 158 | #Then we get the lengths, we need for loss 159 | self.strings_len=[len(x)for x in self.strings_int] 160 | string_count = len(strings) 161 | #We can write it in a neat list comprehension, enough optimization for me haha 162 | self.image_list=[np.expand_dims(np.array(FakeTextDataGenerator.generate(*j)),0) for j in zip( 163 | [i for i in range(0, string_count)], 164 | strings, 165 | [self.fonts[random.randrange(0, len(self.fonts))] for _ in range(0, string_count)], 166 | [self.format] * string_count, 167 | [self.extension] * string_count, 168 | [self.skew_angle] * string_count, 169 | [self.random_skew] * string_count, 170 | [self.blur] * string_count, 171 | [self.random_blur] * string_count, 172 | [self.background] * string_count, 173 | [self.distorsion] * string_count, 174 | [self.distorsion_orientation] * string_count, 175 | [self.handwritten] * string_count, 176 | [self.name_format] * string_count, 177 | [self.width] * string_count, 178 | [self.alignment] * string_count, 179 | [self.text_color] * string_count, 180 | [self.orientation] * string_count, 181 | [self.space_width] * string_count )] 182 | 183 | 184 | self.seq = augmentations 185 | 186 | def __len__(self): 187 | 'Denotes the total number of samples' 188 | return self.batch_size*self.epoch_size 189 | def transform(self,img): 190 | return self.seq.augment_images(img) 191 | 192 | def __getitem__(self, index): 193 | 'Generates one sample of data' 194 | # Select sample 195 | X=self.image_list[index]#/255 196 | y=self.strings_int[index] 197 | y_len=len(y) 198 | 199 | 200 | if self.transform== True: 201 | X=self.seq.augment_images(X) 202 | X=np.squeeze(X) 203 | #X=cv2.cvtColor(X,cv2.COLOR_RGB2GRAY) 204 | #X=cv2.cvtColor(X,cv2.COLOR_GRAY2RGB) 205 | 206 | # resize_shape=(12,int(12*X.shape[1]/X.shape[0])) 207 | # X = resize(X,resize_shape,mode="constant") 208 | # 209 | # resize_shape=(32,int(32*X.shape[1]/X.shape[0])) 210 | # X = resize(X,resize_shape,mode="constant") 211 | # 212 | X=np.expand_dims(X,0) 213 | X =X/255 214 | x_len=X.shape[2] 215 | 216 | return X, y,x_len,y_len 217 | 218 | # a simple custom collate function, just to show the idea 219 | #Basically the collate gets calleda list of the putputs of all the batches 220 | 221 | def my_collate(batch): 222 | #some shapes 223 | one,height,wi,channels=batch[0][0].shape 224 | print(wi) 225 | #batch size 226 | batch_size=len(batch) 227 | #get hte max witdth for padding 228 | widths=np.array([x[2] for x in batch]) 229 | max_width=np.max(widths) 230 | #get label in a long array 231 | label_stack=np.concatenate([x[1] for x in batch]) 232 | #get all the lengths 233 | length_stack_y=np.array([x[3] for x in batch]) 234 | #Here we will inster images, aka we pad them 235 | img_stack=np.zeros(shape=(batch_size,height,max_width,channels)) 236 | 237 | #We loop over the batch once 238 | for enu,img in enumerate(batch): 239 | shape=img[2] 240 | img_stack[enu,:,0:shape,:]=np.squeeze(img[0]) 241 | 242 | img_stack=torch.tensor(img_stack).cuda().float().permute((0,3,1,2)) 243 | label_stack=torch.tensor(label_stack, dtype=torch.int32).cuda() 244 | widths=torch.tensor(widths,dtype=torch.int32) 245 | length_stack_y=torch.tensor(length_stack_y,dtype=torch.int32) 246 | 247 | return img_stack,label_stack,widths,length_stack_y 248 | 249 | -------------------------------------------------------------------------------- /fake_texts/pytorch_dataset_fake_2.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path.append("fake_texts/") 3 | #sys.path.append("/home/leander/AI/repos/gen_text/TextRecognitionDataGenerator/fonts") 4 | import argparse 5 | import os, errno 6 | import random 7 | from random import randint 8 | import string 9 | from tqdm import tqdm 10 | 11 | from string_generator import ( 12 | create_strings_from_dict, 13 | create_strings_from_file, 14 | create_strings_from_wikipedia, 15 | create_strings_randomly 16 | ) 17 | 18 | from data_generator import FakeTextDataGenerator 19 | 20 | import torch 21 | from torch.utils.data import DataLoader 22 | from torchvision import transforms 23 | import torchvision.datasets as datasets 24 | import matplotlib.pyplot as plt 25 | import imgaug as ia 26 | from imgaug import augmenters as iaa 27 | from imgaug_transformations import augmentations 28 | from skimage.transform import resize 29 | import cv2 30 | 31 | 32 | 33 | #Basically we want to add the 34 | 35 | def load_dict(lang): 36 | """ 37 | Read the dictionnary file and returns all words in it. 38 | """ 39 | 40 | lang_dict = [] 41 | with open(os.path.join('dicts', lang + '.txt'), 'r', encoding="utf8", errors='ignore') as d: 42 | lang_dict = d.readlines() 43 | return lang_dict 44 | 45 | 46 | def load_fonts(lang): 47 | """ 48 | Load all fonts in the fonts directories 49 | """ 50 | 51 | if lang == 'cn': 52 | return [os.path.join('fonts/cn', font) for font in os.listdir('fonts/cn')] 53 | else: 54 | return [os.path.join("fonts/latin/", font) for font in os.listdir("fonts/latin/")] 55 | 56 | 57 | import numpy as np 58 | from nltk import word_tokenize 59 | import torch 60 | 61 | import torch 62 | from torch.utils import data 63 | 64 | class Dataset(data.Dataset): 65 | 'Characterizes a dataset for PyTorch' 66 | def __init__(self,epoch_size=10,random_strings=True,num_words=5,transform=False,width=-1,alignment=1,height=32): 67 | 'Initialization' 68 | #Still in inti 69 | self.transform=True 70 | self.random_sequences=False 71 | self.random_strings=True 72 | self.include_letters=True 73 | self.include_numbers=True 74 | self.include_symbols=True 75 | self.length=10 76 | #self.random=False 77 | self.format=32 78 | self.use_wikipedia=False 79 | self.text_color='#282828' 80 | self.orientation=0 81 | self.extension="jpg" 82 | self.handwritten=False 83 | self.name_format=0 84 | 85 | pool = '' 86 | pool += "abcdefghijklmnopqrstuvwxyz" 87 | pool += "0123456789" 88 | pool += "!\"#$%&'()*+,-./:;?@[\\]^_`{|}~" 89 | pool += ' ' 90 | self.keys = list(pool) 91 | self.values = np.array(range(1,len(pool)+1)) 92 | self.dictionary = dict(zip(self.keys, self.values)) 93 | self.fonts = load_fonts("en") 94 | 95 | self.decode_dict=dict((v,k) for k,v in self.dictionary.items()) 96 | self.decode_dict.update({67 : "OOK"}) 97 | self.seq = augmentations 98 | 99 | def __len__(self): 100 | 'Denotes the total number of samples' 101 | return 10000 102 | def transform(self,img): 103 | return self.seq.augment_images(img) 104 | 105 | def __getitem__(self, index): 106 | #We are actually only getting one item here obvoiusly! 107 | 108 | 109 | num_words=randint(5,6) 110 | language="en" 111 | count=32 112 | skew_angle=0 113 | random_skew=True 114 | blur=0 115 | random_blur=True 116 | background=randint(0,1) 117 | distorsion=randint(0,2) 118 | distorsion_orientation=randint(0,2) 119 | 120 | #as class function 121 | def take_10(x): 122 | 123 | if len(x)>num_words: 124 | return x[0:num_words] 125 | else: 126 | return x 127 | 128 | #if random.random()>0.8: 129 | # self.width=random.randint(500,800) 130 | #else: 131 | width=-1 132 | 133 | alignment=random.randint(0,2) 134 | 135 | strings = [] 136 | #try: 137 | # strings = create_strings_from_wikipedia(1, 1, "en") 138 | #except: 139 | strings = create_strings_randomly(num_words, False, 1, 140 | True, True,True, "en") 141 | 142 | strings =[" ".join(take_10(word_tokenize(x))) for x in strings] 143 | 144 | strings_=[list(x)for x in strings] 145 | self.strings_int=[[self.dictionary[x.lower() ] if x.lower() in self.keys else 67 for x in m ] for m in strings_] 146 | self.strings_len=[len(x)for x in self.strings_int] 147 | string_count = len(strings) 148 | 149 | #What we do here is we space the words quite far appart. 150 | if random.random()>0.8: 151 | width_scale=random.random()*900 152 | space_width=width_scale/np.sum(self.strings_len) 153 | else: 154 | #width_scale=random.randint(50,100)/100 155 | #width_scale=1 156 | #space_width=width_scale/np.sum(self.strings_len) 157 | space_width=2#random.randint(50,100)/100 158 | 159 | if random.random()>0.85 and np.max(self.strings_len)<30: 160 | width=random.randint(500,800) 161 | 162 | image_list=[np.expand_dims(np.array(FakeTextDataGenerator.generate(*j)),0) for j in zip( 163 | [i for i in range(0, string_count)], 164 | strings, 165 | [self.fonts[random.randrange(0, len(self.fonts))] for _ in range(0, string_count)], 166 | [self.format] * string_count, 167 | [self.extension] * string_count, 168 | [skew_angle] * string_count, 169 | [random_skew] * string_count, 170 | [blur] * string_count, 171 | [random_blur] * string_count, 172 | [background] * string_count, 173 | [distorsion] * string_count, 174 | [distorsion_orientation] * string_count, 175 | [self.handwritten] * string_count, 176 | [self.name_format] * string_count, 177 | [width] * string_count, 178 | [alignment] * string_count, 179 | [self.text_color] * string_count, 180 | [self.orientation] * string_count, 181 | [space_width] * string_count )] 182 | 183 | 184 | X=image_list[0] 185 | y=self.strings_int[0] 186 | 187 | y_len=len(y) 188 | 189 | #Here we include some random horizontal lines cause they appear quite often in real life. 190 | if random.random()>0.8: 191 | for j in range(random.randint(0,3)): 192 | 193 | random_channel=random.randint(0,2) 194 | random_h=random.randint(0,31) 195 | random_w_s=random.randint(0,int(X.shape[2]/2)) 196 | random_w_e=random.randint(int(X.shape[2]/2),int(X.shape[2])) 197 | 198 | 199 | X[0,random_h,random_w_s:random_w_e,random_channel]=random.randint(0,255) 200 | 201 | if self.transform== True: 202 | X=self.seq.augment_images(X) 203 | 204 | #X=np.squeeze(X) 205 | 206 | #X=np.expand_dims(X,0) 207 | 208 | 209 | X =X/255 210 | x_len=X.shape[2] 211 | 212 | return X, y,x_len,y_len 213 | 214 | -------------------------------------------------------------------------------- /fake_texts/string_generator.py: -------------------------------------------------------------------------------- 1 | import random 2 | import re 3 | import string 4 | import requests 5 | 6 | from bs4 import BeautifulSoup 7 | 8 | def create_strings_from_file(filename, count): 9 | """ 10 | Create all strings by reading lines in specified files 11 | """ 12 | 13 | strings = [] 14 | 15 | with open(filename, 'r', encoding="utf8") as f: 16 | lines = [l.strip()[0:200] for l in f.readlines()] 17 | if len(lines) == 0: 18 | raise Exception("No lines could be read in file") 19 | while len(strings) < count: 20 | if len(lines) >= count - len(strings): 21 | strings.extend(lines[0:count - len(strings)]) 22 | else: 23 | strings.extend(lines) 24 | 25 | return strings 26 | 27 | def create_strings_from_dict(length, allow_variable, count, lang_dict): 28 | """ 29 | Create all strings by picking X random word in the dictionnary 30 | """ 31 | 32 | dict_len = len(lang_dict) 33 | strings = [] 34 | for _ in range(0, count): 35 | current_string = "" 36 | for _ in range(0, random.randint(1, length) if allow_variable else length): 37 | current_string += lang_dict[random.randrange(dict_len)][:-1] 38 | current_string += ' ' 39 | strings.append(current_string[:-1]) 40 | return strings 41 | 42 | def create_strings_from_wikipedia(minimum_length, count, lang): 43 | """ 44 | Create all string by randomly picking Wikipedia articles and taking sentences from them. 45 | """ 46 | sentences = [] 47 | 48 | while len(sentences) < count: 49 | # We fetch a random page 50 | page = requests.get('https://{}.wikipedia.org/wiki/Special:Random'.format(lang)) 51 | 52 | soup = BeautifulSoup(page.text, 'html.parser') 53 | 54 | for script in soup(["script", "style"]): 55 | script.extract() 56 | 57 | # Only take a certain length 58 | lines = list(filter( 59 | lambda s: 60 | len(s.split(' ')) > minimum_length 61 | and not "Wikipedia" in s 62 | and not "wikipedia" in s, 63 | [ 64 | ' '.join(re.findall(r"[\w']+", s.strip()))[0:200] for s in soup.get_text().splitlines() 65 | ] 66 | )) 67 | 68 | # Remove the last lines that talks about contributing 69 | sentences.extend(lines[0:max([1, len(lines) - 5])]) 70 | 71 | return sentences[0:count] 72 | 73 | def create_strings_randomly(length, allow_variable, count, let, num, sym, lang): 74 | """ 75 | Create all strings by randomly sampling from a pool of characters. 76 | """ 77 | 78 | # If none specified, use all three 79 | if True not in (let, num, sym): 80 | let, num, sym = True, True, True 81 | 82 | pool = '' 83 | if let: 84 | if lang == 'cn': 85 | pool += ''.join([chr(i) for i in range(19968, 40908)]) # Unicode range of CHK characters 86 | else: 87 | pool += string.ascii_letters 88 | if num: 89 | pool += "0123456789" 90 | if sym: 91 | pool += "!\"#$%&'()*+,-./:;?@[\\]^_`{|}~" 92 | 93 | if lang == 'cn': 94 | min_seq_len = 1 95 | max_seq_len = 2 96 | else: 97 | min_seq_len = 2 98 | max_seq_len = 10 99 | 100 | strings = [] 101 | for _ in range(0, count): 102 | current_string = "" 103 | for _ in range(0, random.randint(1, length) if allow_variable else length): 104 | seq_len = random.randint(min_seq_len, max_seq_len) 105 | 106 | 107 | current_string += ''.join([random.choice(pool) for _ in range(seq_len)]) 108 | current_string += ' ' 109 | strings.append(current_string[:-1]) 110 | 111 | 112 | return strings -------------------------------------------------------------------------------- /fonts/cn/SourceHanSans-Normal.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/cn/SourceHanSans-Normal.ttf -------------------------------------------------------------------------------- /fonts/latin/AllerDisplay.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/AllerDisplay.ttf -------------------------------------------------------------------------------- /fonts/latin/Aller_Bd.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Aller_Bd.ttf -------------------------------------------------------------------------------- /fonts/latin/Aller_BdIt.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Aller_BdIt.ttf -------------------------------------------------------------------------------- /fonts/latin/Aller_It.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Aller_It.ttf -------------------------------------------------------------------------------- /fonts/latin/Aller_Lt.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Aller_Lt.ttf -------------------------------------------------------------------------------- /fonts/latin/Aller_LtIt.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Aller_LtIt.ttf -------------------------------------------------------------------------------- /fonts/latin/Aller_Rg.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Aller_Rg.ttf -------------------------------------------------------------------------------- /fonts/latin/Amatic-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Amatic-Bold.ttf -------------------------------------------------------------------------------- /fonts/latin/AmaticSC-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/AmaticSC-Regular.ttf -------------------------------------------------------------------------------- /fonts/latin/BEBAS___.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/BEBAS___.ttf -------------------------------------------------------------------------------- /fonts/latin/Capture_it.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Capture_it.ttf -------------------------------------------------------------------------------- /fonts/latin/Capture_it_2.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Capture_it_2.ttf -------------------------------------------------------------------------------- /fonts/latin/CaviarDreams.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/CaviarDreams.ttf -------------------------------------------------------------------------------- /fonts/latin/CaviarDreams_BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/CaviarDreams_BoldItalic.ttf -------------------------------------------------------------------------------- /fonts/latin/CaviarDreams_Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/CaviarDreams_Italic.ttf -------------------------------------------------------------------------------- /fonts/latin/Caviar_Dreams_Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Caviar_Dreams_Bold.ttf -------------------------------------------------------------------------------- /fonts/latin/DroidSans-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/DroidSans-Bold.ttf -------------------------------------------------------------------------------- /fonts/latin/DroidSans.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/DroidSans.ttf -------------------------------------------------------------------------------- /fonts/latin/FFF_Tusj.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/FFF_Tusj.ttf -------------------------------------------------------------------------------- /fonts/latin/Lato-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Lato-Black.ttf -------------------------------------------------------------------------------- /fonts/latin/Lato-BlackItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Lato-BlackItalic.ttf -------------------------------------------------------------------------------- /fonts/latin/Lato-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Lato-Bold.ttf -------------------------------------------------------------------------------- /fonts/latin/Lato-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Lato-BoldItalic.ttf -------------------------------------------------------------------------------- /fonts/latin/Lato-Hairline.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Lato-Hairline.ttf -------------------------------------------------------------------------------- /fonts/latin/Lato-HairlineItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Lato-HairlineItalic.ttf -------------------------------------------------------------------------------- /fonts/latin/Lato-Heavy.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Lato-Heavy.ttf -------------------------------------------------------------------------------- /fonts/latin/Lato-HeavyItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Lato-HeavyItalic.ttf -------------------------------------------------------------------------------- /fonts/latin/Lato-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Lato-Italic.ttf -------------------------------------------------------------------------------- /fonts/latin/Lato-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Lato-Light.ttf -------------------------------------------------------------------------------- /fonts/latin/Lato-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Lato-LightItalic.ttf -------------------------------------------------------------------------------- /fonts/latin/Lato-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Lato-Medium.ttf -------------------------------------------------------------------------------- /fonts/latin/Lato-MediumItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Lato-MediumItalic.ttf -------------------------------------------------------------------------------- /fonts/latin/Lato-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Lato-Regular.ttf -------------------------------------------------------------------------------- /fonts/latin/Lato-Semibold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Lato-Semibold.ttf -------------------------------------------------------------------------------- /fonts/latin/Lato-SemiboldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Lato-SemiboldItalic.ttf -------------------------------------------------------------------------------- /fonts/latin/Lato-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Lato-Thin.ttf -------------------------------------------------------------------------------- /fonts/latin/Lato-ThinItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Lato-ThinItalic.ttf -------------------------------------------------------------------------------- /fonts/latin/OpenSans-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/OpenSans-Bold.ttf -------------------------------------------------------------------------------- /fonts/latin/OpenSans-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/OpenSans-BoldItalic.ttf -------------------------------------------------------------------------------- /fonts/latin/OpenSans-ExtraBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/OpenSans-ExtraBold.ttf -------------------------------------------------------------------------------- /fonts/latin/OpenSans-ExtraBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/OpenSans-ExtraBoldItalic.ttf -------------------------------------------------------------------------------- /fonts/latin/OpenSans-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/OpenSans-Italic.ttf -------------------------------------------------------------------------------- /fonts/latin/OpenSans-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/OpenSans-Light.ttf -------------------------------------------------------------------------------- /fonts/latin/OpenSans-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/OpenSans-LightItalic.ttf -------------------------------------------------------------------------------- /fonts/latin/OpenSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/OpenSans-Regular.ttf -------------------------------------------------------------------------------- /fonts/latin/OpenSans-Semibold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/OpenSans-Semibold.ttf -------------------------------------------------------------------------------- /fonts/latin/OpenSans-SemiboldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/OpenSans-SemiboldItalic.ttf -------------------------------------------------------------------------------- /fonts/latin/Pacifico.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Pacifico.ttf -------------------------------------------------------------------------------- /fonts/latin/Raleway-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Raleway-Black.ttf -------------------------------------------------------------------------------- /fonts/latin/Raleway-BlackItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Raleway-BlackItalic.ttf -------------------------------------------------------------------------------- /fonts/latin/Raleway-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Raleway-Bold.ttf -------------------------------------------------------------------------------- /fonts/latin/Raleway-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Raleway-BoldItalic.ttf -------------------------------------------------------------------------------- /fonts/latin/Raleway-ExtraBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Raleway-ExtraBold.ttf -------------------------------------------------------------------------------- /fonts/latin/Raleway-ExtraBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Raleway-ExtraBoldItalic.ttf -------------------------------------------------------------------------------- /fonts/latin/Raleway-ExtraLight.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Raleway-ExtraLight.ttf -------------------------------------------------------------------------------- /fonts/latin/Raleway-ExtraLightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Raleway-ExtraLightItalic.ttf -------------------------------------------------------------------------------- /fonts/latin/Raleway-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Raleway-Italic.ttf -------------------------------------------------------------------------------- /fonts/latin/Raleway-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Raleway-Light.ttf -------------------------------------------------------------------------------- /fonts/latin/Raleway-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Raleway-LightItalic.ttf -------------------------------------------------------------------------------- /fonts/latin/Raleway-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Raleway-Medium.ttf -------------------------------------------------------------------------------- /fonts/latin/Raleway-MediumItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Raleway-MediumItalic.ttf -------------------------------------------------------------------------------- /fonts/latin/Raleway-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Raleway-Regular.ttf -------------------------------------------------------------------------------- /fonts/latin/Raleway-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Raleway-SemiBold.ttf -------------------------------------------------------------------------------- /fonts/latin/Raleway-SemiBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Raleway-SemiBoldItalic.ttf -------------------------------------------------------------------------------- /fonts/latin/Raleway-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Raleway-Thin.ttf -------------------------------------------------------------------------------- /fonts/latin/Raleway-ThinItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Raleway-ThinItalic.ttf -------------------------------------------------------------------------------- /fonts/latin/Roboto-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Roboto-Black.ttf -------------------------------------------------------------------------------- /fonts/latin/Roboto-BlackItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Roboto-BlackItalic.ttf -------------------------------------------------------------------------------- /fonts/latin/Roboto-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Roboto-Bold.ttf -------------------------------------------------------------------------------- /fonts/latin/Roboto-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Roboto-BoldItalic.ttf -------------------------------------------------------------------------------- /fonts/latin/Roboto-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Roboto-Italic.ttf -------------------------------------------------------------------------------- /fonts/latin/Roboto-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Roboto-Light.ttf -------------------------------------------------------------------------------- /fonts/latin/Roboto-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Roboto-LightItalic.ttf -------------------------------------------------------------------------------- /fonts/latin/Roboto-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Roboto-Medium.ttf -------------------------------------------------------------------------------- /fonts/latin/Roboto-MediumItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Roboto-MediumItalic.ttf -------------------------------------------------------------------------------- /fonts/latin/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Roboto-Regular.ttf -------------------------------------------------------------------------------- /fonts/latin/Roboto-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Roboto-Thin.ttf -------------------------------------------------------------------------------- /fonts/latin/Roboto-ThinItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Roboto-ThinItalic.ttf -------------------------------------------------------------------------------- /fonts/latin/RobotoCondensed-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/RobotoCondensed-Bold.ttf -------------------------------------------------------------------------------- /fonts/latin/RobotoCondensed-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/RobotoCondensed-BoldItalic.ttf -------------------------------------------------------------------------------- /fonts/latin/RobotoCondensed-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/RobotoCondensed-Italic.ttf -------------------------------------------------------------------------------- /fonts/latin/RobotoCondensed-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/RobotoCondensed-Light.ttf -------------------------------------------------------------------------------- /fonts/latin/RobotoCondensed-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/RobotoCondensed-LightItalic.ttf -------------------------------------------------------------------------------- /fonts/latin/RobotoCondensed-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/RobotoCondensed-Regular.ttf -------------------------------------------------------------------------------- /fonts/latin/SEASRN__.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/SEASRN__.ttf -------------------------------------------------------------------------------- /fonts/latin/Sansation-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Sansation-Bold.ttf -------------------------------------------------------------------------------- /fonts/latin/Sansation-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Sansation-BoldItalic.ttf -------------------------------------------------------------------------------- /fonts/latin/Sansation-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Sansation-Italic.ttf -------------------------------------------------------------------------------- /fonts/latin/Sansation-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Sansation-Light.ttf -------------------------------------------------------------------------------- /fonts/latin/Sansation-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Sansation-LightItalic.ttf -------------------------------------------------------------------------------- /fonts/latin/Sansation-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Sansation-Regular.ttf -------------------------------------------------------------------------------- /fonts/latin/Walkway_Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Walkway_Black.ttf -------------------------------------------------------------------------------- /fonts/latin/Walkway_Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Walkway_Bold.ttf -------------------------------------------------------------------------------- /fonts/latin/Walkway_Oblique.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Walkway_Oblique.ttf -------------------------------------------------------------------------------- /fonts/latin/Walkway_Oblique_Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Walkway_Oblique_Black.ttf -------------------------------------------------------------------------------- /fonts/latin/Walkway_Oblique_Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Walkway_Oblique_Bold.ttf -------------------------------------------------------------------------------- /fonts/latin/Walkway_Oblique_SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Walkway_Oblique_SemiBold.ttf -------------------------------------------------------------------------------- /fonts/latin/Walkway_Oblique_UltraBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Walkway_Oblique_UltraBold.ttf -------------------------------------------------------------------------------- /fonts/latin/Walkway_SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Walkway_SemiBold.ttf -------------------------------------------------------------------------------- /fonts/latin/Walkway_UltraBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leanderloew/Pytorch-OCR-Fully-Convolutional/229cbad96fb5ba23528aee865eb65d739bd1bdc0/fonts/latin/Walkway_UltraBold.ttf -------------------------------------------------------------------------------- /fully_conv_model.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | import torchvision.models as models 4 | 5 | import numpy as np 6 | 7 | from torch.nn.modules.normalization import LayerNorm,LocalResponseNorm 8 | from torch.nn import BatchNorm2d,MaxPool2d 9 | 10 | #This tells us which dimension we want to layernomalize over, by default, if we give it one value it will normalize over the 11 | #channels dimension, it has to be a fixed dimension, so the only alternative is if we would use the the height to normalize, we can try tht by exchanging the last two dimnsions like this (0,1,3,2) 12 | #After experiements, it makes by far the most sense that we normalize over the channel dimension (this was also their feedback) 13 | chan_norm=(0,3,2,1) 14 | 15 | 16 | class depthwise_separable_conv_bn(nn.Module): 17 | def __init__(self, nin, nout,ks=3,p=1): 18 | super(depthwise_separable_conv_bn, self).__init__() 19 | self.depthwise = nn.Conv2d(nin, nin, kernel_size=ks, padding=p, groups=nin,bias=False) 20 | self.bnorm = BatchNorm2d(nin) 21 | self.pointwise = nn.Conv2d(nin, nout, kernel_size=1) 22 | def forward(self, x): 23 | out = self.depthwise(x) 24 | #We do bnorm inbetween the seperable and the pointwise 25 | out = self.bnorm(out) 26 | out = self.pointwise(out) 27 | return out 28 | 29 | 30 | 31 | class attention_block(nn.Module): 32 | def __init__(self, input_channels=8,output_channels=8,input_height=32): 33 | super(attention_block, self).__init__() 34 | 35 | reduce_dim=int(output_channels/2) 36 | self.reduce_dim=reduce_dim 37 | 38 | #When we increase the dimension in such a layer we have to "increase" the input for the residual connection 39 | if input_channels==output_channels: 40 | self.residual=False 41 | else: 42 | self.reduce_residual=nn.Conv2d(input_channels, output_channels, kernel_size=1) 43 | self.residual=True 44 | 45 | self.reduce1 = nn.Conv2d(input_channels, reduce_dim, kernel_size=1) 46 | self.conv1 = depthwise_separable_conv_bn(reduce_dim,reduce_dim) 47 | 48 | self.el=nn.ELU() 49 | self.reduce2 = nn.Conv2d(reduce_dim, reduce_dim*3, kernel_size=1) 50 | self.tanh1=nn.Tanh() 51 | self.tanh2=nn.Tanh() 52 | self.sig1=nn.Sigmoid() 53 | 54 | self.reduce3 = nn.Conv2d(reduce_dim,output_channels, kernel_size=1) 55 | self.conv2=depthwise_separable_conv_bn(output_channels,output_channels) 56 | self.el2=nn.ELU() 57 | 58 | w=reduce_dim 59 | #elementwise affine true means we learn if we want to normalize, false we always normalize 60 | self.ln_1=LayerNorm(w,elementwise_affine=True) 61 | self.ln_2=LayerNorm(w,elementwise_affine=False) 62 | self.ln_3=LayerNorm(w,elementwise_affine=False) 63 | self.ln_4=LayerNorm(w,elementwise_affine=False) 64 | self.ln_5=LayerNorm(w,elementwise_affine=False) 65 | 66 | def forward(self, inpu): 67 | 68 | x=self.reduce1(inpu) 69 | x=self.conv1(x) 70 | x=self.el(x) 71 | 72 | x=self.ln_1(x.permute(chan_norm)).permute(chan_norm) 73 | 74 | x=self.reduce2(x) 75 | k,q,v=torch.split(x,self.reduce_dim,dim=1) 76 | 77 | k,v,q=self.tanh1(k),self.tanh2(v),self.sig1(q) 78 | 79 | k,v,q=self.ln_2(k.permute(chan_norm)).permute(chan_norm),self.ln_3(v.permute(chan_norm)).permute(chan_norm),self.ln_4(q.permute(chan_norm)).permute(chan_norm) 80 | 81 | 82 | atn=(k-v)*q 83 | 84 | x=self.ln_5(atn.permute(chan_norm)).permute(chan_norm) 85 | x=self.reduce3(x) 86 | x=self.conv2(x) 87 | x=self.el2(x) 88 | 89 | #If we want to use it to change the dimension 90 | if self.residual ==True: 91 | inpu=self.reduce_residual(inpu) 92 | 93 | 94 | x=x+inpu 95 | return x 96 | 97 | #to do a 1x1 at the beginning also we want to change later too. 98 | 99 | class cnn_attention_ocr(nn.Module): 100 | def __init__(self, n_layers, nclasses=5,model_dim=128,input_dim=3): 101 | super(cnn_attention_ocr, self).__init__() 102 | 103 | self.classes=nclasses+1 104 | self.input_dim=input_dim 105 | self.n_layers=n_layers 106 | self.atn_blocks_0=attention_block(19,model_dim) 107 | #what we can do then is whenever we reduce size we are allowed to increase dimension 108 | self.atn_blocks_1=attention_block(model_dim,model_dim) 109 | self.atn_blocks_2=attention_block(model_dim,model_dim) 110 | self.mp1=MaxPool2d((2,2)) 111 | self.atn_blocks_3=attention_block(model_dim,model_dim*4,input_height=16) 112 | self.mp2=MaxPool2d((2,1)) 113 | 114 | #For now we do 8 layers only 115 | if n_layers>4: 116 | 117 | self.atn_blocks_4=attention_block(model_dim*4,model_dim*8,input_height=16) 118 | 119 | atn_blocks = nn.ModuleList([attention_block(model_dim*8,model_dim*8,input_height=8) for i in range(n_layers-5)]) 120 | self.layers=nn.Sequential(*atn_blocks) 121 | #For now we do 8 layers only 122 | if n_layers>8: 123 | 124 | atn_blocks_16 = nn.ModuleList([attention_block(model_dim*8,model_dim*8,input_height=8) for i in range(n_layers-8)]) 125 | self.layers_16=nn.Sequential(*atn_blocks) 126 | 127 | self.conv1=depthwise_separable_conv_bn(16,16,13,6) 128 | 129 | self.reduce1 = nn.Conv2d(3, 16, kernel_size=1) 130 | self.reduce2 = nn.Conv2d(model_dim*8, self.classes, kernel_size=1) 131 | 132 | self.drop1=nn.Dropout2d(0.75) 133 | self.drop2=nn.Dropout2d(0.5) 134 | 135 | self.ln_3=LayerNorm(self.classes) 136 | self.ln_1=LayerNorm(3).cuda() 137 | self.ln_4=LayerNorm(16).cuda() 138 | self.soft_norm=nn.Softmax2d() 139 | 140 | 141 | 142 | def forward(self, x): 143 | 144 | 145 | #It makes the most sense to either normalize over the channel or the width dim, cause those are the ones that are fixed 146 | #But does normalizing voer with really make sense ? I think over the channels makes more sense 147 | #x=self.ln_1(x.permute(chan_norm)).permute(chan_norm) 148 | x_res=x 149 | x=self.reduce1(x) 150 | x=self.soft_norm(x) 151 | x=self.conv1(x) 152 | x=self.drop1(x) 153 | x=self.ln_4(x.permute(chan_norm)).permute(chan_norm) 154 | x=torch.cat([x,x_res],dim=1) 155 | 156 | x=self.atn_blocks_0(x) 157 | x=self.atn_blocks_1(x) 158 | x=self.atn_blocks_2(x) 159 | x=self.mp1(x) 160 | x=self.atn_blocks_3(x) 161 | 162 | if self.n_layers>4: 163 | x=self.atn_blocks_4(x) 164 | x=self.mp2(x) 165 | x=self.layers(x) 166 | if self.n_layers>8: 167 | x=self.layers_16(x) 168 | 169 | x=self.drop2(x) 170 | x=self.reduce2(x) 171 | x=torch.mean(x,dim=2) 172 | #x=LayerNorm((self.classes,input_width)).cuda()(x) 173 | x=self.ln_3(x.permute((0,2,1))).permute((0,2,1)) 174 | x=nn.LogSoftmax(dim=1)(x) 175 | 176 | return x -------------------------------------------------------------------------------- /inference_demo.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 2, 6 | "metadata": { 7 | "collapsed": true 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "### This is supposed to be a demo for how to do inference using the model and a new line of text\n" 12 | ] 13 | }, 14 | { 15 | "cell_type": "code", 16 | "execution_count": 100, 17 | "metadata": {}, 18 | "outputs": [ 19 | { 20 | "data": { 21 | "image/png": "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\nefk0F6rMY6MSEtXHue5LNAk2MTFBKBRSRjOxym3qKCT2c+wIY2JiQukUYq+TY9EDgQBer1cJBpAJ\nBAIMDw8zPDys1FP4tAymLpWPfb8cbhzLxYsXeffddykrK2Pnzp2K5T1TPsn+f6fTSVZWVtzCNvke\nl8uFEIL8/PzbsoLliebJyckZrwmFQoyMjODxeJSRZSydnZ0cPHgQi8XC+vXr2bdvX8JtBBYShy6E\nUKL4biddU1lRH7per1cWx1y+fJlz584xPDw87bq2tjaOHDlCe3s7NpttWkYbjUYlnOgrX/kKNpuN\nV155hY8++gi73c7evXvx+Xx873vf4+LFi1y8eBGTyYTJZMJoNGIymThz5gyvv/66El4lhOCXv/wl\n77zzDs8++yx/8zd/k1BxL6Tw5mLLli381V/9Fd3d3bz88stxq9oCgQB+v3/aQhur1cqTTz7J17/+\ndZ555hlWr14dd17umOQ4Y/i0c4j9MxqNJCUlcerUKQ4dOqRMGMv4fD5eeeUV2tvbefbZZ1m/fj1G\no5GGhga8Xi/f/e53OX/+fFw6NRoNb7/9Ni+//DJdXV2KPPPJK61Wy5tvvslbb73Fc889xze/+c15\n5WUwGMTv98eldykJh8PKJJbL5VrQRJY8mpyq2IaHh7l58yY6nW6ae2Uh7hw57VMVutlsprKykomJ\nCfbv309/f3/ccwOBAB9++CGHDh0iGAxis9mUNQMLwe/309PTQ0dHB36/n/z8fEpLS+fcd8hkMjEy\nMsLvf/97rly5Mm3B24cffsi3vvUt+vr6eOihh25rc7VAIKC4d2dDVvyBQEBZHS2E4MSJExw5coRt\n27bxxS9+kYqKiln3hJkvkiTR19dHU1MTg4ODC0pTIu6KhUWrV69mzZo1uN1ujh8/Tl5eHlarFa/X\ny9jYGB0dHaSlpSmhioCy1F+2kOSVbhBRch988AEbN27kwQcfZPPmzQwODvLOO+9w6dIl5RpAiXdt\nb29ndHSUiYkJINIYPvnkE5qamnjiiSfQarVcvXpVqRChUAiLxYLNZiM5OTnhMvz5EGvNJicnU19f\nz+nTpzl79ixXr15lx44dQKRRWiwWpZLJ1p5er2fTpk1xz4y16uRIm9hVhLJfOVbBJyUlYTKZFGV1\n6dIlnE6nMqro7OykpaWFgoIC/vRP/1SJaKmvr2d4eJgjR45w4cIFLBaL8jx5tOH1ehfkH5R9lEeO\n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\n", 22 | "text/plain": [ 23 | "
" 24 | ] 25 | }, 26 | "metadata": {}, 27 | "output_type": "display_data" 28 | }, 29 | { 30 | "data": { 31 | "text/plain": [ 32 | "
" 33 | ] 34 | }, 35 | "metadata": {}, 36 | "output_type": "display_data" 37 | }, 38 | { 39 | "name": "stdout", 40 | "output_type": "stream", 41 | "text": [ 42 | "63e2^\\mjbc zp ; j^rd^/p h2 !\n" 43 | ] 44 | } 45 | ], 46 | "source": [ 47 | "#General Imports\n", 48 | "import torch\n", 49 | "import torch.nn as nn\n", 50 | "import torch.optim as optim\n", 51 | "import numpy as np\n", 52 | "from torch.utils.data import DataLoader\n", 53 | "import random\n", 54 | "import matplotlib.pyplot as plt\n", 55 | "import ctcdecode\n", 56 | "\n", 57 | "import matplotlib.pyplot as plt\n", 58 | "from tensorboardX import SummaryWriter\n", 59 | "\n", 60 | "#Load fake, non handwritten generator \n", 61 | "from fake_texts.pytorch_dataset_fake_2 import Dataset\n", 62 | "\n", 63 | "#Import the loss from baidu \n", 64 | "from torch_baidu_ctc import CTCLoss\n", 65 | "\n", 66 | "#Import the model \n", 67 | "from fully_conv_model import cnn_attention_ocr\n", 68 | "\n", 69 | "#Helper to count params\n", 70 | "def count_parameters(model):\n", 71 | " return sum(p.numel() for p in model.parameters() if p.requires_grad)\n", 72 | "from evaluation import wer_eval,preds_to_integer,show,my_collate,AverageMeter\n", 73 | "\n", 74 | "ds=Dataset()\n", 75 | "\n", 76 | "batch_size=1\n", 77 | "width=-1\n", 78 | "alignment=1\n", 79 | "\n", 80 | "ds=Dataset()\n", 81 | "\n", 82 | "elem=ds[0]\n", 83 | "\n", 84 | "plt.imshow(elem[0][0,:,:,:])\n", 85 | "plt.figure(figsize=(10,10))\n", 86 | "plt.show()\n", 87 | "print(\"\".join([ds.decode_dict[x] for x in elem[1][0:elem[3]]]))" 88 | ] 89 | }, 90 | { 91 | "cell_type": "code", 92 | "execution_count": 2, 93 | "metadata": {}, 94 | "outputs": [ 95 | { 96 | "data": { 97 | "text/plain": [ 98 | "'plt.figure(figsize=(10,10))\\n\\nelem=ds[0]\\nplt.imshow(elem[0][0,:,:,:])\\nplt.show()\\nprint(\"\".join([ds.decode_dict[j] for j in elem[1]] ))\\nds.decode_dict'" 99 | ] 100 | }, 101 | "execution_count": 2, 102 | "metadata": {}, 103 | "output_type": "execute_result" 104 | } 105 | ], 106 | "source": [ 107 | "'''plt.figure(figsize=(10,10))\n", 108 | "\n", 109 | "elem=ds[0]\n", 110 | "plt.imshow(elem[0][0,:,:,:])\n", 111 | "plt.show()\n", 112 | "print(\"\".join([ds.decode_dict[j] for j in elem[1]] ))\n", 113 | "ds.decode_dict'''" 114 | ] 115 | }, 116 | { 117 | "cell_type": "code", 118 | "execution_count": 3, 119 | "metadata": { 120 | "collapsed": true 121 | }, 122 | "outputs": [], 123 | "source": [ 124 | "###Set up model. \n", 125 | "cnn=cnn_attention_ocr(model_dim=128,nclasses=67,n_layers=8)\n", 126 | "cnn=cnn.eval().cpu()#.cuda()\n", 127 | "cnn.load_state_dict(torch.load(\"400ksteps_augment_new_gen_e15.pt\"))\n", 128 | "#count_parameters(cnn)" 129 | ] 130 | }, 131 | { 132 | "cell_type": "code", 133 | "execution_count": 2, 134 | "metadata": { 135 | "collapsed": true 136 | }, 137 | "outputs": [], 138 | "source": [ 139 | "from PIL import Image\n", 140 | "import numpy as np\n", 141 | "from glob import glob" 142 | ] 143 | }, 144 | { 145 | "cell_type": "code", 146 | "execution_count": 5, 147 | "metadata": { 148 | "collapsed": true 149 | }, 150 | "outputs": [], 151 | "source": [ 152 | "input_folder=\"/home/leander/AI/data/test_seg/\"" 153 | ] 154 | }, 155 | { 156 | "cell_type": "code", 157 | "execution_count": 6, 158 | "metadata": { 159 | "collapsed": true 160 | }, 161 | "outputs": [], 162 | "source": [ 163 | "files=glob(input_folder+\"*.png\")" 164 | ] 165 | }, 166 | { 167 | "cell_type": "code", 168 | "execution_count": 7, 169 | "metadata": { 170 | "collapsed": true 171 | }, 172 | "outputs": [], 173 | "source": [ 174 | "from skimage.color import gray2rgb\n", 175 | "from skimage.transform import resize\n", 176 | "import cv2\n" 177 | ] 178 | }, 179 | { 180 | "cell_type": "code", 181 | "execution_count": 7, 182 | "metadata": { 183 | "collapsed": true 184 | }, 185 | "outputs": [], 186 | "source": [ 187 | "imglist=[]\n", 188 | "for j in files:\n", 189 | " img = Image.open(j).convert('L')\n", 190 | " img=cv2.cvtColor(np.array(img),cv2.COLOR_GRAY2RGB)\n", 191 | " #img=np.array(img)\n", 192 | " #img=img.astype(float)\n", 193 | " img=img/255\n", 194 | " resize_shape=(32,int(32*img.shape[1]/img.shape[0]))\n", 195 | " img = resize(img,resize_shape,mode=\"constant\")\n", 196 | " img=np.expand_dims(img,0)\n", 197 | " img=torch.tensor(img).cuda().float().permute((0,3,1,2))\n", 198 | " \n", 199 | " imglist.append(img)" 200 | ] 201 | }, 202 | { 203 | "cell_type": "code", 204 | "execution_count": 8, 205 | "metadata": { 206 | "collapsed": true 207 | }, 208 | "outputs": [], 209 | "source": [ 210 | "img = Image.open(\"/home/leander/Pictures/test.png\").convert('L')\n", 211 | "img=cv2.cvtColor(np.array(img),cv2.COLOR_GRAY2RGB)\n", 212 | "#img=np.array(img)\n", 213 | "#img=img.astype(float)\n", 214 | "img=img/255\n", 215 | "resize_shape=(32,int(32*img.shape[1]/img.shape[0]))\n", 216 | "img = resize(img,resize_shape,mode=\"constant\")\n", 217 | "img=np.expand_dims(img,0)\n", 218 | "img=torch.tensor(img).cuda().float().permute((0,3,1,2))\n", 219 | "\n", 220 | "#imglist.append(img)" 221 | ] 222 | }, 223 | { 224 | "cell_type": "code", 225 | "execution_count": 3, 226 | "metadata": { 227 | "collapsed": true 228 | }, 229 | "outputs": [], 230 | "source": [ 231 | "\n", 232 | "#\"\".join(" 233 | ] 234 | }, 235 | { 236 | "cell_type": "code", 237 | "execution_count": 9, 238 | "metadata": {}, 239 | "outputs": [ 240 | { 241 | "data": { 242 | "image/png": "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\n3AAAAABJRU5ErkJggg==\n", 243 | "text/plain": [ 244 | "
" 245 | ] 246 | }, 247 | "metadata": {}, 248 | "output_type": "display_data" 249 | } 250 | ], 251 | "source": [] 252 | }, 253 | { 254 | "cell_type": "code", 255 | "execution_count": 10, 256 | "metadata": {}, 257 | "outputs": [ 258 | { 259 | "data": { 260 | "text/plain": [ 261 | "67" 262 | ] 263 | }, 264 | "execution_count": 10, 265 | "metadata": {}, 266 | "output_type": "execute_result" 267 | } 268 | ], 269 | "source": [ 270 | "len(list(ds.decode_dict.values()))" 271 | ] 272 | }, 273 | { 274 | "cell_type": "code", 275 | "execution_count": 11, 276 | "metadata": { 277 | "collapsed": true 278 | }, 279 | "outputs": [], 280 | "source": [ 281 | "import sys\n", 282 | "sys.path.append(\"/home/leander/AI/repos/CTCDecoder/src\")" 283 | ] 284 | }, 285 | { 286 | "cell_type": "code", 287 | "execution_count": 12, 288 | "metadata": { 289 | "collapsed": true 290 | }, 291 | "outputs": [], 292 | "source": [ 293 | "from BestPath import ctcBestPath" 294 | ] 295 | }, 296 | { 297 | "cell_type": "code", 298 | "execution_count": 13, 299 | "metadata": { 300 | "collapsed": true 301 | }, 302 | "outputs": [], 303 | "source": [ 304 | "from BeamSearch import ctcBeamSearch\n", 305 | "example_image=img" 306 | ] 307 | }, 308 | { 309 | "cell_type": "code", 310 | "execution_count": 14, 311 | "metadata": {}, 312 | "outputs": [ 313 | { 314 | "data": { 315 | "image/png": "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\noU+gWuuflVIO0p8fjIgfWRw/HBF/GhG/lLnhec1aj75UaS6zoft6QruOhPMeDU066k9F0eObolUu\nFcJQ3U/p2zcqZ6ss3OYtrfNoKM9seGBOU5rVHq1z6349fhda97qQ67CcPfppZFSznPXfMxpOtWds\nZce5yj+rz2f9b1Yjr3Tqyj9E9tnm8vOxGf6WRpjL50xWW6/CRav5WdVJtj/0+JiYw3fVHOF0lQ+l\nnnlXkfUDlfVVxGn4m9fPbCj20bWc79ca5z2++OaYh3v2Ya16H/X92BP2GeH9h9oLZX0UZp+7Zwxi\nuZRfnFG/WWofNup/Uz3rqC/IUT9NWV9PWX+ZPf5LFaN7oVY5+e+4P+A8lP8e/N0zv43ucVvXje7l\nevxJKv+YKg39+WD+vCfDkOe8B1T7CExjP0PZ8aPSevbs2XthiHVO27Vr1/JYveurtOz7FfPqq68u\nj19++eVJ2s6dO5fH73jHOyZp+DzoOyhi2pbXXnvtJG303bV13iijPoH21FqPRUQs/r+7dWIp5SOl\nlEdKKY+89tprg7czxhhjjDHGGGOMMRfCpjuGrrV+otZ6f631fvYEbowxxhhjjDHGGGO2htEQ8cdL\nKXtrrcdKKXsj4kTmolLK0jSrx2QzK+tSZqFzmRBnTWDVvdEUUF2nTCzVdWwq1wrzyKaFylSSTRRb\n142a9PaEeW1dp8w0e8xosya9o2aoCvU82ZCPylxdmXQrlJwlO356pACKOWReo2GFR80vlaREzW/q\nfkomq8Y50iNjbd1b0RNeNythyraBqsseOZiaR7Km+modUfln5/zRUPJKBjUqRUL4eTBkbk+fzc7l\nmD+f2yN9aj3fXFIapMfcG/cOPM+fPn16eczrNYYgxpC5Z86cmZyHsgQGnwFN4Bkl53/xxRcnafgM\n3HZoPr9nz57lMbcNlkutfVkZV4Te06i+r6Qh2b1jtlxqrlD5KMkPjzslA2+Fj+8J9T6H/JTJlkXN\np9m1r0fyh9cpuSbPFVn55kb3b+XfuleEloMhPHax32xGf1DvSVkpZI9cKtunTpyYvgrj/Hrbbbct\nj1kadMMNNyyPecy98sory2OcxyMibryxHZNJrZlZdxujEmAlfcSQ6tzGWEdXXXVV+t7ZvZcq8xtv\nvNFMw7JcffXVkzSsP25/vE6trYq53C20GLUE+mxEPLQ4figiPjOYjzHGGGOMMcYYY4zZAjIh4n8v\n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\nl1SI+FLKFbH2Aeh3a61/tPjz8VLK3lrrsVLK3og40c5h3TyH0hgOf4ygRpK1jXgdhzRFDSHr2/E3\nagNZ858NOc1pGI6YtZr4DKz/xLKoMHVKc4l6Y/YHgRpJ1i/ivVkTiXpJ9J/A5VJtzv40Tp48uTxW\nunFsE84DtZrs7ybr40ilKd8EKmx2NrQz+8XAtkMfMxFTDS7XA9YRW+rhuezPR4Vlxvu9/PLLy2PW\n4+Iz8PNgHqyfVvpZrAfWz+OY5zZAfxrc9zEf1Wex/3Gd8PMh6EMFfZFETJ+V5zrsw0qLzuMC74c6\naK4T/GjPOmgcgyo0ttKK83XoK4nnPiwLlpnzUP5BlA4ew7fyPIXl4vGJY41DgGJ74bweMe1vqj8r\nfw1qjWnlsVEa1tErr7wyScM64zkG2xn7pQrLzvONqmf8zeMA5ym8N1/H/RufR4V9RTgP9FfGvsvw\n3ipsulqvuX9nfa8wLd95yjcW/6Md+lHjfRGWmddTnH/QPxCfq/oK9jeeS7EfcTti/fX4S1C+V7J+\nUrL+e7gNsj7vetp/NBy68jmiwoe38ld7Jl6nsv5venxkjLbrHKi6VL7fWuetl0/r3Ky/mJ51RF2n\n5re53v3Ow+NFtbFCjadsmXG/GzF9H7r99tuXx7yn5Xk+e2+1Pxj1lYTrK49JnIfV3Kr6HvrHZN9y\n6Guwx7ct7r14/67KiXniebzHUO8VuP9QfY/bWI0flabmipF5KxMdrETEb0fE47XW34Ckz0bEQ4vj\nhyLiM913N8YYY4wxxhhjjDFbQsYS6Icj4mci4m9LKV9Z/O2XI+JjEfHpUsqHI+K5iPjpzSmiMcYY\nY4wxxhhjjLlQNvwIVGv9PxHRsh370d4bnjdXUiHrlGmUktKwKRbmw3IwNGdWZu9s9oymX2jedejQ\nocl5aJbOZUYTO07D+6nQsfysSg6GJnaqLjFPNtVHU3A290bzbCWLY7NNFTY9G2a6FYKRr1PholVo\nTVVHDJoQslm6ug6fAc0jlfyD88PrWBKBZVHPwyaqKFlRYYW5fTBcOYY47pFIYZ2wlAL7GOeJYdTZ\npBPlJizdwHy4HtDEF8c893XMg+cUhKVIaL7Kod5xvmG5ET4ry/WwLBxaHqV2t9566/JYSUU5DduO\nJR5KDoZwv8F65zLjb2w77pcjIYAjpm3CYwSfR5k985jEsvGYbJljzxUqVJULUXPfTTfdNElD03aW\n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\nxhhjVoD/D183UEztBG7tAAAAAElFTkSuQmCC\n", 316 | "text/plain": [ 317 | "" 318 | ] 319 | }, 320 | "metadata": {}, 321 | "output_type": "display_data" 322 | } 323 | ], 324 | "source": [ 325 | "#example_image=example_image[:,:,:,0:750]\n", 326 | "show(example_image.detach().cpu()[0,:,:,:])\n", 327 | "plt.show()" 328 | ] 329 | }, 330 | { 331 | "cell_type": "code", 332 | "execution_count": 15, 333 | "metadata": {}, 334 | "outputs": [ 335 | { 336 | "data": { 337 | "text/plain": [ 338 | "'fotq~ma9l }ete 8 ( ) x5 ( ) 2,5 ( )'" 339 | ] 340 | }, 341 | "execution_count": 15, 342 | "metadata": {}, 343 | "output_type": "execute_result" 344 | } 345 | ], 346 | "source": [ 347 | "log_probs = cnn(example_image.cpu()).permute((2,0,1))[:,0,:]\n", 348 | "#preds_to_integer(log_probs)\n", 349 | "\"\".join([ds.decode_dict[j] for j in preds_to_integer(log_probs)])" 350 | ] 351 | }, 352 | { 353 | "cell_type": "code", 354 | "execution_count": 16, 355 | "metadata": { 356 | "collapsed": true 357 | }, 358 | "outputs": [], 359 | "source": [ 360 | "mat = np.array([[0.4, 0, 0.6], [0.4, 0, 0.6]])" 361 | ] 362 | }, 363 | { 364 | "cell_type": "code", 365 | "execution_count": 17, 366 | "metadata": { 367 | "collapsed": true 368 | }, 369 | "outputs": [], 370 | "source": [ 371 | "log_probs=log_probs[:,0:67]" 372 | ] 373 | }, 374 | { 375 | "cell_type": "code", 376 | "execution_count": 18, 377 | "metadata": { 378 | "collapsed": true 379 | }, 380 | "outputs": [], 381 | "source": [ 382 | "log_probs=torch.nn.Softmax(dim=1)(log_probs)" 383 | ] 384 | }, 385 | { 386 | "cell_type": "code", 387 | "execution_count": 19, 388 | "metadata": { 389 | "collapsed": true 390 | }, 391 | "outputs": [], 392 | "source": [ 393 | "log_probs=log_probs.cpu().detach().numpy()" 394 | ] 395 | }, 396 | { 397 | "cell_type": "code", 398 | "execution_count": 20, 399 | "metadata": {}, 400 | "outputs": [ 401 | { 402 | "data": { 403 | "text/plain": [ 404 | "'abcdefghijklmnopqrstuvwxyz0123456789!\"#$%&\\'()*+,-./:;?@[\\\\]^_`{|}~ '" 405 | ] 406 | }, 407 | "execution_count": 20, 408 | "metadata": {}, 409 | "output_type": "execute_result" 410 | } 411 | ], 412 | "source": [ 413 | "\"\".join(list(ds.decode_dict.values())[:-1])" 414 | ] 415 | }, 416 | { 417 | "cell_type": "code", 418 | "execution_count": 21, 419 | "metadata": {}, 420 | "outputs": [ 421 | { 422 | "data": { 423 | "text/plain": [ 424 | "'abcdefghijklmnopqrstuvwxyz0123456789!\"#$%&\\'()*+,-./:;?@[\\\\]^_`{|}~ '" 425 | ] 426 | }, 427 | "execution_count": 21, 428 | "metadata": {}, 429 | "output_type": "execute_result" 430 | } 431 | ], 432 | "source": [ 433 | "\"\".join(list(ds.dictionary.keys()))" 434 | ] 435 | }, 436 | { 437 | "cell_type": "code", 438 | "execution_count": 55, 439 | "metadata": { 440 | "collapsed": true 441 | }, 442 | "outputs": [], 443 | "source": [ 444 | "mat=log_probs" 445 | ] 446 | }, 447 | { 448 | "cell_type": "code", 449 | "execution_count": 56, 450 | "metadata": { 451 | "collapsed": true 452 | }, 453 | "outputs": [], 454 | "source": [ 455 | "maxT, maxC = mat.shape\n", 456 | "label = ''\n", 457 | "classes = \"\".join([\" \"]+list(ds.dictionary.keys()))" 458 | ] 459 | }, 460 | { 461 | "cell_type": "code", 462 | "execution_count": 63, 463 | "metadata": {}, 464 | "outputs": [ 465 | { 466 | "data": { 467 | "text/plain": [ 468 | "' abcdefghijklmnopqrstuvwxyz0123456789!\"#$%&\\'()*+,-./:;?@[\\\\]^_`{|}~ '" 469 | ] 470 | }, 471 | "execution_count": 63, 472 | "metadata": {}, 473 | "output_type": "execute_result" 474 | } 475 | ], 476 | "source": [ 477 | "\"\".join([\" \"]+list(ds.dictionary.keys())+[\" \"])" 478 | ] 479 | }, 480 | { 481 | "cell_type": "code", 482 | "execution_count": 64, 483 | "metadata": {}, 484 | "outputs": [ 485 | { 486 | "ename": "IndexError", 487 | "evalue": "index 68 is out of bounds for axis 1 with size 67", 488 | "output_type": "error", 489 | "traceback": [ 490 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 491 | "\u001b[0;31mIndexError\u001b[0m Traceback (most recent call last)", 492 | "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mctcBeamSearch\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlog_probs\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\"\"\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mjoin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\" \"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m+\u001b[0m\u001b[0mlist\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mds\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdictionary\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mkeys\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m+\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\" \"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", 493 | "\u001b[0;32m~/AI/repos/CTCDecoder/src/BeamSearch.py\u001b[0m in \u001b[0;36mctcBeamSearch\u001b[0;34m(mat, classes, lm)\u001b[0m\n\u001b[1;32m 82\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 83\u001b[0m \u001b[0;31m# probability of paths ending with a blank\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 84\u001b[0;31m \u001b[0mprBlank\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mlast\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mentries\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mlabeling\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mprTotal\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mmat\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mt\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mblankIdx\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 85\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 86\u001b[0m \u001b[0;31m# add beam at current time-step if needed\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 494 | "\u001b[0;31mIndexError\u001b[0m: index 68 is out of bounds for axis 1 with size 67" 495 | ] 496 | } 497 | ], 498 | "source": [ 499 | "ctcBeamSearch(log_probs,\"\".join([\" \"]+list(ds.dictionary.keys())+[\" \"]),None)" 500 | ] 501 | }, 502 | { 503 | "cell_type": "code", 504 | "execution_count": 65, 505 | "metadata": {}, 506 | "outputs": [ 507 | { 508 | "data": { 509 | "text/plain": [ 510 | "(161, 67)" 511 | ] 512 | }, 513 | "execution_count": 65, 514 | "metadata": {}, 515 | "output_type": "execute_result" 516 | } 517 | ], 518 | "source": [ 519 | "log_probs.shape" 520 | ] 521 | }, 522 | { 523 | "cell_type": "code", 524 | "execution_count": 58, 525 | "metadata": { 526 | "collapsed": true 527 | }, 528 | "outputs": [], 529 | "source": [ 530 | "blankIdx = 0\n", 531 | "lastMaxIdx = maxC" 532 | ] 533 | }, 534 | { 535 | "cell_type": "code", 536 | "execution_count": 59, 537 | "metadata": {}, 538 | "outputs": [ 539 | { 540 | "ename": "IndexError", 541 | "evalue": "string index out of range", 542 | "output_type": "error", 543 | "traceback": [ 544 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 545 | "\u001b[0;31mIndexError\u001b[0m Traceback (most recent call last)", 546 | "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 7\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mmaxIdx\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mlastMaxIdx\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mmaxIdx\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mblankIdx\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 8\u001b[0;31m \u001b[0mlabel\u001b[0m \u001b[0;34m+=\u001b[0m \u001b[0mclasses\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mmaxIdx\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 9\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 10\u001b[0m \u001b[0mlastMaxIdx\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmaxIdx\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 547 | "\u001b[0;31mIndexError\u001b[0m: string index out of range" 548 | ] 549 | } 550 | ], 551 | "source": [ 552 | "#'len(classes)\n", 553 | "lastMaxIdx = maxC # init with invalid label\n", 554 | "\n", 555 | "for t in range(maxT):\n", 556 | " maxIdx = np.argmax(mat[t, :])\n", 557 | "\n", 558 | " if maxIdx != lastMaxIdx and maxIdx != blankIdx:\n", 559 | " label += classes[maxIdx]\n", 560 | "\n", 561 | " lastMaxIdx = maxIdx" 562 | ] 563 | }, 564 | { 565 | "cell_type": "code", 566 | "execution_count": 54, 567 | "metadata": {}, 568 | "outputs": [ 569 | { 570 | "data": { 571 | "text/plain": [ 572 | "'fotq~ma9l }ete 8 ( ) x5 ( ) 2,5 ( )'" 573 | ] 574 | }, 575 | "execution_count": 54, 576 | "metadata": {}, 577 | "output_type": "execute_result" 578 | } 579 | ], 580 | "source": [ 581 | "label" 582 | ] 583 | }, 584 | { 585 | "cell_type": "code", 586 | "execution_count": 33, 587 | "metadata": {}, 588 | "outputs": [ 589 | { 590 | "data": { 591 | "text/plain": [ 592 | "' '" 593 | ] 594 | }, 595 | "execution_count": 33, 596 | "metadata": {}, 597 | "output_type": "execute_result" 598 | } 599 | ], 600 | "source": [ 601 | "classes[66]" 602 | ] 603 | }, 604 | { 605 | "cell_type": "code", 606 | "execution_count": 32, 607 | "metadata": {}, 608 | "outputs": [ 609 | { 610 | "data": { 611 | "text/plain": [ 612 | "'fotq~ma9l }ete 8 ( ) x5 ( ) 2,5 ( )'" 613 | ] 614 | }, 615 | "execution_count": 32, 616 | "metadata": {}, 617 | "output_type": "execute_result" 618 | } 619 | ], 620 | "source": [ 621 | "label" 622 | ] 623 | }, 624 | { 625 | "cell_type": "code", 626 | "execution_count": 36, 627 | "metadata": {}, 628 | "outputs": [ 629 | { 630 | "data": { 631 | "text/plain": [ 632 | "'gpur nb!mO~fufO9O)O*Oy6O)O*O3-6O)O*'" 633 | ] 634 | }, 635 | "execution_count": 36, 636 | "metadata": {}, 637 | "output_type": "execute_result" 638 | } 639 | ], 640 | "source": [ 641 | "ctcBestPath(log_probs,\"\".join(list(ds.decode_dict.values())))" 642 | ] 643 | }, 644 | { 645 | "cell_type": "code", 646 | "execution_count": 57, 647 | "metadata": {}, 648 | "outputs": [ 649 | { 650 | "data": { 651 | "text/plain": [ 652 | "'06/05/2017 ( upon feceipt )'" 653 | ] 654 | }, 655 | "execution_count": 57, 656 | "metadata": {}, 657 | "output_type": "execute_result" 658 | } 659 | ], 660 | "source": [ 661 | "log_probs = cnn(example_image.cpu()).permute((2,0,1))[:,0,:]\n", 662 | "#preds_to_integer(log_probs)\n", 663 | "\"\".join([ds.decode_dict[j] for j in preds_to_integer(log_probs)])" 664 | ] 665 | }, 666 | { 667 | "cell_type": "code", 668 | "execution_count": 59, 669 | "metadata": {}, 670 | "outputs": [ 671 | { 672 | "data": { 673 | "text/plain": [ 674 | "' cu gpa,, 3.s5 cum tota|s 37 0o 37 0 ) 37 q0 924'" 675 | ] 676 | }, 677 | "execution_count": 59, 678 | "metadata": {}, 679 | "output_type": "execute_result" 680 | } 681 | ], 682 | "source": [ 683 | "log_probs = cnn(example_image.cpu()).permute((2,0,1))[:,0,:]\n", 684 | "#preds_to_integer(log_probs)\n", 685 | "\"\".join([ds.decode_dict[j] for j in preds_to_integer(log_probs)])" 686 | ] 687 | }, 688 | { 689 | "cell_type": "code", 690 | "execution_count": 87, 691 | "metadata": {}, 692 | "outputs": [ 693 | { 694 | "data": { 695 | "image/png": "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\nQEQb/mPqwlVy27iBx4UzgQAAAAA6oAQCAAAA6IASCAAAAKADSiAAAACADiiBAAAAADqgBAIAAADo\ngBIIAAAAoANKIAAAAIAOKIEAAAAAOqAEAgAAAOiAEggAAACgA0ogAAAAgA4ogQAAAAA6oAQCAAAA\n6IASCAAAAKADSiAAAACADiiBAAAAADowOZs7Wz4RsXplzuYun9W6qTbXI/x/1q4aj9ul0rjdxguR\ndcNzVbVmqu6ninnGaZaIhbmGPdc8O+tmZo3b2nM/PbuF+Hw+TsbttqmYZ9zuJ4/vZzdO99W4PRbm\nI2cCAQAAAHRACQQAAADQASUQAAAAQAeUQAAAAAAdUAIBAAAAdEAJBAAAANABJRAAAABAB5RAAAAA\nAB1QAgEAAAB0QAkEAAAA0AElEAAAAEAHlEAAAAAAHVACAQAAAHRACQQAAADQASUQAAAAQAeUQAAA\nAAAdUAIBAAAAdCBba7O3s8xvR8TDh9jsxIjYNAvjwEyxhpnvrGHmO2uY+c4aZiGwjpnv5tsaPrO1\ndtKhNprVEuhwZOatrbU1cz0HHClrmPnOGma+s4aZ76xhFgLrmPluoa5hbwcDAAAA6IASCAAAAKAD\n41gCXTvXA8BA1jDznTXMfGcNM99ZwywE1jHz3YJcw2P3mUAAAAAA1BvHM4EAAAAAKDY2JVBmXpWZ\n92bmA5n5nrmeBw5HZr4/M5/IzDsPuu6EzLwpM+8ffT1+LmeE7yYzz8jMv8zMuzPzrsx89+h665h5\nITOXZeYtmfnV0Rr+D6Prz87MdaM1/L8zc8lczwrfTWZOZOZXMvPPRpetYeaNzHwoM+/IzNsz89bR\ndY4lmDcyc2Vmfjwz7xkdF79ioa7hsSiBMnMiIn4/It4YERdGxDsy88K5nQoOywci4qrvuO49EXFz\na+28iLh5dBnG1XRE/EJr7YKIeHlEvGv0/GsdM1/sjojXttYujohLIuKqzHx5RPxmRPzOaA0/GRHv\nnMMZ4XC8OyLuPuiyNcx8c2Vr7ZKDfqW2Ywnmk/8aEZ9urb0oIi6OA8/HC3INj0UJFBEvi4gHWmsP\nttb2RMRHI+KtczwTHFJr7QsRsfk7rn5rRFw3+v66iPjBWR0KnoPW2mOttS+Pvt8WB17wTgvrmHmi\nHfD06OLi0X8tIl4bER8fXW8NM9Yy8/SI+P6IeO/ocoY1zPznWIJ5ITOPjYjLI+J9ERGttT2ttadi\nga7hcSmBTouIbx10ef3oOpiPntdaeyziwA/YEXHyHM8DhyUzz4qISyNiXVjHzCOjt9HcHhFPRMRN\nEfGNiHiqtTY92sRxBePudyPilyJi/+jyqrCGmV9aRHwmM2/LzGtG1zmWYL44JyK+HRF/NHpb7nsz\nc3ks0DU8LiVQPsN1fm0ZwCz66/8iAAACXklEQVTJzBUR8ccR8XOtta1zPQ88F621fa21SyLi9Dhw\ndvEFz7TZ7E4FhyczfyAinmit3Xbw1c+wqTXMOHtla+2yOPDxHu/KzMvneiB4DiYj4rKI+IPW2qUR\nsT0WyFu/nsm4lEDrI+KMgy6fHhGPztEsMNTGzDw1ImL09Yk5nge+q8xcHAcKoA+11j4xuto6Zt4Z\nnbr9+Tjw+VYrM3Ny9EeOKxhnr4yIt2TmQ3HgIxFeGwfODLKGmTdaa4+Ovj4REZ+MA4W8Ywnmi/UR\nsb61tm50+eNxoBRakGt4XEqgv4+I80a/BWFJRLw9Im6c45ngSN0YEVePvr86Im6Yw1nguxp97sT7\nIuLu1tpvH/RH1jHzQmaelJkrR98fFRHfFwc+2+ovI+KfjTazhhlbrbVfbq2d3lo7Kw4cA3+utfZj\nYQ0zT2Tm8sw85h++j4g3RMSd4ViCeaK19nhEfCszzx9d9bqI+Hos0DWcrY3HmaWZ+aY48K8eExHx\n/tbar8/xSHBImfmRiLgiIk6MiI0R8asR8ScRcX1EvCAiHomIt7XWvvPDo2EsZOarIuKLEXFH/L/P\noviVOPC5QNYxYy8zXxIHPqxxIg7849b1rbX/mJnnxIGzKk6IiK9ExI+31nbP3aRwaJl5RUT8Ymvt\nB6xh5ovRWv3k6OJkRHy4tfbrmbkqHEswT2TmJXHgw/mXRMSDEfGTMTquiAW2hsemBAIAAABg5ozL\n28EAAAAAmEFKIAAAAIAOKIEAAAAAOqAEAgAAAOiAEggAAACgA0ogAAAAgA4ogQAAAAA6oAQCAAAA\n6MD/BQcglnLRAKMbAAAAAElFTkSuQmCC\n", 696 | "text/plain": [ 697 | "" 698 | ] 699 | }, 700 | "metadata": {}, 701 | "output_type": "display_data" 702 | } 703 | ], 704 | "source": [ 705 | "show(example_image.detach().cpu()[0,:,:,:])\n", 706 | "plt.show()" 707 | ] 708 | }, 709 | { 710 | "cell_type": "code", 711 | "execution_count": 81, 712 | "metadata": {}, 713 | "outputs": [ 714 | { 715 | "data": { 716 | "text/plain": [ 717 | "'J, TN7b,q'" 718 | ] 719 | }, 720 | "execution_count": 81, 721 | "metadata": {}, 722 | "output_type": "execute_result" 723 | } 724 | ], 725 | "source": [ 726 | "log_probs = cnn(example_image).permute((2,0,1))[:,0,:]\n", 727 | "#preds_to_integer(log_probs)\n", 728 | "\"\".join([ds.decode_dict[j] for j in preds_to_integer(log_probs)])" 729 | ] 730 | }, 731 | { 732 | "cell_type": "code", 733 | "execution_count": null, 734 | "metadata": { 735 | "collapsed": true 736 | }, 737 | "outputs": [], 738 | "source": [] 739 | }, 740 | { 741 | "cell_type": "code", 742 | "execution_count": null, 743 | "metadata": { 744 | "collapsed": true 745 | }, 746 | "outputs": [], 747 | "source": [] 748 | } 749 | ], 750 | "metadata": { 751 | "kernelspec": { 752 | "display_name": "Python (pytorchenv)", 753 | "language": "python", 754 | "name": "pytorch" 755 | }, 756 | "language_info": { 757 | "codemirror_mode": { 758 | "name": "ipython", 759 | "version": 3 760 | }, 761 | "file_extension": ".py", 762 | "mimetype": "text/x-python", 763 | "name": "python", 764 | "nbconvert_exporter": "python", 765 | "pygments_lexer": "ipython3", 766 | "version": "3.6.3" 767 | } 768 | }, 769 | "nbformat": 4, 770 | "nbformat_minor": 2 771 | } 772 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | torch 2 | pandas 3 | numpy 4 | torch-baidu-ctc 5 | tensorboardX 6 | skimage 7 | torchvision 8 | opencv-python 9 | re 10 | string --------------------------------------------------------------------------------