├── models ├── __init__.py ├── losses.py ├── model.py └── backbones │ └── resnet.py ├── __init__.py ├── datasets ├── test │ ├── input │ │ ├── 01.png │ │ ├── 02.jpg │ │ ├── 03.jpeg │ │ └── 04.jpg │ └── output │ │ ├── 01.png │ │ ├── 02.jpg │ │ ├── 04.jpg │ │ └── 03.jpeg └── data │ ├── val │ ├── X51005230616.jpg │ ├── X51005230621.jpg │ └── X51005230648.jpg │ ├── train │ ├── X00016469670.jpg │ ├── X00016469671.jpg │ └── X51005200931.jpg │ └── val.json ├── datatools ├── train_val_split.py └── labelme2data.py ├── README.md ├── config.py ├── train.py ├── transform.py ├── inference.py └── generate.py /models/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | from .models.model import DBNet 2 | import config 3 | -------------------------------------------------------------------------------- /datasets/test/input/01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonasw/DBNet/HEAD/datasets/test/input/01.png -------------------------------------------------------------------------------- /datasets/test/input/02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonasw/DBNet/HEAD/datasets/test/input/02.jpg -------------------------------------------------------------------------------- /datasets/test/input/03.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonasw/DBNet/HEAD/datasets/test/input/03.jpeg -------------------------------------------------------------------------------- /datasets/test/input/04.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonasw/DBNet/HEAD/datasets/test/input/04.jpg -------------------------------------------------------------------------------- /datasets/test/output/01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonasw/DBNet/HEAD/datasets/test/output/01.png -------------------------------------------------------------------------------- /datasets/test/output/02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonasw/DBNet/HEAD/datasets/test/output/02.jpg -------------------------------------------------------------------------------- /datasets/test/output/04.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonasw/DBNet/HEAD/datasets/test/output/04.jpg -------------------------------------------------------------------------------- /datasets/test/output/03.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonasw/DBNet/HEAD/datasets/test/output/03.jpeg -------------------------------------------------------------------------------- /datasets/data/val/X51005230616.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonasw/DBNet/HEAD/datasets/data/val/X51005230616.jpg -------------------------------------------------------------------------------- /datasets/data/val/X51005230621.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonasw/DBNet/HEAD/datasets/data/val/X51005230621.jpg -------------------------------------------------------------------------------- /datasets/data/val/X51005230648.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonasw/DBNet/HEAD/datasets/data/val/X51005230648.jpg -------------------------------------------------------------------------------- /datasets/data/train/X00016469670.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonasw/DBNet/HEAD/datasets/data/train/X00016469670.jpg -------------------------------------------------------------------------------- /datasets/data/train/X00016469671.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonasw/DBNet/HEAD/datasets/data/train/X00016469671.jpg -------------------------------------------------------------------------------- /datasets/data/train/X51005200931.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonasw/DBNet/HEAD/datasets/data/train/X51005200931.jpg -------------------------------------------------------------------------------- /datatools/train_val_split.py: -------------------------------------------------------------------------------- 1 | import os 2 | import os.path as osp 3 | import json 4 | import random 5 | 6 | from tqdm import tqdm 7 | 8 | 9 | val_ratio = 0.01 10 | 11 | 12 | ann_path = "annotations.json" 13 | 14 | with open(ann_path, "r", encoding="utf8") as f: 15 | datas = json.load(f) 16 | 17 | data_root = datas["data_root"] 18 | data_list = datas["data_list"] 19 | 20 | data_num = len(data_list) 21 | val_num = int(data_num * val_ratio) if val_ratio < 1 else val_ratio 22 | train_num = data_num - val_num 23 | 24 | random.shuffle(data_list) 25 | 26 | train_list = data_list[:train_num] 27 | val_list = data_list[train_num:] 28 | 29 | 30 | train = {"data_root": data_root, "data_list": train_list} 31 | with open("train.json", "w", encoding="utf8") as f: 32 | json.dump(train, f, indent=2) 33 | 34 | 35 | val = {"data_root": data_root, "data_list": val_list} 36 | with open("val.json", "w", encoding="utf8") as f: 37 | json.dump(val, f, indent=2) 38 | 39 | -------------------------------------------------------------------------------- /datatools/labelme2data.py: -------------------------------------------------------------------------------- 1 | import os 2 | import os.path as osp 3 | import json 4 | 5 | from tqdm import tqdm 6 | from glob import glob 7 | 8 | 9 | save_ann_path = "annotations.json" 10 | ext = ".jpg" 11 | labelme_data_dir = "/hd2/data/labelme_data" 12 | 13 | 14 | img_dir = osp.join(labelme_data_dir, "*"+ext) 15 | img_paths = glob(img_dir) 16 | 17 | data_list = [] 18 | for img_path in tqdm(img_paths): 19 | img_name = osp.split(img_path)[-1] 20 | annotations = [] 21 | labelme_ann_path = osp.join(labelme_data_dir, osp.splitext(img_name)[0]+".json") 22 | with open(labelme_ann_path, "r", encoding="utf8") as f: 23 | labelme_ann = json.load(f) 24 | shapes = labelme_ann["shapes"] 25 | for shape in shapes: 26 | points = shape["points"] 27 | text = "unknown" 28 | annotations.append({"polygon": points, "text": text}) 29 | data_list.append({"img_name": img_name, "annotations": annotations}) 30 | 31 | ann = {"data_root": labelme_data_dir, "data_list": data_list} 32 | with open(save_ann_path, "w", encoding="utf8") as f: 33 | json.dump(ann, f, indent=2) 34 | 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Introduction 3 | This is a tensorflow2.x implementation of "[Real-time Scene Text Detection with Differentiable Binarization](https://arxiv.org/abs/1911.08947)". 4 | 5 | 6 | ## Requirements: 7 | - Python3 8 | - TensorFlow >= 2.2.0 9 | 10 | 11 | ## Models 12 | Download Trained models [Baidu Drive](https://pan.baidu.com/s/1FmrK3f_bzwMc93qZ464cPg) (download code: kva3), [Google Drive](https://drive.google.com/file/d/1e0K1AAYECL_-ZpbVqFa65msBZoDhP0sl/view?usp=sharing). 13 | 14 | 15 | ## Datasets 16 | The data set is from [WenmuZhou](https://github.com/WenmuZhou/OCR_DataSet) 17 | 18 | 19 | ## Inference 20 | ```bash 21 | python inference.py 22 | ``` 23 | 24 | 25 | ## Train 26 | ```bash 27 | python train.py 28 | ``` 29 | 30 | 31 | ## Demo 32 | #### input 33 | ![input_01](datasets/test/input/01.png) 34 | ![input_02](datasets/test/input/02.jpg) 35 | ![input_03](datasets/test/input/03.jpeg) 36 | ![input_04](datasets/test/input/04.jpg) 37 | 38 | #### output 39 | ![output_01](datasets/test/output/01.png) 40 | ![output_02](datasets/test/output/02.jpg) 41 | ![output_03](datasets/test/output/03.jpeg) 42 | ![output_04](datasets/test/output/04.jpg) 43 | 44 | -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Time : 2020/6/16 23:49 3 | # @Author : zonas.wang 4 | # @Email : zonas.wang@gmail.com 5 | # @File : config.py 6 | import os 7 | import os.path as osp 8 | import datetime 9 | 10 | 11 | class DBConfig(object): 12 | 13 | STEPS_PER_EPOCH = 1000 14 | 15 | # Number of validation steps to run at the end of every training epoch. 16 | # A bigger number improves accuracy of validation stats, but slows 17 | # down the training. 18 | VALIDATION_STEPS = 20 19 | 20 | # Backbone network architecture 21 | # Supported values are: ResNet50 22 | BACKBONE = "ResNet50" 23 | 24 | 25 | # train 26 | EPOCHS = 1000 27 | INITIAL_EPOCH = 0 28 | # PRETRAINED_MODEL_PATH = 'checkpoints/ckpt/db_173_2.0138_2.0660.h5' 29 | PRETRAINED_MODEL_PATH = '' 30 | LOG_DIR = 'datasets/logs' 31 | CHECKPOINT_DIR = 'checkpoints' 32 | LEARNING_RATE = 1e-4 33 | 34 | 35 | # dataset 36 | IGNORE_TEXT = ["*", "###"] 37 | 38 | TRAIN_DATA_PATH = '/hd2/zonas/data/text_detection/merge/train.json' 39 | VAL_DATA_PATH = '/hd2/zonas/data/text_detection/merge/val.json' 40 | 41 | IMAGE_SIZE = 640 42 | BATCH_SIZE = 8 43 | 44 | MIN_TEXT_SIZE = 8 45 | SHRINK_RATIO = 0.4 46 | 47 | THRESH_MIN = 0.3 48 | THRESH_MAX = 0.7 49 | 50 | 51 | def __init__(self): 52 | """Set values of computed attributes.""" 53 | 54 | if not osp.exists(self.LOG_DIR): 55 | os.makedirs(self.LOG_DIR) 56 | 57 | self.CHECKPOINT_DIR = osp.join(self.CHECKPOINT_DIR, str(datetime.date.today())) 58 | if not osp.exists(self.CHECKPOINT_DIR): 59 | os.makedirs(self.CHECKPOINT_DIR) 60 | 61 | -------------------------------------------------------------------------------- /train.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Time : 2020/6/16 23:52 3 | # @Author : zonas.wang 4 | # @Email : zonas.wang@gmail.com 5 | # @File : train.py 6 | import os 7 | os.environ["CUDA_VISIBLE_DEVICES"] = "1" 8 | import os.path as osp 9 | 10 | from tensorflow.keras import callbacks 11 | from tensorflow.keras import optimizers 12 | 13 | from generate import generate 14 | from models.model import DBNet 15 | from config import DBConfig 16 | cfg = DBConfig() 17 | 18 | 19 | train_generator = generate(cfg, 'train') 20 | val_generator = generate(cfg, 'val') 21 | 22 | model = DBNet(cfg, model='training') 23 | 24 | 25 | load_weights_path = cfg.PRETRAINED_MODEL_PATH 26 | if load_weights_path: 27 | model.load_weights(load_weights_path, by_name=True, skip_mismatch=True) 28 | 29 | model.compile(optimizer=optimizers.Adam(learning_rate=cfg.LEARNING_RATE), 30 | loss=[None] * len(model.output.shape)) 31 | # model.compile(optimizer=optimizers.SGD(learning_rate=cfg.LEARNING_RATE, momentum=0.9), 32 | # loss=[None] * len(model.output.shape)) 33 | model.summary() 34 | 35 | # callbacks 36 | checkpoint_callback = callbacks.ModelCheckpoint( 37 | osp.join(cfg.CHECKPOINT_DIR, 'db_{epoch:02d}_{loss:.4f}_{val_loss:.4f}.h5')) 38 | tensorboard_callback = callbacks.TensorBoard(log_dir=cfg.LOG_DIR, 39 | histogram_freq=1, 40 | write_graph=True, 41 | write_images=True, 42 | update_freq='epoch', # 'batch'/'epoch'/value_of_int32 43 | profile_batch=2, 44 | embeddings_freq=1, 45 | embeddings_metadata=None) 46 | callbacks = [checkpoint_callback, tensorboard_callback] 47 | 48 | 49 | model.fit( 50 | x=train_generator, 51 | steps_per_epoch=cfg.STEPS_PER_EPOCH, 52 | initial_epoch=cfg.INITIAL_EPOCH, 53 | epochs=cfg.EPOCHS, 54 | verbose=1, 55 | callbacks=callbacks, 56 | validation_data=val_generator, 57 | validation_steps=cfg.VALIDATION_STEPS 58 | ) 59 | 60 | -------------------------------------------------------------------------------- /models/losses.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Time : 2020/6/16 23:48 3 | # @Author : zonas.wang 4 | # @Email : zonas.wang@gmail.com 5 | # @File : losses.py 6 | import tensorflow as tf 7 | import tensorflow.keras as K 8 | 9 | 10 | def balanced_crossentropy_loss(pred, gt, mask, negative_ratio=3.): 11 | pred = pred[..., 0] 12 | positive_mask = (gt * mask) 13 | negative_mask = ((1 - gt) * mask) 14 | positive_count = tf.reduce_sum(positive_mask) 15 | negative_count = tf.reduce_min([tf.reduce_sum(negative_mask), positive_count * negative_ratio]) 16 | # loss_fun = tf.losses.BinaryCrossentropy() 17 | # loss = loss_fun(gt, pred) 18 | # loss = K.losses.binary_crossentropy(gt, pred) 19 | loss = K.backend.binary_crossentropy(gt, pred) 20 | positive_loss = loss * positive_mask 21 | negative_loss = loss * negative_mask 22 | negative_loss, _ = tf.nn.top_k(tf.reshape(negative_loss, (-1,)), tf.cast(negative_count, tf.int32)) 23 | 24 | balanced_loss = (tf.reduce_sum(positive_loss) + tf.reduce_sum(negative_loss)) / ( 25 | positive_count + negative_count + 1e-6) 26 | return balanced_loss, loss 27 | 28 | 29 | def dice_loss(pred, gt, mask, weights): 30 | """ 31 | Args: 32 | pred: (b, h, w, 1) 33 | gt: (b, h, w) 34 | mask: (b, h, w) 35 | weights: (b, h, w) 36 | Returns: 37 | """ 38 | pred = pred[..., 0] 39 | weights = (weights - tf.reduce_min(weights)) / (tf.reduce_max(weights) - tf.reduce_min(weights) + 1e-6) + 1. 40 | mask = mask * weights 41 | intersection = tf.reduce_sum(pred * gt * mask) 42 | union = tf.reduce_sum(pred * mask) + tf.reduce_sum(gt * mask) + 1e-6 43 | loss = 1 - 2.0 * intersection / union 44 | return loss 45 | 46 | 47 | def l1_loss(pred, gt, mask): 48 | pred = pred[..., 0] 49 | mask_sum = tf.reduce_sum(mask) 50 | loss = K.backend.switch(mask_sum > 0, tf.reduce_sum(tf.abs(pred - gt) * mask) / (mask_sum + 1e-6), tf.constant(0.)) 51 | return loss 52 | 53 | 54 | def compute_cls_acc(pred, gt, mask): 55 | 56 | zero = tf.zeros_like(pred, tf.float32) 57 | one = tf.ones_like(pred, tf.float32) 58 | 59 | pred = tf.where(pred < 0.3, x=zero, y=one) 60 | acc = tf.reduce_mean(tf.cast(tf.equal(pred * mask, gt * mask), tf.float32)) 61 | 62 | return acc 63 | 64 | 65 | def db_loss(args, alpha=5.0, beta=10.0, ohem_ratio=3.0): 66 | input_gt, input_mask, input_thresh, input_thresh_mask, binarize_map, thresh_binary, threshold_map = args 67 | 68 | threshold_loss = l1_loss(threshold_map, input_thresh, input_thresh_mask) 69 | binarize_loss, dice_loss_weights = balanced_crossentropy_loss(binarize_map, input_gt, input_mask, negative_ratio=ohem_ratio) 70 | thresh_binary_loss = dice_loss(thresh_binary, input_gt, input_mask, dice_loss_weights) 71 | 72 | model_loss = alpha * binarize_loss + beta * threshold_loss + thresh_binary_loss 73 | return model_loss 74 | 75 | 76 | def db_acc(args): 77 | input_gt, input_mask, binarize_map, thresh_binary = args 78 | binarize_acc = compute_cls_acc(binarize_map, input_gt, input_mask) 79 | thresh_binary_acc = compute_cls_acc(thresh_binary, input_gt, input_mask) 80 | return binarize_acc, thresh_binary_acc 81 | -------------------------------------------------------------------------------- /transform.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Time : 2020/6/16 23:54 3 | # @Author : zonas.wang 4 | # @Email : zonas.wang@gmail.com 5 | # @File : transform.py 6 | import cv2 7 | import numpy as np 8 | import imgaug 9 | 10 | 11 | def transform(aug, image, anns): 12 | image_shape = image.shape 13 | image = aug.augment_image(image) 14 | new_anns = [] 15 | for ann in anns: 16 | keypoints = [imgaug.Keypoint(p[0], p[1]) for p in ann['poly']] 17 | keypoints = aug.augment_keypoints( 18 | [imgaug.KeypointsOnImage(keypoints, shape=image_shape)])[0].keypoints 19 | poly = [(min(max(0, p.x), image.shape[1] - 1), min(max(0, p.y), image.shape[0] - 1)) for p in keypoints] 20 | new_ann = {'poly': poly, 'text': ann['text']} 21 | new_anns.append(new_ann) 22 | return image, new_anns 23 | 24 | 25 | def split_regions(axis): 26 | regions = [] 27 | min_axis_index = 0 28 | for i in range(1, axis.shape[0]): 29 | if axis[i] != axis[i - 1] + 1: 30 | region = axis[min_axis_index:i] 31 | min_axis_index = i 32 | regions.append(region) 33 | return regions 34 | 35 | 36 | def random_select(axis): 37 | xx = np.random.choice(axis, size=2) 38 | xmin = np.min(xx) 39 | xmax = np.max(xx) 40 | return xmin, xmax 41 | 42 | 43 | def region_wise_random_select(regions): 44 | selected_index = list(np.random.choice(len(regions), 2)) 45 | selected_values = [] 46 | for index in selected_index: 47 | axis = regions[index] 48 | xx = int(np.random.choice(axis, size=1)) 49 | selected_values.append(xx) 50 | xmin = min(selected_values) 51 | xmax = max(selected_values) 52 | return xmin, xmax 53 | 54 | 55 | def crop(image, anns, max_tries=10, min_crop_side_ratio=0.1): 56 | h, w, _ = image.shape 57 | h_array = np.zeros(h, dtype=np.int32) 58 | w_array = np.zeros(w, dtype=np.int32) 59 | for ann in anns: 60 | points = np.round(ann['poly'], decimals=0).astype(np.int32) 61 | minx = np.min(points[:, 0]) 62 | maxx = np.max(points[:, 0]) 63 | w_array[minx:maxx] = 1 64 | miny = np.min(points[:, 1]) 65 | maxy = np.max(points[:, 1]) 66 | h_array[miny:maxy] = 1 67 | # ensure the cropped area not across a text 68 | h_axis = np.where(h_array == 0)[0] 69 | w_axis = np.where(w_array == 0)[0] 70 | 71 | if len(h_axis) == 0 or len(w_axis) == 0: 72 | return image, anns 73 | 74 | h_regions = split_regions(h_axis) 75 | w_regions = split_regions(w_axis) 76 | 77 | for i in range(max_tries): 78 | if len(w_regions) > 1: 79 | xmin, xmax = region_wise_random_select(w_regions) 80 | else: 81 | xmin, xmax = random_select(w_axis) 82 | if len(h_regions) > 1: 83 | ymin, ymax = region_wise_random_select(h_regions) 84 | else: 85 | ymin, ymax = random_select(h_axis) 86 | 87 | if xmax - xmin < min_crop_side_ratio * w or ymax - ymin < min_crop_side_ratio * h: 88 | # area too small 89 | continue 90 | new_anns = [] 91 | for ann in anns: 92 | poly = np.array(ann['poly']) 93 | if not (poly[:, 0].min() > xmax 94 | or poly[:, 0].max() < xmin 95 | or poly[:, 1].min() > ymax 96 | or poly[:, 1].max() < ymin): 97 | poly[:, 0] -= xmin 98 | poly[:, 0] = np.clip(poly[:, 0], 0., (xmax - xmin - 1) * 1.) 99 | poly[:, 1] -= ymin 100 | poly[:, 1] = np.clip(poly[:, 1], 0., (ymax - ymin - 1) * 1.) 101 | new_ann = {'poly': poly.tolist(), 'text': ann['text']} 102 | new_anns.append(new_ann) 103 | 104 | if len(new_anns) > 0: 105 | return image[ymin:ymax, xmin:xmax], new_anns 106 | 107 | return image, anns 108 | 109 | 110 | def resize(size, image, anns): 111 | h, w, c = image.shape 112 | scale_w = size / w 113 | scale_h = size / h 114 | scale = min(scale_w, scale_h) 115 | h = int(h * scale) 116 | w = int(w * scale) 117 | padimg = np.zeros((size, size, c), image.dtype) 118 | padimg[:h, :w] = cv2.resize(image, (w, h)) 119 | new_anns = [] 120 | for ann in anns: 121 | poly = np.array(ann['poly']).astype(np.float64) 122 | poly *= scale 123 | new_ann = {'poly': poly.tolist(), 'text': ann['text']} 124 | new_anns.append(new_ann) 125 | return padimg, new_anns 126 | -------------------------------------------------------------------------------- /models/model.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Time : 2020/6/16 23:46 3 | # @Author : zonas.wang 4 | # @Email : zonas.wang@gmail.com 5 | # @File : model.py 6 | from tensorflow import keras as K 7 | from tensorflow.keras import layers as KL 8 | import tensorflow as tf 9 | 10 | from models.backbones.resnet import ResNet50 11 | from models.losses import db_loss 12 | 13 | 14 | def DBNet(cfg, k=50, model='training'): 15 | assert model in ['training', 'inference'], 'error' 16 | 17 | input_image = KL.Input(shape=[None, None, 3], name='input_image') 18 | 19 | backbone = ResNet50(inputs=input_image, include_top=False, freeze_bn=True) 20 | C2, C3, C4, C5 = backbone.outputs 21 | 22 | # in2 23 | in2 = KL.Conv2D(256, (1, 1), padding='same', kernel_initializer='he_normal', name='in2')(C2) 24 | in2 = KL.BatchNormalization()(in2) 25 | in2 = KL.ReLU()(in2) 26 | # in3 27 | in3 = KL.Conv2D(256, (1, 1), padding='same', kernel_initializer='he_normal', name='in3')(C3) 28 | in3 = KL.BatchNormalization()(in3) 29 | in3 = KL.ReLU()(in3) 30 | # in4 31 | in4 = KL.Conv2D(256, (1, 1), padding='same', kernel_initializer='he_normal', name='in4')(C4) 32 | in4 = KL.BatchNormalization()(in4) 33 | in4 = KL.ReLU()(in4) 34 | # in5 35 | in5 = KL.Conv2D(256, (1, 1), padding='same', kernel_initializer='he_normal', name='in5')(C5) 36 | in5 = KL.BatchNormalization()(in5) 37 | in5 = KL.ReLU()(in5) 38 | 39 | # P5 40 | P5 = KL.Conv2D(64, (3, 3), padding='same', kernel_initializer='he_normal')(in5) 41 | P5 = KL.BatchNormalization()(P5) 42 | P5 = KL.ReLU()(P5) 43 | P5 = KL.UpSampling2D(size=(8, 8))(P5) 44 | # P4 45 | out4 = KL.Add()([in4, KL.UpSampling2D(size=(2, 2))(in5)]) 46 | P4 = KL.Conv2D(64, (3, 3), padding='same', kernel_initializer='he_normal')(out4) 47 | P4 = KL.BatchNormalization()(P4) 48 | P4 = KL.ReLU()(P4) 49 | P4 = KL.UpSampling2D(size=(4, 4))(P4) 50 | # P3 51 | out3 = KL.Add()([in3, KL.UpSampling2D(size=(2, 2))(out4)]) 52 | P3 = KL.Conv2D(64, (3, 3), padding='same', kernel_initializer='he_normal')(out3) 53 | P3 = KL.BatchNormalization()(P3) 54 | P3 = KL.ReLU()(P3) 55 | P3 = KL.UpSampling2D(size=(2, 2))(P3) 56 | # P2 57 | out2 = KL.Add()([in2, KL.UpSampling2D(size=(2, 2))(out3)]) 58 | P2 = KL.Conv2D(64, (3, 3), padding='same', kernel_initializer='he_normal')(out2) 59 | P2 = KL.BatchNormalization()(P2) 60 | P2 = KL.ReLU()(P2) 61 | 62 | fuse = KL.Concatenate()([P2, P3, P4, P5]) 63 | 64 | # binarize map 65 | p = KL.Conv2D(64, (3, 3), padding='same', kernel_initializer='he_normal', use_bias=False)(fuse) 66 | p = KL.BatchNormalization()(p) 67 | p = KL.ReLU()(p) 68 | p = KL.Conv2DTranspose(64, (2, 2), strides=(2, 2), kernel_initializer='he_normal', use_bias=False)(p) 69 | p = KL.BatchNormalization()(p) 70 | p = KL.ReLU()(p) 71 | binarize_map = KL.Conv2DTranspose(1, (2, 2), strides=(2, 2), kernel_initializer='he_normal', 72 | activation='sigmoid', name='binarize_map')(p) 73 | 74 | # threshold map 75 | t = KL.Conv2D(64, (3, 3), padding='same', kernel_initializer='he_normal', use_bias=False)(fuse) 76 | t = KL.BatchNormalization()(t) 77 | t = KL.ReLU()(t) 78 | t = KL.Conv2DTranspose(64, (2, 2), strides=(2, 2), kernel_initializer='he_normal', use_bias=False)(t) 79 | t = KL.BatchNormalization()(t) 80 | t = KL.ReLU()(t) 81 | threshold_map = KL.Conv2DTranspose(1, (2, 2), strides=(2, 2), kernel_initializer='he_normal', 82 | activation='sigmoid', name='threshold_map')(t) 83 | 84 | # thresh binary map 85 | thresh_binary = KL.Lambda(lambda x: 1 / (1 + tf.exp(-k * (x[0] - x[1]))))([binarize_map, threshold_map]) 86 | 87 | if model == 'training': 88 | input_gt = KL.Input(shape=[cfg.IMAGE_SIZE, cfg.IMAGE_SIZE], name='input_gt') 89 | input_mask = KL.Input(shape=[cfg.IMAGE_SIZE, cfg.IMAGE_SIZE], name='input_mask') 90 | input_thresh = KL.Input(shape=[cfg.IMAGE_SIZE, cfg.IMAGE_SIZE], name='input_thresh') 91 | input_thresh_mask = KL.Input(shape=[cfg.IMAGE_SIZE, cfg.IMAGE_SIZE], name='input_thresh_mask') 92 | 93 | loss_layer = KL.Lambda(db_loss, name='db_loss')( 94 | [input_gt, input_mask, input_thresh, input_thresh_mask, binarize_map, thresh_binary, threshold_map]) 95 | 96 | db_model = K.Model(inputs=[input_image, input_gt, input_mask, input_thresh, input_thresh_mask], 97 | outputs=[loss_layer]) 98 | 99 | loss_names = ["db_loss"] 100 | for layer_name in loss_names: 101 | layer = db_model.get_layer(layer_name) 102 | db_model.add_loss(layer.output) 103 | # db_model.add_metric(layer.output, name=layer_name, aggregation="mean") 104 | else: 105 | db_model = K.Model(inputs=input_image, 106 | outputs=binarize_map) 107 | """ 108 | db_model = K.Model(inputs=input_image, 109 | outputs=thresh_binary) 110 | """ 111 | return db_model 112 | 113 | 114 | if __name__ == '__main__': 115 | from config import DBConfig 116 | cfg = DBConfig() 117 | model = DBNet(cfg, model='inference') 118 | model.summary() 119 | -------------------------------------------------------------------------------- /inference.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Time : 2020/6/16 23:51 3 | # @Author : zonas.wang 4 | # @Email : zonas.wang@gmail.com 5 | # @File : inference.py 6 | import math 7 | import os 8 | os.environ["CUDA_VISIBLE_DEVICES"] = "-1" 9 | import os.path as osp 10 | import time 11 | 12 | import tensorflow as tf 13 | import cv2 14 | import glob 15 | import numpy as np 16 | import pyclipper 17 | from shapely.geometry import Polygon 18 | from tqdm import tqdm 19 | 20 | from models.model import DBNet 21 | from config import DBConfig 22 | cfg = DBConfig() 23 | 24 | 25 | def resize_image(image, image_short_side=736): 26 | height, width, _ = image.shape 27 | if height < width: 28 | new_height = image_short_side 29 | new_width = int(math.ceil(new_height / height * width / 32) * 32) 30 | else: 31 | new_width = image_short_side 32 | new_height = int(math.ceil(new_width / width * height / 32) * 32) 33 | resized_img = cv2.resize(image, (new_width, new_height)) 34 | return resized_img 35 | 36 | 37 | def box_score_fast(bitmap, _box): 38 | h, w = bitmap.shape[:2] 39 | box = _box.copy() 40 | xmin = np.clip(np.floor(box[:, 0].min()).astype(np.int), 0, w - 1) 41 | xmax = np.clip(np.ceil(box[:, 0].max()).astype(np.int), 0, w - 1) 42 | ymin = np.clip(np.floor(box[:, 1].min()).astype(np.int), 0, h - 1) 43 | ymax = np.clip(np.ceil(box[:, 1].max()).astype(np.int), 0, h - 1) 44 | 45 | mask = np.zeros((ymax - ymin + 1, xmax - xmin + 1), dtype=np.uint8) 46 | box[:, 0] = box[:, 0] - xmin 47 | box[:, 1] = box[:, 1] - ymin 48 | cv2.fillPoly(mask, box.reshape(1, -1, 2).astype(np.int32), 1) 49 | return cv2.mean(bitmap[ymin:ymax + 1, xmin:xmax + 1], mask)[0] 50 | 51 | 52 | def unclip(box, unclip_ratio=1.5): 53 | poly = Polygon(box) 54 | distance = poly.area * unclip_ratio / poly.length 55 | offset = pyclipper.PyclipperOffset() 56 | offset.AddPath(box, pyclipper.JT_ROUND, pyclipper.ET_CLOSEDPOLYGON) 57 | expanded = np.array(offset.Execute(distance)) 58 | return expanded 59 | 60 | 61 | def get_mini_boxes(contour): 62 | if not contour.size: 63 | return [], 0 64 | bounding_box = cv2.minAreaRect(contour) 65 | points = sorted(list(cv2.boxPoints(bounding_box)), key=lambda x: x[0]) 66 | 67 | index_1, index_2, index_3, index_4 = 0, 1, 2, 3 68 | if points[1][1] > points[0][1]: 69 | index_1 = 0 70 | index_4 = 1 71 | else: 72 | index_1 = 1 73 | index_4 = 0 74 | if points[3][1] > points[2][1]: 75 | index_2 = 2 76 | index_3 = 3 77 | else: 78 | index_2 = 3 79 | index_3 = 2 80 | 81 | box = [points[index_1], points[index_2], 82 | points[index_3], points[index_4]] 83 | return box, min(bounding_box[1]) 84 | 85 | 86 | def polygons_from_bitmap(pred, bitmap, dest_width, dest_height, max_candidates=500, box_thresh=0.7): 87 | pred = pred[..., 0] 88 | bitmap = bitmap[..., 0] 89 | height, width = bitmap.shape 90 | boxes = [] 91 | scores = [] 92 | 93 | contours, _ = cv2.findContours((bitmap * 255).astype(np.uint8), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) 94 | 95 | for contour in contours[:max_candidates]: 96 | epsilon = 0.001 * cv2.arcLength(contour, True) 97 | approx = cv2.approxPolyDP(contour, epsilon, True) 98 | points = approx.reshape((-1, 2)) 99 | if points.shape[0] < 4: 100 | continue 101 | score = box_score_fast(pred, points.reshape(-1, 2)) 102 | if box_thresh > score: 103 | continue 104 | if points.shape[0] > 2: 105 | box = unclip(points, unclip_ratio=2.0) 106 | if len(box) > 1: 107 | continue 108 | else: 109 | continue 110 | 111 | box = box.reshape(-1, 2) 112 | _, sside = get_mini_boxes(box.reshape((-1, 1, 2))) 113 | if sside < 5: 114 | continue 115 | 116 | box[:, 0] = np.clip(np.round(box[:, 0] / width * dest_width), 0, dest_width) 117 | box[:, 1] = np.clip(np.round(box[:, 1] / height * dest_height), 0, dest_height) 118 | boxes.append(box.tolist()) 119 | scores.append(score) 120 | return boxes, scores 121 | 122 | 123 | def main(): 124 | BOX_THRESH = 0.5 125 | mean = np.array([103.939, 116.779, 123.68]) 126 | 127 | model_path = "checkpoints/2020-07-24/db_83_2.0894_1.9788.h5" 128 | 129 | img_dir = 'datasets/test/input' 130 | img_names = os.listdir(img_dir) 131 | 132 | model = DBNet(cfg, model='inference') 133 | model.load_weights(model_path, by_name=True, skip_mismatch=True) 134 | for img_name in tqdm(img_names): 135 | img_path = osp.join(img_dir, img_name) 136 | image = cv2.imread(img_path) 137 | src_image = image.copy() 138 | h, w = image.shape[:2] 139 | image = resize_image(image) 140 | image = image.astype(np.float32) 141 | image -= mean 142 | image_input = np.expand_dims(image, axis=0) 143 | image_input_tensor = tf.convert_to_tensor(image_input) 144 | start_time = time.time() 145 | p = model.predict(image_input_tensor)[0] 146 | end_time = time.time() 147 | print("time: ", end_time - start_time) 148 | 149 | bitmap = p > 0.3 150 | boxes, scores = polygons_from_bitmap(p, bitmap, w, h, box_thresh=BOX_THRESH) 151 | for box in boxes: 152 | cv2.drawContours(src_image, [np.array(box)], -1, (0, 255, 0), 2) 153 | image_fname = osp.split(img_path)[-1] 154 | cv2.imwrite('datasets/test/output/' + image_fname, src_image) 155 | 156 | 157 | if __name__ == '__main__': 158 | main() 159 | 160 | -------------------------------------------------------------------------------- /generate.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Time : 2020/6/16 23:50 3 | # @Author : zonas.wang 4 | # @Email : zonas.wang@gmail.com 5 | # @File : generate.py 6 | import math 7 | import json 8 | import os.path as osp 9 | 10 | import cv2 11 | import numpy as np 12 | import pyclipper 13 | from shapely.geometry import Polygon 14 | import imgaug.augmenters as iaa 15 | 16 | from transform import transform, crop, resize 17 | from config import DBConfig 18 | cfg = DBConfig() 19 | 20 | 21 | mean = [103.939, 116.779, 123.68] 22 | 23 | 24 | def show_polys(image, anns, window_name): 25 | for ann in anns: 26 | poly = np.array(ann['poly']).astype(np.int32) 27 | cv2.drawContours(image, np.expand_dims(poly, axis=0), -1, (0, 255, 0), 2) 28 | 29 | cv2.namedWindow(window_name, cv2.WINDOW_NORMAL) 30 | cv2.imshow(window_name, image) 31 | 32 | 33 | def draw_thresh_map(polygon, canvas, mask, shrink_ratio=0.4): 34 | polygon = np.array(polygon) 35 | assert polygon.ndim == 2 36 | assert polygon.shape[1] == 2 37 | 38 | polygon_shape = Polygon(polygon) 39 | distance = polygon_shape.area * (1 - np.power(shrink_ratio, 2)) / polygon_shape.length 40 | subject = [tuple(l) for l in polygon] 41 | padding = pyclipper.PyclipperOffset() 42 | padding.AddPath(subject, pyclipper.JT_ROUND, pyclipper.ET_CLOSEDPOLYGON) 43 | padded_polygon = np.array(padding.Execute(distance)[0]) 44 | cv2.fillPoly(mask, [padded_polygon.astype(np.int32)], 1.0) 45 | 46 | xmin = padded_polygon[:, 0].min() 47 | xmax = padded_polygon[:, 0].max() 48 | ymin = padded_polygon[:, 1].min() 49 | ymax = padded_polygon[:, 1].max() 50 | width = xmax - xmin + 1 51 | height = ymax - ymin + 1 52 | 53 | polygon[:, 0] = polygon[:, 0] - xmin 54 | polygon[:, 1] = polygon[:, 1] - ymin 55 | 56 | xs = np.broadcast_to(np.linspace(0, width - 1, num=width).reshape(1, width), (height, width)) 57 | ys = np.broadcast_to(np.linspace(0, height - 1, num=height).reshape(height, 1), (height, width)) 58 | 59 | distance_map = np.zeros((polygon.shape[0], height, width), dtype=np.float32) 60 | for i in range(polygon.shape[0]): 61 | j = (i + 1) % polygon.shape[0] 62 | absolute_distance = compute_distance(xs, ys, polygon[i], polygon[j]) 63 | distance_map[i] = np.clip(absolute_distance / distance, 0, 1) 64 | distance_map = np.min(distance_map, axis=0) 65 | 66 | xmin_valid = min(max(0, xmin), canvas.shape[1] - 1) 67 | xmax_valid = min(max(0, xmax), canvas.shape[1] - 1) 68 | ymin_valid = min(max(0, ymin), canvas.shape[0] - 1) 69 | ymax_valid = min(max(0, ymax), canvas.shape[0] - 1) 70 | canvas[ymin_valid:ymax_valid, xmin_valid:xmax_valid] = np.fmax( 71 | 1 - distance_map[ 72 | ymin_valid - ymin:ymax_valid - ymin, 73 | xmin_valid - xmin:xmax_valid - xmin], 74 | canvas[ymin_valid:ymax_valid, xmin_valid:xmax_valid]) 75 | 76 | 77 | def compute_distance(xs, ys, point_1, point_2): 78 | square_distance_1 = np.square(xs - point_1[0]) + np.square(ys - point_1[1]) 79 | square_distance_2 = np.square(xs - point_2[0]) + np.square(ys - point_2[1]) 80 | square_distance = np.square(point_1[0] - point_2[0]) + np.square(point_1[1] - point_2[1]) 81 | 82 | cosin = (square_distance - square_distance_1 - square_distance_2) / \ 83 | (2 * np.sqrt(square_distance_1 * square_distance_2) + 1e-6) 84 | square_sin = 1 - np.square(cosin) 85 | square_sin = np.nan_to_num(square_sin) 86 | result = np.sqrt(square_distance_1 * square_distance_2 * square_sin / (square_distance + 1e-6)) 87 | 88 | result[cosin < 0] = np.sqrt(np.fmin(square_distance_1, square_distance_2))[cosin < 0] 89 | return result 90 | 91 | 92 | def generate(cfg, train_or_val='train'): 93 | def init_input(): 94 | batch_images = np.zeros([cfg.BATCH_SIZE, cfg.IMAGE_SIZE, cfg.IMAGE_SIZE, 3], dtype=np.float32) 95 | batch_gts = np.zeros([cfg.BATCH_SIZE, cfg.IMAGE_SIZE, cfg.IMAGE_SIZE], dtype=np.float32) 96 | batch_masks = np.zeros([cfg.BATCH_SIZE, cfg.IMAGE_SIZE, cfg.IMAGE_SIZE], dtype=np.float32) 97 | batch_thresh_maps = np.zeros([cfg.BATCH_SIZE, cfg.IMAGE_SIZE, cfg.IMAGE_SIZE], dtype=np.float32) 98 | batch_thresh_masks = np.zeros([cfg.BATCH_SIZE, cfg.IMAGE_SIZE, cfg.IMAGE_SIZE], dtype=np.float32) 99 | # batch_loss = np.zeros([cfg.BATCH_SIZE, ], dtype=np.float32) 100 | return [batch_images, batch_gts, batch_masks, batch_thresh_maps, batch_thresh_masks] 101 | 102 | data_path = cfg.TRAIN_DATA_PATH if train_or_val=='train' else cfg.VAL_DATA_PATH 103 | 104 | with open(data_path, encoding='utf8') as f: 105 | data = json.load(f) 106 | 107 | data_root_dir = data["data_root"] 108 | data_list = data["data_list"] 109 | 110 | image_paths = [] 111 | all_anns = [] 112 | 113 | for data_item in data_list: 114 | img_name = data_item["img_name"] 115 | annotations = data_item["annotations"] 116 | anns = [] 117 | for annotation in annotations: 118 | item = {} 119 | text = annotation["text"] 120 | poly = annotation["polygon"] 121 | if len(poly) < 3: 122 | continue 123 | item['text'] = text 124 | item['poly'] = poly 125 | anns.append(item) 126 | image_paths.append(osp.join(data_root_dir, img_name)) 127 | all_anns.append(anns) 128 | 129 | transform_aug = iaa.Sequential([iaa.Affine(rotate=(-10, 10)), iaa.Resize((0.5, 3.0))]) 130 | dataset_size = len(image_paths) 131 | indices = np.arange(dataset_size) 132 | if train_or_val=='train': 133 | np.random.shuffle(indices) 134 | 135 | current_idx = 0 136 | b = 0 137 | while True: 138 | if current_idx >= dataset_size: 139 | if train_or_val=='train': 140 | np.random.shuffle(indices) 141 | current_idx = 0 142 | if b == 0: 143 | batch_images, batch_gts, batch_masks, batch_thresh_maps, batch_thresh_masks = init_input() 144 | i = indices[current_idx] 145 | image_path = image_paths[i] 146 | anns = all_anns[i] 147 | """ 148 | [{'text': 'chinese', 'poly': [[17.86985870232934, 29.2253341902275], [18.465581783660582, 7.2334012599376365], [525.2796724953414, 20.9621104524324], [524.6839494140104, 42.954043382722375]]}, 149 | {'text': 'chinese', 'poly': [[9.746362138723043, 329.1153286941807], [10.667025082598343, 295.12779598373265], [589.454714475228, 310.8061443514931], [588.5340515313526, 344.79367706194114]]}] 150 | """ 151 | image = cv2.imread(image_path) 152 | # show_polys(image.copy(), anns, 'before_aug') 153 | if train_or_val=='train': 154 | transform_aug = transform_aug.to_deterministic() 155 | image, anns = transform(transform_aug, image, anns) 156 | image, anns = crop(image, anns) 157 | image, anns = resize(cfg.IMAGE_SIZE, image, anns) 158 | # show_polys(image.copy(), anns, 'after_aug') 159 | # cv2.waitKey(0) 160 | anns = [ann for ann in anns if Polygon(ann['poly']).is_valid] 161 | gt = np.zeros((cfg.IMAGE_SIZE, cfg.IMAGE_SIZE), dtype=np.float32) 162 | mask = np.ones((cfg.IMAGE_SIZE, cfg.IMAGE_SIZE), dtype=np.float32) 163 | thresh_map = np.zeros((cfg.IMAGE_SIZE, cfg.IMAGE_SIZE), dtype=np.float32) 164 | thresh_mask = np.zeros((cfg.IMAGE_SIZE, cfg.IMAGE_SIZE), dtype=np.float32) 165 | for ann in anns: 166 | poly = np.array(ann['poly']) 167 | height = max(poly[:, 1]) - min(poly[:, 1]) 168 | width = max(poly[:, 0]) - min(poly[:, 0]) 169 | polygon = Polygon(poly) 170 | # generate gt and mask 171 | if polygon.area < 1 or min(height, width) < cfg.MIN_TEXT_SIZE or ann['text'] in cfg.IGNORE_TEXT: 172 | cv2.fillPoly(mask, poly.astype(np.int32)[np.newaxis, :, :], 0) 173 | continue 174 | else: 175 | distance = polygon.area * (1 - np.power(cfg.SHRINK_RATIO, 2)) / polygon.length 176 | subject = [tuple(l) for l in ann['poly']] 177 | padding = pyclipper.PyclipperOffset() 178 | padding.AddPath(subject, pyclipper.JT_ROUND, pyclipper.ET_CLOSEDPOLYGON) 179 | shrinked = padding.Execute(-distance) 180 | if len(shrinked) == 0: 181 | cv2.fillPoly(mask, poly.astype(np.int32)[np.newaxis, :, :], 0) 182 | continue 183 | else: 184 | shrinked = np.array(shrinked[0]).reshape(-1, 2) 185 | if shrinked.shape[0] > 2 and Polygon(shrinked).is_valid: 186 | cv2.fillPoly(gt, [shrinked.astype(np.int32)], 1) 187 | else: 188 | cv2.fillPoly(mask, poly.astype(np.int32)[np.newaxis, :, :], 0) 189 | continue 190 | # generate thresh map and thresh mask 191 | draw_thresh_map(ann['poly'], thresh_map, thresh_mask, shrink_ratio=cfg.SHRINK_RATIO) 192 | thresh_map = thresh_map * (cfg.THRESH_MAX - cfg.THRESH_MIN) + cfg.THRESH_MIN 193 | 194 | image = image.astype(np.float32) 195 | image -= mean 196 | batch_images[b] = image 197 | batch_gts[b] = gt 198 | batch_masks[b] = mask 199 | batch_thresh_maps[b] = thresh_map 200 | batch_thresh_masks[b] = thresh_mask 201 | 202 | b += 1 203 | current_idx += 1 204 | if b == cfg.BATCH_SIZE: 205 | inputs = [batch_images, batch_gts, batch_masks, batch_thresh_maps, batch_thresh_masks] 206 | # outputs = batch_loss 207 | outputs = [] 208 | yield inputs, outputs 209 | b = 0 210 | 211 | -------------------------------------------------------------------------------- /models/backbones/resnet.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Time : 2020/6/16 23:47 3 | # @Author : zonas.wang 4 | # @Email : zonas.wang@gmail.com 5 | # @File : resnet.py 6 | import tensorflow as tf 7 | import tensorflow.keras as K 8 | from tensorflow.keras import layers as KL 9 | 10 | 11 | class BatchNormalization(KL.BatchNormalization): 12 | """ 13 | Identical to KL.BatchNormalization, but adds the option to freeze parameters. 14 | """ 15 | def __init__(self, freeze, *args, **kwargs): 16 | self.freeze = freeze 17 | super(BatchNormalization, self).__init__(*args, **kwargs) 18 | 19 | # set to non-trainable if freeze is true 20 | self.trainable = not self.freeze 21 | 22 | def call(self, *args, **kwargs): 23 | # Force test mode if frozen, otherwise use default behaviour (i.e., training=None). 24 | if self.freeze: 25 | kwargs['training'] = False 26 | return super(BatchNormalization, self).call(*args, **kwargs) 27 | 28 | def get_config(self): 29 | config = super(BatchNormalization, self).get_config() 30 | config.update({'freeze': self.freeze}) 31 | return config 32 | 33 | 34 | parameters = { 35 | "kernel_initializer": "he_normal" 36 | } 37 | 38 | class ResNet(tf.keras.Model): 39 | """ 40 | Constructs a `keras.models.Model` object using the given block count. 41 | :param inputs: input tensor (e.g. an instance of `keras.layers.Input`) 42 | :param blocks: the network’s residual architecture 43 | :param block: a residual block (e.g. an instance of `keras_resnet.blocks.basic_2d`) 44 | :param include_top: if true, includes classification layers 45 | :param classes: number of classes to classify (include_top must be true) 46 | :param freeze_bn: if true, freezes BatchNormalization layers (ie. no updates are done in these layers) 47 | :param numerical_names: list of bool, same size as blocks, used to indicate whether names of layers should include numbers or letters 48 | :return model: ResNet model with encoding output (if `include_top=False`) or classification output (if `include_top=True`) 49 | Usage: 50 | """ 51 | def __init__( 52 | self, 53 | inputs, 54 | blocks, 55 | block, 56 | include_top=True, 57 | classes=1000, 58 | freeze_bn=True, 59 | numerical_names=None, 60 | *args, 61 | **kwargs 62 | ): 63 | if K.backend.image_data_format() == "channels_last": 64 | axis = 3 65 | else: 66 | axis = 1 67 | 68 | if numerical_names is None: 69 | numerical_names = [True] * len(blocks) 70 | 71 | x = KL.Conv2D(64, (7, 7), strides=(2, 2), use_bias=False, name="conv1", padding="same")(inputs) 72 | x = BatchNormalization(axis=axis, epsilon=1e-5, freeze=freeze_bn, name="bn_conv1")(x) 73 | x = KL.Activation("relu", name="conv1_relu")(x) 74 | x = KL.MaxPooling2D((3, 3), strides=(2, 2), padding="same", name="pool1")(x) 75 | 76 | features = 64 77 | 78 | outputs = [] 79 | 80 | for stage_id, iterations in enumerate(blocks): 81 | for block_id in range(iterations): 82 | x = block( 83 | features, 84 | stage_id, 85 | block_id, 86 | numerical_name=(block_id > 0 and numerical_names[stage_id]), 87 | freeze_bn=freeze_bn 88 | )(x) 89 | 90 | features *= 2 91 | 92 | outputs.append(x) 93 | 94 | if include_top: 95 | assert classes > 0 96 | 97 | x = KL.GlobalAveragePooling2D(name="pool5")(x) 98 | x = KL.Dense(classes, activation="softmax", name="fc1000")(x) 99 | 100 | super(ResNet, self).__init__(inputs=inputs, outputs=x, *args, **kwargs) 101 | else: 102 | # Else output each stages features 103 | super(ResNet, self).__init__(inputs=inputs, outputs=outputs, *args, **kwargs) 104 | 105 | 106 | def resnet_basic( 107 | filters, 108 | stage=0, 109 | block=0, 110 | kernel_size=3, 111 | numerical_name=False, 112 | stride=None, 113 | freeze_bn=False 114 | ): 115 | """ 116 | A two-dimensional basic block. 117 | :param filters: the output’s feature space 118 | :param stage: int representing the stage of this block (starting from 0) 119 | :param block: int representing this block (starting from 0) 120 | :param kernel_size: size of the kernel 121 | :param numerical_name: if true, uses numbers to represent blocks instead of chars (ResNet{101, 152, 200}) 122 | :param stride: int representing the stride used in the shortcut and the first conv layer, default derives stride from block id 123 | :param freeze_bn: if true, freezes BatchNormalization layers (ie. no updates are done in these layers) 124 | Usage: 125 | """ 126 | if stride is None: 127 | if block != 0 or stage == 0: 128 | stride = 1 129 | else: 130 | stride = 2 131 | 132 | if K.backend.image_data_format() == "channels_last": 133 | axis = 3 134 | else: 135 | axis = 1 136 | 137 | if block > 0 and numerical_name: 138 | block_char = "b{}".format(block) 139 | else: 140 | block_char = chr(ord('a') + block) 141 | 142 | stage_char = str(stage + 2) 143 | 144 | def f(x): 145 | y = KL.ZeroPadding2D(padding=1, name="padding{}{}_branch2a".format(stage_char, block_char))(x) 146 | 147 | y = KL.Conv2D(filters, kernel_size, strides=stride, use_bias=False, name="res{}{}_branch2a".format(stage_char, block_char), **parameters)(y) 148 | 149 | y = BatchNormalization(axis=axis, epsilon=1e-5, freeze=freeze_bn, name="bn{}{}_branch2a".format(stage_char, block_char))(y) 150 | 151 | y = KL.Activation("relu", name="res{}{}_branch2a_relu".format(stage_char, block_char))(y) 152 | 153 | y = KL.ZeroPadding2D(padding=1, name="padding{}{}_branch2b".format(stage_char, block_char))(y) 154 | 155 | y = KL.Conv2D(filters, kernel_size, use_bias=False, name="res{}{}_branch2b".format(stage_char, block_char), **parameters)(y) 156 | 157 | y = BatchNormalization(axis=axis, epsilon=1e-5, freeze=freeze_bn, name="bn{}{}_branch2b".format(stage_char, block_char))(y) 158 | 159 | if block == 0: 160 | shortcut = KL.Conv2D(filters, (1, 1), strides=stride, use_bias=False, name="res{}{}_branch1".format(stage_char, block_char), **parameters)(x) 161 | 162 | shortcut = BatchNormalization(axis=axis, epsilon=1e-5, freeze=freeze_bn, name="bn{}{}_branch1".format(stage_char, block_char))(shortcut) 163 | else: 164 | shortcut = x 165 | 166 | y = KL.Add(name="res{}{}".format(stage_char, block_char))([y, shortcut]) 167 | 168 | y = KL.Activation("relu", name="res{}{}_relu".format(stage_char, block_char))(y) 169 | 170 | return y 171 | 172 | return f 173 | 174 | 175 | def resnet_bottleneck( 176 | filters, 177 | stage=0, 178 | block=0, 179 | kernel_size=3, 180 | numerical_name=False, 181 | stride=None, 182 | freeze_bn=False 183 | ): 184 | """ 185 | A two-dimensional bottleneck block. 186 | :param filters: the output’s feature space 187 | :param stage: int representing the stage of this block (starting from 0) 188 | :param block: int representing this block (starting from 0) 189 | :param kernel_size: size of the kernel 190 | :param numerical_name: if true, uses numbers to represent blocks instead of chars (ResNet{101, 152, 200}) 191 | :param stride: int representing the stride used in the shortcut and the first conv layer, default derives stride from block id 192 | :param freeze_bn: if true, freezes BatchNormalization layers (ie. no updates are done in these layers) 193 | Usage: 194 | """ 195 | if stride is None: 196 | if block != 0 or stage == 0: 197 | stride = 1 198 | else: 199 | stride = 2 200 | 201 | if K.backend.image_data_format() == "channels_last": 202 | axis = 3 203 | else: 204 | axis = 1 205 | 206 | if block > 0 and numerical_name: 207 | block_char = "b{}".format(block) 208 | else: 209 | block_char = chr(ord('a') + block) 210 | 211 | stage_char = str(stage + 2) 212 | 213 | def f(x): 214 | y = KL.Conv2D(filters, (1, 1), strides=stride, use_bias=False, name="res{}{}_branch2a".format(stage_char, block_char), **parameters)(x) 215 | 216 | y = BatchNormalization(axis=axis, epsilon=1e-5, freeze=freeze_bn, name="bn{}{}_branch2a".format(stage_char, block_char))(y) 217 | 218 | y = KL.Activation("relu", name="res{}{}_branch2a_relu".format(stage_char, block_char))(y) 219 | 220 | y = KL.ZeroPadding2D(padding=1, name="padding{}{}_branch2b".format(stage_char, block_char))(y) 221 | 222 | y = KL.Conv2D(filters, kernel_size, use_bias=False, name="res{}{}_branch2b".format(stage_char, block_char), **parameters)(y) 223 | 224 | y = BatchNormalization(axis=axis, epsilon=1e-5, freeze=freeze_bn, name="bn{}{}_branch2b".format(stage_char, block_char))(y) 225 | 226 | y = KL.Activation("relu", name="res{}{}_branch2b_relu".format(stage_char, block_char))(y) 227 | 228 | y = KL.Conv2D(filters * 4, (1, 1), use_bias=False, name="res{}{}_branch2c".format(stage_char, block_char), **parameters)(y) 229 | 230 | y = BatchNormalization(axis=axis, epsilon=1e-5, freeze=freeze_bn, name="bn{}{}_branch2c".format(stage_char, block_char))(y) 231 | 232 | if block == 0: 233 | shortcut = KL.Conv2D(filters * 4, (1, 1), strides=stride, use_bias=False, name="res{}{}_branch1".format(stage_char, block_char), **parameters)(x) 234 | 235 | shortcut = BatchNormalization(axis=axis, epsilon=1e-5, freeze=freeze_bn, name="bn{}{}_branch1".format(stage_char, block_char))(shortcut) 236 | else: 237 | shortcut = x 238 | 239 | y = KL.Add(name="res{}{}".format(stage_char, block_char))([y, shortcut]) 240 | 241 | y = KL.Activation("relu", name="res{}{}_relu".format(stage_char, block_char))(y) 242 | 243 | return y 244 | 245 | return f 246 | 247 | class ResNet18(ResNet): 248 | """ 249 | Constructs a `keras.models.Model` according to the ResNet18 specifications. 250 | :param inputs: input tensor (e.g. an instance of `keras.layers.Input`) 251 | :param blocks: the network’s residual architecture 252 | :param include_top: if true, includes classification layers 253 | :param classes: number of classes to classify (include_top must be true) 254 | :param freeze_bn: if true, freezes BatchNormalization layers (ie. no updates are done in these layers) 255 | :return model: ResNet model with encoding output (if `include_top=False`) or classification output (if `include_top=True`) 256 | Usage: 257 | """ 258 | def __init__(self, inputs, blocks=None, include_top=True, classes=1000, freeze_bn=False, *args, **kwargs): 259 | if blocks is None: 260 | blocks = [2, 2, 2, 2] 261 | 262 | super(ResNet18, self).__init__( 263 | inputs, 264 | blocks, 265 | block=resnet_basic, 266 | include_top=include_top, 267 | classes=classes, 268 | freeze_bn=freeze_bn, 269 | *args, 270 | **kwargs 271 | ) 272 | 273 | 274 | class ResNet34(ResNet): 275 | """ 276 | Constructs a `keras.models.Model` according to the ResNet34 specifications. 277 | :param inputs: input tensor (e.g. an instance of `keras.layers.Input`) 278 | :param blocks: the network’s residual architecture 279 | :param include_top: if true, includes classification layers 280 | :param classes: number of classes to classify (include_top must be true) 281 | :param freeze_bn: if true, freezes BatchNormalization layers (ie. no updates are done in these layers) 282 | :return model: ResNet model with encoding output (if `include_top=False`) or classification output (if `include_top=True`) 283 | Usage: 284 | """ 285 | def __init__(self, inputs, blocks=None, include_top=True, classes=1000, freeze_bn=False, *args, **kwargs): 286 | if blocks is None: 287 | blocks = [3, 4, 6, 3] 288 | 289 | super(ResNet34, self).__init__( 290 | inputs, 291 | blocks, 292 | block=resnet_basic, 293 | include_top=include_top, 294 | classes=classes, 295 | freeze_bn=freeze_bn, 296 | *args, 297 | **kwargs 298 | ) 299 | 300 | 301 | class ResNet50(ResNet): 302 | """ 303 | Constructs a `keras.models.Model` according to the ResNet50 specifications. 304 | :param inputs: input tensor (e.g. an instance of `keras.layers.Input`) 305 | :param blocks: the network’s residual architecture 306 | :param include_top: if true, includes classification layers 307 | :param classes: number of classes to classify (include_top must be true) 308 | :param freeze_bn: if true, freezes BatchNormalization layers (ie. no updates are done in these layers) 309 | :return model: ResNet model with encoding output (if `include_top=False`) or classification output (if `include_top=True`) 310 | Usage: 311 | """ 312 | def __init__(self, inputs, blocks=None, include_top=True, classes=1000, freeze_bn=False, *args, **kwargs): 313 | if blocks is None: 314 | blocks = [3, 4, 6, 3] 315 | 316 | numerical_names = [False, False, False, False] 317 | 318 | super(ResNet50, self).__init__( 319 | inputs, 320 | blocks, 321 | numerical_names=numerical_names, 322 | block=resnet_bottleneck, 323 | include_top=include_top, 324 | classes=classes, 325 | freeze_bn=freeze_bn, 326 | *args, 327 | **kwargs 328 | ) 329 | 330 | 331 | class ResNet101(ResNet): 332 | """ 333 | Constructs a `keras.models.Model` according to the ResNet101 specifications. 334 | :param inputs: input tensor (e.g. an instance of `keras.layers.Input`) 335 | :param blocks: the network’s residual architecture 336 | :param include_top: if true, includes classification layers 337 | :param classes: number of classes to classify (include_top must be true) 338 | :param freeze_bn: if true, freezes BatchNormalization layers (ie. no updates are done in these layers) 339 | :return model: ResNet model with encoding output (if `include_top=False`) or classification output (if `include_top=True`) 340 | Usage: 341 | """ 342 | def __init__(self, inputs, blocks=None, include_top=True, classes=1000, freeze_bn=False, *args, **kwargs): 343 | if blocks is None: 344 | blocks = [3, 4, 23, 3] 345 | 346 | numerical_names = [False, True, True, False] 347 | 348 | super(ResNet101, self).__init__( 349 | inputs, 350 | blocks, 351 | numerical_names=numerical_names, 352 | block=resnet_bottleneck, 353 | include_top=include_top, 354 | classes=classes, 355 | freeze_bn=freeze_bn, 356 | *args, 357 | **kwargs 358 | ) 359 | 360 | 361 | class ResNet152(ResNet): 362 | """ 363 | Constructs a `keras.models.Model` according to the ResNet152 specifications. 364 | :param inputs: input tensor (e.g. an instance of `keras.layers.Input`) 365 | :param blocks: the network’s residual architecture 366 | :param include_top: if true, includes classification layers 367 | :param classes: number of classes to classify (include_top must be true) 368 | :param freeze_bn: if true, freezes BatchNormalization layers (ie. no updates are done in these layers) 369 | :return model: ResNet model with encoding output (if `include_top=False`) or classification output (if `include_top=True`) 370 | Usage: 371 | """ 372 | def __init__(self, inputs, blocks=None, include_top=True, classes=1000, freeze_bn=False, *args, **kwargs): 373 | if blocks is None: 374 | blocks = [3, 8, 36, 3] 375 | 376 | numerical_names = [False, True, True, False] 377 | 378 | super(ResNet152, self).__init__( 379 | inputs, 380 | blocks, 381 | numerical_names=numerical_names, 382 | block=resnet_bottleneck, 383 | include_top=include_top, 384 | classes=classes, 385 | freeze_bn=freeze_bn, 386 | *args, 387 | **kwargs 388 | ) 389 | 390 | 391 | class ResNet200(ResNet): 392 | """ 393 | Constructs a `keras.models.Model` according to the ResNet200 specifications. 394 | :param inputs: input tensor (e.g. an instance of `keras.layers.Input`) 395 | :param blocks: the network’s residual architecture 396 | :param include_top: if true, includes classification layers 397 | :param classes: number of classes to classify (include_top must be true) 398 | :param freeze_bn: if true, freezes BatchNormalization layers (ie. no updates are done in these layers) 399 | :return model: ResNet model with encoding output (if `include_top=False`) or classification output (if `include_top=True`) 400 | Usage: 401 | """ 402 | def __init__(self, inputs, blocks=None, include_top=True, classes=1000, freeze_bn=False, *args, **kwargs): 403 | if blocks is None: 404 | blocks = [3, 24, 36, 3] 405 | 406 | numerical_names = [False, True, True, False] 407 | 408 | super(ResNet200, self).__init__( 409 | inputs, 410 | blocks, 411 | numerical_names=numerical_names, 412 | block=resnet_bottleneck, 413 | include_top=include_top, 414 | classes=classes, 415 | freeze_bn=freeze_bn, 416 | *args, 417 | **kwargs 418 | ) 419 | -------------------------------------------------------------------------------- /datasets/data/val.json: -------------------------------------------------------------------------------- 1 | { 2 | "data_root": "datasets/data/test", 3 | "data_list": [ 4 | { 5 | "img_name": "X51005230616.jpg", 6 | "annotations": [ 7 | { 8 | "polygon": [ 9 | [ 10 | 244.0, 11 | 105.0 12 | ], 13 | [ 14 | 327.0, 15 | 105.0 16 | ], 17 | [ 18 | 327.0, 19 | 167.0 20 | ], 21 | [ 22 | 244.0, 23 | 167.0 24 | ] 25 | ], 26 | "text": "190", 27 | "illegibility": false, 28 | "language": "Latin", 29 | "chars": [ 30 | { 31 | "polygon": [], 32 | "char": "", 33 | "illegibility": false, 34 | "language": "Latin" 35 | } 36 | ] 37 | }, 38 | { 39 | "polygon": [ 40 | [ 41 | 37.0, 42 | 188.0 43 | ], 44 | [ 45 | 477.0, 46 | 188.0 47 | ], 48 | [ 49 | 477.0, 50 | 225.0 51 | ], 52 | [ 53 | 37.0, 54 | 225.0 55 | ] 56 | ], 57 | "text": "GERBANG ALAF RESTAURANTS SDN BHD", 58 | "illegibility": false, 59 | "language": "Latin", 60 | "chars": [ 61 | { 62 | "polygon": [], 63 | "char": "", 64 | "illegibility": false, 65 | "language": "Latin" 66 | } 67 | ] 68 | }, 69 | { 70 | "polygon": [ 71 | [ 72 | 192.0, 73 | 227.0 74 | ], 75 | [ 76 | 310.0, 77 | 227.0 78 | ], 79 | [ 80 | 310.0, 81 | 260.0 82 | ], 83 | [ 84 | 192.0, 85 | 260.0 86 | ] 87 | ], 88 | "text": "(65351-M)", 89 | "illegibility": false, 90 | "language": "Latin", 91 | "chars": [ 92 | { 93 | "polygon": [], 94 | "char": "", 95 | "illegibility": false, 96 | "language": "Latin" 97 | } 98 | ] 99 | }, 100 | { 101 | "polygon": [ 102 | [ 103 | 136.0, 104 | 256.0 105 | ], 106 | [ 107 | 366.0, 108 | 256.0 109 | ], 110 | [ 111 | 366.0, 112 | 289.0 113 | ], 114 | [ 115 | 136.0, 116 | 289.0 117 | ] 118 | ], 119 | "text": "FORMERLY KNOWN AS", 120 | "illegibility": false, 121 | "language": "Latin", 122 | "chars": [ 123 | { 124 | "polygon": [], 125 | "char": "", 126 | "illegibility": false, 127 | "language": "Latin" 128 | } 129 | ] 130 | }, 131 | { 132 | "polygon": [ 133 | [ 134 | 27.0, 135 | 287.0 136 | ], 137 | [ 138 | 480.0, 139 | 287.0 140 | ], 141 | [ 142 | 480.0, 143 | 324.0 144 | ], 145 | [ 146 | 27.0, 147 | 324.0 148 | ] 149 | ], 150 | "text": "GOLDEN ARCHES RESTAURANTS SDN BHD", 151 | "illegibility": false, 152 | "language": "Latin", 153 | "chars": [ 154 | { 155 | "polygon": [], 156 | "char": "", 157 | "illegibility": false, 158 | "language": "Latin" 159 | } 160 | ] 161 | }, 162 | { 163 | "polygon": [ 164 | [ 165 | 103.0, 166 | 323.0 167 | ], 168 | [ 169 | 419.0, 170 | 323.0 171 | ], 172 | [ 173 | 419.0, 174 | 362.0 175 | ], 176 | [ 177 | 103.0, 178 | 362.0 179 | ] 180 | ], 181 | "text": "LICENSEE OF MCDONALD'S", 182 | "illegibility": false, 183 | "language": "Latin", 184 | "chars": [ 185 | { 186 | "polygon": [], 187 | "char": "", 188 | "illegibility": false, 189 | "language": "Latin" 190 | } 191 | ] 192 | }, 193 | { 194 | "polygon": [ 195 | [ 196 | 4.0, 197 | 356.0 198 | ], 199 | [ 200 | 529.0, 201 | 356.0 202 | ], 203 | [ 204 | 529.0, 205 | 391.0 206 | ], 207 | [ 208 | 4.0, 209 | 391.0 210 | ] 211 | ], 212 | "text": " DAMANSARA UPTOWN3", 213 | "illegibility": false, 214 | "language": "Latin", 215 | "chars": [ 216 | { 217 | "polygon": [], 218 | "char": "", 219 | "illegibility": false, 220 | "language": "Latin" 221 | } 222 | ] 223 | }, 224 | { 225 | "polygon": [ 226 | [ 227 | 2.0, 228 | 389.0 229 | ], 230 | [ 231 | 533.0, 232 | 389.0 233 | ], 234 | [ 235 | 533.0, 236 | 428.0 237 | ], 238 | [ 239 | 2.0, 240 | 428.0 241 | ] 242 | ], 243 | "text": "47400 PETALING JAYA", 244 | "illegibility": false, 245 | "language": "Latin", 246 | "chars": [ 247 | { 248 | "polygon": [], 249 | "char": "", 250 | "illegibility": false, 251 | "language": "Latin" 252 | } 253 | ] 254 | }, 255 | { 256 | "polygon": [ 257 | [ 258 | 205.0, 259 | 422.0 260 | ], 261 | [ 262 | 313.0, 263 | 422.0 264 | ], 265 | [ 266 | 313.0, 267 | 453.0 268 | ], 269 | [ 270 | 205.0, 271 | 453.0 272 | ] 273 | ], 274 | "text": "SELANGOR", 275 | "illegibility": false, 276 | "language": "Latin", 277 | "chars": [ 278 | { 279 | "polygon": [], 280 | "char": "", 281 | "illegibility": false, 282 | "language": "Latin" 283 | } 284 | ] 285 | }, 286 | { 287 | "polygon": [ 288 | [ 289 | 101.0, 290 | 451.0 291 | ], 292 | [ 293 | 434.0, 294 | 451.0 295 | ], 296 | [ 297 | 434.0, 298 | 486.0 299 | ], 300 | [ 301 | 101.0, 302 | 486.0 303 | ] 304 | ], 305 | "text": "(GST ID NO: 000504664064)", 306 | "illegibility": false, 307 | "language": "Latin", 308 | "chars": [ 309 | { 310 | "polygon": [], 311 | "char": "", 312 | "illegibility": false, 313 | "language": "Latin" 314 | } 315 | ] 316 | }, 317 | { 318 | "polygon": [ 319 | [ 320 | 23.0, 321 | 484.0 322 | ], 323 | [ 324 | 505.0, 325 | 484.0 326 | ], 327 | [ 328 | 505.0, 329 | 519.0 330 | ], 331 | [ 332 | 23.0, 333 | 519.0 334 | ] 335 | ], 336 | "text": "MCDONALD'S PETRONAS TAMAN MELATI DT", 337 | "illegibility": false, 338 | "language": "Latin", 339 | "chars": [ 340 | { 341 | "polygon": [], 342 | "char": "", 343 | "illegibility": false, 344 | "language": "Latin" 345 | } 346 | ] 347 | }, 348 | { 349 | "polygon": [ 350 | [ 351 | 112.0, 352 | 517.0 353 | ], 354 | [ 355 | 418.0, 356 | 517.0 357 | ], 358 | [ 359 | 418.0, 360 | 550.0 361 | ], 362 | [ 363 | 112.0, 364 | 550.0 365 | ] 366 | ], 367 | "text": "TEL NO. : 03-4147-3107", 368 | "illegibility": false, 369 | "language": "Latin", 370 | "chars": [ 371 | { 372 | "polygon": [], 373 | "char": "", 374 | "illegibility": false, 375 | "language": "Latin" 376 | } 377 | ] 378 | }, 379 | { 380 | "polygon": [ 381 | [ 382 | 188.0, 383 | 552.0 384 | ], 385 | [ 386 | 345.0, 387 | 552.0 388 | ], 389 | [ 390 | 345.0, 391 | 583.0 392 | ], 393 | [ 394 | 188.0, 395 | 583.0 396 | ] 397 | ], 398 | "text": "TAX INVOICE", 399 | "illegibility": false, 400 | "language": "Latin", 401 | "chars": [ 402 | { 403 | "polygon": [], 404 | "char": "", 405 | "illegibility": false, 406 | "language": "Latin" 407 | } 408 | ] 409 | }, 410 | { 411 | "polygon": [ 412 | [ 413 | 194.0, 414 | 616.0 415 | ], 416 | [ 417 | 347.0, 418 | 616.0 419 | ], 420 | [ 421 | 347.0, 422 | 653.0 423 | ], 424 | [ 425 | 194.0, 426 | 653.0 427 | ] 428 | ], 429 | "text": "MFY SIDE 1", 430 | "illegibility": false, 431 | "language": "Latin", 432 | "chars": [ 433 | { 434 | "polygon": [], 435 | "char": "", 436 | "illegibility": false, 437 | "language": "Latin" 438 | } 439 | ] 440 | }, 441 | { 442 | "polygon": [ 443 | [ 444 | 4.0, 445 | 680.0 446 | ], 447 | [ 448 | 277.0, 449 | 680.0 450 | ], 451 | [ 452 | 277.0, 453 | 721.0 454 | ], 455 | [ 456 | 4.0, 457 | 721.0 458 | ] 459 | ], 460 | "text": "INV# 141900016842", 461 | "illegibility": false, 462 | "language": "Latin", 463 | "chars": [ 464 | { 465 | "polygon": [], 466 | "char": "", 467 | "illegibility": false, 468 | "language": "Latin" 469 | } 470 | ] 471 | }, 472 | { 473 | "polygon": [ 474 | [ 475 | 4.0, 476 | 715.0 477 | ], 478 | [ 479 | 502.0, 480 | 715.0 481 | ], 482 | [ 483 | 502.0, 484 | 752.0 485 | ], 486 | [ 487 | 4.0, 488 | 752.0 489 | ] 490 | ], 491 | "text": "ORD #90 -REG #19- 18/01/2018 17:09:21", 492 | "illegibility": false, 493 | "language": "Latin", 494 | "chars": [ 495 | { 496 | "polygon": [], 497 | "char": "", 498 | "illegibility": false, 499 | "language": "Latin" 500 | } 501 | ] 502 | }, 503 | { 504 | "polygon": [ 505 | [ 506 | 4.0, 507 | 751.0 508 | ], 509 | [ 510 | 118.0, 511 | 751.0 512 | ], 513 | [ 514 | 118.0, 515 | 782.0 516 | ], 517 | [ 518 | 4.0, 519 | 782.0 520 | ] 521 | ], 522 | "text": "QTY ITEM", 523 | "illegibility": false, 524 | "language": "Latin", 525 | "chars": [ 526 | { 527 | "polygon": [], 528 | "char": "", 529 | "illegibility": false, 530 | "language": "Latin" 531 | } 532 | ] 533 | }, 534 | { 535 | "polygon": [ 536 | [ 537 | 29.0, 538 | 782.0 539 | ], 540 | [ 541 | 240.0, 542 | 782.0 543 | ], 544 | [ 545 | 240.0, 546 | 813.0 547 | ], 548 | [ 549 | 29.0, 550 | 813.0 551 | ] 552 | ], 553 | "text": "2 M SPICYDELUXE", 554 | "illegibility": false, 555 | "language": "Latin", 556 | "chars": [ 557 | { 558 | "polygon": [], 559 | "char": "", 560 | "illegibility": false, 561 | "language": "Latin" 562 | } 563 | ] 564 | }, 565 | { 566 | "polygon": [ 567 | [ 568 | 50.0, 569 | 819.0 570 | ], 571 | [ 572 | 166.0, 573 | 819.0 574 | ], 575 | [ 576 | 166.0, 577 | 850.0 578 | ], 579 | [ 580 | 50.0, 581 | 850.0 582 | ] 583 | ], 584 | "text": "2 M COKE", 585 | "illegibility": false, 586 | "language": "Latin", 587 | "chars": [ 588 | { 589 | "polygon": [], 590 | "char": "", 591 | "illegibility": false, 592 | "language": "Latin" 593 | } 594 | ] 595 | }, 596 | { 597 | "polygon": [ 598 | [ 599 | 52.0, 600 | 848.0 601 | ], 602 | [ 603 | 191.0, 604 | 848.0 605 | ], 606 | [ 607 | 191.0, 608 | 887.0 609 | ], 610 | [ 611 | 52.0, 612 | 887.0 613 | ] 614 | ], 615 | "text": "2 M FRIES", 616 | "illegibility": false, 617 | "language": "Latin", 618 | "chars": [ 619 | { 620 | "polygon": [], 621 | "char": "", 622 | "illegibility": false, 623 | "language": "Latin" 624 | } 625 | ] 626 | }, 627 | { 628 | "polygon": [ 629 | [ 630 | 31.0, 631 | 881.0 632 | ], 633 | [ 634 | 238.0, 635 | 881.0 636 | ], 637 | [ 638 | 238.0, 639 | 914.0 640 | ], 641 | [ 642 | 31.0, 643 | 914.0 644 | ] 645 | ], 646 | "text": "1 M GRILCHICBGR", 647 | "illegibility": false, 648 | "language": "Latin", 649 | "chars": [ 650 | { 651 | "polygon": [], 652 | "char": "", 653 | "illegibility": false, 654 | "language": "Latin" 655 | } 656 | ] 657 | }, 658 | { 659 | "polygon": [ 660 | [ 661 | 54.0, 662 | 914.0 663 | ], 664 | [ 665 | 168.0, 666 | 914.0 667 | ], 668 | [ 669 | 168.0, 670 | 945.0 671 | ], 672 | [ 673 | 54.0, 674 | 945.0 675 | ] 676 | ], 677 | "text": "1 M COKE", 678 | "illegibility": false, 679 | "language": "Latin", 680 | "chars": [ 681 | { 682 | "polygon": [], 683 | "char": "", 684 | "illegibility": false, 685 | "language": "Latin" 686 | } 687 | ] 688 | }, 689 | { 690 | "polygon": [ 691 | [ 692 | 56.0, 693 | 949.0 694 | ], 695 | [ 696 | 186.0, 697 | 949.0 698 | ], 699 | [ 700 | 186.0, 701 | 976.0 702 | ], 703 | [ 704 | 56.0, 705 | 976.0 706 | ] 707 | ], 708 | "text": "1 M FRIES", 709 | "illegibility": false, 710 | "language": "Latin", 711 | "chars": [ 712 | { 713 | "polygon": [], 714 | "char": "", 715 | "illegibility": false, 716 | "language": "Latin" 717 | } 718 | ] 719 | }, 720 | { 721 | "polygon": [ 722 | [ 723 | 27.0, 724 | 980.0 725 | ], 726 | [ 727 | 205.0, 728 | 980.0 729 | ], 730 | [ 731 | 205.0, 732 | 1015.0 733 | ], 734 | [ 735 | 27.0, 736 | 1015.0 737 | ] 738 | ], 739 | "text": "1 SMALL CONE", 740 | "illegibility": false, 741 | "language": "Latin", 742 | "chars": [ 743 | { 744 | "polygon": [], 745 | "char": "", 746 | "illegibility": false, 747 | "language": "Latin" 748 | } 749 | ] 750 | }, 751 | { 752 | "polygon": [ 753 | [ 754 | 5.0, 755 | 1048.0 756 | ], 757 | [ 758 | 321.0, 759 | 1048.0 760 | ], 761 | [ 762 | 321.0, 763 | 1080.0 764 | ], 765 | [ 766 | 5.0, 767 | 1080.0 768 | ] 769 | ], 770 | "text": "TAKEOUT TOTAL (INCL GST)", 771 | "illegibility": false, 772 | "language": "Latin", 773 | "chars": [ 774 | { 775 | "polygon": [], 776 | "char": "", 777 | "illegibility": false, 778 | "language": "Latin" 779 | } 780 | ] 781 | }, 782 | { 783 | "polygon": [ 784 | [ 785 | 5.0, 786 | 1082.0 787 | ], 788 | [ 789 | 183.0, 790 | 1082.0 791 | ], 792 | [ 793 | 183.0, 794 | 1111.0 795 | ], 796 | [ 797 | 5.0, 798 | 1111.0 799 | ] 800 | ], 801 | "text": "TOTAL ROUNDED", 802 | "illegibility": false, 803 | "language": "Latin", 804 | "chars": [ 805 | { 806 | "polygon": [], 807 | "char": "", 808 | "illegibility": false, 809 | "language": "Latin" 810 | } 811 | ] 812 | }, 813 | { 814 | "polygon": [ 815 | [ 816 | 2.0, 817 | 1110.0 818 | ], 819 | [ 820 | 190.0, 821 | 1110.0 822 | ], 823 | [ 824 | 190.0, 825 | 1143.0 826 | ], 827 | [ 828 | 2.0, 829 | 1143.0 830 | ] 831 | ], 832 | "text": "CASH TENDERED", 833 | "illegibility": false, 834 | "language": "Latin", 835 | "chars": [ 836 | { 837 | "polygon": [], 838 | "char": "", 839 | "illegibility": false, 840 | "language": "Latin" 841 | } 842 | ] 843 | }, 844 | { 845 | "polygon": [ 846 | [ 847 | 5.0, 848 | 1148.0 849 | ], 850 | [ 851 | 90.0, 852 | 1148.0 853 | ], 854 | [ 855 | 90.0, 856 | 1179.0 857 | ], 858 | [ 859 | 5.0, 860 | 1179.0 861 | ] 862 | ], 863 | "text": "CHANGE", 864 | "illegibility": false, 865 | "language": "Latin", 866 | "chars": [ 867 | { 868 | "polygon": [], 869 | "char": "", 870 | "illegibility": false, 871 | "language": "Latin" 872 | } 873 | ] 874 | }, 875 | { 876 | "polygon": [ 877 | [ 878 | 8.0, 879 | 1212.0 880 | ], 881 | [ 882 | 285.0, 883 | 1212.0 884 | ], 885 | [ 886 | 285.0, 887 | 1249.0 888 | ], 889 | [ 890 | 8.0, 891 | 1249.0 892 | ] 893 | ], 894 | "text": "TOTAL INCLUDES 6% GST", 895 | "illegibility": false, 896 | "language": "Latin", 897 | "chars": [ 898 | { 899 | "polygon": [], 900 | "char": "", 901 | "illegibility": false, 902 | "language": "Latin" 903 | } 904 | ] 905 | }, 906 | { 907 | "polygon": [ 908 | [ 909 | 455.0, 910 | 751.0 911 | ], 912 | [ 913 | 527.0, 914 | 751.0 915 | ], 916 | [ 917 | 527.0, 918 | 784.0 919 | ], 920 | [ 921 | 455.0, 922 | 784.0 923 | ] 924 | ], 925 | "text": "TOTAL", 926 | "illegibility": false, 927 | "language": "Latin", 928 | "chars": [ 929 | { 930 | "polygon": [], 931 | "char": "", 932 | "illegibility": false, 933 | "language": "Latin" 934 | } 935 | ] 936 | }, 937 | { 938 | "polygon": [ 939 | [ 940 | 455.0, 941 | 788.0 942 | ], 943 | [ 944 | 525.0, 945 | 788.0 946 | ], 947 | [ 948 | 525.0, 949 | 817.0 950 | ], 951 | [ 952 | 455.0, 953 | 817.0 954 | ] 955 | ], 956 | "text": "25.40", 957 | "illegibility": false, 958 | "language": "Latin", 959 | "chars": [ 960 | { 961 | "polygon": [], 962 | "char": "", 963 | "illegibility": false, 964 | "language": "Latin" 965 | } 966 | ] 967 | }, 968 | { 969 | "polygon": [ 970 | [ 971 | 459.0, 972 | 881.0 973 | ], 974 | [ 975 | 529.0, 976 | 881.0 977 | ], 978 | [ 979 | 529.0, 980 | 916.0 981 | ], 982 | [ 983 | 459.0, 984 | 916.0 985 | ] 986 | ], 987 | "text": "12.50", 988 | "illegibility": false, 989 | "language": "Latin", 990 | "chars": [ 991 | { 992 | "polygon": [], 993 | "char": "", 994 | "illegibility": false, 995 | "language": "Latin" 996 | } 997 | ] 998 | }, 999 | { 1000 | "polygon": [ 1001 | [ 1002 | 467.0, 1003 | 982.0 1004 | ], 1005 | [ 1006 | 533.0, 1007 | 982.0 1008 | ], 1009 | [ 1010 | 533.0, 1011 | 1015.0 1012 | ], 1013 | [ 1014 | 467.0, 1015 | 1015.0 1016 | ] 1017 | ], 1018 | "text": "1.00", 1019 | "illegibility": false, 1020 | "language": "Latin", 1021 | "chars": [ 1022 | { 1023 | "polygon": [], 1024 | "char": "", 1025 | "illegibility": false, 1026 | "language": "Latin" 1027 | } 1028 | ] 1029 | }, 1030 | { 1031 | "polygon": [ 1032 | [ 1033 | 451.0, 1034 | 1048.0 1035 | ], 1036 | [ 1037 | 525.0, 1038 | 1048.0 1039 | ], 1040 | [ 1041 | 525.0, 1042 | 1079.0 1043 | ], 1044 | [ 1045 | 451.0, 1046 | 1079.0 1047 | ] 1048 | ], 1049 | "text": "38.90", 1050 | "illegibility": false, 1051 | "language": "Latin", 1052 | "chars": [ 1053 | { 1054 | "polygon": [], 1055 | "char": "", 1056 | "illegibility": false, 1057 | "language": "Latin" 1058 | } 1059 | ] 1060 | }, 1061 | { 1062 | "polygon": [ 1063 | [ 1064 | 451.0, 1065 | 1084.0 1066 | ], 1067 | [ 1068 | 528.0, 1069 | 1084.0 1070 | ], 1071 | [ 1072 | 528.0, 1073 | 1113.0 1074 | ], 1075 | [ 1076 | 451.0, 1077 | 1113.0 1078 | ] 1079 | ], 1080 | "text": "38.90", 1081 | "illegibility": false, 1082 | "language": "Latin", 1083 | "chars": [ 1084 | { 1085 | "polygon": [], 1086 | "char": "", 1087 | "illegibility": false, 1088 | "language": "Latin" 1089 | } 1090 | ] 1091 | }, 1092 | { 1093 | "polygon": [ 1094 | [ 1095 | 447.0, 1096 | 1112.0 1097 | ], 1098 | [ 1099 | 528.0, 1100 | 1112.0 1101 | ], 1102 | [ 1103 | 528.0, 1104 | 1143.0 1105 | ], 1106 | [ 1107 | 447.0, 1108 | 1143.0 1109 | ] 1110 | ], 1111 | "text": "50.00", 1112 | "illegibility": false, 1113 | "language": "Latin", 1114 | "chars": [ 1115 | { 1116 | "polygon": [], 1117 | "char": "", 1118 | "illegibility": false, 1119 | "language": "Latin" 1120 | } 1121 | ] 1122 | }, 1123 | { 1124 | "polygon": [ 1125 | [ 1126 | 455.0, 1127 | 1150.0 1128 | ], 1129 | [ 1130 | 527.0, 1131 | 1150.0 1132 | ], 1133 | [ 1134 | 527.0, 1135 | 1177.0 1136 | ], 1137 | [ 1138 | 455.0, 1139 | 1177.0 1140 | ] 1141 | ], 1142 | "text": "11.10", 1143 | "illegibility": false, 1144 | "language": "Latin", 1145 | "chars": [ 1146 | { 1147 | "polygon": [], 1148 | "char": "", 1149 | "illegibility": false, 1150 | "language": "Latin" 1151 | } 1152 | ] 1153 | }, 1154 | { 1155 | "polygon": [ 1156 | [ 1157 | 465.0, 1158 | 1214.0 1159 | ], 1160 | [ 1161 | 527.0, 1162 | 1214.0 1163 | ], 1164 | [ 1165 | 527.0, 1166 | 1245.0 1167 | ], 1168 | [ 1169 | 465.0, 1170 | 1245.0 1171 | ] 1172 | ], 1173 | "text": "2.20", 1174 | "illegibility": false, 1175 | "language": "Latin", 1176 | "chars": [ 1177 | { 1178 | "polygon": [], 1179 | "char": "", 1180 | "illegibility": false, 1181 | "language": "Latin" 1182 | } 1183 | ] 1184 | }, 1185 | { 1186 | "polygon": [ 1187 | [ 1188 | 108.0, 1189 | 1307.0 1190 | ], 1191 | [ 1192 | 476.0, 1193 | 1307.0 1194 | ], 1195 | [ 1196 | 476.0, 1197 | 1342.0 1198 | ], 1199 | [ 1200 | 108.0, 1201 | 1342.0 1202 | ] 1203 | ], 1204 | "text": "LOVE TO HEAR YOUR FEEDBACK!", 1205 | "illegibility": false, 1206 | "language": "Latin", 1207 | "chars": [ 1208 | { 1209 | "polygon": [], 1210 | "char": "", 1211 | "illegibility": false, 1212 | "language": "Latin" 1213 | } 1214 | ] 1215 | }, 1216 | { 1217 | "polygon": [ 1218 | [ 1219 | 66.0, 1220 | 1375.0 1221 | ], 1222 | [ 1223 | 451.0, 1224 | 1375.0 1225 | ], 1226 | [ 1227 | 451.0, 1228 | 1406.0 1229 | ], 1230 | [ 1231 | 66.0, 1232 | 1406.0 1233 | ] 1234 | ], 1235 | "text": "VISIT WWW.MCDFEEDBACK.COM.MY", 1236 | "illegibility": false, 1237 | "language": "Latin", 1238 | "chars": [ 1239 | { 1240 | "polygon": [], 1241 | "char": "", 1242 | "illegibility": false, 1243 | "language": "Latin" 1244 | } 1245 | ] 1246 | }, 1247 | { 1248 | "polygon": [ 1249 | [ 1250 | 238.0, 1251 | 1408.0 1252 | ], 1253 | [ 1254 | 279.0, 1255 | 1408.0 1256 | ], 1257 | [ 1258 | 279.0, 1259 | 1439.0 1260 | ], 1261 | [ 1262 | 238.0, 1263 | 1439.0 1264 | ] 1265 | ], 1266 | "text": "OR", 1267 | "illegibility": false, 1268 | "language": "Latin", 1269 | "chars": [ 1270 | { 1271 | "polygon": [], 1272 | "char": "", 1273 | "illegibility": false, 1274 | "language": "Latin" 1275 | } 1276 | ] 1277 | }, 1278 | { 1279 | "polygon": [ 1280 | [ 1281 | 66.0, 1282 | 1443.0 1283 | ], 1284 | [ 1285 | 465.0, 1286 | 1443.0 1287 | ], 1288 | [ 1289 | 465.0, 1290 | 1478.0 1291 | ], 1292 | [ 1293 | 66.0, 1294 | 1478.0 1295 | ] 1296 | ], 1297 | "text": "DOWNLOAD MY MCD FEEDBACK FROM", 1298 | "illegibility": false, 1299 | "language": "Latin", 1300 | "chars": [ 1301 | { 1302 | "polygon": [], 1303 | "char": "", 1304 | "illegibility": false, 1305 | "language": "Latin" 1306 | } 1307 | ] 1308 | }, 1309 | { 1310 | "polygon": [ 1311 | [ 1312 | 116.0, 1313 | 1476.0 1314 | ], 1315 | [ 1316 | 416.0, 1317 | 1476.0 1318 | ], 1319 | [ 1320 | 416.0, 1321 | 1511.0 1322 | ], 1323 | [ 1324 | 116.0, 1325 | 1511.0 1326 | ] 1327 | ], 1328 | "text": "GOOGLE PLAY/APP STORE", 1329 | "illegibility": false, 1330 | "language": "Latin", 1331 | "chars": [ 1332 | { 1333 | "polygon": [], 1334 | "char": "", 1335 | "illegibility": false, 1336 | "language": "Latin" 1337 | } 1338 | ] 1339 | }, 1340 | { 1341 | "polygon": [ 1342 | [ 1343 | 93.0, 1344 | 1541.0 1345 | ], 1346 | [ 1347 | 422.0, 1348 | 1541.0 1349 | ], 1350 | [ 1351 | 422.0, 1352 | 1578.0 1353 | ], 1354 | [ 1355 | 93.0, 1356 | 1578.0 1357 | ] 1358 | ], 1359 | "text": "TO SHARE YOUR EXPERIENCE", 1360 | "illegibility": false, 1361 | "language": "Latin", 1362 | "chars": [ 1363 | { 1364 | "polygon": [], 1365 | "char": "", 1366 | "illegibility": false, 1367 | "language": "Latin" 1368 | } 1369 | ] 1370 | }, 1371 | { 1372 | "polygon": [ 1373 | [ 1374 | 27.0, 1375 | 1600.0 1376 | ], 1377 | [ 1378 | 457.0, 1379 | 1600.0 1380 | ], 1381 | [ 1382 | 457.0, 1383 | 1637.0 1384 | ], 1385 | [ 1386 | 27.0, 1387 | 1637.0 1388 | ] 1389 | ], 1390 | "text": "THANK YOU AND PLEASE COME AGAIN.", 1391 | "illegibility": false, 1392 | "language": "Latin", 1393 | "chars": [ 1394 | { 1395 | "polygon": [], 1396 | "char": "", 1397 | "illegibility": false, 1398 | "language": "Latin" 1399 | } 1400 | ] 1401 | }, 1402 | { 1403 | "polygon": [ 1404 | [ 1405 | 19.0, 1406 | 1644.0 1407 | ], 1408 | [ 1409 | 532.0, 1410 | 1644.0 1411 | ], 1412 | [ 1413 | 532.0, 1414 | 1677.0 1415 | ], 1416 | [ 1417 | 19.0, 1418 | 1677.0 1419 | ] 1420 | ], 1421 | "text": "CUSTOMER SERVICE HOTLINE : 03-2726-5600", 1422 | "illegibility": false, 1423 | "language": "Latin", 1424 | "chars": [ 1425 | { 1426 | "polygon": [], 1427 | "char": "", 1428 | "illegibility": false, 1429 | "language": "Latin" 1430 | } 1431 | ] 1432 | } 1433 | ] 1434 | }, 1435 | { 1436 | "img_name": "X51005230621.jpg", 1437 | "annotations": [ 1438 | { 1439 | "polygon": [ 1440 | [ 1441 | 166.0, 1442 | 329.0 1443 | ], 1444 | [ 1445 | 409.0, 1446 | 329.0 1447 | ], 1448 | [ 1449 | 409.0, 1450 | 355.0 1451 | ], 1452 | [ 1453 | 166.0, 1454 | 355.0 1455 | ] 1456 | ], 1457 | "text": "SIN LIANHAP SDN BHD", 1458 | "illegibility": false, 1459 | "language": "Latin", 1460 | "chars": [ 1461 | { 1462 | "polygon": [], 1463 | "char": "", 1464 | "illegibility": false, 1465 | "language": "Latin" 1466 | } 1467 | ] 1468 | }, 1469 | { 1470 | "polygon": [ 1471 | [ 1472 | 179.0, 1473 | 358.0 1474 | ], 1475 | [ 1476 | 333.0, 1477 | 358.0 1478 | ], 1479 | [ 1480 | 333.0, 1481 | 382.0 1482 | ], 1483 | [ 1484 | 179.0, 1485 | 382.0 1486 | ] 1487 | ], 1488 | "text": " JALAN", 1489 | "illegibility": false, 1490 | "language": "Latin", 1491 | "chars": [ 1492 | { 1493 | "polygon": [], 1494 | "char": "", 1495 | "illegibility": false, 1496 | "language": "Latin" 1497 | } 1498 | ] 1499 | }, 1500 | { 1501 | "polygon": [ 1502 | [ 1503 | 170.0, 1504 | 404.0 1505 | ], 1506 | [ 1507 | 410.0, 1508 | 404.0 1509 | ], 1510 | [ 1511 | 410.0, 1512 | 426.0 1513 | ], 1514 | [ 1515 | 170.0, 1516 | 426.0 1517 | ] 1518 | ], 1519 | "text": "SIN LIANHAP SDN BHD", 1520 | "illegibility": false, 1521 | "language": "Latin", 1522 | "chars": [ 1523 | { 1524 | "polygon": [], 1525 | "char": "", 1526 | "illegibility": false, 1527 | "language": "Latin" 1528 | } 1529 | ] 1530 | }, 1531 | { 1532 | "polygon": [ 1533 | [ 1534 | 181.0, 1535 | 431.0 1536 | ], 1537 | [ 1538 | 396.0, 1539 | 431.0 1540 | ], 1541 | [ 1542 | 396.0, 1543 | 455.0 1544 | ], 1545 | [ 1546 | 181.0, 1547 | 455.0 1548 | ] 1549 | ], 1550 | "text": "", 1551 | "illegibility": false, 1552 | "language": "Latin", 1553 | "chars": [ 1554 | { 1555 | "polygon": [], 1556 | "char": "", 1557 | "illegibility": false, 1558 | "language": "Latin" 1559 | } 1560 | ] 1561 | }, 1562 | { 1563 | "polygon": [ 1564 | [ 1565 | 140.0, 1566 | 463.0 1567 | ], 1568 | [ 1569 | 442.0, 1570 | 463.0 1571 | ], 1572 | [ 1573 | 442.0, 1574 | 485.0 1575 | ], 1576 | [ 1577 | 140.0, 1578 | 485.0 1579 | ] 1580 | ], 1581 | "text": " ULU YAM LAMA", 1582 | "illegibility": false, 1583 | "language": "Latin", 1584 | "chars": [ 1585 | { 1586 | "polygon": [], 1587 | "char": "", 1588 | "illegibility": false, 1589 | "language": "Latin" 1590 | } 1591 | ] 1592 | }, 1593 | { 1594 | "polygon": [ 1595 | [ 1596 | 140.0, 1597 | 493.0 1598 | ], 1599 | [ 1600 | 437.0, 1601 | 493.0 1602 | ], 1603 | [ 1604 | 437.0, 1605 | 515.0 1606 | ], 1607 | [ 1608 | 140.0, 1609 | 515.0 1610 | ] 1611 | ], 1612 | "text": " SELANGOR.", 1613 | "illegibility": false, 1614 | "language": "Latin", 1615 | "chars": [ 1616 | { 1617 | "polygon": [], 1618 | "char": "", 1619 | "illegibility": false, 1620 | "language": "Latin" 1621 | } 1622 | ] 1623 | }, 1624 | { 1625 | "polygon": [ 1626 | [ 1627 | 42.0, 1628 | 525.0 1629 | ], 1630 | [ 1631 | 497.0, 1632 | 525.0 1633 | ], 1634 | [ 1635 | 497.0, 1636 | 545.0 1637 | ], 1638 | [ 1639 | 42.0, 1640 | 545.0 1641 | ] 1642 | ], 1643 | "text": "TEL:03-60752222(HUNTING LINE) FAX: 03-60752572", 1644 | "illegibility": false, 1645 | "language": "Latin", 1646 | "chars": [ 1647 | { 1648 | "polygon": [], 1649 | "char": "", 1650 | "illegibility": false, 1651 | "language": "Latin" 1652 | } 1653 | ] 1654 | }, 1655 | { 1656 | "polygon": [ 1657 | [ 1658 | 128.0, 1659 | 556.0 1660 | ], 1661 | [ 1662 | 448.0, 1663 | 556.0 1664 | ], 1665 | [ 1666 | 448.0, 1667 | 582.0 1668 | ], 1669 | [ 1670 | 128.0, 1671 | 582.0 1672 | ] 1673 | ], 1674 | "text": "(COMPANY REG NO: 284922-D)", 1675 | "illegibility": false, 1676 | "language": "Latin", 1677 | "chars": [ 1678 | { 1679 | "polygon": [], 1680 | "char": "", 1681 | "illegibility": false, 1682 | "language": "Latin" 1683 | } 1684 | ] 1685 | }, 1686 | { 1687 | "polygon": [ 1688 | [ 1689 | 146.0, 1690 | 589.0 1691 | ], 1692 | [ 1693 | 437.0, 1694 | 589.0 1695 | ], 1696 | [ 1697 | 437.0, 1698 | 617.0 1699 | ], 1700 | [ 1701 | 146.0, 1702 | 617.0 1703 | ] 1704 | ], 1705 | "text": "(GST REG NO: 001610833920)", 1706 | "illegibility": false, 1707 | "language": "Latin", 1708 | "chars": [ 1709 | { 1710 | "polygon": [], 1711 | "char": "", 1712 | "illegibility": false, 1713 | "language": "Latin" 1714 | } 1715 | ] 1716 | }, 1717 | { 1718 | "polygon": [ 1719 | [ 1720 | 210.0, 1721 | 641.0 1722 | ], 1723 | [ 1724 | 348.0, 1725 | 641.0 1726 | ], 1727 | [ 1728 | 348.0, 1729 | 661.0 1730 | ], 1731 | [ 1732 | 210.0, 1733 | 661.0 1734 | ] 1735 | ], 1736 | "text": "TAX INVOICE", 1737 | "illegibility": false, 1738 | "language": "Latin", 1739 | "chars": [ 1740 | { 1741 | "polygon": [], 1742 | "char": "", 1743 | "illegibility": false, 1744 | "language": "Latin" 1745 | } 1746 | ] 1747 | }, 1748 | { 1749 | "polygon": [ 1750 | [ 1751 | 44.0, 1752 | 706.0 1753 | ], 1754 | [ 1755 | 234.0, 1756 | 706.0 1757 | ], 1758 | [ 1759 | 234.0, 1760 | 727.0 1761 | ], 1762 | [ 1763 | 44.0, 1764 | 727.0 1765 | ] 1766 | ], 1767 | "text": "CASH CUSTOMER", 1768 | "illegibility": false, 1769 | "language": "Latin", 1770 | "chars": [ 1771 | { 1772 | "polygon": [], 1773 | "char": "", 1774 | "illegibility": false, 1775 | "language": "Latin" 1776 | } 1777 | ] 1778 | }, 1779 | { 1780 | "polygon": [ 1781 | [ 1782 | 41.0, 1783 | 748.0 1784 | ], 1785 | [ 1786 | 164.0, 1787 | 748.0 1788 | ], 1789 | [ 1790 | 164.0, 1791 | 771.0 1792 | ], 1793 | [ 1794 | 41.0, 1795 | 771.0 1796 | ] 1797 | ], 1798 | "text": "INVOLCE NO.:", 1799 | "illegibility": false, 1800 | "language": "Latin", 1801 | "chars": [ 1802 | { 1803 | "polygon": [], 1804 | "char": "", 1805 | "illegibility": false, 1806 | "language": "Latin" 1807 | } 1808 | ] 1809 | }, 1810 | { 1811 | "polygon": [ 1812 | [ 1813 | 213.0, 1814 | 746.0 1815 | ], 1816 | [ 1817 | 329.0, 1818 | 746.0 1819 | ], 1820 | [ 1821 | 329.0, 1822 | 769.0 1823 | ], 1824 | [ 1825 | 213.0, 1826 | 769.0 1827 | ] 1828 | ], 1829 | "text": ":H0003939", 1830 | "illegibility": false, 1831 | "language": "Latin", 1832 | "chars": [ 1833 | { 1834 | "polygon": [], 1835 | "char": "", 1836 | "illegibility": false, 1837 | "language": "Latin" 1838 | } 1839 | ] 1840 | }, 1841 | { 1842 | "polygon": [ 1843 | [ 1844 | 44.0, 1845 | 778.0 1846 | ], 1847 | [ 1848 | 106.0, 1849 | 778.0 1850 | ], 1851 | [ 1852 | 106.0, 1853 | 801.0 1854 | ], 1855 | [ 1856 | 44.0, 1857 | 801.0 1858 | ] 1859 | ], 1860 | "text": "DATE:", 1861 | "illegibility": false, 1862 | "language": "Latin", 1863 | "chars": [ 1864 | { 1865 | "polygon": [], 1866 | "char": "", 1867 | "illegibility": false, 1868 | "language": "Latin" 1869 | } 1870 | ] 1871 | }, 1872 | { 1873 | "polygon": [ 1874 | [ 1875 | 215.0, 1876 | 780.0 1877 | ], 1878 | [ 1879 | 339.0, 1880 | 780.0 1881 | ], 1882 | [ 1883 | 339.0, 1884 | 801.0 1885 | ], 1886 | [ 1887 | 215.0, 1888 | 801.0 1889 | ] 1890 | ], 1891 | "text": ":05/02/2018", 1892 | "illegibility": false, 1893 | "language": "Latin", 1894 | "chars": [ 1895 | { 1896 | "polygon": [], 1897 | "char": "", 1898 | "illegibility": false, 1899 | "language": "Latin" 1900 | } 1901 | ] 1902 | }, 1903 | { 1904 | "polygon": [ 1905 | [ 1906 | 41.0, 1907 | 810.0 1908 | ], 1909 | [ 1910 | 148.0, 1911 | 810.0 1912 | ], 1913 | [ 1914 | 148.0, 1915 | 833.0 1916 | ], 1917 | [ 1918 | 41.0, 1919 | 833.0 1920 | ] 1921 | ], 1922 | "text": "CASHIER#:", 1923 | "illegibility": false, 1924 | "language": "Latin", 1925 | "chars": [ 1926 | { 1927 | "polygon": [], 1928 | "char": "", 1929 | "illegibility": false, 1930 | "language": "Latin" 1931 | } 1932 | ] 1933 | }, 1934 | { 1935 | "polygon": [ 1936 | [ 1937 | 211.0, 1938 | 812.0 1939 | ], 1940 | [ 1941 | 223.0, 1942 | 812.0 1943 | ], 1944 | [ 1945 | 223.0, 1946 | 834.0 1947 | ], 1948 | [ 1949 | 211.0, 1950 | 834.0 1951 | ] 1952 | ], 1953 | "text": ":", 1954 | "illegibility": false, 1955 | "language": "Latin", 1956 | "chars": [ 1957 | { 1958 | "polygon": [], 1959 | "char": "", 1960 | "illegibility": false, 1961 | "language": "Latin" 1962 | } 1963 | ] 1964 | }, 1965 | { 1966 | "polygon": [ 1967 | [ 1968 | 297.0, 1969 | 859.0 1970 | ], 1971 | [ 1972 | 334.0, 1973 | 859.0 1974 | ], 1975 | [ 1976 | 334.0, 1977 | 880.0 1978 | ], 1979 | [ 1980 | 297.0, 1981 | 880.0 1982 | ] 1983 | ], 1984 | "text": "RM", 1985 | "illegibility": false, 1986 | "language": "Latin", 1987 | "chars": [ 1988 | { 1989 | "polygon": [], 1990 | "char": "", 1991 | "illegibility": false, 1992 | "language": "Latin" 1993 | } 1994 | ] 1995 | }, 1996 | { 1997 | "polygon": [ 1998 | [ 1999 | 436.0, 2000 | 858.0 2001 | ], 2002 | [ 2003 | 495.0, 2004 | 858.0 2005 | ], 2006 | [ 2007 | 495.0, 2008 | 881.0 2009 | ], 2010 | [ 2011 | 436.0, 2012 | 881.0 2013 | ] 2014 | ], 2015 | "text": "CODE", 2016 | "illegibility": false, 2017 | "language": "Latin", 2018 | "chars": [ 2019 | { 2020 | "polygon": [], 2021 | "char": "", 2022 | "illegibility": false, 2023 | "language": "Latin" 2024 | } 2025 | ] 2026 | }, 2027 | { 2028 | "polygon": [ 2029 | [ 2030 | 42.0, 2031 | 896.0 2032 | ], 2033 | [ 2034 | 241.0, 2035 | 896.0 2036 | ], 2037 | [ 2038 | 241.0, 2039 | 920.0 2040 | ], 2041 | [ 2042 | 42.0, 2043 | 920.0 2044 | ] 2045 | ], 2046 | "text": "PPD 4MM DENLIME", 2047 | "illegibility": false, 2048 | "language": "Latin", 2049 | "chars": [ 2050 | { 2051 | "polygon": [], 2052 | "char": "", 2053 | "illegibility": false, 2054 | "language": "Latin" 2055 | } 2056 | ] 2057 | }, 2058 | { 2059 | "polygon": [ 2060 | [ 2061 | 287.0, 2062 | 933.0 2063 | ], 2064 | [ 2065 | 342.0, 2066 | 933.0 2067 | ], 2068 | [ 2069 | 342.0, 2070 | 956.0 2071 | ], 2072 | [ 2073 | 287.0, 2074 | 956.0 2075 | ] 2076 | ], 2077 | "text": "1.887", 2078 | "illegibility": false, 2079 | "language": "Latin", 2080 | "chars": [ 2081 | { 2082 | "polygon": [], 2083 | "char": "", 2084 | "illegibility": false, 2085 | "language": "Latin" 2086 | } 2087 | ] 2088 | }, 2089 | { 2090 | "polygon": [ 2091 | [ 2092 | 475.0, 2093 | 935.0 2094 | ], 2095 | [ 2096 | 507.0, 2097 | 935.0 2098 | ], 2099 | [ 2100 | 507.0, 2101 | 956.0 2102 | ], 2103 | [ 2104 | 475.0, 2105 | 956.0 2106 | ] 2107 | ], 2108 | "text": "SR", 2109 | "illegibility": false, 2110 | "language": "Latin", 2111 | "chars": [ 2112 | { 2113 | "polygon": [], 2114 | "char": "", 2115 | "illegibility": false, 2116 | "language": "Latin" 2117 | } 2118 | ] 2119 | }, 2120 | { 2121 | "polygon": [ 2122 | [ 2123 | 56.0, 2124 | 967.0 2125 | ], 2126 | [ 2127 | 147.0, 2128 | 967.0 2129 | ], 2130 | [ 2131 | 147.0, 2132 | 989.0 2133 | ], 2134 | [ 2135 | 56.0, 2136 | 989.0 2137 | ] 2138 | ], 2139 | "text": "1.000PC", 2140 | "illegibility": false, 2141 | "language": "Latin", 2142 | "chars": [ 2143 | { 2144 | "polygon": [], 2145 | "char": "", 2146 | "illegibility": false, 2147 | "language": "Latin" 2148 | } 2149 | ] 2150 | }, 2151 | { 2152 | "polygon": [ 2153 | [ 2154 | 459.0, 2155 | 968.0 2156 | ], 2157 | [ 2158 | 504.0, 2159 | 968.0 2160 | ], 2161 | [ 2162 | 504.0, 2163 | 989.0 2164 | ], 2165 | [ 2166 | 459.0, 2167 | 989.0 2168 | ] 2169 | ], 2170 | "text": "2.00", 2171 | "illegibility": false, 2172 | "language": "Latin", 2173 | "chars": [ 2174 | { 2175 | "polygon": [], 2176 | "char": "", 2177 | "illegibility": false, 2178 | "language": "Latin" 2179 | } 2180 | ] 2181 | }, 2182 | { 2183 | "polygon": [ 2184 | [ 2185 | 46.0, 2186 | 1012.0 2187 | ], 2188 | [ 2189 | 206.0, 2190 | 1012.0 2191 | ], 2192 | [ 2193 | 206.0, 2194 | 1035.0 2195 | ], 2196 | [ 2197 | 46.0, 2198 | 1035.0 2199 | ] 2200 | ], 2201 | "text": "6023# GARDEN", 2202 | "illegibility": false, 2203 | "language": "Latin", 2204 | "chars": [ 2205 | { 2206 | "polygon": [], 2207 | "char": "", 2208 | "illegibility": false, 2209 | "language": "Latin" 2210 | } 2211 | ] 2212 | }, 2213 | { 2214 | "polygon": [ 2215 | [ 2216 | 283.0, 2217 | 1050.0 2218 | ], 2219 | [ 2220 | 338.0, 2221 | 1050.0 2222 | ], 2223 | [ 2224 | 338.0, 2225 | 1072.0 2226 | ], 2227 | [ 2228 | 283.0, 2229 | 1072.0 2230 | ] 2231 | ], 2232 | "text": "5.000", 2233 | "illegibility": false, 2234 | "language": "Latin", 2235 | "chars": [ 2236 | { 2237 | "polygon": [], 2238 | "char": "", 2239 | "illegibility": false, 2240 | "language": "Latin" 2241 | } 2242 | ] 2243 | }, 2244 | { 2245 | "polygon": [ 2246 | [ 2247 | 470.0, 2248 | 1050.0 2249 | ], 2250 | [ 2251 | 505.0, 2252 | 1050.0 2253 | ], 2254 | [ 2255 | 505.0, 2256 | 1072.0 2257 | ], 2258 | [ 2259 | 470.0, 2260 | 1072.0 2261 | ] 2262 | ], 2263 | "text": "SR", 2264 | "illegibility": false, 2265 | "language": "Latin", 2266 | "chars": [ 2267 | { 2268 | "polygon": [], 2269 | "char": "", 2270 | "illegibility": false, 2271 | "language": "Latin" 2272 | } 2273 | ] 2274 | }, 2275 | { 2276 | "polygon": [ 2277 | [ 2278 | 58.0, 2279 | 1083.0 2280 | ], 2281 | [ 2282 | 148.0, 2283 | 1083.0 2284 | ], 2285 | [ 2286 | 148.0, 2287 | 1106.0 2288 | ], 2289 | [ 2290 | 58.0, 2291 | 1106.0 2292 | ] 2293 | ], 2294 | "text": "1.000 PC", 2295 | "illegibility": false, 2296 | "language": "Latin", 2297 | "chars": [ 2298 | { 2299 | "polygon": [], 2300 | "char": "", 2301 | "illegibility": false, 2302 | "language": "Latin" 2303 | } 2304 | ] 2305 | }, 2306 | { 2307 | "polygon": [ 2308 | [ 2309 | 461.0, 2310 | 1084.0 2311 | ], 2312 | [ 2313 | 507.0, 2314 | 1084.0 2315 | ], 2316 | [ 2317 | 507.0, 2318 | 1107.0 2319 | ], 2320 | [ 2321 | 461.0, 2322 | 1107.0 2323 | ] 2324 | ], 2325 | "text": "5.30", 2326 | "illegibility": false, 2327 | "language": "Latin", 2328 | "chars": [ 2329 | { 2330 | "polygon": [], 2331 | "char": "", 2332 | "illegibility": false, 2333 | "language": "Latin" 2334 | } 2335 | ] 2336 | }, 2337 | { 2338 | "polygon": [ 2339 | [ 2340 | 227.0, 2341 | 1140.0 2342 | ], 2343 | [ 2344 | 321.0, 2345 | 1140.0 2346 | ], 2347 | [ 2348 | 321.0, 2349 | 1161.0 2350 | ], 2351 | [ 2352 | 227.0, 2353 | 1161.0 2354 | ] 2355 | ], 2356 | "text": "SUBTOTAL :", 2357 | "illegibility": false, 2358 | "language": "Latin", 2359 | "chars": [ 2360 | { 2361 | "polygon": [], 2362 | "char": "", 2363 | "illegibility": false, 2364 | "language": "Latin" 2365 | } 2366 | ] 2367 | }, 2368 | { 2369 | "polygon": [ 2370 | [ 2371 | 460.0, 2372 | 1140.0 2373 | ], 2374 | [ 2375 | 504.0, 2376 | 1140.0 2377 | ], 2378 | [ 2379 | 504.0, 2380 | 1163.0 2381 | ], 2382 | [ 2383 | 460.0, 2384 | 1163.0 2385 | ] 2386 | ], 2387 | "text": "7.30", 2388 | "illegibility": false, 2389 | "language": "Latin", 2390 | "chars": [ 2391 | { 2392 | "polygon": [], 2393 | "char": "", 2394 | "illegibility": false, 2395 | "language": "Latin" 2396 | } 2397 | ] 2398 | }, 2399 | { 2400 | "polygon": [ 2401 | [ 2402 | 58.0, 2403 | 1184.0 2404 | ], 2405 | [ 2406 | 326.0, 2407 | 1184.0 2408 | ], 2409 | [ 2410 | 326.0, 2411 | 1214.0 2412 | ], 2413 | [ 2414 | 58.0, 2415 | 1214.0 2416 | ] 2417 | ], 2418 | "text": "TOTAL SALES EXCLUDING GST :", 2419 | "illegibility": false, 2420 | "language": "Latin", 2421 | "chars": [ 2422 | { 2423 | "polygon": [], 2424 | "char": "", 2425 | "illegibility": false, 2426 | "language": "Latin" 2427 | } 2428 | ] 2429 | }, 2430 | { 2431 | "polygon": [ 2432 | [ 2433 | 462.0, 2434 | 1187.0 2435 | ], 2436 | [ 2437 | 509.0, 2438 | 1187.0 2439 | ], 2440 | [ 2441 | 509.0, 2442 | 1208.0 2443 | ], 2444 | [ 2445 | 462.0, 2446 | 1208.0 2447 | ] 2448 | ], 2449 | "text": "6.69", 2450 | "illegibility": false, 2451 | "language": "Latin", 2452 | "chars": [ 2453 | { 2454 | "polygon": [], 2455 | "char": "", 2456 | "illegibility": false, 2457 | "language": "Latin" 2458 | } 2459 | ] 2460 | }, 2461 | { 2462 | "polygon": [ 2463 | [ 2464 | 46.0, 2465 | 1217.0 2466 | ], 2467 | [ 2468 | 324.0, 2469 | 1217.0 2470 | ], 2471 | [ 2472 | 324.0, 2473 | 1241.0 2474 | ], 2475 | [ 2476 | 46.0, 2477 | 1241.0 2478 | ] 2479 | ], 2480 | "text": "TOTAL SALES INCLUSLVE OF GST:", 2481 | "illegibility": false, 2482 | "language": "Latin", 2483 | "chars": [ 2484 | { 2485 | "polygon": [], 2486 | "char": "", 2487 | "illegibility": false, 2488 | "language": "Latin" 2489 | } 2490 | ] 2491 | }, 2492 | { 2493 | "polygon": [ 2494 | [ 2495 | 466.0, 2496 | 1220.0 2497 | ], 2498 | [ 2499 | 510.0, 2500 | 1220.0 2501 | ], 2502 | [ 2503 | 510.0, 2504 | 1241.0 2505 | ], 2506 | [ 2507 | 466.0, 2508 | 1241.0 2509 | ] 2510 | ], 2511 | "text": "7.30", 2512 | "illegibility": false, 2513 | "language": "Latin", 2514 | "chars": [ 2515 | { 2516 | "polygon": [], 2517 | "char": "", 2518 | "illegibility": false, 2519 | "language": "Latin" 2520 | } 2521 | ] 2522 | }, 2523 | { 2524 | "polygon": [ 2525 | [ 2526 | 110.0, 2527 | 1251.0 2528 | ], 2529 | [ 2530 | 318.0, 2531 | 1251.0 2532 | ], 2533 | [ 2534 | 318.0, 2535 | 1279.0 2536 | ], 2537 | [ 2538 | 110.0, 2539 | 1279.0 2540 | ] 2541 | ], 2542 | "text": "ROUNDING ADJUSTMENT :", 2543 | "illegibility": false, 2544 | "language": "Latin", 2545 | "chars": [ 2546 | { 2547 | "polygon": [], 2548 | "char": "", 2549 | "illegibility": false, 2550 | "language": "Latin" 2551 | } 2552 | ] 2553 | }, 2554 | { 2555 | "polygon": [ 2556 | [ 2557 | 462.0, 2558 | 1254.0 2559 | ], 2560 | [ 2561 | 508.0, 2562 | 1254.0 2563 | ], 2564 | [ 2565 | 508.0, 2566 | 1274.0 2567 | ], 2568 | [ 2569 | 462.0, 2570 | 1274.0 2571 | ] 2572 | ], 2573 | "text": "0.00", 2574 | "illegibility": false, 2575 | "language": "Latin", 2576 | "chars": [ 2577 | { 2578 | "polygon": [], 2579 | "char": "", 2580 | "illegibility": false, 2581 | "language": "Latin" 2582 | } 2583 | ] 2584 | }, 2585 | { 2586 | "polygon": [ 2587 | [ 2588 | 213.0, 2589 | 1306.0 2590 | ], 2591 | [ 2592 | 312.0, 2593 | 1306.0 2594 | ], 2595 | [ 2596 | 312.0, 2597 | 1334.0 2598 | ], 2599 | [ 2600 | 213.0, 2601 | 1334.0 2602 | ] 2603 | ], 2604 | "text": "PAYMENT :", 2605 | "illegibility": false, 2606 | "language": "Latin", 2607 | "chars": [ 2608 | { 2609 | "polygon": [], 2610 | "char": "", 2611 | "illegibility": false, 2612 | "language": "Latin" 2613 | } 2614 | ] 2615 | }, 2616 | { 2617 | "polygon": [ 2618 | [ 2619 | 461.0, 2620 | 1307.0 2621 | ], 2622 | [ 2623 | 508.0, 2624 | 1307.0 2625 | ], 2626 | [ 2627 | 508.0, 2628 | 1330.0 2629 | ], 2630 | [ 2631 | 461.0, 2632 | 1330.0 2633 | ] 2634 | ], 2635 | "text": "7.30", 2636 | "illegibility": false, 2637 | "language": "Latin", 2638 | "chars": [ 2639 | { 2640 | "polygon": [], 2641 | "char": "", 2642 | "illegibility": false, 2643 | "language": "Latin" 2644 | } 2645 | ] 2646 | }, 2647 | { 2648 | "polygon": [ 2649 | [ 2650 | 191.0, 2651 | 1342.0 2652 | ], 2653 | [ 2654 | 325.0, 2655 | 1342.0 2656 | ], 2657 | [ 2658 | 325.0, 2659 | 1370.0 2660 | ], 2661 | [ 2662 | 191.0, 2663 | 1370.0 2664 | ] 2665 | ], 2666 | "text": "CHANGE DUE :", 2667 | "illegibility": false, 2668 | "language": "Latin", 2669 | "chars": [ 2670 | { 2671 | "polygon": [], 2672 | "char": "", 2673 | "illegibility": false, 2674 | "language": "Latin" 2675 | } 2676 | ] 2677 | }, 2678 | { 2679 | "polygon": [ 2680 | [ 2681 | 465.0, 2682 | 1344.0 2683 | ], 2684 | [ 2685 | 510.0, 2686 | 1344.0 2687 | ], 2688 | [ 2689 | 510.0, 2690 | 1367.0 2691 | ], 2692 | [ 2693 | 465.0, 2694 | 1367.0 2695 | ] 2696 | ], 2697 | "text": "0.00", 2698 | "illegibility": false, 2699 | "language": "Latin", 2700 | "chars": [ 2701 | { 2702 | "polygon": [], 2703 | "char": "", 2704 | "illegibility": false, 2705 | "language": "Latin" 2706 | } 2707 | ] 2708 | }, 2709 | { 2710 | "polygon": [ 2711 | [ 2712 | 45.0, 2713 | 1409.0 2714 | ], 2715 | [ 2716 | 182.0, 2717 | 1409.0 2718 | ], 2719 | [ 2720 | 182.0, 2721 | 1441.0 2722 | ], 2723 | [ 2724 | 45.0, 2725 | 1441.0 2726 | ] 2727 | ], 2728 | "text": "TOTAL ITEM(S) :", 2729 | "illegibility": false, 2730 | "language": "Latin", 2731 | "chars": [ 2732 | { 2733 | "polygon": [], 2734 | "char": "", 2735 | "illegibility": false, 2736 | "language": "Latin" 2737 | } 2738 | ] 2739 | }, 2740 | { 2741 | "polygon": [ 2742 | [ 2743 | 42.0, 2744 | 1459.0 2745 | ], 2746 | [ 2747 | 188.0, 2748 | 1459.0 2749 | ], 2750 | [ 2751 | 188.0, 2752 | 1488.0 2753 | ], 2754 | [ 2755 | 42.0, 2756 | 1488.0 2757 | ] 2758 | ], 2759 | "text": "GST SUMMARY", 2760 | "illegibility": false, 2761 | "language": "Latin", 2762 | "chars": [ 2763 | { 2764 | "polygon": [], 2765 | "char": "", 2766 | "illegibility": false, 2767 | "language": "Latin" 2768 | } 2769 | ] 2770 | }, 2771 | { 2772 | "polygon": [ 2773 | [ 2774 | 42.0, 2775 | 1493.0 2776 | ], 2777 | [ 2778 | 76.0, 2779 | 1493.0 2780 | ], 2781 | [ 2782 | 76.0, 2783 | 1516.0 2784 | ], 2785 | [ 2786 | 42.0, 2787 | 1516.0 2788 | ] 2789 | ], 2790 | "text": "SR", 2791 | "illegibility": false, 2792 | "language": "Latin", 2793 | "chars": [ 2794 | { 2795 | "polygon": [], 2796 | "char": "", 2797 | "illegibility": false, 2798 | "language": "Latin" 2799 | } 2800 | ] 2801 | }, 2802 | { 2803 | "polygon": [ 2804 | [ 2805 | 275.0, 2806 | 1466.0 2807 | ], 2808 | [ 2809 | 355.0, 2810 | 1466.0 2811 | ], 2812 | [ 2813 | 355.0, 2814 | 1486.0 2815 | ], 2816 | [ 2817 | 275.0, 2818 | 1486.0 2819 | ] 2820 | ], 2821 | "text": "AMONUT", 2822 | "illegibility": false, 2823 | "language": "Latin", 2824 | "chars": [ 2825 | { 2826 | "polygon": [], 2827 | "char": "", 2828 | "illegibility": false, 2829 | "language": "Latin" 2830 | } 2831 | ] 2832 | }, 2833 | { 2834 | "polygon": [ 2835 | [ 2836 | 322.0, 2837 | 1496.0 2838 | ], 2839 | [ 2840 | 367.0, 2841 | 1496.0 2842 | ], 2843 | [ 2844 | 367.0, 2845 | 1518.0 2846 | ], 2847 | [ 2848 | 322.0, 2849 | 1518.0 2850 | ] 2851 | ], 2852 | "text": "6.89", 2853 | "illegibility": false, 2854 | "language": "Latin", 2855 | "chars": [ 2856 | { 2857 | "polygon": [], 2858 | "char": "", 2859 | "illegibility": false, 2860 | "language": "Latin" 2861 | } 2862 | ] 2863 | }, 2864 | { 2865 | "polygon": [ 2866 | [ 2867 | 462.0, 2868 | 1463.0 2869 | ], 2870 | [ 2871 | 500.0, 2872 | 1463.0 2873 | ], 2874 | [ 2875 | 500.0, 2876 | 1485.0 2877 | ], 2878 | [ 2879 | 462.0, 2880 | 1485.0 2881 | ] 2882 | ], 2883 | "text": "TAX", 2884 | "illegibility": false, 2885 | "language": "Latin", 2886 | "chars": [ 2887 | { 2888 | "polygon": [], 2889 | "char": "", 2890 | "illegibility": false, 2891 | "language": "Latin" 2892 | } 2893 | ] 2894 | }, 2895 | { 2896 | "polygon": [ 2897 | [ 2898 | 466.0, 2899 | 1497.0 2900 | ], 2901 | [ 2902 | 509.0, 2903 | 1497.0 2904 | ], 2905 | [ 2906 | 509.0, 2907 | 1520.0 2908 | ], 2909 | [ 2910 | 466.0, 2911 | 1520.0 2912 | ] 2913 | ], 2914 | "text": "0.41", 2915 | "illegibility": false, 2916 | "language": "Latin", 2917 | "chars": [ 2918 | { 2919 | "polygon": [], 2920 | "char": "", 2921 | "illegibility": false, 2922 | "language": "Latin" 2923 | } 2924 | ] 2925 | }, 2926 | { 2927 | "polygon": [ 2928 | [ 2929 | 169.0, 2930 | 1633.0 2931 | ], 2932 | [ 2933 | 358.0, 2934 | 1633.0 2935 | ], 2936 | [ 2937 | 358.0, 2938 | 1665.0 2939 | ], 2940 | [ 2941 | 169.0, 2942 | 1665.0 2943 | ] 2944 | ], 2945 | "text": "THANK YOU", 2946 | "illegibility": false, 2947 | "language": "Latin", 2948 | "chars": [ 2949 | { 2950 | "polygon": [], 2951 | "char": "", 2952 | "illegibility": false, 2953 | "language": "Latin" 2954 | } 2955 | ] 2956 | }, 2957 | { 2958 | "polygon": [ 2959 | [ 2960 | 102.0, 2961 | 1666.0 2962 | ], 2963 | [ 2964 | 444.0, 2965 | 1666.0 2966 | ], 2967 | [ 2968 | 444.0, 2969 | 1699.0 2970 | ], 2971 | [ 2972 | 102.0, 2973 | 1699.0 2974 | ] 2975 | ], 2976 | "text": "PLEASE COME AGAIN", 2977 | "illegibility": false, 2978 | "language": "Latin", 2979 | "chars": [ 2980 | { 2981 | "polygon": [], 2982 | "char": "", 2983 | "illegibility": false, 2984 | "language": "Latin" 2985 | } 2986 | ] 2987 | }, 2988 | { 2989 | "polygon": [ 2990 | [ 2991 | 175.0, 2992 | 971.0 2993 | ], 2994 | [ 2995 | 190.0, 2996 | 971.0 2997 | ], 2998 | [ 2999 | 190.0, 3000 | 989.0 3001 | ], 3002 | [ 3003 | 175.0, 3004 | 989.0 3005 | ] 3006 | ], 3007 | "text": "X", 3008 | "illegibility": false, 3009 | "language": "Latin", 3010 | "chars": [ 3011 | { 3012 | "polygon": [], 3013 | "char": "", 3014 | "illegibility": false, 3015 | "language": "Latin" 3016 | } 3017 | ] 3018 | }, 3019 | { 3020 | "polygon": [ 3021 | [ 3022 | 178.0, 3023 | 1087.0 3024 | ], 3025 | [ 3026 | 193.0, 3027 | 1087.0 3028 | ], 3029 | [ 3030 | 193.0, 3031 | 1106.0 3032 | ], 3033 | [ 3034 | 178.0, 3035 | 1106.0 3036 | ] 3037 | ], 3038 | "text": "X", 3039 | "illegibility": false, 3040 | "language": "Latin", 3041 | "chars": [ 3042 | { 3043 | "polygon": [], 3044 | "char": "", 3045 | "illegibility": false, 3046 | "language": "Latin" 3047 | } 3048 | ] 3049 | }, 3050 | { 3051 | "polygon": [ 3052 | [ 3053 | 228.0, 3054 | 1413.0 3055 | ], 3056 | [ 3057 | 245.0, 3058 | 1413.0 3059 | ], 3060 | [ 3061 | 245.0, 3062 | 1434.0 3063 | ], 3064 | [ 3065 | 228.0, 3066 | 1434.0 3067 | ] 3068 | ], 3069 | "text": "2", 3070 | "illegibility": false, 3071 | "language": "Latin", 3072 | "chars": [ 3073 | { 3074 | "polygon": [], 3075 | "char": "", 3076 | "illegibility": false, 3077 | "language": "Latin" 3078 | } 3079 | ] 3080 | }, 3081 | { 3082 | "polygon": [ 3083 | [ 3084 | 101.0, 3085 | 1494.0 3086 | ], 3087 | [ 3088 | 144.0, 3089 | 1494.0 3090 | ], 3091 | [ 3092 | 144.0, 3093 | 1523.0 3094 | ], 3095 | [ 3096 | 101.0, 3097 | 1523.0 3098 | ] 3099 | ], 3100 | "text": "@6", 3101 | "illegibility": false, 3102 | "language": "Latin", 3103 | "chars": [ 3104 | { 3105 | "polygon": [], 3106 | "char": "", 3107 | "illegibility": false, 3108 | "language": "Latin" 3109 | } 3110 | ] 3111 | } 3112 | ] 3113 | }, 3114 | { 3115 | "img_name": "X51005230648.jpg", 3116 | "annotations": [ 3117 | { 3118 | "polygon": [ 3119 | [ 3120 | 155.0, 3121 | 127.0 3122 | ], 3123 | [ 3124 | 551.0, 3125 | 127.0 3126 | ], 3127 | [ 3128 | 551.0, 3129 | 149.0 3130 | ], 3131 | [ 3132 | 155.0, 3133 | 149.0 3134 | ] 3135 | ], 3136 | "text": "CROSS CHANNEL NETWORK SDN. BHD.", 3137 | "illegibility": false, 3138 | "language": "Latin", 3139 | "chars": [ 3140 | { 3141 | "polygon": [], 3142 | "char": "", 3143 | "illegibility": false, 3144 | "language": "Latin" 3145 | } 3146 | ] 3147 | }, 3148 | { 3149 | "polygon": [ 3150 | [ 3151 | 197.0, 3152 | 153.0 3153 | ], 3154 | [ 3155 | 507.0, 3156 | 153.0 3157 | ], 3158 | [ 3159 | 507.0, 3160 | 179.0 3161 | ], 3162 | [ 3163 | 197.0, 3164 | 179.0 3165 | ] 3166 | ], 3167 | "text": "", 3168 | "illegibility": false, 3169 | "language": "Latin", 3170 | "chars": [ 3171 | { 3172 | "polygon": [], 3173 | "char": "", 3174 | "illegibility": false, 3175 | "language": "Latin" 3176 | } 3177 | ] 3178 | }, 3179 | { 3180 | "polygon": [ 3181 | [ 3182 | 197.0, 3183 | 178.0 3184 | ], 3185 | [ 3186 | 511.0, 3187 | 178.0 3188 | ], 3189 | [ 3190 | 511.0, 3191 | 203.0 3192 | ], 3193 | [ 3194 | 197.0, 3195 | 203.0 3196 | ] 3197 | ], 3198 | "text": "", 3199 | "illegibility": false, 3200 | "language": "Latin", 3201 | "chars": [ 3202 | { 3203 | "polygon": [], 3204 | "char": "", 3205 | "illegibility": false, 3206 | "language": "Latin" 3207 | } 3208 | ] 3209 | }, 3210 | { 3211 | "polygon": [ 3212 | [ 3213 | 183.0, 3214 | 204.0 3215 | ], 3216 | [ 3217 | 520.0, 3218 | 204.0 3219 | ], 3220 | [ 3221 | 520.0, 3222 | 230.0 3223 | ], 3224 | [ 3225 | 183.0, 3226 | 230.0 3227 | ] 3228 | ], 3229 | "text": " SELANGOR.", 3230 | "illegibility": false, 3231 | "language": "Latin", 3232 | "chars": [ 3233 | { 3234 | "polygon": [], 3235 | "char": "", 3236 | "illegibility": false, 3237 | "language": "Latin" 3238 | } 3239 | ] 3240 | }, 3241 | { 3242 | "polygon": [ 3243 | [ 3244 | 143.0, 3245 | 231.0 3246 | ], 3247 | [ 3248 | 339.0, 3249 | 231.0 3250 | ], 3251 | [ 3252 | 339.0, 3253 | 255.0 3254 | ], 3255 | [ 3256 | 143.0, 3257 | 255.0 3258 | ] 3259 | ], 3260 | "text": "TEL : 03-6057 9688", 3261 | "illegibility": false, 3262 | "language": "Latin", 3263 | "chars": [ 3264 | { 3265 | "polygon": [], 3266 | "char": "", 3267 | "illegibility": false, 3268 | "language": "Latin" 3269 | } 3270 | ] 3271 | }, 3272 | { 3273 | "polygon": [ 3274 | [ 3275 | 362.0, 3276 | 231.0 3277 | ], 3278 | [ 3279 | 562.0, 3280 | 231.0 3281 | ], 3282 | [ 3283 | 562.0, 3284 | 255.0 3285 | ], 3286 | [ 3287 | 362.0, 3288 | 255.0 3289 | ] 3290 | ], 3291 | "text": "FAX : 03-6057 9678", 3292 | "illegibility": false, 3293 | "language": "Latin", 3294 | "chars": [ 3295 | { 3296 | "polygon": [], 3297 | "char": "", 3298 | "illegibility": false, 3299 | "language": "Latin" 3300 | } 3301 | ] 3302 | }, 3303 | { 3304 | "polygon": [ 3305 | [ 3306 | 236.0, 3307 | 258.0 3308 | ], 3309 | [ 3310 | 454.0, 3311 | 258.0 3312 | ], 3313 | [ 3314 | 454.0, 3315 | 279.0 3316 | ], 3317 | [ 3318 | 236.0, 3319 | 279.0 3320 | ] 3321 | ], 3322 | "text": "GST ID : 001151500288", 3323 | "illegibility": false, 3324 | "language": "Latin", 3325 | "chars": [ 3326 | { 3327 | "polygon": [], 3328 | "char": "", 3329 | "illegibility": false, 3330 | "language": "Latin" 3331 | } 3332 | ] 3333 | }, 3334 | { 3335 | "polygon": [ 3336 | [ 3337 | 269.0, 3338 | 282.0 3339 | ], 3340 | [ 3341 | 417.0, 3342 | 282.0 3343 | ], 3344 | [ 3345 | 417.0, 3346 | 308.0 3347 | ], 3348 | [ 3349 | 269.0, 3350 | 308.0 3351 | ] 3352 | ], 3353 | "text": "TAX INVOLCE", 3354 | "illegibility": false, 3355 | "language": "Latin", 3356 | "chars": [ 3357 | { 3358 | "polygon": [], 3359 | "char": "", 3360 | "illegibility": false, 3361 | "language": "Latin" 3362 | } 3363 | ] 3364 | }, 3365 | { 3366 | "polygon": [ 3367 | [ 3368 | 431.0, 3369 | 303.0 3370 | ], 3371 | [ 3372 | 590.0, 3373 | 303.0 3374 | ], 3375 | [ 3376 | 590.0, 3377 | 323.0 3378 | ], 3379 | [ 3380 | 431.0, 3381 | 323.0 3382 | ] 3383 | ], 3384 | "text": "NO.:BTG-052332", 3385 | "illegibility": false, 3386 | "language": "Latin", 3387 | "chars": [ 3388 | { 3389 | "polygon": [], 3390 | "char": "", 3391 | "illegibility": false, 3392 | "language": "Latin" 3393 | } 3394 | ] 3395 | }, 3396 | { 3397 | "polygon": [ 3398 | [ 3399 | 446.0, 3400 | 329.0 3401 | ], 3402 | [ 3403 | 488.0, 3404 | 329.0 3405 | ], 3406 | [ 3407 | 488.0, 3408 | 356.0 3409 | ], 3410 | [ 3411 | 446.0, 3412 | 356.0 3413 | ] 3414 | ], 3415 | "text": "QTY", 3416 | "illegibility": false, 3417 | "language": "Latin", 3418 | "chars": [ 3419 | { 3420 | "polygon": [], 3421 | "char": "", 3422 | "illegibility": false, 3423 | "language": "Latin" 3424 | } 3425 | ] 3426 | }, 3427 | { 3428 | "polygon": [ 3429 | [ 3430 | 511.0, 3431 | 328.0 3432 | ], 3433 | [ 3434 | 549.0, 3435 | 328.0 3436 | ], 3437 | [ 3438 | 549.0, 3439 | 351.0 3440 | ], 3441 | [ 3442 | 511.0, 3443 | 351.0 3444 | ] 3445 | ], 3446 | "text": "RM", 3447 | "illegibility": false, 3448 | "language": "Latin", 3449 | "chars": [ 3450 | { 3451 | "polygon": [], 3452 | "char": "", 3453 | "illegibility": false, 3454 | "language": "Latin" 3455 | } 3456 | ] 3457 | }, 3458 | { 3459 | "polygon": [ 3460 | [ 3461 | 552.0, 3462 | 329.0 3463 | ], 3464 | [ 3465 | 595.0, 3466 | 329.0 3467 | ], 3468 | [ 3469 | 595.0, 3470 | 351.0 3471 | ], 3472 | [ 3473 | 552.0, 3474 | 351.0 3475 | ] 3476 | ], 3477 | "text": "TAX", 3478 | "illegibility": false, 3479 | "language": "Latin", 3480 | "chars": [ 3481 | { 3482 | "polygon": [], 3483 | "char": "", 3484 | "illegibility": false, 3485 | "language": "Latin" 3486 | } 3487 | ] 3488 | }, 3489 | { 3490 | "polygon": [ 3491 | [ 3492 | 89.0, 3493 | 356.0 3494 | ], 3495 | [ 3496 | 423.0, 3497 | 356.0 3498 | ], 3499 | [ 3500 | 423.0, 3501 | 380.0 3502 | ], 3503 | [ 3504 | 89.0, 3505 | 380.0 3506 | ] 3507 | ], 3508 | "text": "SCHNEIDER E15R 13A SWITCH", 3509 | "illegibility": false, 3510 | "language": "Latin", 3511 | "chars": [ 3512 | { 3513 | "polygon": [], 3514 | "char": "", 3515 | "illegibility": false, 3516 | "language": "Latin" 3517 | } 3518 | ] 3519 | }, 3520 | { 3521 | "polygon": [ 3522 | [ 3523 | 460.0, 3524 | 356.0 3525 | ], 3526 | [ 3527 | 475.0, 3528 | 356.0 3529 | ], 3530 | [ 3531 | 475.0, 3532 | 380.0 3533 | ], 3534 | [ 3535 | 460.0, 3536 | 380.0 3537 | ] 3538 | ], 3539 | "text": "1", 3540 | "illegibility": false, 3541 | "language": "Latin", 3542 | "chars": [ 3543 | { 3544 | "polygon": [], 3545 | "char": "", 3546 | "illegibility": false, 3547 | "language": "Latin" 3548 | } 3549 | ] 3550 | }, 3551 | { 3552 | "polygon": [ 3553 | [ 3554 | 87.0, 3555 | 383.0 3556 | ], 3557 | [ 3558 | 381.0, 3559 | 383.0 3560 | ], 3561 | [ 3562 | 381.0, 3563 | 408.0 3564 | ], 3565 | [ 3566 | 87.0, 3567 | 408.0 3568 | ] 3569 | ], 3570 | "text": "SOCKET OUTLET @ 6.0000", 3571 | "illegibility": false, 3572 | "language": "Latin", 3573 | "chars": [ 3574 | { 3575 | "polygon": [], 3576 | "char": "", 3577 | "illegibility": false, 3578 | "language": "Latin" 3579 | } 3580 | ] 3581 | }, 3582 | { 3583 | "polygon": [ 3584 | [ 3585 | 132.0, 3586 | 417.0 3587 | ], 3588 | [ 3589 | 409.0, 3590 | 417.0 3591 | ], 3592 | [ 3593 | 409.0, 3594 | 443.0 3595 | ], 3596 | [ 3597 | 132.0, 3598 | 443.0 3599 | ] 3600 | ], 3601 | "text": "TOTAL AMT INCL. GST @ 6% :", 3602 | "illegibility": false, 3603 | "language": "Latin", 3604 | "chars": [ 3605 | { 3606 | "polygon": [], 3607 | "char": "", 3608 | "illegibility": false, 3609 | "language": "Latin" 3610 | } 3611 | ] 3612 | }, 3613 | { 3614 | "polygon": [ 3615 | [ 3616 | 181.0, 3617 | 440.0 3618 | ], 3619 | [ 3620 | 408.0, 3621 | 440.0 3622 | ], 3623 | [ 3624 | 408.0, 3625 | 465.0 3626 | ], 3627 | [ 3628 | 181.0, 3629 | 465.0 3630 | ] 3631 | ], 3632 | "text": "ROUNDING ADJUSTMENT :", 3633 | "illegibility": false, 3634 | "language": "Latin", 3635 | "chars": [ 3636 | { 3637 | "polygon": [], 3638 | "char": "", 3639 | "illegibility": false, 3640 | "language": "Latin" 3641 | } 3642 | ] 3643 | }, 3644 | { 3645 | "polygon": [ 3646 | [ 3647 | 213.0, 3648 | 464.0 3649 | ], 3650 | [ 3651 | 409.0, 3652 | 464.0 3653 | ], 3654 | [ 3655 | 409.0, 3656 | 489.0 3657 | ], 3658 | [ 3659 | 213.0, 3660 | 489.0 3661 | ] 3662 | ], 3663 | "text": "TOTAL AMT PAYABLE :", 3664 | "illegibility": false, 3665 | "language": "Latin", 3666 | "chars": [ 3667 | { 3668 | "polygon": [], 3669 | "char": "", 3670 | "illegibility": false, 3671 | "language": "Latin" 3672 | } 3673 | ] 3674 | }, 3675 | { 3676 | "polygon": [ 3677 | [ 3678 | 266.0, 3679 | 484.0 3680 | ], 3681 | [ 3682 | 408.0, 3683 | 484.0 3684 | ], 3685 | [ 3686 | 408.0, 3687 | 505.0 3688 | ], 3689 | [ 3690 | 266.0, 3691 | 505.0 3692 | ] 3693 | ], 3694 | "text": "PAID AMOUNT :", 3695 | "illegibility": false, 3696 | "language": "Latin", 3697 | "chars": [ 3698 | { 3699 | "polygon": [], 3700 | "char": "", 3701 | "illegibility": false, 3702 | "language": "Latin" 3703 | } 3704 | ] 3705 | }, 3706 | { 3707 | "polygon": [ 3708 | [ 3709 | 317.0, 3710 | 504.0 3711 | ], 3712 | [ 3713 | 411.0, 3714 | 504.0 3715 | ], 3716 | [ 3717 | 411.0, 3718 | 529.0 3719 | ], 3720 | [ 3721 | 317.0, 3722 | 529.0 3723 | ] 3724 | ], 3725 | "text": "CHANGE :", 3726 | "illegibility": false, 3727 | "language": "Latin", 3728 | "chars": [ 3729 | { 3730 | "polygon": [], 3731 | "char": "", 3732 | "illegibility": false, 3733 | "language": "Latin" 3734 | } 3735 | ] 3736 | }, 3737 | { 3738 | "polygon": [ 3739 | [ 3740 | 226.0, 3741 | 524.0 3742 | ], 3743 | [ 3744 | 412.0, 3745 | 524.0 3746 | ], 3747 | [ 3748 | 412.0, 3749 | 548.0 3750 | ], 3751 | [ 3752 | 226.0, 3753 | 548.0 3754 | ] 3755 | ], 3756 | "text": "TOTAL QTY TENDER:", 3757 | "illegibility": false, 3758 | "language": "Latin", 3759 | "chars": [ 3760 | { 3761 | "polygon": [], 3762 | "char": "", 3763 | "illegibility": false, 3764 | "language": "Latin" 3765 | } 3766 | ] 3767 | }, 3768 | { 3769 | "polygon": [ 3770 | [ 3771 | 443.0, 3772 | 419.0 3773 | ], 3774 | [ 3775 | 491.0, 3776 | 419.0 3777 | ], 3778 | [ 3779 | 491.0, 3780 | 441.0 3781 | ], 3782 | [ 3783 | 443.0, 3784 | 441.0 3785 | ] 3786 | ], 3787 | "text": "6.36", 3788 | "illegibility": false, 3789 | "language": "Latin", 3790 | "chars": [ 3791 | { 3792 | "polygon": [], 3793 | "char": "", 3794 | "illegibility": false, 3795 | "language": "Latin" 3796 | } 3797 | ] 3798 | }, 3799 | { 3800 | "polygon": [ 3801 | [ 3802 | 437.0, 3803 | 443.0 3804 | ], 3805 | [ 3806 | 488.0, 3807 | 443.0 3808 | ], 3809 | [ 3810 | 488.0, 3811 | 463.0 3812 | ], 3813 | [ 3814 | 437.0, 3815 | 463.0 3816 | ] 3817 | ], 3818 | "text": "-0.01", 3819 | "illegibility": false, 3820 | "language": "Latin", 3821 | "chars": [ 3822 | { 3823 | "polygon": [], 3824 | "char": "", 3825 | "illegibility": false, 3826 | "language": "Latin" 3827 | } 3828 | ] 3829 | }, 3830 | { 3831 | "polygon": [ 3832 | [ 3833 | 443.0, 3834 | 464.0 3835 | ], 3836 | [ 3837 | 491.0, 3838 | 464.0 3839 | ], 3840 | [ 3841 | 491.0, 3842 | 486.0 3843 | ], 3844 | [ 3845 | 443.0, 3846 | 486.0 3847 | ] 3848 | ], 3849 | "text": "6.35", 3850 | "illegibility": false, 3851 | "language": "Latin", 3852 | "chars": [ 3853 | { 3854 | "polygon": [], 3855 | "char": "", 3856 | "illegibility": false, 3857 | "language": "Latin" 3858 | } 3859 | ] 3860 | }, 3861 | { 3862 | "polygon": [ 3863 | [ 3864 | 434.0, 3865 | 486.0 3866 | ], 3867 | [ 3868 | 490.0, 3869 | 486.0 3870 | ], 3871 | [ 3872 | 490.0, 3873 | 506.0 3874 | ], 3875 | [ 3876 | 434.0, 3877 | 506.0 3878 | ] 3879 | ], 3880 | "text": "10.00", 3881 | "illegibility": false, 3882 | "language": "Latin", 3883 | "chars": [ 3884 | { 3885 | "polygon": [], 3886 | "char": "", 3887 | "illegibility": false, 3888 | "language": "Latin" 3889 | } 3890 | ] 3891 | }, 3892 | { 3893 | "polygon": [ 3894 | [ 3895 | 445.0, 3896 | 506.0 3897 | ], 3898 | [ 3899 | 492.0, 3900 | 506.0 3901 | ], 3902 | [ 3903 | 492.0, 3904 | 525.0 3905 | ], 3906 | [ 3907 | 445.0, 3908 | 525.0 3909 | ] 3910 | ], 3911 | "text": "3.65", 3912 | "illegibility": false, 3913 | "language": "Latin", 3914 | "chars": [ 3915 | { 3916 | "polygon": [], 3917 | "char": "", 3918 | "illegibility": false, 3919 | "language": "Latin" 3920 | } 3921 | ] 3922 | }, 3923 | { 3924 | "polygon": [ 3925 | [ 3926 | 445.0, 3927 | 526.0 3928 | ], 3929 | [ 3930 | 489.0, 3931 | 526.0 3932 | ], 3933 | [ 3934 | 489.0, 3935 | 546.0 3936 | ], 3937 | [ 3938 | 445.0, 3939 | 546.0 3940 | ] 3941 | ], 3942 | "text": "1.00", 3943 | "illegibility": false, 3944 | "language": "Latin", 3945 | "chars": [ 3946 | { 3947 | "polygon": [], 3948 | "char": "", 3949 | "illegibility": false, 3950 | "language": "Latin" 3951 | } 3952 | ] 3953 | }, 3954 | { 3955 | "polygon": [ 3956 | [ 3957 | 140.0, 3958 | 554.0 3959 | ], 3960 | [ 3961 | 284.0, 3962 | 554.0 3963 | ], 3964 | [ 3965 | 284.0, 3966 | 582.0 3967 | ], 3968 | [ 3969 | 140.0, 3970 | 582.0 3971 | ] 3972 | ], 3973 | "text": "GST SUMMARY", 3974 | "illegibility": false, 3975 | "language": "Latin", 3976 | "chars": [ 3977 | { 3978 | "polygon": [], 3979 | "char": "", 3980 | "illegibility": false, 3981 | "language": "Latin" 3982 | } 3983 | ] 3984 | }, 3985 | { 3986 | "polygon": [ 3987 | [ 3988 | 348.0, 3989 | 555.0 3990 | ], 3991 | [ 3992 | 428.0, 3993 | 555.0 3994 | ], 3995 | [ 3996 | 428.0, 3997 | 579.0 3998 | ], 3999 | [ 4000 | 348.0, 4001 | 579.0 4002 | ] 4003 | ], 4004 | "text": "AMOUNT", 4005 | "illegibility": false, 4006 | "language": "Latin", 4007 | "chars": [ 4008 | { 4009 | "polygon": [], 4010 | "char": "", 4011 | "illegibility": false, 4012 | "language": "Latin" 4013 | } 4014 | ] 4015 | }, 4016 | { 4017 | "polygon": [ 4018 | [ 4019 | 529.0, 4020 | 552.0 4021 | ], 4022 | [ 4023 | 571.0, 4024 | 552.0 4025 | ], 4026 | [ 4027 | 571.0, 4028 | 576.0 4029 | ], 4030 | [ 4031 | 529.0, 4032 | 576.0 4033 | ] 4034 | ], 4035 | "text": "TAX", 4036 | "illegibility": false, 4037 | "language": "Latin", 4038 | "chars": [ 4039 | { 4040 | "polygon": [], 4041 | "char": "", 4042 | "illegibility": false, 4043 | "language": "Latin" 4044 | } 4045 | ] 4046 | }, 4047 | { 4048 | "polygon": [ 4049 | [ 4050 | 376.0, 4051 | 582.0 4052 | ], 4053 | [ 4054 | 427.0, 4055 | 582.0 4056 | ], 4057 | [ 4058 | 427.0, 4059 | 607.0 4060 | ], 4061 | [ 4062 | 376.0, 4063 | 607.0 4064 | ] 4065 | ], 4066 | "text": "(RM)", 4067 | "illegibility": false, 4068 | "language": "Latin", 4069 | "chars": [ 4070 | { 4071 | "polygon": [], 4072 | "char": "", 4073 | "illegibility": false, 4074 | "language": "Latin" 4075 | } 4076 | ] 4077 | }, 4078 | { 4079 | "polygon": [ 4080 | [ 4081 | 518.0, 4082 | 578.0 4083 | ], 4084 | [ 4085 | 569.0, 4086 | 578.0 4087 | ], 4088 | [ 4089 | 569.0, 4090 | 603.0 4091 | ], 4092 | [ 4093 | 518.0, 4094 | 603.0 4095 | ] 4096 | ], 4097 | "text": "(RM)", 4098 | "illegibility": false, 4099 | "language": "Latin", 4100 | "chars": [ 4101 | { 4102 | "polygon": [], 4103 | "char": "", 4104 | "illegibility": false, 4105 | "language": "Latin" 4106 | } 4107 | ] 4108 | }, 4109 | { 4110 | "polygon": [ 4111 | [ 4112 | 139.0, 4113 | 612.0 4114 | ], 4115 | [ 4116 | 218.0, 4117 | 612.0 4118 | ], 4119 | [ 4120 | 218.0, 4121 | 636.0 4122 | ], 4123 | [ 4124 | 139.0, 4125 | 636.0 4126 | ] 4127 | ], 4128 | "text": "SR @ A", 4129 | "illegibility": false, 4130 | "language": "Latin", 4131 | "chars": [ 4132 | { 4133 | "polygon": [], 4134 | "char": "", 4135 | "illegibility": false, 4136 | "language": "Latin" 4137 | } 4138 | ] 4139 | }, 4140 | { 4141 | "polygon": [ 4142 | [ 4143 | 381.0, 4144 | 612.0 4145 | ], 4146 | [ 4147 | 428.0, 4148 | 612.0 4149 | ], 4150 | [ 4151 | 428.0, 4152 | 634.0 4153 | ], 4154 | [ 4155 | 381.0, 4156 | 634.0 4157 | ] 4158 | ], 4159 | "text": "6.00", 4160 | "illegibility": false, 4161 | "language": "Latin", 4162 | "chars": [ 4163 | { 4164 | "polygon": [], 4165 | "char": "", 4166 | "illegibility": false, 4167 | "language": "Latin" 4168 | } 4169 | ] 4170 | }, 4171 | { 4172 | "polygon": [ 4173 | [ 4174 | 523.0, 4175 | 611.0 4176 | ], 4177 | [ 4178 | 571.0, 4179 | 611.0 4180 | ], 4181 | [ 4182 | 571.0, 4183 | 634.0 4184 | ], 4185 | [ 4186 | 523.0, 4187 | 634.0 4188 | ] 4189 | ], 4190 | "text": "0.36", 4191 | "illegibility": false, 4192 | "language": "Latin", 4193 | "chars": [ 4194 | { 4195 | "polygon": [], 4196 | "char": "", 4197 | "illegibility": false, 4198 | "language": "Latin" 4199 | } 4200 | ] 4201 | }, 4202 | { 4203 | "polygon": [ 4204 | [ 4205 | 138.0, 4206 | 643.0 4207 | ], 4208 | [ 4209 | 193.0, 4210 | 643.0 4211 | ], 4212 | [ 4213 | 193.0, 4214 | 666.0 4215 | ], 4216 | [ 4217 | 138.0, 4218 | 666.0 4219 | ] 4220 | ], 4221 | "text": "TOTAL", 4222 | "illegibility": false, 4223 | "language": "Latin", 4224 | "chars": [ 4225 | { 4226 | "polygon": [], 4227 | "char": "", 4228 | "illegibility": false, 4229 | "language": "Latin" 4230 | } 4231 | ] 4232 | }, 4233 | { 4234 | "polygon": [ 4235 | [ 4236 | 380.0, 4237 | 643.0 4238 | ], 4239 | [ 4240 | 426.0, 4241 | 643.0 4242 | ], 4243 | [ 4244 | 426.0, 4245 | 665.0 4246 | ], 4247 | [ 4248 | 380.0, 4249 | 665.0 4250 | ] 4251 | ], 4252 | "text": "6.00", 4253 | "illegibility": false, 4254 | "language": "Latin", 4255 | "chars": [ 4256 | { 4257 | "polygon": [], 4258 | "char": "", 4259 | "illegibility": false, 4260 | "language": "Latin" 4261 | } 4262 | ] 4263 | }, 4264 | { 4265 | "polygon": [ 4266 | [ 4267 | 523.0, 4268 | 644.0 4269 | ], 4270 | [ 4271 | 570.0, 4272 | 644.0 4273 | ], 4274 | [ 4275 | 570.0, 4276 | 665.0 4277 | ], 4278 | [ 4279 | 523.0, 4280 | 665.0 4281 | ] 4282 | ], 4283 | "text": "0.36", 4284 | "illegibility": false, 4285 | "language": "Latin", 4286 | "chars": [ 4287 | { 4288 | "polygon": [], 4289 | "char": "", 4290 | "illegibility": false, 4291 | "language": "Latin" 4292 | } 4293 | ] 4294 | }, 4295 | { 4296 | "polygon": [ 4297 | [ 4298 | 320.0, 4299 | 675.0 4300 | ], 4301 | [ 4302 | 459.0, 4303 | 675.0 4304 | ], 4305 | [ 4306 | 459.0, 4307 | 698.0 4308 | ], 4309 | [ 4310 | 320.0, 4311 | 698.0 4312 | ] 4313 | ], 4314 | "text": "THANK YOU", 4315 | "illegibility": false, 4316 | "language": "Latin", 4317 | "chars": [ 4318 | { 4319 | "polygon": [], 4320 | "char": "", 4321 | "illegibility": false, 4322 | "language": "Latin" 4323 | } 4324 | ] 4325 | }, 4326 | { 4327 | "polygon": [ 4328 | [ 4329 | 129.0, 4330 | 698.0 4331 | ], 4332 | [ 4333 | 603.0, 4334 | 698.0 4335 | ], 4336 | [ 4337 | 603.0, 4338 | 724.0 4339 | ], 4340 | [ 4341 | 129.0, 4342 | 724.0 4343 | ] 4344 | ], 4345 | "text": "GOODS SOLD ARE NOT REFUNDABLE NOR EXCHANGEBLE.", 4346 | "illegibility": false, 4347 | "language": "Latin", 4348 | "chars": [ 4349 | { 4350 | "polygon": [], 4351 | "char": "", 4352 | "illegibility": false, 4353 | "language": "Latin" 4354 | } 4355 | ] 4356 | }, 4357 | { 4358 | "polygon": [ 4359 | [ 4360 | 199.0, 4361 | 725.0 4362 | ], 4363 | [ 4364 | 534.0, 4365 | 725.0 4366 | ], 4367 | [ 4368 | 534.0, 4369 | 752.0 4370 | ], 4371 | [ 4372 | 199.0, 4373 | 752.0 4374 | ] 4375 | ], 4376 | "text": " PLEASE E-MAIL US:", 4377 | "illegibility": false, 4378 | "language": "Latin", 4379 | "chars": [ 4380 | { 4381 | "polygon": [], 4382 | "char": "", 4383 | "illegibility": false, 4384 | "language": "Latin" 4385 | } 4386 | ] 4387 | }, 4388 | { 4389 | "polygon": [ 4390 | [ 4391 | 253.0, 4392 | 752.0 4393 | ], 4394 | [ 4395 | 479.0, 4396 | 752.0 4397 | ], 4398 | [ 4399 | 479.0, 4400 | 778.0 4401 | ], 4402 | [ 4403 | 253.0, 4404 | 778.0 4405 | ] 4406 | ], 4407 | "text": "GOWA9688@GMAIL.COM", 4408 | "illegibility": false, 4409 | "language": "Latin", 4410 | "chars": [ 4411 | { 4412 | "polygon": [], 4413 | "char": "", 4414 | "illegibility": false, 4415 | "language": "Latin" 4416 | } 4417 | ] 4418 | }, 4419 | { 4420 | "polygon": [ 4421 | [ 4422 | 245.0, 4423 | 777.0 4424 | ], 4425 | [ 4426 | 487.0, 4427 | 777.0 4428 | ], 4429 | [ 4430 | 487.0, 4431 | 803.0 4432 | ], 4433 | [ 4434 | 245.0, 4435 | 803.0 4436 | ] 4437 | ], 4438 | "text": "29/01/2018 4:40:40 PM", 4439 | "illegibility": false, 4440 | "language": "Latin", 4441 | "chars": [ 4442 | { 4443 | "polygon": [], 4444 | "char": "", 4445 | "illegibility": false, 4446 | "language": "Latin" 4447 | } 4448 | ] 4449 | }, 4450 | { 4451 | "polygon": [ 4452 | [ 4453 | 504.0, 4454 | 357.0 4455 | ], 4456 | [ 4457 | 552.0, 4458 | 357.0 4459 | ], 4460 | [ 4461 | 552.0, 4462 | 379.0 4463 | ], 4464 | [ 4465 | 504.0, 4466 | 379.0 4467 | ] 4468 | ], 4469 | "text": "6.36", 4470 | "illegibility": false, 4471 | "language": "Latin", 4472 | "chars": [ 4473 | { 4474 | "polygon": [], 4475 | "char": "", 4476 | "illegibility": false, 4477 | "language": "Latin" 4478 | } 4479 | ] 4480 | }, 4481 | { 4482 | "polygon": [ 4483 | [ 4484 | 561.0, 4485 | 359.0 4486 | ], 4487 | [ 4488 | 589.0, 4489 | 359.0 4490 | ], 4491 | [ 4492 | 589.0, 4493 | 378.0 4494 | ], 4495 | [ 4496 | 561.0, 4497 | 378.0 4498 | ] 4499 | ], 4500 | "text": "SR", 4501 | "illegibility": false, 4502 | "language": "Latin", 4503 | "chars": [ 4504 | { 4505 | "polygon": [], 4506 | "char": "", 4507 | "illegibility": false, 4508 | "language": "Latin" 4509 | } 4510 | ] 4511 | } 4512 | ] 4513 | } 4514 | ] 4515 | } 4516 | --------------------------------------------------------------------------------