├── dataset └── iamdataset │ ├── form │ └── a01-000u.png │ ├── line │ └── a01 │ │ └── a01-000u │ │ └── a01-000u-00.png │ ├── subject │ ├── validationset1.txt │ ├── validationset2.txt │ └── testset.txt │ └── xml │ └── a01-000u.xml ├── utilities ├── expand_bounding_box.py ├── draw_text_on_image.py ├── draw_box_on_image.py ├── beam_search.py ├── word_to_line.py ├── handwriting_line_recognition.py ├── iam_dataset.py └── word_and_line_segmentation.py └── README.md /dataset/iamdataset/form/a01-000u.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KevinGThomas/HandwrittenTextRecognition/HEAD/dataset/iamdataset/form/a01-000u.png -------------------------------------------------------------------------------- /dataset/iamdataset/line/a01/a01-000u/a01-000u-00.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KevinGThomas/HandwrittenTextRecognition/HEAD/dataset/iamdataset/line/a01/a01-000u/a01-000u-00.png -------------------------------------------------------------------------------- /utilities/expand_bounding_box.py: -------------------------------------------------------------------------------- 1 | def expand_bounding_box(bb, expand_bb_scale_x=0.05, expand_bb_scale_y=0.05): 2 | (x, y, w, h) = bb 3 | new_w = (1 + expand_bb_scale_x) * w 4 | new_h = (1 + expand_bb_scale_y) * h 5 | 6 | x = x - (new_w - w)/2 7 | y = y - (new_h - h)/2 8 | w = new_w 9 | h = new_h 10 | return (x, y, w, h) 11 | -------------------------------------------------------------------------------- /utilities/draw_text_on_image.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | import cv2 5 | import numpy as np 6 | 7 | def draw_text_on_image(images, text): 8 | output_image_shape = (images.shape[0], images.shape[1], images.shape[2] * 2, images.shape[3]) # Double the output_image_shape to print the text in the bottom 9 | 10 | output_images = np.zeros(shape=output_image_shape) 11 | for i in range(images.shape[0]): 12 | white_image_shape = (images.shape[2], images.shape[3]) 13 | white_image = np.ones(shape=white_image_shape)*1.0 14 | text_image = cv2.putText(white_image, text[i], org=(5, 30), fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, color=0.0, thickness=1) 15 | output_images[i, :, :images.shape[2], :] = images[i] 16 | output_images[i, :, images.shape[2]:, :] = text_image 17 | return output_images 18 | -------------------------------------------------------------------------------- /utilities/draw_box_on_image.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | import numpy as np 5 | from skimage.draw import line_aa 6 | import matplotlib.pyplot as plt 7 | 8 | def draw_line(image, y1, x1, y2, x2, line_type): 9 | rr, cc, val = line_aa(y1, x1, y2, x2) 10 | if line_type == "dotted": 11 | rr = np.delete(rr, np.arange(0, rr.size, 5)) 12 | cc = np.delete(cc, np.arange(0, cc.size, 5)) 13 | image[rr, cc] = 0 14 | return image 15 | 16 | def draw_box(bounding_box, image, line_type, is_xywh=True): 17 | image_h, image_w = image.shape[-2:] 18 | if is_xywh: 19 | (x, y, w, h) = bounding_box 20 | (x1, y1, x2, y2) = (x, y, x + w, y + h) 21 | else: 22 | (x1, y1, x2, y2) = bounding_box 23 | (x1, y1, x2, y2) = (int(x1), int(y1), int(x2), int(y2)) 24 | if y2 >= image_h: 25 | y2 = image_h - 1 26 | if x2 >= image_w: 27 | x2 = image_w - 1 28 | if y1 >= image_h: 29 | y1 = image_h - 1 30 | if x1 >= image_w: 31 | x1 = image_w - 1 32 | if y2 < 0: 33 | y2 = 0 34 | if x2 < 0: 35 | x2 =0 36 | if y1 < 0: 37 | y1 = 0 38 | if x1 < 0: 39 | x1 = 0 40 | 41 | image = draw_line(image, y1, x1, y2, x1, line_type) 42 | image = draw_line(image, y2, x1, y2, x2, line_type) 43 | image = draw_line(image, y2, x2, y1, x2, line_type) 44 | image = draw_line(image, y1, x2, y1, x1, line_type) 45 | return image 46 | 47 | def draw_boxes_on_image(pred, label, images): 48 | ''' Function to draw multiple bounding boxes on the images. Predicted bounding boxes will be 49 | presented with a dotted line and actual boxes are presented with a solid line. 50 | 51 | Parameters 52 | ---------- 53 | 54 | pred: [n x [x, y, w, h]] 55 | The predicted bounding boxes in percentages. 56 | n is the number of bounding boxes predicted on an image 57 | 58 | label: [n x [x, y, w, h]] 59 | The actual bounding boxes in percentages 60 | n is the number of bounding boxes predicted on an image 61 | 62 | images: [[np.array]] 63 | The correponding images. 64 | 65 | Returns 66 | ------- 67 | 68 | images: [[np.array]] 69 | Images with bounding boxes printed on them. 70 | ''' 71 | image_h, image_w = images.shape[-2:] 72 | label[:, :, 0], label[:, :, 1] = label[:, :, 0] * image_w, label[:, :, 1] * image_h 73 | label[:, :, 2], label[:, :, 3] = label[:, :, 2] * image_w, label[:, :, 3] * image_h 74 | for i in range(len(pred)): 75 | pred_b = pred[i] 76 | pred_b[:, 0], pred_b[:, 1] = pred_b[:, 0] * image_w, pred_b[:, 1] * image_h 77 | pred_b[:, 2], pred_b[:, 3] = pred_b[:, 2] * image_w, pred_b[:, 3] * image_h 78 | 79 | image = images[i, 0] 80 | for j in range(pred_b.shape[0]): 81 | image = draw_box(pred_b[j, :], image, line_type="dotted") 82 | 83 | for k in range(label.shape[1]): 84 | image = draw_box(label[i, k, :], image, line_type="solid") 85 | images[i, 0, :, :] = image 86 | return images 87 | 88 | def draw_box_on_image(pred, label, images): 89 | ''' Function to draw bounding boxes on the images. Predicted bounding boxes will be 90 | presented with a dotted line and actual boxes are presented with a solid line. 91 | 92 | Parameters 93 | ---------- 94 | 95 | pred: [[x, y, w, h]] 96 | The predicted bounding boxes in percentages 97 | 98 | label: [[x, y, w, h]] 99 | The actual bounding boxes in percentages 100 | 101 | images: [[np.array]] 102 | The correponding images. 103 | 104 | Returns 105 | ------- 106 | 107 | images: [[np.array]] 108 | Images with bounding boxes printed on them. 109 | ''' 110 | 111 | image_h, image_w = images.shape[-2:] 112 | pred[:, 0], pred[:, 1] = pred[:, 0] * image_w, pred[:, 1] * image_h 113 | pred[:, 2], pred[:, 3] = pred[:, 2] * image_w, pred[:, 3] * image_h 114 | 115 | label[:, 0], label[:, 1] = label[:, 0] * image_w, label[:, 1] * image_h 116 | label[:, 2], label[:, 3] = label[:, 2] * image_w, label[:, 3] * image_h 117 | 118 | for i in range(images.shape[0]): 119 | image = images[i, 0] 120 | image = draw_box(pred[i, :], image, line_type="dotted") 121 | image = draw_box(label[i, :], image, line_type="solid") 122 | images[i, 0, :, :] = image 123 | return images 124 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HandwrittenTextRecognition 2 | Recognizing Handwritten Text by segmenting the page into paragraphs and lines and then converting them to digital text. 3 | 4 | # Overview 5 | This is the full code for 'Handwritten Text Recognition'. This code helps to convert a handwritten page into digital text by identifying the paragraph present in the page, segmenting the lines and running handwriting recognition to accurately identify the text. The dataset used is the IAMDataset (http://www.fki.inf.unibe.ch/databases/iam-handwriting-database) containing about 6,000 labeled sentences and about 120,000 labeled words. 6 | 7 | # Dependency 8 | * mxnet 9 | * pandas 10 | * matplotlib 11 | * numpy 12 | * skimage 13 | 14 | # Methodology 15 | ## Paragraph Segmentation 16 | 17 | 18 | 19 | ### Pre-processing 20 | The pre-processing is a series of operations performed of scanned input image. It essentially enhances the image rendering for suitable segmentation. The role of pre-processing is to segment the interesting pattern from the background. Methods like data augmentation (a copy of the input image is made and slight alterations such as small rotation of the image are done and both of these images are sent to the model to increase it’s dataset examples), grey-scaling (images are turned to black and white for the model to accurately detect the presence of handwritten text). 21 | 22 | ### Feature Extraction 23 | The art of finding useful features in a machine learning problem can be tedious and heavily affected by human bias but by using Convolution Neural Networks, we are able to detect features by itself by comparing similar patterns in the images. To extract features from the DCNN model, first we need to train the CNN network with the last sigmoid/logistic dense layer (here dimension 4). The objective of the training network is to identify the correct weight for the network by multiple forward and backward iterations, which eventually try to minimise mean square error. We use MXNet in order to solve the problem for a given problem and set MSE (Mean Square Error) as the evaluation metric. We will optimize the model by attempting to reduce MSE value in each new epoch. 24 | 25 | ### Segmentation 26 | Here is the architecture of the DCNN model. 27 | 28 | The model gives 4 values as output in the end, (x,y,w,h). (x,y) are the coordinates of the starting of the paragraph that the model has recognized, w is the width of the paragraph and h is the height of the paragraph. Using this parameters, a bounding box can be formed around the paragraph to successfully segment the paragraph from the given image. 29 | 30 |
31 | 32 | ## Line Segmentation 33 | Similarly, line segmentation is done through pre-processing, feature extraction and segmentation. Line Segmentation is used to identify the lines present in the paragraph. This is important as many people have a tendency to not write in a straight line. 34 | 35 | 36 | Here is the architecture of the SSD network model. 37 | 38 | The model contains a list of bounding boxes each containing 4 values as output in the end, [n][(x,y,w,h)]. n is the number of words detected in the paragraph, (x,y) are the coordinates of the starting of the word that the model has recognized, w is the width of the word and h is the height of the word. Using this parameters, a bounding box can be formed around each word to successfully detect the words from the given image to segment to lines (checks if y coordinate of the bounding boxes overlap each other). 39 | 40 |
41 | 42 | ## Handwriting Recognition 43 | The final model is the handwriting recognition model which takes a line as input and converts the line into digital text. This model consits of a CNN-biLSTM architecture. The loss used is the CTC (Connectionist Temporal Classification) loss. 44 |

45 | 46 |

47 | 48 | Here is the CNN-biLSTM architecture model. 49 | 50 | The input lines are sent into the CNN to extract features from similar patterns. These image features are then sent to a sequential learner which are the bidirectional LSTMs which are then sent to the output string that predict the character based on the alphabet with the highest predicted value given by the model. 51 | 52 | # Results 53 | ## Paragraph Segmentation 54 | 55 | 56 | ## Line Segmentation 57 | 58 | 59 | ## Handwriting Recognition 60 | 61 | -------------------------------------------------------------------------------- /utilities/beam_search.py: -------------------------------------------------------------------------------- 1 | # From https://github.com/githubharald/CTCDecoder 2 | # 3 | #MIT License 4 | 5 | #Copyright (c) 2018 Harald Scheidl 6 | 7 | #Permission is hereby granted, free of charge, to any person obtaining a copy 8 | #of this software and associated documentation files (the "Software"), to deal 9 | #in the Software without restriction, including without limitation the rights 10 | #to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | #copies of the Software, and to permit persons to whom the Software is 12 | #furnished to do so, subject to the following conditions: 13 | 14 | #The above copyright notice and this permission notice shall be included in all 15 | #copies or substantial portions of the Software. 16 | 17 | #THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | #IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | #FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | #AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | #LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | #OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | #SOFTWARE. 24 | 25 | from __future__ import division 26 | from __future__ import print_function 27 | import numpy as np 28 | 29 | class BeamEntry: 30 | "information about one single beam at specific time-step" 31 | def __init__(self): 32 | self.prTotal = 0 # blank and non-blank 33 | self.prNonBlank = 0 # non-blank 34 | self.prBlank = 0 # blank 35 | self.prText = 1 # LM score 36 | self.lmApplied = False # flag if LM was already applied to this beam 37 | self.labeling = () # beam-labeling 38 | 39 | class BeamState: 40 | "information about the beams at specific time-step" 41 | def __init__(self): 42 | self.entries = {} 43 | 44 | def norm(self): 45 | "length-normalise LM score" 46 | for (k, _) in self.entries.items(): 47 | labelingLen = len(self.entries[k].labeling) 48 | self.entries[k].prText = self.entries[k].prText ** (1.0 / (labelingLen if labelingLen else 1.0)) 49 | 50 | def sort(self): 51 | "return beam-labelings, sorted by probability" 52 | beams = [v for (_, v) in self.entries.items()] 53 | sortedBeams = sorted(beams, reverse=True, key=lambda x: x.prTotal*x.prText) 54 | return [x.labeling for x in sortedBeams] 55 | 56 | def applyLM(parentBeam, childBeam, classes, lm): 57 | "calculate LM score of child beam by taking score from parent beam and bigram probability of last two chars" 58 | if lm and not childBeam.lmApplied: 59 | c1 = classes[parentBeam.labeling[-1] if parentBeam.labeling else classes.index(' ')] # first char 60 | c2 = classes[childBeam.labeling[-1]] # second char 61 | lmFactor = 0.01 # influence of language model 62 | bigramProb = lm.getCharBigram(c1, c2) ** lmFactor # probability of seeing first and second char next to each other 63 | childBeam.prText = parentBeam.prText * bigramProb # probability of char sequence 64 | childBeam.lmApplied = True # only apply LM once per beam entry 65 | 66 | def addBeam(beamState, labeling): 67 | "add beam if it does not yet exist" 68 | if labeling not in beamState.entries: 69 | beamState.entries[labeling] = BeamEntry() 70 | 71 | def ctcBeamSearch(mat, classes, lm, beamWidth): 72 | "beam search as described by the paper of Hwang et al. and the paper of Graves et al." 73 | 74 | blankIdx = len(classes) 75 | maxT, maxC = mat.shape 76 | 77 | # initialise beam state 78 | last = BeamState() 79 | labeling = () 80 | last.entries[labeling] = BeamEntry() 81 | last.entries[labeling].prBlank = 1 82 | last.entries[labeling].prTotal = 1 83 | 84 | # go over all time-steps 85 | for t in range(maxT): 86 | curr = BeamState() 87 | 88 | # get beam-labelings of best beams 89 | bestLabelings = last.sort()[0:beamWidth] 90 | 91 | # go over best beams 92 | for labeling in bestLabelings: 93 | 94 | # probability of paths ending with a non-blank 95 | prNonBlank = 0 96 | # in case of non-empty beam 97 | if labeling: 98 | # probability of paths with repeated last char at the end 99 | try: 100 | prNonBlank = last.entries[labeling].prNonBlank * mat[t, labeling[-1]] 101 | except FloatingPointError: 102 | prNonBlank = 0 103 | 104 | # probability of paths ending with a blank 105 | prBlank = (last.entries[labeling].prTotal) * mat[t, blankIdx] 106 | 107 | # add beam at current time-step if needed 108 | addBeam(curr, labeling) 109 | 110 | # fill in data 111 | curr.entries[labeling].labeling = labeling 112 | curr.entries[labeling].prNonBlank += prNonBlank 113 | curr.entries[labeling].prBlank += prBlank 114 | curr.entries[labeling].prTotal += prBlank + prNonBlank 115 | curr.entries[labeling].prText = last.entries[labeling].prText # beam-labeling not changed, therefore also LM score unchanged from 116 | curr.entries[labeling].lmApplied = True # LM already applied at previous time-step for this beam-labeling 117 | 118 | # extend current beam-labeling 119 | for c in range(maxC - 1): 120 | # add new char to current beam-labeling 121 | newLabeling = labeling + (c,) 122 | 123 | # if new labeling contains duplicate char at the end, only consider paths ending with a blank 124 | if labeling and labeling[-1] == c: 125 | prNonBlank = mat[t, c] * last.entries[labeling].prBlank 126 | else: 127 | prNonBlank = mat[t, c] * last.entries[labeling].prTotal 128 | 129 | # add beam at current time-step if needed 130 | addBeam(curr, newLabeling) 131 | 132 | # fill in data 133 | curr.entries[newLabeling].labeling = newLabeling 134 | curr.entries[newLabeling].prNonBlank += prNonBlank 135 | curr.entries[newLabeling].prTotal += prNonBlank 136 | 137 | # apply LM 138 | applyLM(curr.entries[labeling], curr.entries[newLabeling], classes, lm) 139 | 140 | # set new beam state 141 | last = curr 142 | 143 | # normalise LM scores according to beam-labeling-length 144 | last.norm() 145 | 146 | # sort by probability 147 | bestLabelings = last.sort()[:beamWidth] # get most probable labeling 148 | 149 | output = [] 150 | for bestLabeling in bestLabelings: 151 | # map labels to chars 152 | res = '' 153 | for l in bestLabeling: 154 | res += classes[l] 155 | output.append(res) 156 | return output 157 | 158 | def testBeamSearch(): 159 | "test decoder" 160 | classes = 'ab' 161 | mat = np.array([[0.4, 0, 0.6], [0.4, 0, 0.6]]) 162 | print('Test beam search') 163 | expected = 'a' 164 | actual = ctcBeamSearch(mat, classes, None) 165 | print('Expected: "' + expected + '"') 166 | print('Actual: "' + actual + '"') 167 | print('OK' if expected == actual else 'ERROR') 168 | 169 | if __name__ == '__main__': 170 | testBeamSearch() -------------------------------------------------------------------------------- /utilities/word_to_line.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | import math 5 | 6 | import numpy as np 7 | from scipy.cluster.hierarchy import fcluster 8 | 9 | from .expand_bounding_box import expand_bounding_box 10 | 11 | def _clip_value(value, max_value): 12 | ''' 13 | Helper function to make sure that "value" will not be greater than max_value 14 | or lower than 0. 15 | ''' 16 | output = value 17 | if output < 0: 18 | output = 0 19 | if output > max_value: 20 | output = max_value 21 | return int(output) 22 | 23 | def _get_max_coord(bbs, x_or_y): 24 | ''' 25 | Helper function to find the largest coordinate given a list of 26 | bounding boxes in the x or y direction. 27 | ''' 28 | assert x_or_y in ["x", "y"], "x_or_y can only be x or y" 29 | max_value = 0.0 30 | for bb in bbs: 31 | if x_or_y == "x": 32 | value = bb[0] + bb[2] 33 | else: 34 | value = bb[1] + bb[3] 35 | if value > max_value: 36 | max_value = value 37 | return max_value 38 | 39 | def _get_min_coord(bbs, x_or_y): 40 | ''' 41 | Helper function to find the largest coordinate given a list of 42 | bounding boxes in the x or y direction. 43 | ''' 44 | assert x_or_y in ["x", "y"], "x_or_y can only be x or y" 45 | min_value = 100 46 | for bb in bbs: 47 | if x_or_y == "x": 48 | value = bb[0] 49 | else: 50 | value = bb[1] 51 | if value < min_value: 52 | min_value = value 53 | return min_value 54 | 55 | def _get_bounding_box_of_bb_list(bbs_in_a_line): 56 | ''' 57 | Given a list of bounding boxes, find the maximum x, y and 58 | minimum x, y coordinates. This is the bounding box that 59 | emcompasses all the words. Return this bounding box in the form 60 | (x', y', w', h'). 61 | ''' 62 | max_x = _get_max_coord(bbs_in_a_line, x_or_y="x") 63 | min_x = _get_min_coord(bbs_in_a_line, x_or_y="x") 64 | 65 | max_y = _get_max_coord(bbs_in_a_line, x_or_y="y") 66 | min_y = _get_min_coord(bbs_in_a_line, x_or_y="y") 67 | 68 | line_bb = (min_x, min_y, max_x - min_x, max_y - min_y) 69 | return line_bb 70 | 71 | def _filter_bbs(bbs, min_size=0.005): 72 | ''' 73 | Remove bounding boxes that are too small 74 | ''' 75 | output_bbs = [] 76 | for bb in bbs: 77 | if bb[2] * bb[3] > min_size: 78 | output_bbs.append(bb) 79 | return np.array(output_bbs) 80 | 81 | def _get_line_overlap_percentage(y1, h1, y2, h2): 82 | ''' 83 | Calculates how much (percentage) y2->y2+h2 overlaps with y1->y1+h1. 84 | Algorithm assumes that y2 is larger than y1 85 | ''' 86 | if y2 > y1 and (y1 + h1) > y2: 87 | # Is y2 enclosed in y1 88 | if (y1 + h1) > (y2 + h2): 89 | return 1.0 90 | else: 91 | return ((y1 + h1) - (y2))/h1 92 | else: 93 | return 0.0 94 | 95 | def _get_rect_overlap_percentage(x1, y1, w1, h1, x2, y2, w2, h2): 96 | ''' 97 | Calculate how much (in percentage) that rect2 overlaps with rect1 98 | ''' 99 | # Check if rect overlaps 100 | x_overlap = (x1 + w1 >= x2 and x2 >= x1) or (x2 + w2 >= x1 and x1 >= x2) 101 | y_overlap = (y1 + h1 >= y2 and y2 >= y1) or (y2 + h2 >= y1 and y1 >= y2) 102 | if x_overlap and y_overlap: 103 | intersect_size = max(0, min(x1 + w1, x2 + w2) - min(x1, x2)) * max(0, min(y1 + h1, y2 + h2) - max(y1, y2)) 104 | s1 = w1 * h1 105 | return intersect_size / s1 106 | else: 107 | return 0 108 | 109 | def combine_bbs_into_lines(bbs, y_overlap=0.2): 110 | ''' 111 | Algorithm to group word crops into lines. 112 | Iterates over every bb, if the overlap in the y direction 113 | between 2 boxes has less than y_overlap overlap, then group the previous words together. 114 | ''' 115 | line_bbs = [] 116 | bbs_in_a_line = [] 117 | y_indexes = np.argsort(bbs[:, 1]) 118 | # Iterate through the sorted bounding box. 119 | previous_y_coords = None 120 | for y_index in y_indexes: 121 | y_coords = (bbs[y_index, 1], bbs[y_index, 3]) # y and height 122 | 123 | # new line if the overlap is more than y_overlap 124 | if previous_y_coords is not None: 125 | line_overlap_percentage1 = _get_line_overlap_percentage( 126 | previous_y_coords[0], previous_y_coords[1], 127 | y_coords[0], y_coords[1]) 128 | line_overlap_percentage2 = _get_line_overlap_percentage( 129 | y_coords[0], y_coords[1], 130 | previous_y_coords[0], previous_y_coords[1]) 131 | line_overlap_percentage = max(line_overlap_percentage1, line_overlap_percentage2) 132 | if line_overlap_percentage < y_overlap: 133 | line_bb = _get_bounding_box_of_bb_list(bbs_in_a_line) 134 | line_bbs.append(line_bb) 135 | bbs_in_a_line = [] 136 | bbs_in_a_line.append(bbs[y_index, :]) 137 | previous_y_coords = y_coords 138 | 139 | # process the last line 140 | line_bb = _get_bounding_box_of_bb_list(bbs_in_a_line) 141 | line_bbs.append(line_bb) 142 | return line_bbs 143 | 144 | def sort_bbs_line_by_line(bbs, y_overlap=0.2): 145 | ''' 146 | Function to combine word bbs into lines. 147 | ''' 148 | line_bbs = _filter_bbs(bbs, min_size=0.0001) #Filter small word BBs 149 | line_bbs = combine_bbs_into_lines(line_bbs, y_overlap) 150 | line_bb_expanded = [] 151 | for line_bb in line_bbs: 152 | line_bb_i = expand_bounding_box(line_bb, expand_bb_scale_x=0.1, 153 | expand_bb_scale_y=0.05) 154 | line_bb_expanded.append(line_bb_i) 155 | line_bbs = np.array(line_bb_expanded) 156 | 157 | # X start heuristics 158 | # Remove lines that start more than 150% away 159 | x_start_within_boundary = line_bbs[:, 0] < 0.5 160 | line_bbs = line_bbs[x_start_within_boundary] 161 | 162 | # Remove lines that start 20% away from the average 163 | x_start_line_bbs = line_bbs[:, 0] 164 | x_start_diff = np.abs(x_start_line_bbs - np.median(x_start_line_bbs)) 165 | x_start_remove = x_start_diff < 0.2 166 | line_bbs = line_bbs[x_start_remove] 167 | 168 | # X length heuristics 169 | # Remove lines that are 50% shorter excluding the last element 170 | if len(line_bbs) > 1: 171 | x_length_line_bbs = line_bbs[:-1, 0] - line_bbs[:-1, 2] 172 | x_length_diff = np.abs(x_length_line_bbs - np.median(x_length_line_bbs)) 173 | x_length_remove = x_length_diff < 0.5 174 | last_line = line_bbs[-1] 175 | line_bbs = line_bbs[:-1][x_length_remove] 176 | line_bbs = np.vstack([line_bbs, last_line]) 177 | 178 | # Y height heuristics 179 | # Split lines that are more than 1.5 of the others 180 | y_height = line_bbs[:, 3] 181 | y_height_diff = np.abs(y_height/np.median(y_height)) 182 | y_height_remove = y_height_diff > 1.65 183 | 184 | new_line_bbs = [] 185 | for i in range(line_bbs.shape[0]): 186 | if y_height_remove[i]: 187 | # split line into 2 188 | new_line_top = np.copy(line_bbs[i]) 189 | new_line_top[3] = new_line_top[3] / 2 190 | 191 | new_line_bottom = np.copy(line_bbs[i]) 192 | new_line_bottom[1] = new_line_bottom[1] + new_line_bottom[3]/2 193 | new_line_bottom[3] = new_line_bottom[3] / 2 194 | 195 | new_line_bbs.append(new_line_top) 196 | new_line_bbs.append(new_line_bottom) 197 | else: 198 | new_line_bbs.append(line_bbs[i]) 199 | line_bbs = np.vstack(new_line_bbs) 200 | 201 | # Y consistency heuristics 202 | # Remove lines that overlap by 40% with other lines 203 | line_total_overlap = [] 204 | for i in range(line_bbs.shape[0]): 205 | overlap_i = 0.0 206 | for j in range(line_bbs.shape[0]): 207 | if i != j: 208 | line_i, line_j = line_bbs[i], line_bbs[j] 209 | overlap_i += _get_rect_overlap_percentage(line_i[0], line_i[1], line_i[2], line_i[3], 210 | line_j[0], line_j[1], line_j[2], line_j[3]) 211 | line_total_overlap.append(overlap_i) 212 | overlap_remove = np.array(line_total_overlap) < 1 213 | line_bbs = line_bbs[overlap_remove] 214 | return line_bbs 215 | 216 | def crop_line_images(image, line_bbs): 217 | ''' 218 | Given the input form image, crop the image given a list of bounding boxes. 219 | ''' 220 | line_images = [] 221 | for line_bb in line_bbs: 222 | (x, y, w, h) = line_bb 223 | image_h, image_w = image.shape[-2:] 224 | (x, y, w, h) = (x * image_w, y * image_h, w * image_w, h * image_h) 225 | x1 = _clip_value(x, max_value=image_w) 226 | x2 = _clip_value(x + w, max_value=image_w) 227 | y1 = _clip_value(y, max_value=image_h) 228 | y2 = _clip_value(y + h, max_value=image_h) 229 | 230 | line_image = image[y1:y2, x1:x2] 231 | if line_image.shape[0] > 0 and line_image.shape[1] > 0: 232 | line_images.append(line_image) 233 | return line_images 234 | -------------------------------------------------------------------------------- /dataset/iamdataset/subject/validationset1.txt: -------------------------------------------------------------------------------- 1 | f04-032-00 2 | f04-032-01 3 | f04-032-02 4 | f04-032-03 5 | f04-032-04 6 | f04-032-05 7 | f04-032-06 8 | f04-032-07 9 | f04-032-08 10 | f04-035-00 11 | f04-035-01 12 | f04-035-02 13 | f04-035-03 14 | f04-035-04 15 | f04-039-00 16 | f04-039-01 17 | f04-039-02 18 | f04-039-03 19 | f04-039-04 20 | f04-039-05 21 | f04-039-06 22 | f04-039-07 23 | f04-043-00 24 | f04-043-01 25 | f04-043-02 26 | f04-043-03 27 | f04-043-04 28 | f04-043-05 29 | f04-043-06 30 | f04-043-07 31 | f04-046-00 32 | f04-046-01 33 | f04-046-02 34 | f04-046-03 35 | f04-046-04 36 | f04-046-05 37 | f04-049-00 38 | f04-049-01 39 | f04-049-02 40 | f04-049-03 41 | f04-049-04 42 | f04-049-05 43 | f04-049-06 44 | f04-053-00 45 | f04-053-01 46 | f04-053-02 47 | f04-053-03 48 | f04-053-04 49 | f04-053-05 50 | f04-053-06 51 | f04-057-00 52 | f04-057-01 53 | f04-057-02 54 | f04-057-03 55 | f04-057-04 56 | f04-057-05 57 | f04-057-06 58 | f04-057-07 59 | f04-057-08 60 | f04-061-00 61 | f04-061-01 62 | f04-061-02 63 | f04-061-03 64 | f04-061-04 65 | f04-061-05 66 | f04-061-06 67 | f04-061-07 68 | f04-064-00 69 | f04-064-01 70 | f04-064-02 71 | f04-064-03 72 | f04-064-04 73 | f04-064-05 74 | f04-064-06 75 | f04-064-07 76 | f04-068-00 77 | f04-068-01 78 | f04-068-02 79 | f04-068-03 80 | f04-068-04 81 | f04-068-05 82 | f04-068-06 83 | f04-071-00 84 | f04-071-01 85 | f04-071-02 86 | f04-071-03 87 | f04-071-04 88 | f04-071-05 89 | f04-071-06 90 | f04-071-07 91 | f04-071-08 92 | f04-071-09 93 | f04-074-00 94 | f04-074-01 95 | f04-074-02 96 | f04-074-03 97 | f04-074-04 98 | f04-074-05 99 | f04-074-06 100 | f04-074-07 101 | f04-074-08 102 | f04-074-09 103 | f04-074-10 104 | f04-074-11 105 | f04-079-00 106 | f04-079-01 107 | f04-079-02 108 | f04-079-03 109 | f04-079-04 110 | f04-079-05 111 | f04-079-06 112 | f04-079-07 113 | f04-079-08 114 | f04-079-09 115 | f04-083-00 116 | f04-083-01 117 | f04-083-02 118 | f04-083-03 119 | f04-083-04 120 | f04-083-05 121 | f04-083-06 122 | f04-083-07 123 | f04-083-08 124 | f04-083-09 125 | f04-087-00 126 | f04-087-01 127 | f04-087-02 128 | f04-087-03 129 | f04-087-04 130 | f07-000-00 131 | f07-000-01 132 | f07-000-02 133 | f07-000-03 134 | f07-000-04 135 | f07-000-05 136 | f07-000-06 137 | f07-000-07 138 | f07-039a-00 139 | f07-039a-01 140 | f07-039a-02 141 | f07-039a-03 142 | f07-039a-04 143 | f07-039a-05 144 | f07-039a-06 145 | f07-042a-00 146 | f07-042a-01 147 | f07-042a-02 148 | f07-042a-03 149 | f07-042a-04 150 | f07-042a-05 151 | f07-042a-06 152 | f07-042a-07 153 | f07-046a-00 154 | f07-046a-01 155 | f07-046a-02 156 | f07-046a-03 157 | f07-046a-04 158 | f07-046a-05 159 | f07-046a-06 160 | f07-046a-07 161 | f07-046a-08 162 | f07-046a-09 163 | g07-079a-00 164 | g07-079a-01 165 | g07-079a-02 166 | g07-079a-03 167 | g07-079a-04 168 | g07-079a-05 169 | g07-079a-06 170 | g07-079a-07 171 | g07-079a-08 172 | g07-079a-09 173 | g07-079a-10 174 | f07-000b-00 175 | f07-000b-01 176 | f07-000b-02 177 | f07-000b-03 178 | f07-000b-04 179 | f07-000b-05 180 | f07-000b-06 181 | f07-002-00 182 | f07-002-01 183 | f07-002-02 184 | f07-002-03 185 | f07-002-04 186 | f07-002-05 187 | f07-002-06 188 | f07-002-07 189 | f07-002-08 190 | f07-006-00 191 | f07-006-01 192 | f07-006-02 193 | f07-006-03 194 | f07-006-04 195 | f07-006-05 196 | f07-006-06 197 | f07-009-00 198 | f07-009-01 199 | f07-009-02 200 | f07-009-03 201 | f07-009-04 202 | f07-009-05 203 | f07-009-06 204 | f07-009-07 205 | f07-009-08 206 | f07-009-09 207 | f07-013-00 208 | f07-013-01 209 | f07-013-02 210 | f07-013-03 211 | f07-013-04 212 | f07-013-05 213 | f07-013-06 214 | f07-013-07 215 | f07-013-08 216 | f07-013-09 217 | f07-013-10 218 | f07-016-00 219 | f07-016-01 220 | f07-016-02 221 | f07-016-03 222 | f07-016-04 223 | f07-016-05 224 | f07-016-06 225 | f07-016-07 226 | f07-016-08 227 | f07-016-09 228 | f07-016-10 229 | f07-019b-00 230 | f07-019b-01 231 | f07-019b-02 232 | f07-019b-03 233 | f07-019b-04 234 | f07-019b-05 235 | f07-019b-06 236 | f07-019b-07 237 | f07-019b-08 238 | f07-019b-09 239 | f07-019b-10 240 | f07-021b-00 241 | f07-021b-01 242 | f07-021b-02 243 | f07-021b-03 244 | f07-021b-04 245 | f07-021b-05 246 | f07-021b-06 247 | f07-021b-07 248 | f07-021b-08 249 | f07-021b-09 250 | f07-024b-00 251 | f07-024b-01 252 | f07-024b-02 253 | f07-024b-03 254 | f07-024b-04 255 | f07-024b-05 256 | f07-024b-06 257 | f07-024b-07 258 | f07-024b-08 259 | f07-024b-09 260 | f07-019a-00 261 | f07-019a-01 262 | f07-019a-02 263 | f07-019a-03 264 | f07-019a-04 265 | f07-019a-05 266 | f07-019a-06 267 | f07-019a-07 268 | f07-021a-00 269 | f07-021a-01 270 | f07-021a-02 271 | f07-021a-03 272 | f07-021a-04 273 | f07-021a-05 274 | f07-021a-06 275 | f07-024a-00 276 | f07-024a-01 277 | f07-024a-02 278 | f07-024a-03 279 | f07-024a-04 280 | f07-024a-05 281 | f07-024a-06 282 | f07-024a-07 283 | f07-028a-00 284 | f07-028a-01 285 | f07-028a-02 286 | f07-028a-03 287 | f07-028a-04 288 | f07-028a-05 289 | f07-028a-06 290 | f07-028a-07 291 | f07-028a-08 292 | f07-032a-00 293 | f07-032a-01 294 | f07-032a-02 295 | f07-032a-03 296 | f07-032a-04 297 | f07-032a-05 298 | f07-032a-06 299 | f07-032a-07 300 | f07-032a-08 301 | f07-032a-09 302 | f07-028b-00 303 | f07-028b-01 304 | f07-028b-02 305 | f07-028b-03 306 | f07-028b-04 307 | f07-028b-05 308 | f07-028b-06 309 | f07-028b-07 310 | f07-028b-08 311 | f07-028b-09 312 | f07-028b-10 313 | f07-028b-11 314 | f07-032b-00 315 | f07-032b-01 316 | f07-032b-02 317 | f07-032b-03 318 | f07-032b-04 319 | f07-032b-05 320 | f07-032b-06 321 | f07-032b-07 322 | f07-032b-08 323 | f07-032b-09 324 | f07-032b-10 325 | f07-032b-11 326 | f07-036-00 327 | f07-036-01 328 | f07-036-02 329 | f07-036-03 330 | f07-036-04 331 | f07-036-05 332 | f07-036-06 333 | f07-036-07 334 | f07-039b-00 335 | f07-039b-01 336 | f07-039b-02 337 | f07-039b-03 338 | f07-039b-04 339 | f07-039b-05 340 | f07-039b-06 341 | f07-039b-07 342 | f07-039b-08 343 | f07-042b-00 344 | f07-042b-01 345 | f07-042b-02 346 | f07-042b-03 347 | f07-042b-04 348 | f07-042b-05 349 | f07-042b-06 350 | f07-042b-07 351 | f07-046b-00 352 | f07-046b-01 353 | f07-046b-02 354 | f07-046b-03 355 | f07-046b-04 356 | f07-046b-05 357 | f07-046b-06 358 | f07-046b-07 359 | f07-046b-08 360 | f07-069-00 361 | f07-069-01 362 | f07-069-02 363 | f07-069-03 364 | f07-069-04 365 | f07-069-05 366 | f07-069-06 367 | f07-069-07 368 | f07-069-08 369 | f07-069-09 370 | f07-073-00 371 | f07-073-01 372 | f07-073-02 373 | f07-073-03 374 | f07-073-04 375 | f07-073-05 376 | f07-073-06 377 | f07-073-07 378 | f07-073-08 379 | f07-073-09 380 | f07-081b-00 381 | f07-081b-01 382 | f07-081b-02 383 | f07-081b-03 384 | f07-081b-04 385 | f07-081b-05 386 | f07-081b-06 387 | f07-084b-00 388 | f07-084b-01 389 | f07-084b-02 390 | f07-084b-03 391 | f07-084b-04 392 | f07-084b-05 393 | f07-084b-06 394 | f07-084b-07 395 | f07-084b-08 396 | f07-076a-00 397 | f07-076a-01 398 | f07-076a-02 399 | f07-076a-03 400 | f07-076a-04 401 | f07-076a-05 402 | f07-076a-06 403 | f07-076a-07 404 | f07-076a-08 405 | f07-076a-09 406 | f07-076a-10 407 | f07-081a-00 408 | f07-081a-01 409 | f07-081a-02 410 | f07-081a-03 411 | f07-081a-04 412 | f07-081a-05 413 | f07-081a-06 414 | f07-081a-07 415 | f07-081a-08 416 | f07-081a-09 417 | f07-084a-00 418 | f07-084a-01 419 | f07-084a-02 420 | f07-084a-03 421 | f07-084a-04 422 | f07-084a-05 423 | f07-084a-06 424 | f07-084a-07 425 | f07-084a-08 426 | f07-084a-09 427 | f07-084a-10 428 | f07-088a-00 429 | f07-088a-01 430 | f07-088a-02 431 | f07-088a-03 432 | f07-088a-04 433 | f07-088a-05 434 | f07-088a-06 435 | f07-088a-07 436 | f07-088a-08 437 | f07-092a-00 438 | f07-092a-01 439 | f07-092a-02 440 | f07-092a-03 441 | f07-092a-04 442 | f07-092a-05 443 | f07-092a-06 444 | f07-092a-07 445 | f07-092a-08 446 | f07-088b-00 447 | f07-088b-01 448 | f07-088b-02 449 | f07-088b-03 450 | f07-088b-04 451 | f07-088b-05 452 | f07-092b-00 453 | f07-092b-01 454 | f07-092b-02 455 | f07-092b-03 456 | f07-092b-04 457 | f07-092b-05 458 | f07-096-00 459 | f07-096-01 460 | f07-096-02 461 | f07-096-03 462 | f07-096-04 463 | f07-096-05 464 | f07-101b-00 465 | f07-101b-01 466 | f07-101b-02 467 | f07-101b-03 468 | f07-101b-04 469 | g07-000b-00 470 | g07-000b-01 471 | g07-000b-02 472 | g07-000b-03 473 | g07-000b-04 474 | g07-000b-05 475 | g07-000b-06 476 | g07-000b-07 477 | g07-000b-08 478 | g01-004-00 479 | g01-004-01 480 | g01-004-02 481 | g01-004-03 482 | g01-004-04 483 | g01-004-05 484 | g01-008-00 485 | g01-008-01 486 | g01-008-02 487 | g01-008-03 488 | g01-008-04 489 | g01-008-05 490 | g01-008-06 491 | g01-008-07 492 | g01-008-08 493 | g01-012-00 494 | g01-012-01 495 | g01-012-02 496 | g01-012-03 497 | g01-012-04 498 | g01-012-05 499 | g01-016-00 500 | g01-016-01 501 | g01-016-02 502 | g01-016-03 503 | g01-016-04 504 | g01-016-05 505 | g01-016-06 506 | g01-016-07 507 | g01-019-00 508 | g01-019-01 509 | g01-019-02 510 | g01-019-03 511 | g01-019-04 512 | g01-019-05 513 | g01-019-06 514 | g01-019-07 515 | g01-019-08 516 | g01-019-09 517 | g01-025-00 518 | g01-025-01 519 | g01-025-02 520 | g01-025-03 521 | g01-025-04 522 | g01-025-05 523 | g01-025-06 524 | g01-025-07 525 | g01-025-08 526 | g01-025-09 527 | g01-027-00 528 | g01-027-01 529 | g01-027-02 530 | g01-027-03 531 | g01-027-04 532 | g01-027-05 533 | g01-027-06 534 | g01-027-07 535 | g01-027-08 536 | g01-027-09 537 | g01-031-00 538 | g01-031-01 539 | g01-031-02 540 | g01-031-03 541 | g01-031-04 542 | g01-031-05 543 | g01-031-06 544 | g01-031-07 545 | g01-031-08 546 | g01-031-09 547 | g01-031-10 548 | g01-034-00 549 | g01-034-01 550 | g01-034-02 551 | g01-034-03 552 | g01-034-04 553 | g01-034-05 554 | g01-034-06 555 | g01-034-07 556 | g01-034-08 557 | g01-037-00 558 | g01-037-01 559 | g01-037-02 560 | g01-037-03 561 | g01-037-04 562 | g01-037-05 563 | g01-037-06 564 | g01-037-07 565 | g01-037-08 566 | g01-037-09 567 | g01-039-00 568 | g01-039-01 569 | g01-039-02 570 | g01-039-03 571 | g01-039-04 572 | g01-039-05 573 | g01-039-06 574 | g01-039-07 575 | g01-039-08 576 | g01-039-09 577 | g01-043-00 578 | g01-043-01 579 | g01-043-02 580 | g01-043-03 581 | g01-043-04 582 | g01-043-05 583 | g01-043-06 584 | g01-043-07 585 | g01-043-08 586 | g01-043-09 587 | g01-045-00 588 | g01-045-01 589 | g01-045-02 590 | g01-045-03 591 | g01-045-04 592 | g01-045-05 593 | g01-045-06 594 | g01-045-07 595 | g01-045-08 596 | g01-045-09 597 | g01-067-00 598 | g01-067-01 599 | g01-067-02 600 | g01-067-03 601 | g01-067-04 602 | g01-067-05 603 | g01-067-06 604 | g01-067-07 605 | g01-067-08 606 | g01-067-09 607 | g01-070-00 608 | g01-070-01 609 | g01-070-02 610 | g01-070-03 611 | g01-070-04 612 | g01-070-05 613 | g01-070-06 614 | g01-070-07 615 | g01-070-08 616 | g01-070-09 617 | g01-074-00 618 | g01-074-01 619 | g01-074-02 620 | g01-074-03 621 | g01-074-04 622 | g01-074-05 623 | g01-074-06 624 | g01-074-07 625 | g01-074-08 626 | g01-074-09 627 | g01-083-00 628 | g01-083-01 629 | g01-083-02 630 | g01-083-03 631 | g01-083-04 632 | g01-083-05 633 | g01-083-06 634 | g01-083-07 635 | g01-083-08 636 | g01-083-09 637 | g01-083-10 638 | h01-004-00 639 | h01-004-01 640 | h01-004-02 641 | h01-004-03 642 | h01-004-04 643 | h01-004-05 644 | g01-088-00 645 | g01-088-01 646 | g01-088-02 647 | g01-088-03 648 | g01-088-04 649 | g01-088-05 650 | g01-088-06 651 | g01-088-07 652 | g01-088-08 653 | g01-088-09 654 | g02-059-00 655 | g02-059-01 656 | g02-059-02 657 | g02-059-03 658 | g02-059-04 659 | g02-059-05 660 | g02-059-06 661 | g02-059-07 662 | g02-062-00 663 | g02-062-01 664 | g02-062-02 665 | g02-062-03 666 | g02-062-04 667 | g02-062-05 668 | g02-062-06 669 | g02-062-07 670 | g02-062-08 671 | g02-065-00 672 | g02-065-01 673 | g02-065-02 674 | g02-065-03 675 | g02-065-04 676 | g02-065-05 677 | g02-065-06 678 | g02-065-07 679 | g02-065-08 680 | g02-065-09 681 | g02-073-00 682 | g02-073-01 683 | g02-073-02 684 | g02-073-03 685 | g02-073-04 686 | g02-073-05 687 | g03-000-01 688 | g03-000-02 689 | g03-000-03 690 | g03-000-04 691 | g03-000-05 692 | g03-000-06 693 | g03-000-07 694 | g03-000-08 695 | g03-004-00 696 | g03-004-01 697 | g03-004-02 698 | g03-004-03 699 | g03-004-04 700 | g03-004-05 701 | g03-016-00 702 | g03-016-01 703 | g03-016-02 704 | g03-016-03 705 | g03-016-04 706 | g03-016-05 707 | g03-016-06 708 | g03-016-07 709 | g03-032-00 710 | g03-032-01 711 | g03-032-02 712 | g03-032-03 713 | g03-032-04 714 | g03-032-05 715 | g03-032-06 716 | g03-032-07 717 | g03-040-00 718 | g03-040-01 719 | g03-040-02 720 | g03-040-03 721 | g03-040-04 722 | g03-040-05 723 | g03-040-06 724 | g03-043-00 725 | g03-043-01 726 | g03-043-02 727 | g03-043-03 728 | g03-043-04 729 | g03-043-05 730 | g03-043-06 731 | g03-043-07 732 | g03-043-08 733 | g03-043-09 734 | g03-043-10 735 | g03-049-00 736 | g03-049-01 737 | g03-049-02 738 | g03-049-03 739 | g03-049-04 740 | g03-049-05 741 | g03-049-06 742 | g03-049-07 743 | n02-104-00 744 | n02-104-01 745 | n02-104-02 746 | n02-104-03 747 | n02-104-04 748 | n02-104-05 749 | n02-104-06 750 | n02-109-00 751 | n02-109-01 752 | n02-109-02 753 | n02-109-03 754 | n02-109-04 755 | n02-109-05 756 | n02-109-06 757 | n02-109-07 758 | n02-114-00 759 | n02-114-01 760 | n02-114-02 761 | n02-114-03 762 | n02-114-04 763 | n02-114-05 764 | n02-114-06 765 | n02-114-07 766 | n02-114-08 767 | n02-120-00 768 | n02-120-01 769 | n02-120-02 770 | n02-120-03 771 | n02-120-04 772 | n02-120-05 773 | n02-120-06 774 | n02-127-00 775 | n02-127-01 776 | n02-127-02 777 | n02-127-03 778 | n02-127-04 779 | n02-127-05 780 | n02-127-06 781 | n02-127-07 782 | r03-053-00 783 | r03-053-01 784 | r03-053-02 785 | r03-053-03 786 | r03-053-04 787 | r03-053-05 788 | r03-053-06 789 | r03-053-07 790 | r03-053-08 791 | g03-052-00 792 | g03-052-01 793 | g03-052-02 794 | g03-052-03 795 | g03-052-04 796 | g03-052-05 797 | g03-052-06 798 | g03-052-07 799 | g03-052-08 800 | g03-052-09 801 | g03-052-10 802 | g03-058-00 803 | g03-058-01 804 | g03-058-02 805 | g03-058-03 806 | g03-058-04 807 | g03-058-05 808 | g03-058-06 809 | g03-064-00 810 | g03-064-01 811 | g03-064-02 812 | g03-064-03 813 | g03-064-04 814 | g03-064-05 815 | g03-064-06 816 | g03-064-07 817 | g04-022-00 818 | g04-022-01 819 | g04-022-02 820 | g04-022-03 821 | g04-022-04 822 | g04-022-05 823 | g04-022-06 824 | g04-036-00 825 | g04-036-01 826 | g04-036-02 827 | g04-036-03 828 | g04-036-04 829 | g04-036-05 830 | g04-036-06 831 | g04-039-00 832 | g04-039-01 833 | g04-039-02 834 | g04-039-03 835 | g04-039-04 836 | g04-039-05 837 | g04-039-06 838 | g04-043-00 839 | g04-043-01 840 | g04-043-02 841 | g04-043-03 842 | g04-043-04 843 | g04-043-05 844 | g04-043-06 845 | g04-043-07 846 | g04-048-00 847 | g04-048-01 848 | g04-048-02 849 | g04-048-03 850 | g04-048-04 851 | g04-048-05 852 | g04-048-06 853 | g04-052-00 854 | g04-052-01 855 | g04-052-02 856 | g04-052-03 857 | g04-052-04 858 | g04-052-05 859 | g04-055-00 860 | g04-055-01 861 | g04-055-02 862 | g04-055-03 863 | g04-055-04 864 | g04-055-05 865 | g04-055-06 866 | g04-055-07 867 | g04-055-08 868 | g04-060-00 869 | g04-060-01 870 | g04-060-02 871 | g04-060-03 872 | g04-060-04 873 | g04-060-05 874 | g04-063-00 875 | g04-063-01 876 | g04-063-02 877 | g04-063-03 878 | g04-063-04 879 | g04-063-05 880 | g04-063-06 881 | g04-063-07 882 | g04-063-08 883 | g04-068-00 884 | g04-068-01 885 | g04-068-02 886 | g04-068-03 887 | g04-068-04 888 | g04-068-05 889 | g04-068-06 890 | g04-068-07 891 | g04-068-08 892 | g04-068-09 893 | g04-072-00 894 | g04-072-01 895 | g04-072-02 896 | g04-072-03 897 | g04-072-04 898 | g04-072-05 899 | g04-072-06 900 | g04-072-07 901 | -------------------------------------------------------------------------------- /dataset/iamdataset/subject/validationset2.txt: -------------------------------------------------------------------------------- 1 | c04-110-00 2 | c04-110-01 3 | c04-110-02 4 | c04-110-03 5 | c04-116-00 6 | c04-116-01 7 | c04-116-02 8 | c04-116-03 9 | c04-134-00 10 | c04-134-01 11 | c04-134-02 12 | c04-134-03 13 | c04-134-05 14 | c04-139-00 15 | c04-139-01 16 | c04-139-02 17 | c04-139-03 18 | c04-139-04 19 | c04-139-05 20 | c04-139-06 21 | c04-144-00 22 | c04-144-01 23 | c04-144-02 24 | c04-144-03 25 | c04-144-04 26 | c04-144-05 27 | c04-144-06 28 | c04-144-07 29 | c04-150-00 30 | c04-150-01 31 | c04-150-02 32 | c04-150-03 33 | c04-150-04 34 | c04-150-05 35 | c04-150-06 36 | c04-156-00 37 | c04-156-01 38 | c04-156-02 39 | c04-156-03 40 | c04-156-04 41 | c04-156-05 42 | c04-160-00 43 | c04-160-01 44 | c04-160-02 45 | c04-160-03 46 | c04-160-04 47 | c04-160-05 48 | c04-160-06 49 | c04-160-07 50 | c04-160-08 51 | c04-160-09 52 | c04-165-00 53 | c04-165-01 54 | c04-165-02 55 | c04-165-03 56 | c04-165-04 57 | c04-165-05 58 | c04-165-06 59 | c04-165-07 60 | c04-165-08 61 | c04-170-00 62 | c04-170-01 63 | c04-170-02 64 | c04-170-03 65 | c04-170-04 66 | c06-011-00 67 | c06-011-01 68 | c06-011-02 69 | c06-011-03 70 | c06-011-04 71 | c06-011-05 72 | c06-011-06 73 | c06-011-07 74 | c06-011-08 75 | d06-008-00 76 | d06-008-01 77 | d06-008-02 78 | d06-008-03 79 | d06-008-04 80 | d06-008-05 81 | d06-008-06 82 | d06-008-07 83 | d06-008-08 84 | d06-008-09 85 | d06-020-00 86 | d06-020-01 87 | d06-020-02 88 | d06-020-03 89 | d06-020-04 90 | d06-020-05 91 | d06-020-06 92 | d06-020-07 93 | d06-020-08 94 | d06-020-09 95 | d06-046-00 96 | d06-046-01 97 | d06-046-02 98 | d06-046-03 99 | d06-046-04 100 | d06-046-05 101 | d06-046-06 102 | d06-046-07 103 | d06-046-08 104 | d06-046-09 105 | d06-063-00 106 | d06-063-01 107 | d06-063-02 108 | d06-063-03 109 | d06-063-04 110 | d06-063-05 111 | d06-063-06 112 | d06-063-07 113 | d06-063-08 114 | d06-063-09 115 | d06-063-10 116 | c06-083-01 117 | c06-083-02 118 | c06-083-03 119 | c06-083-04 120 | c06-083-05 121 | c06-083-06 122 | c06-083-07 123 | d06-015-00 124 | d06-015-01 125 | d06-015-02 126 | d06-015-03 127 | d06-015-04 128 | d06-030-00 129 | d06-030-01 130 | d06-030-02 131 | d06-030-03 132 | d06-030-04 133 | d06-050-00 134 | d06-050-01 135 | d06-050-02 136 | d06-050-03 137 | d06-050-04 138 | d06-050-05 139 | d06-082-00 140 | d06-082-01 141 | d06-082-02 142 | d06-082-03 143 | d06-082-04 144 | d01-016-00 145 | d01-016-01 146 | d01-016-02 147 | d01-016-03 148 | d01-019-00 149 | d01-019-01 150 | d01-019-02 151 | d01-019-03 152 | d01-019-04 153 | d01-019-05 154 | d01-024-00 155 | d01-024-01 156 | d01-024-02 157 | d01-024-03 158 | d01-024-04 159 | d01-024-05 160 | d01-024-06 161 | d01-049-00 162 | d01-049-01 163 | d01-049-02 164 | d01-049-03 165 | d01-049-04 166 | d01-049-05 167 | d01-049-06 168 | d01-049-07 169 | d01-052-00 170 | d01-052-01 171 | d01-052-02 172 | d01-052-03 173 | d01-052-04 174 | d01-052-05 175 | d01-052-06 176 | d01-052-07 177 | d01-056-00 178 | d01-056-01 179 | d01-056-02 180 | d01-056-03 181 | d01-056-04 182 | d01-056-05 183 | d01-056-06 184 | d01-056-07 185 | d01-056-08 186 | d01-060-00 187 | d01-060-01 188 | d01-060-02 189 | d01-060-03 190 | d01-060-04 191 | d01-060-05 192 | d01-060-06 193 | d01-060-07 194 | d01-060-08 195 | d01-060-09 196 | d01-080-00 197 | d01-080-01 198 | d01-080-02 199 | d01-080-03 200 | d01-080-04 201 | d01-080-05 202 | d01-080-06 203 | d01-085-00 204 | d01-085-01 205 | d01-085-02 206 | d01-085-03 207 | d01-085-04 208 | d01-085-05 209 | d01-085-06 210 | d01-098-00 211 | d01-098-01 212 | d01-098-02 213 | d01-098-03 214 | d01-098-04 215 | d01-098-05 216 | d01-098-06 217 | d01-104-00 218 | d01-104-01 219 | d01-104-02 220 | d01-104-03 221 | d01-104-04 222 | d01-104-05 223 | d01-104-06 224 | d01-104-07 225 | d01-118-00 226 | d01-118-01 227 | d01-118-02 228 | d01-118-03 229 | d01-118-04 230 | d01-118-05 231 | d01-118-06 232 | d01-123-00 233 | d01-123-01 234 | d01-123-02 235 | d01-123-03 236 | d01-123-04 237 | d01-123-05 238 | d01-123-06 239 | d01-123-07 240 | e01-055-00 241 | e01-055-01 242 | e01-055-02 243 | e01-055-03 244 | e01-055-04 245 | e01-055-05 246 | e01-055-06 247 | e01-055-07 248 | e01-055-08 249 | e01-055-09 250 | d03-112-00 251 | d03-112-01 252 | d03-112-02 253 | d03-112-03 254 | d03-112-04 255 | d03-112-05 256 | d03-112-06 257 | d03-112-07 258 | d03-112-08 259 | d03-117-00 260 | d03-117-01 261 | d03-117-02 262 | d03-117-03 263 | d03-117-04 264 | d03-117-05 265 | d03-117-06 266 | d03-117-07 267 | d03-117-08 268 | d04-005-00 269 | d04-005-01 270 | d04-005-02 271 | d04-005-03 272 | d04-005-04 273 | d04-005-05 274 | d04-005-06 275 | d04-008-00 276 | d04-008-01 277 | d04-008-02 278 | d04-008-03 279 | d04-008-04 280 | d04-008-05 281 | d04-012-00 282 | d04-012-01 283 | d04-012-02 284 | d04-012-03 285 | d04-012-04 286 | d04-012-05 287 | d04-012-06 288 | d04-012-07 289 | d04-012-08 290 | d04-012-09 291 | d04-016-00 292 | d04-016-01 293 | d04-016-02 294 | d04-016-03 295 | d04-016-04 296 | d04-016-05 297 | d04-016-06 298 | d04-016-07 299 | d04-016-08 300 | d04-021-00 301 | d04-021-01 302 | d04-021-02 303 | d04-021-03 304 | d04-021-04 305 | d04-021-05 306 | d04-021-06 307 | d04-028-00 308 | d04-028-01 309 | d04-028-02 310 | d04-028-03 311 | d04-028-04 312 | d04-028-05 313 | d04-028-06 314 | d04-032-00 315 | d04-032-01 316 | d04-032-02 317 | d04-032-03 318 | d04-032-04 319 | d04-032-05 320 | d04-032-06 321 | d04-032-07 322 | d04-032-08 323 | d04-032-09 324 | d04-032-10 325 | d04-037-00 326 | d04-037-01 327 | d04-037-02 328 | d04-037-03 329 | d04-037-04 330 | d04-037-05 331 | d04-047-00 332 | d04-047-01 333 | d04-047-02 334 | d04-047-03 335 | d04-047-04 336 | d04-047-05 337 | d04-047-06 338 | d04-047-07 339 | d04-053-00 340 | d04-053-01 341 | d04-053-02 342 | d04-053-03 343 | d04-053-04 344 | d04-053-05 345 | d04-053-06 346 | d04-053-07 347 | d04-053-08 348 | d04-053-09 349 | d04-050-00 350 | d04-050-01 351 | d04-050-02 352 | d04-050-03 353 | d04-050-04 354 | d04-058-00 355 | d04-058-01 356 | d04-058-02 357 | d04-058-03 358 | d04-058-04 359 | d04-058-05 360 | d04-058-06 361 | d04-062-00 362 | d04-062-01 363 | d04-062-02 364 | d04-062-03 365 | d04-062-04 366 | d04-066-00 367 | d04-066-01 368 | d04-066-02 369 | d04-066-03 370 | d04-066-04 371 | d04-066-05 372 | d04-066-06 373 | d04-066-07 374 | d04-066-08 375 | d04-066-09 376 | d04-071-00 377 | d04-071-01 378 | d04-071-02 379 | d04-071-03 380 | d04-071-04 381 | d04-071-05 382 | d04-071-06 383 | d04-075-00 384 | d04-075-01 385 | d04-075-02 386 | d04-075-03 387 | d04-075-04 388 | d04-075-05 389 | d04-075-06 390 | d04-075-07 391 | d04-075-08 392 | d04-075-09 393 | d04-081-00 394 | d04-081-01 395 | d04-081-02 396 | d04-081-03 397 | d04-081-04 398 | d04-081-05 399 | d04-081-06 400 | d04-081-07 401 | d04-081-08 402 | f04-093-00 403 | f04-093-01 404 | f04-093-02 405 | f04-093-03 406 | f04-093-04 407 | f04-093-05 408 | f04-093-06 409 | f04-093-07 410 | f04-093-08 411 | f04-093-09 412 | f04-096-00 413 | f04-096-01 414 | f04-096-02 415 | f04-096-03 416 | f04-096-04 417 | f04-096-05 418 | f04-096-06 419 | f04-096-07 420 | f04-096-08 421 | f04-096-09 422 | f04-096-10 423 | f04-100-00 424 | f04-100-01 425 | f04-100-02 426 | f04-100-03 427 | f04-100-04 428 | f04-100-05 429 | f04-100-06 430 | f04-100-07 431 | f04-100-08 432 | f04-100-09 433 | f04-100-10 434 | d04-086-00 435 | d04-086-01 436 | d04-086-02 437 | d04-086-03 438 | d04-086-04 439 | d04-086-05 440 | d04-089-00 441 | d04-089-01 442 | d04-089-02 443 | d04-089-03 444 | d04-089-04 445 | d04-089-05 446 | d04-089-06 447 | d04-089-07 448 | d04-096-00 449 | d04-096-01 450 | d04-096-02 451 | d04-096-03 452 | d04-096-04 453 | d04-096-05 454 | d04-096-06 455 | d04-101-00 456 | d04-101-01 457 | d04-101-02 458 | d04-101-03 459 | d04-101-04 460 | d04-101-05 461 | d04-111-00 462 | d04-111-01 463 | d04-111-02 464 | d04-111-03 465 | d04-111-04 466 | d04-111-05 467 | d04-111-06 468 | d04-111-07 469 | d04-111-08 470 | d04-111-09 471 | d04-111-10 472 | d04-111-11 473 | d04-117-00 474 | d04-117-01 475 | d04-117-02 476 | d04-117-03 477 | d04-117-04 478 | d04-117-05 479 | d04-117-06 480 | d04-121-00 481 | d04-121-01 482 | d04-121-02 483 | d04-121-03 484 | d04-121-04 485 | d04-121-05 486 | d04-125-00 487 | d04-125-01 488 | d04-125-02 489 | d04-125-03 490 | d04-125-04 491 | d04-125-05 492 | d04-131-00 493 | d04-131-01 494 | d04-131-02 495 | d04-131-03 496 | d04-131-04 497 | d04-131-05 498 | d04-131-06 499 | d05-008-00 500 | d05-008-01 501 | d05-008-02 502 | d05-008-03 503 | d05-008-04 504 | d05-008-05 505 | d05-008-06 506 | d05-008-07 507 | d05-021-00 508 | d05-021-01 509 | d05-021-02 510 | d05-021-03 511 | d05-021-04 512 | d05-021-05 513 | d05-021-06 514 | d05-021-07 515 | d05-021-08 516 | d05-025-00 517 | d05-025-01 518 | d05-025-02 519 | d05-025-03 520 | d05-025-04 521 | d05-025-05 522 | d05-025-06 523 | d05-025-07 524 | d05-025-08 525 | d05-030-00 526 | d05-030-01 527 | d05-030-02 528 | d05-030-03 529 | d05-030-04 530 | d05-030-05 531 | d05-030-06 532 | d05-030-07 533 | d05-030-08 534 | d05-013-00 535 | d05-013-01 536 | d05-013-02 537 | d05-013-03 538 | d05-013-04 539 | d05-013-05 540 | d05-013-06 541 | d05-013-07 542 | d05-040-00 543 | d05-040-01 544 | d05-040-02 545 | d05-040-03 546 | d05-040-04 547 | d05-040-05 548 | d06-000-00 549 | d06-000-01 550 | d06-000-02 551 | d06-000-03 552 | d06-000-04 553 | d06-000-05 554 | d06-000-06 555 | d06-000-07 556 | d06-000-08 557 | d06-000-09 558 | d06-091-00 559 | d06-091-01 560 | d06-091-02 561 | d06-091-03 562 | d06-091-04 563 | d06-091-05 564 | d06-091-06 565 | d06-091-07 566 | d06-091-08 567 | d06-091-09 568 | d06-091-10 569 | e06-006-00 570 | e06-006-01 571 | e06-006-02 572 | e06-006-03 573 | e06-006-04 574 | e06-006-05 575 | e06-006-06 576 | e06-006-07 577 | e06-006-08 578 | e06-006-09 579 | e06-037-00 580 | e06-037-01 581 | e06-037-02 582 | e06-037-03 583 | e06-037-04 584 | e06-037-05 585 | e06-037-06 586 | e06-037-07 587 | e06-037-08 588 | e06-037-09 589 | e06-037-10 590 | e06-041-00 591 | e06-041-01 592 | e06-041-02 593 | e06-041-03 594 | e06-041-04 595 | e06-041-05 596 | e06-041-06 597 | e06-041-07 598 | e06-041-08 599 | e06-041-09 600 | d06-003-00 601 | d06-003-01 602 | d06-003-02 603 | d06-003-03 604 | d06-003-04 605 | d06-003-05 606 | d06-076-00 607 | d06-076-01 608 | d06-076-02 609 | d06-076-03 610 | d06-076-04 611 | d06-111-00 612 | d06-111-01 613 | d06-111-02 614 | d06-111-03 615 | d06-111-04 616 | d06-111-05 617 | e06-010-00 618 | e06-010-01 619 | e06-010-02 620 | e06-010-03 621 | e06-010-04 622 | e06-010-05 623 | e06-010-06 624 | e06-010-07 625 | e06-046-00 626 | e06-046-01 627 | e06-046-02 628 | e06-046-03 629 | e06-046-04 630 | e06-046-05 631 | d06-011-00 632 | d06-011-01 633 | d06-011-02 634 | d06-011-03 635 | d06-011-04 636 | d06-011-05 637 | d06-011-06 638 | d06-011-07 639 | d06-011-08 640 | d06-011-09 641 | d06-107-00 642 | d06-107-01 643 | d06-107-02 644 | d06-107-03 645 | d06-107-04 646 | d06-107-05 647 | d06-107-06 648 | d06-107-07 649 | d06-107-08 650 | d06-107-09 651 | d06-107-10 652 | e06-003-00 653 | e06-003-01 654 | e06-003-02 655 | e06-003-03 656 | e06-003-04 657 | e06-003-05 658 | e06-003-06 659 | e06-003-07 660 | e06-003-08 661 | e06-003-09 662 | e06-003-10 663 | e06-015-00 664 | e06-015-01 665 | e06-015-02 666 | e06-015-03 667 | e06-015-04 668 | e06-015-05 669 | e06-015-06 670 | e06-015-07 671 | e06-015-08 672 | e06-015-09 673 | e06-030-00 674 | e06-030-01 675 | e06-030-02 676 | e06-030-03 677 | e06-030-04 678 | e06-030-05 679 | e06-030-06 680 | e06-030-07 681 | e06-030-08 682 | e06-030-09 683 | e06-030-10 684 | d06-025-00 685 | d06-025-01 686 | d06-025-02 687 | d06-025-03 688 | d06-025-04 689 | d06-025-05 690 | d06-025-06 691 | d06-025-07 692 | d06-067-00 693 | d06-067-01 694 | d06-067-02 695 | d06-067-03 696 | d06-067-04 697 | d06-067-05 698 | d06-067-06 699 | d06-067-07 700 | d06-067-08 701 | d06-104-00 702 | d06-104-01 703 | d06-104-02 704 | d06-104-03 705 | d06-104-04 706 | d06-104-05 707 | d06-104-06 708 | d06-104-07 709 | d06-104-08 710 | d06-104-09 711 | e06-026-00 712 | e06-026-01 713 | e06-026-02 714 | e06-026-03 715 | e06-026-04 716 | e06-026-05 717 | e06-026-06 718 | e06-026-07 719 | e06-026-08 720 | e06-026-09 721 | e06-049-00 722 | e06-049-01 723 | e06-049-02 724 | e06-049-03 725 | e06-049-04 726 | e06-049-05 727 | e06-049-06 728 | e06-049-07 729 | e06-049-08 730 | e06-049-09 731 | d06-027-00 732 | d06-027-01 733 | d06-027-02 734 | d06-027-03 735 | d06-027-04 736 | d06-027-05 737 | d06-027-06 738 | d06-056-00 739 | d06-056-01 740 | d06-056-02 741 | d06-056-03 742 | d06-056-04 743 | d06-056-05 744 | d06-056-06 745 | d06-056-07 746 | d06-056-08 747 | d06-096-00 748 | d06-096-01 749 | d06-096-02 750 | d06-096-03 751 | d06-096-04 752 | d06-096-05 753 | d06-096-06 754 | d06-096-07 755 | d06-096-08 756 | e06-000-00 757 | e06-000-01 758 | e06-000-02 759 | e06-000-03 760 | e06-000-04 761 | e06-000-05 762 | e06-000-06 763 | e06-000-07 764 | e06-021-00 765 | e06-021-01 766 | e06-021-02 767 | e06-021-03 768 | e06-021-04 769 | e06-021-05 770 | e06-021-06 771 | e06-021-07 772 | e06-021-08 773 | e06-021-09 774 | e06-021-10 775 | d06-037-00 776 | d06-037-01 777 | d06-037-02 778 | d06-037-03 779 | d06-037-04 780 | d06-037-05 781 | d06-037-06 782 | d06-037-07 783 | d06-072-00 784 | d06-072-01 785 | d06-072-02 786 | d06-072-03 787 | d06-072-04 788 | d06-072-05 789 | d06-072-06 790 | d06-072-07 791 | d06-072-08 792 | d06-072-09 793 | d06-072-10 794 | d06-100-00 795 | d06-100-01 796 | d06-100-02 797 | d06-100-03 798 | d06-100-04 799 | d06-100-05 800 | d06-100-06 801 | e06-033-00 802 | e06-033-01 803 | e06-033-02 804 | e06-033-03 805 | e06-033-04 806 | e06-033-05 807 | e06-033-06 808 | e06-033-07 809 | e06-033-08 810 | e06-033-09 811 | e06-070-00 812 | e06-070-01 813 | e06-070-02 814 | e06-070-03 815 | e06-070-04 816 | e06-070-05 817 | e06-070-06 818 | e06-070-07 819 | e06-070-08 820 | e06-070-09 821 | d06-041-00 822 | d06-041-01 823 | d06-041-02 824 | d06-041-03 825 | d06-041-04 826 | d06-041-05 827 | d06-041-06 828 | d06-041-07 829 | d06-060-00 830 | d06-060-01 831 | d06-060-02 832 | d06-060-03 833 | d06-060-04 834 | d06-060-05 835 | d06-060-06 836 | d06-060-07 837 | d06-060-08 838 | d06-086-00 839 | d06-086-01 840 | d06-086-02 841 | d06-086-03 842 | d06-086-04 843 | d06-086-05 844 | d06-086-06 845 | d06-086-07 846 | d06-086-08 847 | d06-086-09 848 | d06-086-10 849 | d06-113-00 850 | d06-113-01 851 | d06-113-02 852 | d06-113-03 853 | e06-053-00 854 | e06-053-01 855 | e06-053-02 856 | e06-053-03 857 | e06-053-04 858 | e06-053-05 859 | e06-053-06 860 | e06-053-07 861 | e06-053-08 862 | d07-082-00 863 | d07-082-01 864 | d07-082-02 865 | d07-082-03 866 | d07-082-04 867 | d07-082-05 868 | d07-082-06 869 | d07-082-07 870 | d07-085-00 871 | d07-085-01 872 | d07-085-02 873 | d07-085-03 874 | d07-085-04 875 | d07-085-05 876 | d07-085-06 877 | d07-085-07 878 | d07-089-00 879 | d07-089-01 880 | d07-089-02 881 | d07-089-03 882 | d07-089-04 883 | d07-089-05 884 | d07-089-06 885 | d07-089-07 886 | d07-089-08 887 | d07-089-09 888 | d07-089-10 889 | d07-093-00 890 | d07-093-01 891 | d07-093-02 892 | d07-093-03 893 | d07-093-04 894 | d07-093-05 895 | d07-093-06 896 | d07-093-07 897 | d07-093-08 898 | d07-096-00 899 | d07-096-01 900 | d07-096-02 901 | d07-096-03 902 | d07-096-04 903 | d07-096-05 904 | d07-096-06 905 | d07-096-07 906 | d07-096-08 907 | d07-096-09 908 | d07-100-00 909 | d07-100-01 910 | d07-100-02 911 | d07-100-03 912 | d07-100-04 913 | d07-100-05 914 | d07-100-06 915 | d07-100-07 916 | d07-100-08 917 | d07-102-00 918 | d07-102-01 919 | d07-102-02 920 | d07-102-03 921 | d07-102-04 922 | d07-102-05 923 | d07-102-06 924 | d07-102-07 925 | e01-014-00 926 | e01-014-01 927 | e01-014-02 928 | e01-014-03 929 | e01-014-04 930 | e01-014-05 931 | e01-014-06 932 | e01-018-00 933 | e01-018-01 934 | e01-018-02 935 | e01-018-03 936 | e01-018-04 937 | e01-018-05 938 | e01-018-06 939 | e01-018-07 940 | e01-018-08 941 | -------------------------------------------------------------------------------- /dataset/iamdataset/xml/a01-000u.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 |
319 | 320 | -------------------------------------------------------------------------------- /utilities/handwriting_line_recognition.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | import time 5 | import random 6 | import os 7 | import matplotlib.pyplot as plt 8 | import argparse 9 | 10 | import mxnet as mx 11 | import numpy as np 12 | from skimage import transform as skimage_tf 13 | from skimage import exposure 14 | 15 | from mxnet import nd, autograd, gluon 16 | from mxboard import SummaryWriter 17 | from mxnet.gluon.model_zoo.vision import resnet34_v1 18 | np.seterr(all='raise') 19 | 20 | import multiprocessing 21 | mx.random.seed(1) 22 | 23 | from utilities.iam_dataset import IAMDataset, resize_image 24 | from utilities.draw_text_on_image import draw_text_on_image 25 | 26 | print_every_n = 1 27 | send_image_every_n = 20 28 | 29 | # Best results: 30 | # python handwriting_line_recognition.py --epochs 251 -n handwriting_line.params -g 0 -l 0.0001 -x 0.1 -y 0.1 -j 0.15 -k 0.15 -p 0.75 -o 2 -a 128 31 | 32 | alphabet_encoding = r' !"#&\'()*+,-./0123456789:;?ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' 33 | alphabet_dict = {alphabet_encoding[i]:i for i in range(len(alphabet_encoding))} 34 | 35 | class EncoderLayer(gluon.HybridBlock): 36 | ''' 37 | The encoder layer takes the image features from a CNN. The image features are transposed so that the LSTM 38 | slices of the image features can be sequentially fed into the LSTM from left to right (and back via the 39 | bidirectional LSTM). 40 | ''' 41 | def __init__(self, hidden_states=200, rnn_layers=1, max_seq_len=100, **kwargs): 42 | self.max_seq_len = max_seq_len 43 | super(EncoderLayer, self).__init__(**kwargs) 44 | with self.name_scope(): 45 | self.lstm = mx.gluon.rnn.LSTM(hidden_states, rnn_layers, bidirectional=True) 46 | 47 | def hybrid_forward(self, F, x): 48 | x = x.transpose((0, 3, 1, 2)) 49 | x = x.flatten() 50 | x = x.split(num_outputs=self.max_seq_len, axis=1) # (SEQ_LEN, N, CHANNELS) 51 | x = F.concat(*[elem.expand_dims(axis=0) for elem in x], dim=0) 52 | x = self.lstm(x) 53 | x = x.transpose((1, 0, 2)) #(N, SEQ_LEN, HIDDEN_UNITS) 54 | return x 55 | 56 | class Network(gluon.HybridBlock): 57 | ''' 58 | The CNN-biLSTM to recognise handwriting text given an image of handwriten text. 59 | Parameters 60 | ---------- 61 | num_downsamples: int, default 2 62 | The number of times to downsample the image features. Each time the features are downsampled, a new LSTM 63 | is created. 64 | resnet_layer_id: int, default 4 65 | The layer ID to obtain features from the resnet34 66 | lstm_hidden_states: int, default 200 67 | The number of hidden states used in the LSTMs 68 | lstm_layers: int, default 1 69 | The number of layers of LSTMs to use 70 | ''' 71 | FEATURE_EXTRACTOR_FILTER = 64 72 | def __init__(self, num_downsamples=2, resnet_layer_id=4, rnn_hidden_states=200, rnn_layers=1, max_seq_len=100, ctx=mx.gpu(0), **kwargs): 73 | super(Network, self).__init__(**kwargs) 74 | self.p_dropout = 0.5 75 | self.num_downsamples = num_downsamples 76 | self.max_seq_len = max_seq_len 77 | self.ctx = ctx 78 | with self.name_scope(): 79 | self.body = self.get_body(resnet_layer_id=resnet_layer_id) 80 | 81 | self.encoders = gluon.nn.HybridSequential() 82 | with self.encoders.name_scope(): 83 | for i in range(self.num_downsamples): 84 | encoder = self.get_encoder(rnn_hidden_states=rnn_hidden_states, rnn_layers=rnn_layers, max_seq_len=max_seq_len) 85 | self.encoders.add(encoder) 86 | self.decoder = self.get_decoder() 87 | self.downsampler = self.get_down_sampler(self.FEATURE_EXTRACTOR_FILTER) 88 | 89 | def get_down_sampler(self, num_filters): 90 | ''' 91 | Creates a two-stacked Conv-BatchNorm-Relu and then a pooling layer to 92 | downsample the image features by half. 93 | 94 | Parameters 95 | ---------- 96 | num_filters: int 97 | To select the number of filters in used the downsampling convolutional layer. 98 | Returns 99 | ------- 100 | network: gluon.nn.HybridSequential 101 | The downsampler network that decreases the width and height of the image features by half. 102 | 103 | ''' 104 | out = gluon.nn.HybridSequential() 105 | with out.name_scope(): 106 | for _ in range(2): 107 | out.add(gluon.nn.Conv2D(num_filters, 3, strides=1, padding=1)) 108 | out.add(gluon.nn.BatchNorm(in_channels=num_filters)) 109 | out.add(gluon.nn.Activation('relu')) 110 | out.add(gluon.nn.MaxPool2D(2)) 111 | out.collect_params().initialize(mx.init.Normal(), ctx=self.ctx) 112 | out.hybridize() 113 | return out 114 | 115 | def get_body(self, resnet_layer_id): 116 | ''' 117 | Create the feature extraction network based on resnet34. 118 | The first layer of the res-net is converted into grayscale by averaging the weights of the 3 channels 119 | of the original resnet. 120 | 121 | Parameters 122 | ---------- 123 | resnet_layer_id: int 124 | The resnet_layer_id specifies which layer to take from 125 | the bottom of the network. 126 | Returns 127 | ------- 128 | network: gluon.nn.HybridSequential 129 | The body network for feature extraction based on resnet 130 | ''' 131 | 132 | pretrained = resnet34_v1(pretrained=True, ctx=self.ctx) 133 | pretrained_2 = resnet34_v1(pretrained=True, ctx=mx.cpu(0)) 134 | first_weights = pretrained_2.features[0].weight.data().mean(axis=1).expand_dims(axis=1) 135 | # First weights could be replaced with individual channels. 136 | 137 | body = gluon.nn.HybridSequential() 138 | with body.name_scope(): 139 | first_layer = gluon.nn.Conv2D(channels=64, kernel_size=(7, 7), padding=(3, 3), strides=(2, 2), in_channels=1, use_bias=False) 140 | first_layer.initialize(mx.init.Xavier(), ctx=self.ctx) 141 | first_layer.weight.set_data(first_weights) 142 | body.add(first_layer) 143 | body.add(*pretrained.features[1:-resnet_layer_id]) 144 | return body 145 | 146 | def get_encoder(self, rnn_hidden_states, rnn_layers, max_seq_len): 147 | ''' 148 | Creates an LSTM to learn the sequential component of the image features. 149 | 150 | Parameters 151 | ---------- 152 | 153 | rnn_hidden_states: int 154 | The number of hidden states in the RNN 155 | 156 | rnn_layers: int 157 | The number of layers to stack the RNN 158 | Returns 159 | ------- 160 | 161 | network: gluon.nn.Sequential 162 | The encoder network to learn the sequential information of the image features 163 | ''' 164 | 165 | encoder = gluon.nn.HybridSequential() 166 | with encoder.name_scope(): 167 | encoder.add(EncoderLayer(hidden_states=rnn_hidden_states, rnn_layers=rnn_layers, max_seq_len=max_seq_len)) 168 | encoder.add(gluon.nn.Dropout(self.p_dropout)) 169 | encoder.collect_params().initialize(mx.init.Xavier(), ctx=self.ctx) 170 | return encoder 171 | 172 | def get_decoder(self): 173 | ''' 174 | Creates a network to convert the output of the encoder into characters. 175 | ''' 176 | 177 | alphabet_size = len(alphabet_encoding) + 1 178 | decoder = mx.gluon.nn.Dense(units=alphabet_size, flatten=False) 179 | decoder.collect_params().initialize(mx.init.Xavier(), ctx=self.ctx) 180 | return decoder 181 | 182 | def hybrid_forward(self, F, x): 183 | features = self.body(x) 184 | hidden_states = [] 185 | hs = self.encoders[0](features) 186 | hidden_states.append(hs) 187 | for i, _ in enumerate(range(self.num_downsamples - 1)): 188 | features = self.downsampler(features) 189 | hs = self.encoders[i+1](features) 190 | hidden_states.append(hs) 191 | hs = F.concat(*hidden_states, dim=2) 192 | output = self.decoder(hs) 193 | return output 194 | 195 | def handwriting_recognition_transform(image, line_image_size): 196 | ''' 197 | Resize and normalise the image to be fed into the network. 198 | ''' 199 | image, _ = resize_image(image, line_image_size) 200 | image = mx.nd.array(image)/255. 201 | image = (image - 0.942532484060557) / 0.15926149044640417 202 | image = image.expand_dims(0).expand_dims(0) 203 | return image 204 | 205 | def transform(image, label): 206 | ''' 207 | This function resizes the input image and converts so that it could be fed into the network. 208 | Furthermore, the label (text) is one-hot encoded. 209 | ''' 210 | image = np.expand_dims(image, axis=0).astype(np.float32) 211 | if image[0, 0, 0] > 1: 212 | image = image/255. 213 | image = (image - 0.942532484060557) / 0.15926149044640417 214 | label_encoded = np.zeros(max_seq_len, dtype=np.float32)-1 215 | i = 0 216 | for word in label: 217 | word = word.replace(""", r'"') 218 | word = word.replace("&", r'&') 219 | word = word.replace('";', '\"') 220 | for letter in word: 221 | label_encoded[i] = alphabet_dict[letter] 222 | i += 1 223 | return image, label_encoded 224 | 225 | def augment_transform(image, label): 226 | ''' 227 | This function randomly: 228 | - translates the input image by +-width_range and +-height_range (percentage). 229 | - scales the image by y_scaling and x_scaling (percentage) 230 | - shears the image by shearing_factor (radians) 231 | ''' 232 | 233 | ty = random.uniform(-random_y_translation, random_y_translation) 234 | tx = random.uniform(-random_x_translation, random_x_translation) 235 | 236 | sx = random.uniform(1. - random_y_scaling, 1. + random_y_scaling) 237 | sy = random.uniform(1. - random_x_scaling, 1. + random_x_scaling) 238 | 239 | s = random.uniform(-random_shearing, random_shearing) 240 | gamma = random.uniform(0.001, random_gamma) 241 | image = exposure.adjust_gamma(image, gamma) 242 | 243 | st = skimage_tf.AffineTransform(scale=(sx, sy), 244 | shear=s, 245 | translation=(tx*image.shape[1], ty*image.shape[0])) 246 | augmented_image = skimage_tf.warp(image, st, cval=1.0) 247 | return transform(augmented_image*255., label) 248 | 249 | 250 | def decode(prediction): 251 | ''' 252 | Returns the string given one-hot encoded vectors. 253 | ''' 254 | 255 | results = [] 256 | for word in prediction: 257 | result = [] 258 | for i, index in enumerate(word): 259 | if i < len(word) - 1 and word[i] == word[i+1] and word[-1] != -1: #Hack to decode label as well 260 | continue 261 | if index == len(alphabet_dict) or index == -1: 262 | continue 263 | else: 264 | result.append(alphabet_encoding[int(index)]) 265 | results.append(result) 266 | words = [''.join(word) for word in results] 267 | return words 268 | 269 | def run_epoch(e, network, dataloader, trainer, log_dir, print_name, is_train): 270 | ''' 271 | Run one epoch to train or test the CNN-biLSTM network 272 | 273 | Parameters 274 | ---------- 275 | 276 | e: int 277 | The epoch number 278 | network: nn.Gluon.HybridSequential 279 | The CNN-biLSTM network 280 | dataloader: gluon.data.DataLoader 281 | The train or testing dataloader that is wrapped around the iam_dataset 282 | 283 | log_dir: Str 284 | The directory to store the log files for mxboard 285 | print_name: Str 286 | Name to print for associating with the data. usually this will be "train" and "test" 287 | 288 | is_train: bool 289 | Boolean to indicate whether or not the network should be updated. is_train should only be set to true for the training data 290 | Returns 291 | ------- 292 | 293 | epoch_loss: float 294 | The loss of the current epoch 295 | ''' 296 | 297 | total_loss = [nd.zeros(1, ctx_) for ctx_ in ctx] 298 | for i, (x_, y_) in enumerate(dataloader): 299 | X = gluon.utils.split_and_load(x_, ctx) 300 | Y = gluon.utils.split_and_load(y_, ctx) 301 | with autograd.record(train_mode=is_train): 302 | output = [network(x) for x in X] 303 | loss_ctc = [ctc_loss(o, y) for o, y in zip(output, Y)] 304 | 305 | if is_train: 306 | [l.backward() for l in loss_ctc] 307 | trainer.step(x_.shape[0]) 308 | 309 | if i == 0 and e % send_image_every_n == 0 and e > 0: 310 | predictions = output[0][:4].softmax().topk(axis=2).asnumpy() 311 | decoded_text = decode(predictions) 312 | image = X[0][:4].asnumpy() 313 | image = image * 0.15926149044640417 + 0.942532484060557 314 | output_image = draw_text_on_image(image, decoded_text) 315 | print("{} first decoded text = {}".format(print_name, decoded_text[0])) 316 | with SummaryWriter(logdir=log_dir, verbose=False, flush_secs=5) as sw: 317 | sw.add_image('bb_{}_image'.format(print_name), output_image, global_step=e) 318 | 319 | for i, l in enumerate(loss_ctc): 320 | total_loss[i] += l.mean() 321 | 322 | epoch_loss = float(sum([tl.asscalar() for tl in total_loss]))/(len(dataloader)*len(ctx)) 323 | 324 | with SummaryWriter(logdir=log_dir, verbose=False, flush_secs=5) as sw: 325 | sw.add_scalar('loss', {print_name: epoch_loss}, global_step=e) 326 | 327 | return epoch_loss 328 | 329 | if __name__ == "__main__": 330 | parser = argparse.ArgumentParser() 331 | parser.add_argument("-g", "--gpu_id", default="0", 332 | help="IDs of the GPU to use, -1 for CPU") 333 | 334 | parser.add_argument("-t", "--line_or_word", default="line", 335 | help="to choose the handwriting to train on words or lines") 336 | 337 | parser.add_argument("-u", "--num_downsamples", default=2, 338 | help="Number of downsamples for the res net") 339 | parser.add_argument("-q", "--resnet_layer_id", default=4, 340 | help="layer ID to obtain features from the resnet34") 341 | parser.add_argument("-a", "--rnn_hidden_states", default=200, 342 | help="Number of hidden states for the RNN encoder") 343 | parser.add_argument("-o", "--rnn_layers", default=1, 344 | help="Number of layers for the RNN") 345 | 346 | parser.add_argument("-e", "--epochs", default=121, 347 | help="Number of epochs to run") 348 | parser.add_argument("-l", "--learning_rate", default=0.0001, 349 | help="Learning rate for training") 350 | parser.add_argument("-w", "--lr_scale", default=1, 351 | help="Amount the divide the learning rate") 352 | parser.add_argument("-r", "--lr_period", default=30, 353 | help="Divides the learning rate after period") 354 | 355 | parser.add_argument("-s", "--batch_size", default=64, 356 | help="Batch size") 357 | 358 | parser.add_argument("-x", "--random_x_translation", default=0.03, 359 | help="Randomly translation the image in the x direction (+ or -)") 360 | parser.add_argument("-y", "--random_y_translation", default=0.03, 361 | help="Randomly translation the image in the y direction (+ or -)") 362 | parser.add_argument("-j", "--random_x_scaling", default=0.10, 363 | help="Randomly scale the image in the x direction") 364 | parser.add_argument("-k", "--random_y_scaling", default=0.10, 365 | help="Randomly scale the image in the y direction") 366 | parser.add_argument("-p", "--random_shearing", default=0.5, 367 | help="Randomly shear the image in radians (+ or -)") 368 | parser.add_argument("-ga", "--random_gamma", default=1, 369 | help="Randomly update gamma of image (+ or -)") 370 | 371 | parser.add_argument("-d", "--log_dir", default="./logs", 372 | help="Directory to store the log files") 373 | parser.add_argument("-c", "--checkpoint_dir", default="model_checkpoint", 374 | help="Directory to store the checkpoints") 375 | parser.add_argument("-n", "--checkpoint_name", default="handwriting_line.params", 376 | help="Name to store the checkpoints") 377 | parser.add_argument("-m", "--load_model", default=None, 378 | help="Name of model to load") 379 | parser.add_argument("-sl", "--max-seq-len", default=None, 380 | help="Maximum sequence length") 381 | args = parser.parse_args() 382 | 383 | print(args) 384 | 385 | gpu_ids = [int(elem) for elem in args.gpu_id.split(",")] 386 | 387 | if gpu_ids == [-1]: 388 | ctx=[mx.cpu()] 389 | else: 390 | ctx=[mx.gpu(i) for i in gpu_ids] 391 | 392 | line_or_word = args.line_or_word 393 | assert line_or_word in ["line", "word"], "{} is not a value option in [\"line\", \"word\"]" 394 | 395 | num_downsamples = int(args.num_downsamples) 396 | resnet_layer_id = int(args.resnet_layer_id) 397 | rnn_hidden_states = int(args.rnn_hidden_states) 398 | rnn_layers = int(args.rnn_layers) 399 | 400 | epochs = int(args.epochs) 401 | learning_rate = float(args.learning_rate) 402 | lr_scale = float(args.lr_scale) 403 | lr_period = float(args.lr_period) 404 | batch_size = int(args.batch_size) 405 | 406 | random_y_translation, random_x_translation = float(args.random_x_translation), float(args.random_y_translation) 407 | random_y_scaling, random_x_scaling = float(args.random_y_scaling), float(args.random_x_scaling) 408 | random_shearing = float(args.random_shearing) 409 | random_gamma = float(args.random_gamma) 410 | 411 | log_dir = args.log_dir 412 | checkpoint_dir, checkpoint_name = args.checkpoint_dir, args.checkpoint_name 413 | load_model = args.load_model 414 | max_seq_len = args.max_seq_len 415 | 416 | if max_seq_len is not None: 417 | max_seq_len = int(max_seq_len) 418 | elif line_or_word == "line": 419 | max_seq_len = 100 420 | else: 421 | max_seq_len = 32 422 | 423 | net = Network(num_downsamples=num_downsamples, resnet_layer_id=resnet_layer_id , rnn_hidden_states=rnn_hidden_states, rnn_layers=rnn_layers, 424 | max_seq_len=max_seq_len, ctx=ctx) 425 | 426 | if load_model is not None and os.path.isfile(os.path.join(checkpoint_dir,load_model)): 427 | net.load_parameters(os.path.join(checkpoint_dir,load_model)) 428 | 429 | train_ds = IAMDataset(line_or_word, output_data="text", train=True) 430 | print("Number of training samples: {}".format(len(train_ds))) 431 | 432 | test_ds = IAMDataset(line_or_word, output_data="text", train=False) 433 | print("Number of testing samples: {}".format(len(test_ds))) 434 | 435 | train_data = gluon.data.DataLoader(train_ds.transform(augment_transform), batch_size, shuffle=True, last_batch="rollover", num_workers=4*len(ctx)) 436 | test_data = gluon.data.DataLoader(test_ds.transform(transform), batch_size, shuffle=True, last_batch="discard", num_workers=4*len(ctx)) 437 | 438 | schedule = mx.lr_scheduler.FactorScheduler(step=lr_period*len(train_data), factor=lr_scale) 439 | schedule.base_lr = learning_rate 440 | 441 | trainer = gluon.Trainer(net.collect_params(), 'adam', {'learning_rate': learning_rate, "lr_scheduler": schedule, 'clip_gradient': 2}) 442 | 443 | ctc_loss = gluon.loss.CTCLoss() 444 | 445 | best_test_loss = 10e10 446 | for e in range(epochs): 447 | train_loss = run_epoch(e, net, train_data, trainer, log_dir, print_name="train", is_train=True) 448 | test_loss = run_epoch(e, net, test_data, trainer, log_dir, print_name="test", is_train=False) 449 | if test_loss < best_test_loss: 450 | print("Saving network, previous best test loss {:.6f}, current test loss {:.6f}".format(best_test_loss, test_loss)) 451 | net.save_parameters(os.path.join(checkpoint_dir, checkpoint_name)) 452 | best_test_loss = test_loss 453 | 454 | if e % print_every_n == 0 and e > 0: 455 | print("Epoch {0}, train_loss {1:.6f}, test_loss {2:.6f}".format(e, train_loss, test_loss)) 456 | -------------------------------------------------------------------------------- /utilities/iam_dataset.py: -------------------------------------------------------------------------------- 1 | import os 2 | import urllib 3 | import sys 4 | import time 5 | import glob 6 | import pickle 7 | import xml.etree.ElementTree as ET 8 | import cv2 9 | import numpy as np 10 | import pandas as pd 11 | import matplotlib.pyplot as plt 12 | import logging 13 | 14 | from mxnet.gluon.data import dataset 15 | 16 | class IAMDataset(dataset.ArrayDataset): 17 | 18 | """ 19 | Parameters 20 | ---------- 21 | parse_method: str, Required 22 | To select the method of parsing the images of the passage 23 | Available options: [form, form_bb, line, word] 24 | 25 | root: str, default: dataset/iamdataset 26 | Location to save the database 27 | 28 | train: bool, default True 29 | Whether to load the training or testing set of writers. 30 | 31 | output_data_type: str, default text 32 | What type of data you want as an output: Text or bounding box. 33 | Available options are: [text, bb] 34 | 35 | output_parse_method: str, default None 36 | If the bounding box (bb) was selected as an output_data_type, 37 | this parameter can select which bb you want to obtain. 38 | Available options: [form, line, word] 39 | 40 | output_form_text_as_array: bool, default False 41 | When output_data is set to text and the parse method is set to form or form_original, 42 | if output_form_text_as_array is true, the output text will be a list of lines string 43 | """ 44 | 45 | MAX_IMAGE_SIZE_FORM = (1120, 800) 46 | MAX_IMAGE_SIZE_LINE = (60, 800) 47 | MAX_IMAGE_SIZE_WORD = (30, 140) 48 | def __init__(self, parse_method, 49 | root=os.path.join(os.path.dirname(__file__), '..', 'dataset', 'iamdataset'), 50 | train=True, output_data="text", 51 | output_parse_method=None, 52 | output_form_text_as_array=False): 53 | 54 | _parse_methods = ["form", "form_original", "form_bb", "line", "word"] 55 | error_message = "{} is not a possible parsing method: {}".format( 56 | parse_method, _parse_methods) 57 | assert parse_method in _parse_methods, error_message 58 | self._parse_method = parse_method 59 | 60 | 61 | self._train = train 62 | 63 | _output_data_types = ["text", "bb"] 64 | error_message = "{} is not a possible output data: {}".format( 65 | output_data, _output_data_types) 66 | assert output_data in _output_data_types, error_message 67 | self._output_data = output_data 68 | 69 | if self._output_data == "bb": 70 | assert self._parse_method in ["form", "form_bb"], "Bounding box only works with form." 71 | _parse_methods = ["form", "line", "word"] 72 | error_message = "{} is not a possible output parsing method: {}".format( 73 | output_parse_method, _parse_methods) 74 | assert output_parse_method in _parse_methods, error_message 75 | self._output_parse_method = output_parse_method 76 | 77 | self.image_data_file_name = os.path.join(root, "image_data-{}-{}-{}*.plk".format( 78 | self._parse_method, self._output_data, self._output_parse_method)) 79 | else: 80 | self.image_data_file_name = os.path.join(root, "image_data-{}-{}*.plk".format(self._parse_method, self._output_data)) 81 | 82 | self._root = root 83 | #if not os.path.isdir(root): 84 | # os.makedirs(root) 85 | self._output_form_text_as_array = output_form_text_as_array 86 | 87 | data = self._get_data() 88 | super(IAMDataset, self).__init__(data) 89 | 90 | def _get_data(self): 91 | 92 | ''' 93 | Function to get the data and to extract the data for training or testing 94 | 95 | Returns 96 | ------- 97 | pd.DataFrame 98 | A dataframe (subject, image, and output) that contains only the training/testing data 99 | 100 | ''' 101 | 102 | #print("Get data") 103 | 104 | 105 | 106 | 107 | if len(glob.glob(self.image_data_file_name)) > 0: 108 | logging.info("Loading data from pickle") 109 | images_data = self._load_dataframe_chunks(self.image_data_file_name) 110 | else: 111 | images_data = self._process_data() 112 | 113 | 114 | #images_data = self._process_data() #comment this line if using pickle 115 | 116 | # Extract train or test data out 117 | train_subjects, test_subjects = self._process_subjects() 118 | if self._train: 119 | data = images_data[np.in1d(self._convert_subject_list(images_data["subject"]), 120 | train_subjects)] 121 | else: 122 | data = images_data[np.in1d(self._convert_subject_list(images_data["subject"]), 123 | test_subjects)] 124 | return data 125 | 126 | def _process_data(self): 127 | ''' 128 | Function that iterates through the downloaded xml file to gather the input images and the 129 | corresponding output. 130 | 131 | Returns 132 | ------- 133 | pd.DataFrame 134 | A pandas dataframe that contains the subject, image and output requested. 135 | ''' 136 | image_data = [] 137 | xml_files = glob.glob(self._root + "/xml/*.xml") 138 | print("Processing data:") 139 | logging.info("Processing data") 140 | for i, xml_file in enumerate(xml_files): 141 | tree = ET.parse(xml_file) 142 | root = tree.getroot() 143 | height, width = int(root.attrib["height"]), int(root.attrib["width"]) 144 | for item in root.iter(self._parse_method.split("_")[0]): #_parse_method=form_original 145 | # Split _ to account for only taking the base "form", "line", "word" that is available in the IAM dataset 146 | #print(self._parse_method.split("_")[1]) 147 | if self._parse_method in ["form", "form_bb", "form_original"]: 148 | image_id = item.attrib["id"] 149 | else: 150 | tmp_id = item.attrib["id"] 151 | tmp_id_split = tmp_id.split("-") 152 | image_id = os.path.join(tmp_id_split[0], tmp_id_split[0] + "-" + tmp_id_split[1], tmp_id) 153 | image_filename = os.path.join(self._root, self._parse_method.split("_")[0], image_id + ".png") 154 | image_arr = self._pre_process_image(image_filename) 155 | if image_arr is None: 156 | continue 157 | output_data = self._get_output_data(item, height, width) 158 | if self._parse_method == "form_bb": 159 | image_arr, output_data = self._crop_and_resize_form_bb(item, image_arr, output_data, height, width) 160 | image_data.append([item.attrib["id"], image_arr, output_data]) 161 | image_data = pd.DataFrame(image_data, columns=["subject", "image", "output"]) 162 | self._save_dataframe_chunks(image_data, self.image_data_file_name) #don't comment line if using pickle 163 | return image_data 164 | 165 | def _pre_process_image(self, img_in): 166 | ''' 167 | Function to read the image and convert it to array data 168 | It also resizes the image to standard size if it is of type ["form","form_bb","line","word"] 169 | 170 | Parameters 171 | ---------- 172 | img_in: str, Required 173 | Contains the filename of the image 174 | 175 | Returns 176 | ---------- 177 | img_arr 178 | An image converted to an array 179 | ''' 180 | #print("Pre Processing Image") 181 | im = cv2.imread(img_in, cv2.IMREAD_GRAYSCALE) 182 | if np.size(im) == 1: # skip if the image data is corrupt. 183 | return None 184 | # reduce the size of form images so that it can fit in memory. 185 | if self._parse_method in ["form", "form_bb"]: 186 | im, _ = resize_image(im, self.MAX_IMAGE_SIZE_FORM) #resize function to be done soon 187 | if self._parse_method == "line": 188 | im, _ = resize_image(im, self.MAX_IMAGE_SIZE_LINE) 189 | if self._parse_method == "word": 190 | im, _ = resize_image(im, self.MAX_IMAGE_SIZE_WORD) 191 | img_arr = np.asarray(im) 192 | return img_arr 193 | 194 | def _get_output_data(self, item, height, width): 195 | 196 | ''' 197 | Function to obtain the output data (both text and bounding boxes). 198 | Note that the bounding boxes are rescaled based on the rescale_ratio parameter. 199 | 200 | Parameter 201 | --------- 202 | item: xml.etree 203 | XML object for a word/line/form. 204 | 205 | height: int 206 | Height of the form to calculate percentages of bounding boxes 207 | 208 | width: int 209 | Width of the form to calculate percentages of bounding boxes 210 | 211 | Returns 212 | ------- 213 | 214 | np.array 215 | A numpy array of the output requested (text or the bounding box) 216 | ''' 217 | 218 | #print("Get output data") 219 | output_data = [] 220 | if self._output_data == "text": 221 | if self._parse_method in ["form", "form_bb", "form_original"]: 222 | text = "" 223 | for line in item.iter('line'): 224 | text += line.attrib["text"] + "\n" 225 | output_data.append(text) 226 | else: 227 | output_data.append(item.attrib['text']) 228 | else: 229 | for item_output in item.iter(self._output_parse_method): 230 | bb = self._get_bb_of_item(item_output, height, width) 231 | if bb == None: # Account for words with no letters 232 | continue 233 | output_data.append(bb) 234 | output_data = np.array(output_data) 235 | return output_data 236 | 237 | def _process_subjects(self, train_subject_lists = ["trainset", "validationset1", "validationset2"], 238 | test_subject_lists = ["testset"]): 239 | 240 | ''' 241 | Function to organise the list of subjects to training and testing. 242 | The IAM dataset provides 4 files: trainset, validationset1, validationset2, and testset each 243 | with a list of subjects. 244 | 245 | Parameters 246 | ---------- 247 | 248 | train_subject_lists: [str], default ["trainset", "validationset1", "validationset2"] 249 | The filenames of the list of subjects to be used for training the model 250 | 251 | test_subject_lists: [str], default ["testset"] 252 | The filenames of the list of subjects to be used for testing the model 253 | 254 | Returns 255 | ------- 256 | 257 | train_subjects: [str] 258 | A list of subjects used for training 259 | 260 | test_subjects: [str] 261 | A list of subjects used for testing 262 | ''' 263 | 264 | #print("Processing subjects") 265 | train_subjects = [] 266 | test_subjects = [] 267 | for train_list in train_subject_lists: 268 | subject_list = pd.read_csv(os.path.join(self._root, "subject", train_list+".txt")) 269 | train_subjects.append(subject_list.values) 270 | for test_list in test_subject_lists: 271 | subject_list = pd.read_csv(os.path.join(self._root, "subject", test_list+".txt")) 272 | test_subjects.append(subject_list.values) 273 | 274 | train_subjects = np.concatenate(train_subjects) 275 | test_subjects = np.concatenate(test_subjects) 276 | if self._parse_method in ["form", "form_bb", "form_original"]: 277 | new_train_subjects = [] 278 | for i in train_subjects: 279 | form_subject_number = i[0].split("-")[0] + "-" + i[0].split("-")[1] 280 | new_train_subjects.append(form_subject_number) 281 | new_test_subjects = [] 282 | for i in test_subjects: 283 | form_subject_number = i[0].split("-")[0] + "-" + i[0].split("-")[1] 284 | new_test_subjects.append(form_subject_number) 285 | train_subjects, test_subjects = new_train_subjects, new_test_subjects 286 | return train_subjects, test_subjects 287 | 288 | def _convert_subject_list(self, subject_list): 289 | 290 | ''' 291 | Function to convert the list of subjects for the "word" parse method 292 | 293 | Parameters 294 | ---------- 295 | 296 | subject_lists: [str] 297 | A list of subjects 298 | 299 | Returns 300 | ------- 301 | 302 | subject_lists: [str] 303 | A list of subjects that is compatible with the "word" parse method 304 | 305 | ''' 306 | 307 | print("Convert subject list") 308 | if self._parse_method == "word": 309 | new_subject_list = [] 310 | for sub in subject_list: 311 | new_subject_number = "-".join(sub.split("-")[:3]) 312 | new_subject_list.append(new_subject_number) 313 | return new_subject_list 314 | else: 315 | return subject_list 316 | 317 | def _get_bb_of_item(self, item, height, width): 318 | ''' Helper function to find the bounding box (bb) of an item in the xml file. 319 | All the characters within the item are found and the left-most (min) and right-most (max + length) 320 | are found. 321 | The bounding box emcompasses the left and right most characters in the x and y direction. 322 | 323 | Parameter 324 | --------- 325 | item: xml.etree object for a word/line/form. 326 | 327 | height: int 328 | Height of the form to calculate percentages of bounding boxes 329 | 330 | width: int 331 | Width of the form to calculate percentages of bounding boxes 332 | 333 | Returns 334 | ------- 335 | list 336 | The bounding box [x, y, w, h] in percentages that encompasses the item. 337 | ''' 338 | 339 | character_list = [a for a in item.iter("cmp")] 340 | if len(character_list) == 0: # To account for some punctuations that have no words 341 | return None 342 | x1 = np.min([int(a.attrib['x']) for a in character_list]) 343 | y1 = np.min([int(a.attrib['y']) for a in character_list]) 344 | x2 = np.max([int(a.attrib['x']) + int(a.attrib['width']) for a in character_list]) 345 | y2 = np.max([int(a.attrib['y']) + int(a.attrib['height'])for a in character_list]) 346 | 347 | x1 = float(x1) / width 348 | x2 = float(x2) / width 349 | y1 = float(y1) / height 350 | y2 = float(y2) / height 351 | bb = [x1, y1, x2 - x1, y2 - y1] 352 | return bb 353 | 354 | def _crop_and_resize_form_bb(self, item, image_arr, output_data, height, width): 355 | bb = self._get_bb_of_item(item, height, width) 356 | 357 | # Expand the form bounding box by 5% 358 | expand_bb_scale = 0.05 359 | new_w = (1 + expand_bb_scale) * bb[2] 360 | new_h = (1 + expand_bb_scale) * bb[3] 361 | 362 | bb[0] = bb[0] - (new_w - bb[2])/2 363 | bb[1] = bb[1] - (new_h - bb[3])/2 364 | bb[2] = new_w 365 | bb[3] = new_h 366 | 367 | image_arr_bb = crop_image(image_arr, bb) 368 | 369 | if self._output_data == "bb": 370 | output_data = self._change_bb_reference(output_data, bb, image_arr.shape, image_arr.shape, image_arr_bb.shape, "minus") 371 | 372 | image_arr_bb_, bb = resize_image(image_arr_bb, desired_size=(700, 700)) 373 | 374 | if self._output_data == "bb": 375 | output_data = self._change_bb_reference(output_data, bb, image_arr_bb.shape, image_arr_bb_.shape, image_arr_bb_.shape, "plus") 376 | image_arr = image_arr_bb_ 377 | return image_arr, output_data 378 | 379 | def _change_bb_reference(self, bb, relative_bb, bb_reference_size, relative_bb_reference_size, output_size, operator): 380 | ''' Helper function to convert bounding boxes relative into another bounding bounding box. 381 | Parameter 382 | -------- 383 | bb: [[int, int, int, int]] 384 | Bounding boxes (x, y, w, h) in percentages to be converted. 385 | 386 | relative_bb: [int, int, int, int] 387 | Reference bounding box (in percentages) to convert bb to 388 | 389 | bb_reference_size: (int, int) 390 | Size (h, w) in pixels of the image containing bb 391 | 392 | relative_bb_reference_size: (int, int) 393 | Size (h, w) in pixels of the image containing relative_bb 394 | 395 | output_size: (int, int) 396 | Size (h, w) in pixels of the output image 397 | 398 | operator: string 399 | Options ["plus", "minus"]. "plus" if relative_bb is within bb and "minus" if bb is within relative_bb 400 | 401 | Returns 402 | ------- 403 | bb: [[int, int, int, int]] 404 | Bounding boxes (x, y, w, h) in percentages that are converted 405 | 406 | ''' 407 | (x1, y1, x2, y2) = (bb[:, 0], bb[:, 1], bb[:, 0] + bb[:, 2], bb[:, 1] + bb[:, 3]) 408 | (x1, y1, x2, y2) = (x1 * bb_reference_size[1], y1 * bb_reference_size[0], 409 | x2 * bb_reference_size[1], y2 * bb_reference_size[0]) 410 | 411 | if operator == "plus": 412 | new_x1 = (x1 + relative_bb[0] * relative_bb_reference_size[1]) / output_size[1] 413 | new_y1 = (y1 + relative_bb[1] * relative_bb_reference_size[0]) / output_size[0] 414 | new_x2 = (x2 + relative_bb[0] * relative_bb_reference_size[1]) / output_size[1] 415 | new_y2 = (y2 + relative_bb[1] * relative_bb_reference_size[0]) / output_size[0] 416 | else: 417 | new_x1 = (x1 - relative_bb[0] * relative_bb_reference_size[1]) / output_size[1] 418 | new_y1 = (y1 - relative_bb[1] * relative_bb_reference_size[0]) / output_size[0] 419 | new_x2 = (x2 - relative_bb[0] * relative_bb_reference_size[1]) / output_size[1] 420 | new_y2 = (y2 - relative_bb[1] * relative_bb_reference_size[0]) / output_size[0] 421 | 422 | new_bbs = np.zeros(shape=bb.shape) 423 | new_bbs[:, 0] = new_x1 424 | new_bbs[:, 1] = new_y1 425 | new_bbs[:, 2] = new_x2 - new_x1 426 | new_bbs[:, 3] = new_y2 - new_y1 427 | return new_bbs 428 | 429 | 430 | 431 | def _save_dataframe_chunks(self, df, name): 432 | for i, df_split in enumerate(np.array_split(df, 4)): 433 | filename = name[:-5] + str(i) + ".plk" # remove *.plk in the filename 434 | df_split.to_pickle(filename, protocol=2) 435 | 436 | def _load_dataframe_chunks(self, name): 437 | image_data_chunks = [] 438 | for fn in sorted(glob.glob(name)): 439 | df = pickle.load(open(fn, 'rb')) 440 | image_data_chunks.append(df) 441 | image_data = pd.concat(image_data_chunks) 442 | return image_data 443 | 444 | 445 | 446 | def __getitem__(self, idx): 447 | return (self._data[0].iloc[idx].image, self._data[0].iloc[idx].output) 448 | 449 | def crop_image(image, bb): 450 | ''' Helper function to crop the image by the bounding box (in percentages) 451 | ''' 452 | (x, y, w, h) = bb 453 | x = x * image.shape[1] 454 | y = y * image.shape[0] 455 | w = w * image.shape[1] 456 | h = h * image.shape[0] 457 | (x1, y1, x2, y2) = (x, y, x + w, y + h) 458 | (x1, y1, x2, y2) = (int(x1), int(y1), int(x2), int(y2)) 459 | return image[y1:y2, x1:x2] 460 | 461 | def resize_image(image, desired_size): 462 | ''' Helper function to resize an image while keeping the aspect ratio. 463 | Parameter 464 | --------- 465 | 466 | image: np.array 467 | The image to be resized. 468 | 469 | desired_size: (int, int) 470 | The (height, width) of the resized image 471 | 472 | Return 473 | ------ 474 | 475 | image: np.array 476 | The image of size = desired_size 477 | 478 | bounding box: (int, int, int, int) 479 | (x, y, w, h) in percentages of the resized image of the original 480 | ''' 481 | size = image.shape[:2] 482 | if size[0] > desired_size[0] or size[1] > desired_size[1]: 483 | ratio_w = float(desired_size[0])/size[0] 484 | ratio_h = float(desired_size[1])/size[1] 485 | ratio = min(ratio_w, ratio_h) 486 | new_size = tuple([int(x*ratio) for x in size]) 487 | image = cv2.resize(image, (new_size[1], new_size[0])) 488 | size = image.shape 489 | 490 | delta_w = max(0, desired_size[1] - size[1]) 491 | delta_h = max(0, desired_size[0] - size[0]) 492 | top, bottom = delta_h//2, delta_h-(delta_h//2) 493 | left, right = delta_w//2, delta_w-(delta_w//2) 494 | 495 | color = image[0][0] 496 | if color < 230: 497 | color = 230 498 | image = cv2.copyMakeBorder(image, top, bottom, left, right, cv2.BORDER_CONSTANT, value=float(color)) 499 | crop_bb = (left/image.shape[1], top/image.shape[0], (image.shape[1] - right - left)/image.shape[1], 500 | (image.shape[0] - bottom - top)/image.shape[0]) 501 | image[image > 230] = 255 502 | return image, crop_bb 503 | 504 | def crop_handwriting_page(image, bb, image_size): 505 | ''' 506 | Given an image and bounding box (bb) crop the input image based on the bounding box. 507 | The final output image was scaled based on the image size. 508 | 509 | Parameters 510 | ---------- 511 | image: np.array 512 | Input form image 513 | 514 | bb: (x, y, w, h) 515 | The bounding box in percentages to crop 516 | 517 | image_size: (h, w) 518 | Image size to scale the output image to. 519 | 520 | Returns 521 | ------- 522 | output_image: np.array 523 | cropped image of size image_size. 524 | ''' 525 | image = crop_image(image, bb) 526 | 527 | image, _ = resize_image(image, desired_size=image_size) 528 | return image 529 | -------------------------------------------------------------------------------- /utilities/word_and_line_segmentation.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | import time 5 | import random 6 | import os 7 | import cv2 8 | import matplotlib.pyplot as plt 9 | import matplotlib.patches as patches 10 | import argparse 11 | 12 | import mxnet as mx 13 | from mxnet.contrib.ndarray import MultiBoxPrior, MultiBoxTarget, MultiBoxDetection, box_nms 14 | import numpy as np 15 | from skimage.draw import line_aa 16 | from skimage import transform as skimage_tf 17 | 18 | from mxnet import nd, autograd, gluon 19 | from mxnet.image import resize_short 20 | from mxboard import SummaryWriter 21 | from mxnet.gluon.model_zoo.vision import resnet34_v1 22 | np.seterr(all='raise') 23 | 24 | import multiprocessing 25 | mx.random.seed(1) 26 | 27 | from utilities.iam_dataset import IAMDataset 28 | from utilities.draw_box_on_image import draw_boxes_on_image 29 | 30 | print_every_n = 5 31 | send_image_every_n = 20 32 | save_every_n = 50 33 | 34 | # To run: 35 | # python word_segmentation.py --min_c 0.01 --overlap_thres 0.10 --topk 150 --epoch 401 --checkpoint_name ssd_400.params 36 | # For fine_tuning: 37 | # python word_segmentation.py -p ssd_550.params 38 | 39 | # python word_segmentation.py --min_c 0.05 --overlap_thres 0.001 --topk 400 --epoch 401 --checkpoint_name word_seg.params 40 | 41 | class SSD(gluon.Block): 42 | def __init__(self, num_classes, ctx, **kwargs): 43 | super(SSD, self).__init__(**kwargs) 44 | 45 | # Seven sets of anchor boxes are defined. For each set, n=2 sizes and m=3 ratios are defined. 46 | # Four anchor boxes (n + m - 1) are generated: 2 square anchor boxes based on the n=2 sizes and 2 rectanges based on 47 | # the sizes and the ratios. See https://discuss.mxnet.io/t/question-regarding-ssd-algorithm/1307 for more information. 48 | 49 | #self.anchor_sizes = [[.1, .2], [.2, .3], [.2, .4], [.4, .6], [.5, .7], [.6, .8], [.7, .9]] 50 | #self.anchor_ratios = [[1, 3, 5], [1, 3, 5], [1, 6, 8], [1, 5, 7], [1, 6, 8], [1, 7, 9], [1, 7, 10]] 51 | 52 | self.anchor_sizes = [[.1, .2], [.2, .3], [.2, .4], [.3, .4], [.3, .5], [.4, .6]] 53 | self.anchor_ratios = [[1, 3, 5], [1, 3, 5], [1, 6, 8], [1, 4, 7], [1, 6, 8], [1, 5, 7]] 54 | 55 | self.num_anchors = len(self.anchor_sizes) 56 | self.num_classes = num_classes 57 | self.ctx = ctx 58 | with self.name_scope(): 59 | self.body, self.downsamples, self.class_preds, self.box_preds = self.get_ssd_model() 60 | self.downsamples.initialize(mx.init.Normal(), ctx=self.ctx) 61 | self.class_preds.initialize(mx.init.Normal(), ctx=self.ctx) 62 | self.box_preds.initialize(mx.init.Normal(), ctx=self.ctx) 63 | 64 | def get_body(self): 65 | ''' 66 | Create the feature extraction network of the SSD based on resnet34. 67 | The first layer of the res-net is converted into grayscale by averaging the weights of the 3 channels 68 | of the original resnet. 69 | 70 | Returns 71 | ------- 72 | network: gluon.nn.HybridSequential 73 | The body network for feature extraction based on resnet 74 | 75 | ''' 76 | pretrained = resnet34_v1(pretrained=True, ctx=self.ctx) 77 | pretrained_2 = resnet34_v1(pretrained=True, ctx=mx.cpu(0)) 78 | first_weights = pretrained_2.features[0].weight.data().mean(axis=1).expand_dims(axis=1) 79 | # First weights could be replaced with individual channels. 80 | 81 | body = gluon.nn.HybridSequential() 82 | with body.name_scope(): 83 | first_layer = gluon.nn.Conv2D(channels=64, kernel_size=(7, 7), padding=(3, 3), strides=(2, 2), in_channels=1, use_bias=False) 84 | first_layer.initialize(mx.init.Normal(), ctx=self.ctx) 85 | first_layer.weight.set_data(first_weights) 86 | body.add(first_layer) 87 | body.add(*pretrained.features[1:-3]) 88 | return body 89 | 90 | def get_class_predictor(self, num_anchors_predicted): 91 | ''' 92 | Creates the category prediction network (takes input from each downsampled feature) 93 | 94 | Parameters 95 | ---------- 96 | 97 | num_anchors_predicted: int 98 | Given n sizes and m ratios, the number of boxes predicted is n+m-1. 99 | e.g., sizes=[.1, .2], ratios=[1, 3, 5] the number of anchors predicted is 4. 100 | 101 | Returns 102 | ------- 103 | 104 | network: gluon.nn.HybridSequential 105 | The class predictor network 106 | ''' 107 | return gluon.nn.Conv2D(num_anchors_predicted*(self.num_classes + 1), kernel_size=3, padding=1) 108 | 109 | def get_box_predictor(self, num_anchors_predicted): 110 | ''' 111 | Creates the bounding box prediction network (takes input from each downsampled feature) 112 | 113 | Parameters 114 | ---------- 115 | 116 | num_anchors_predicted: int 117 | Given n sizes and m ratios, the number of boxes predicted is n+m-1. 118 | e.g., sizes=[.1, .2], ratios=[1, 3, 5] the number of anchors predicted is 4. 119 | 120 | Returns 121 | ------- 122 | 123 | pred: gluon.nn.HybridSequential 124 | The box predictor network 125 | ''' 126 | pred = gluon.nn.HybridSequential() 127 | with pred.name_scope(): 128 | pred.add(gluon.nn.Conv2D(channels=num_anchors_predicted*4, kernel_size=3, padding=1)) 129 | return pred 130 | 131 | def get_down_sampler(self, num_filters): 132 | ''' 133 | Creates a two-stacked Conv-BatchNorm-Relu and then a pooling layer to 134 | downsample the image features by half. 135 | ''' 136 | out = gluon.nn.HybridSequential() 137 | for _ in range(2): 138 | out.add(gluon.nn.Conv2D(num_filters, 3, strides=1, padding=1)) 139 | out.add(gluon.nn.BatchNorm(in_channels=num_filters)) 140 | out.add(gluon.nn.Activation('relu')) 141 | out.add(gluon.nn.MaxPool2D(2)) 142 | out.hybridize() 143 | return out 144 | 145 | def get_ssd_model(self): 146 | ''' 147 | Creates the SSD model that includes the image feature, downsample, category 148 | and bounding boxes prediction networks. 149 | ''' 150 | body = self.get_body() 151 | downsamples = gluon.nn.HybridSequential() 152 | class_preds = gluon.nn.HybridSequential() 153 | box_preds = gluon.nn.HybridSequential() 154 | 155 | downsamples.add(self.get_down_sampler(32)) 156 | downsamples.add(self.get_down_sampler(32)) 157 | downsamples.add(self.get_down_sampler(32)) 158 | 159 | for scale in range(self.num_anchors): 160 | num_anchors_predicted = len(self.anchor_sizes[0]) + len(self.anchor_ratios[0]) - 1 161 | class_preds.add(self.get_class_predictor(num_anchors_predicted)) 162 | box_preds.add(self.get_box_predictor(num_anchors_predicted)) 163 | 164 | return body, downsamples, class_preds, box_preds 165 | 166 | def ssd_forward(self, x): 167 | ''' 168 | Helper function of the forward pass of the sdd 169 | ''' 170 | x = self.body(x) 171 | 172 | default_anchors = [] 173 | predicted_boxes = [] 174 | predicted_classes = [] 175 | 176 | for i in range(self.num_anchors): 177 | default_anchors.append(MultiBoxPrior(x, sizes=self.anchor_sizes[i], ratios=self.anchor_ratios[i])) 178 | predicted_boxes.append(self._flatten_prediction(self.box_preds[i](x))) 179 | predicted_classes.append(self._flatten_prediction(self.class_preds[i](x))) 180 | if i < len(self.downsamples): 181 | x = self.downsamples[i](x) 182 | elif i == 3: 183 | x = nd.Pooling(x, global_pool=True, pool_type='max', kernel=(4, 4)) 184 | return default_anchors, predicted_classes, predicted_boxes 185 | 186 | def forward(self, x): 187 | default_anchors, predicted_classes, predicted_boxes = self.ssd_forward(x) 188 | # we want to concatenate anchors, class predictions, box predictions from different layers 189 | anchors = nd.concat(*default_anchors, dim=1) 190 | box_preds = nd.concat(*predicted_boxes, dim=1) 191 | class_preds = nd.concat(*predicted_classes, dim=1) 192 | class_preds = nd.reshape(class_preds, shape=(0, -1, self.num_classes + 1)) 193 | return anchors, class_preds, box_preds 194 | 195 | def _flatten_prediction(self, pred): 196 | ''' 197 | Helper function to flatten the predicted bounding boxes and categories 198 | ''' 199 | return nd.flatten(nd.transpose(pred, axes=(0, 2, 3, 1))) 200 | 201 | def training_targets(self, default_anchors, class_predicts, labels): 202 | ''' 203 | Helper function to obtain the bounding boxes from the anchors. 204 | ''' 205 | class_predicts = nd.transpose(class_predicts, axes=(0, 2, 1)) 206 | box_target, box_mask, cls_target = MultiBoxTarget(default_anchors, labels, class_predicts) 207 | return box_target, box_mask, cls_target 208 | 209 | class SmoothL1Loss(gluon.loss.Loss): 210 | ''' 211 | A SmoothL1loss function defined in https://gluon.mxnet.io/chapter08_computer-vision/object-detection.html 212 | ''' 213 | def __init__(self, batch_axis=0, **kwargs): 214 | super(SmoothL1Loss, self).__init__(None, batch_axis, **kwargs) 215 | 216 | def hybrid_forward(self, F, output, label, mask): 217 | loss = F.smooth_l1((output - label) * mask, scalar=1.0) 218 | return F.mean(loss, self._batch_axis, exclude=True) 219 | 220 | def augment_transform(image, label): 221 | ''' 222 | 1) Function that randomly translates the input image by +-width_range and +-height_range. 223 | The labels (bounding boxes) are also translated by the same amount. 224 | 2) Each line can also be randomly removed for augmentation. Labels are also reduced to correspond to this 225 | data and label are converted into tensors by calling the "transform" function. 226 | ''' 227 | ty = random.uniform(-random_y_translation, random_y_translation) 228 | tx = random.uniform(-random_x_translation, random_x_translation) 229 | 230 | st = skimage_tf.SimilarityTransform(translation=(tx*image.shape[1], ty*image.shape[0])) 231 | image = skimage_tf.warp(image, st, cval=1.0) 232 | 233 | label[:, 0] = label[:, 0] - tx/2 #NOTE: Check why it has to be halfed (found experimentally) 234 | label[:, 1] = label[:, 1] - ty/2 235 | 236 | index = np.random.uniform(0, 1.0, size=label.shape[0]) > random_remove_box 237 | for i, should_output_bb in enumerate(index): 238 | if should_output_bb == False: 239 | (x, y, w, h) = label[i] 240 | (x1, y1, x2, y2) = (x, y, x + w, y + h) 241 | (x1, y1, x2, y2) = (x1 * image.shape[1], y1 * image.shape[0], 242 | x2 * image.shape[1], y2 * image.shape[0]) 243 | (x1, y1, x2, y2) = (int(x1), int(y1), int(x2), int(y2)) 244 | x1 = 0 if x1 < 0 else x1 245 | y1 = 0 if y1 < 0 else y1 246 | x2 = 0 if x2 < 0 else x2 247 | y2 = 0 if y2 < 0 else y2 248 | image_h, image_w = image.shape 249 | x1 = image_w-1 if x1 >= image_w else x1 250 | y1 = image_h-1 if y1 >= image_h else y1 251 | x2 = image_w-1 if x2 >= image_w else x2 252 | y2 = image_h-1 if y2 >= image_h else y2 253 | image[y1:y2, x1:x2] = image[y1, x1] 254 | 255 | augmented_labels = label[index, :] 256 | return transform(image*255., augmented_labels) 257 | 258 | def transform(image, label): 259 | ''' 260 | Function that converts resizes image into the input image tensor for a CNN. 261 | The labels (bounding boxes) are expanded, converted into (x, y, x+w, y+h), and 262 | zero padded to the maximum number of labels. Finally, it is converted into a float 263 | tensor. 264 | ''' 265 | max_label_n = 128 if detection_box == "word" else 13 266 | 267 | # Resize the image 268 | image = np.expand_dims(image, axis=2) 269 | image = mx.nd.array(image) 270 | image = resize_short(image, image_size) 271 | image = image.transpose([2, 0, 1])/255. 272 | 273 | # Expand the bounding box by expand_bb_scale 274 | bb = label.copy() 275 | new_w = (1 + expand_bb_scale) * bb[:, 2] 276 | new_h = (1 + expand_bb_scale) * bb[:, 3] 277 | 278 | bb[:, 0] = bb[:, 0] - (new_w - bb[:, 2])/2 279 | bb[:, 1] = bb[:, 1] - (new_h - bb[:, 3])/2 280 | bb[:, 2] = new_w 281 | bb[:, 3] = new_h 282 | label = bb 283 | 284 | # Convert the predicted bounding box from (x, y, w, h to (x, y, x + w, y + h) 285 | label = label.astype(np.float32) 286 | label[:, 2] = label[:, 0] + label[:, 2] 287 | label[:, 3] = label[:, 1] + label[:, 3] 288 | 289 | # Zero pad the data 290 | label_n = label.shape[0] 291 | label_padded = np.zeros(shape=(max_label_n, 5)) 292 | label_padded[:label_n, 1:] = label 293 | label_padded[:label_n, 0] = np.ones(shape=(1, label_n)) 294 | label_padded = mx.nd.array(label_padded) 295 | return image, label_padded 296 | 297 | 298 | def generate_output_image(box_predictions, default_anchors, cls_probs, box_target, box_mask, cls_target, x, y): 299 | ''' 300 | Generate the image with the predicted and actual bounding boxes. 301 | Parameters 302 | ---------- 303 | box_predictions: nd.array 304 | Bounding box predictions relative to the anchor boxes, output of the network 305 | 306 | default_anchors: nd.array 307 | Anchors used, output of the network 308 | 309 | cls_probs: nd.array 310 | Output of nd.SoftmaxActivation(nd.transpose(class_predictions, (0, 2, 1)), mode='channel') 311 | where class_predictions is the output of the network. 312 | 313 | box_target: nd.array 314 | Output classification probabilities from network.training_targets(default_anchors, class_predictions, y) 315 | 316 | box_mask: nd.array 317 | Output bounding box predictions from network.training_targets(default_anchors, class_predictions, y) 318 | 319 | cls_target: nd.array 320 | Output targets from network.training_targets(default_anchors, class_predictions, y) 321 | 322 | x: nd.array 323 | The input images 324 | 325 | y: nd.array 326 | The actual labels 327 | 328 | Returns 329 | ------- 330 | output_image: np.array 331 | The images with the predicted and actual bounding boxes drawn on 332 | 333 | number_of_bbs: int 334 | The number of predicting bounding boxes 335 | ''' 336 | output = MultiBoxDetection(*[cls_probs, box_predictions, default_anchors], force_suppress=True, clip=False) 337 | output = box_nms(output, overlap_thresh=overlap_thres, valid_thresh=min_c, topk=topk) 338 | output = output.asnumpy() 339 | 340 | number_of_bbs = 0 341 | predicted_bb = [] 342 | for b in range(output.shape[0]): 343 | predicted_bb_ = output[b, output[b, :, 0] != -1] 344 | predicted_bb_ = predicted_bb_[:, 2:] 345 | number_of_bbs += predicted_bb_.shape[0] 346 | predicted_bb_[:, 2] = predicted_bb_[:, 2] - predicted_bb_[:, 0] 347 | predicted_bb_[:, 3] = predicted_bb_[:, 3] - predicted_bb_[:, 1] 348 | predicted_bb.append(predicted_bb_) 349 | 350 | labels = y[:, :, 1:].asnumpy() 351 | labels[:, :, 2] = labels[:, :, 2] - labels[:, :, 0] 352 | labels[:, :, 3] = labels[:, :, 3] - labels[:, :, 1] 353 | 354 | output_image = draw_boxes_on_image(predicted_bb, labels, x.asnumpy()) 355 | output_image[output_image<0] = 0 356 | output_image[output_image>1] = 1 357 | 358 | return output_image, number_of_bbs 359 | 360 | def predict_bounding_boxes(net, image, min_c, overlap_thres, topk, ctx=mx.gpu()): 361 | ''' 362 | Given the outputs of the dataset (image and bounding box) and the network, 363 | the predicted bounding boxes are provided. 364 | 365 | Parameters 366 | ---------- 367 | net: SSD 368 | The trained SSD network. 369 | 370 | image: np.array 371 | A grayscale image of the handwriting passages. 372 | 373 | Returns 374 | ------- 375 | predicted_bb: [(x, y, w, h)] 376 | The predicted bounding boxes. 377 | ''' 378 | image = mx.nd.array(image).expand_dims(axis=2) 379 | image = mx.image.resize_short(image, 350) 380 | image = image.transpose([2, 0, 1])/255. 381 | 382 | image = image.as_in_context(ctx) 383 | image = image.expand_dims(0) 384 | 385 | bb = np.zeros(shape=(13, 5)) 386 | bb = mx.nd.array(bb) 387 | bb = bb.as_in_context(ctx) 388 | bb = bb.expand_dims(axis=0) 389 | 390 | default_anchors, class_predictions, box_predictions = net(image) 391 | 392 | box_target, box_mask, cls_target = net.training_targets(default_anchors, 393 | class_predictions, bb) 394 | 395 | cls_probs = mx.nd.SoftmaxActivation(mx.nd.transpose(class_predictions, (0, 2, 1)), mode='channel') 396 | 397 | predicted_bb = MultiBoxDetection(*[cls_probs, box_predictions, default_anchors], force_suppress=True, clip=False) 398 | predicted_bb = box_nms(predicted_bb, overlap_thresh=overlap_thres, valid_thresh=min_c, topk=topk) 399 | predicted_bb = predicted_bb.asnumpy() 400 | predicted_bb = predicted_bb[0, predicted_bb[0, :, 0] != -1] 401 | predicted_bb = predicted_bb[:, 2:] 402 | predicted_bb[:, 2] = predicted_bb[:, 2] - predicted_bb[:, 0] 403 | predicted_bb[:, 3] = predicted_bb[:, 3] - predicted_bb[:, 1] 404 | 405 | return predicted_bb 406 | 407 | 408 | def run_epoch(e, network, dataloader, trainer, log_dir, print_name, is_train, update_metric): 409 | ''' 410 | Run one epoch to train or test the SSD network 411 | 412 | Parameters 413 | ---------- 414 | 415 | e: int 416 | The epoch number 417 | 418 | network: nn.Gluon.HybridSequential 419 | The SSD network 420 | 421 | dataloader: gluon.data.DataLoader 422 | The train or testing dataloader that is wrapped around the iam_dataset 423 | 424 | log_dir: Str 425 | The directory to store the log files for mxboard 426 | 427 | print_name: Str 428 | Name to print for associating with the data. usually this will be "train" and "test" 429 | 430 | is_train: bool 431 | Boolean to indicate whether or not the CNN should be updated. is_train should only be set to true for the training data 432 | 433 | Returns 434 | ------- 435 | 436 | network: gluon.nn.HybridSequential 437 | The class predictor network 438 | ''' 439 | 440 | total_losses = [0 for ctx_i in ctx] 441 | for i, (X, Y) in enumerate(dataloader): 442 | X = gluon.utils.split_and_load(X, ctx) 443 | Y = gluon.utils.split_and_load(Y, ctx) 444 | 445 | with autograd.record(train_mode=is_train): 446 | losses = [] 447 | for x, y in zip(X, Y): 448 | default_anchors, class_predictions, box_predictions = network(x) 449 | box_target, box_mask, cls_target = network.training_targets(default_anchors, class_predictions, y) 450 | # losses 451 | loss_class = cls_loss(class_predictions, cls_target) 452 | loss_box = box_loss(box_predictions, box_target, box_mask) 453 | # sum all losses 454 | loss = loss_class + loss_box 455 | losses.append(loss) 456 | 457 | if is_train: 458 | for loss in losses: 459 | loss.backward() 460 | step_size = 0 461 | for x in X: 462 | step_size += x.shape[0] 463 | trainer.step(step_size) 464 | 465 | for index, loss in enumerate(losses): 466 | total_losses[index] += loss.mean().asscalar() 467 | 468 | if update_metric: 469 | cls_metric.update([cls_target], [nd.transpose(class_predictions, (0, 2, 1))]) 470 | box_metric.update([box_target], [box_predictions * box_mask]) 471 | 472 | if i == 0 and e % send_image_every_n == 0 and e > 0: 473 | cls_probs = nd.SoftmaxActivation(nd.transpose(class_predictions, (0, 2, 1)), mode='channel') 474 | output_image, number_of_bbs = generate_output_image(box_predictions, default_anchors, 475 | cls_probs, box_target, box_mask, 476 | cls_target, x, y) 477 | print("Number of predicted {} BBs = {}".format(print_name, number_of_bbs)) 478 | with SummaryWriter(logdir=log_dir, verbose=False, flush_secs=5) as sw: 479 | sw.add_image('bb_{}_image'.format(print_name), output_image, global_step=e) 480 | 481 | 482 | total_loss = 0 483 | for loss in total_losses: 484 | total_loss += loss / (len(dataloader)*len(total_losses)) 485 | 486 | with SummaryWriter(logdir=log_dir, verbose=False, flush_secs=5) as sw: 487 | if update_metric: 488 | name1, val1 = cls_metric.get() 489 | name2, val2 = box_metric.get() 490 | sw.add_scalar(name1, {"test": val1}, global_step=e) 491 | sw.add_scalar(name2, {"test": val2}, global_step=e) 492 | sw.add_scalar('loss', {print_name: total_loss}, global_step=e) 493 | 494 | return total_loss 495 | 496 | if __name__ == "__main__": 497 | parser = argparse.ArgumentParser() 498 | parser.add_argument("-g", "--gpu_count", default=4, 499 | help="Number of GPUs to use") 500 | 501 | parser.add_argument("-b", "--expand_bb_scale", default=0.05, 502 | help="Scale to expand the bounding box") 503 | parser.add_argument("-m", "--min_c", default=0.01, 504 | help="Minimum probability to be considered a bounding box (used in box_nms)") 505 | parser.add_argument("-o", "--overlap_thres", default=0.1, 506 | help="Maximum overlap between bounding boxes") 507 | parser.add_argument("-t", "--topk", default=150, 508 | help="Maximum number of bounding boxes on one slide") 509 | 510 | parser.add_argument("-e", "--epochs", default=351, 511 | help="Number of epochs to run") 512 | parser.add_argument("-l", "--learning_rate", default=0.0001, 513 | help="Learning rate for training") 514 | parser.add_argument("-s", "--batch_size", default=32, 515 | help="Batch size") 516 | parser.add_argument("-w", "--image_size", default=350, 517 | help="Size of the input image (w and h), the value must be less than 700 pixels ") 518 | 519 | parser.add_argument("-x", "--random_x_translation", default=0.03, 520 | help="Randomly translation the image in the x direction (+ or -)") 521 | parser.add_argument("-y", "--random_y_translation", default=0.03, 522 | help="Randomly translation the image in the y direction (+ or -)") 523 | parser.add_argument("-r", "--random_remove_box", default=0.15, 524 | help="Randomly remove bounding boxes and texts with a probability of r") 525 | 526 | parser.add_argument("-d", "--log_dir", default="./logs", 527 | help="Directory to store the log files") 528 | parser.add_argument("-c", "--checkpoint_dir", default="model_checkpoint", 529 | help="Directory to store the checkpoints") 530 | parser.add_argument("-n", "--checkpoint_name", default="ssd.params", 531 | help="Name to store the checkpoints") 532 | parser.add_argument("-db", "--detection_box", default="word", 533 | help="word or line") 534 | parser.add_argument("-p", "--load_model", default=None, 535 | help="Model to load from") 536 | 537 | args = parser.parse_args() 538 | 539 | print(args) 540 | 541 | gpu_count = int(args.gpu_count) 542 | 543 | ctx = [mx.gpu(i) for i in range(gpu_count)] 544 | 545 | expand_bb_scale = float(args.expand_bb_scale) 546 | min_c = float(args.min_c) 547 | overlap_thres = float(args.overlap_thres) 548 | topk = int(args.topk) 549 | 550 | epochs = int(args.epochs) 551 | learning_rate = float(args.learning_rate) 552 | batch_size = int(args.batch_size) * len(ctx) 553 | image_size = int(args.image_size) 554 | 555 | random_y_translation, random_x_translation = float(args.random_x_translation), float(args.random_y_translation) 556 | random_remove_box = float(args.random_remove_box) 557 | 558 | log_dir = args.log_dir 559 | load_model = args.load_model 560 | detection_box = args.detection_box 561 | checkpoint_dir, checkpoint_name = args.checkpoint_dir, detection_box+"_"+args.checkpoint_name 562 | 563 | train_ds = IAMDataset("form_bb", output_data="bb", output_parse_method=detection_box, train=True) 564 | print("Number of training samples: {}".format(len(train_ds))) 565 | 566 | test_ds = IAMDataset("form_bb", output_data="bb", output_parse_method=detection_box, train=False) 567 | print("Number of testing samples: {}".format(len(test_ds))) 568 | 569 | train_data = gluon.data.DataLoader(train_ds.transform(augment_transform), batch_size, shuffle=True, last_batch="rollover", num_workers=multiprocessing.cpu_count()-4) 570 | test_data = gluon.data.DataLoader(test_ds.transform(transform), batch_size, shuffle=False, last_batch="keep", num_workers=multiprocessing.cpu_count()-4) 571 | 572 | net = SSD(2, ctx=ctx) 573 | net.hybridize() 574 | if load_model is not None: 575 | net.load_parameters(os.path.join(checkpoint_dir, load_model)) 576 | 577 | trainer = gluon.Trainer(net.collect_params(), 'adam', {'learning_rate': learning_rate, }) 578 | 579 | cls_loss = gluon.loss.SoftmaxCrossEntropyLoss() 580 | 581 | box_loss = SmoothL1Loss() 582 | 583 | best_test_loss = 10e5 584 | for e in range(epochs): 585 | cls_metric = mx.metric.Accuracy() 586 | box_metric = mx.metric.MAE() 587 | train_loss = run_epoch(e, net, train_data, trainer, log_dir, print_name="train", is_train=True, update_metric=False) 588 | test_loss = run_epoch(e, net, test_data, trainer, log_dir, print_name="test", is_train=False, update_metric=True) 589 | if test_loss < best_test_loss: 590 | print("Saving network, previous best test loss {:.6f}, current test loss {:.6f}".format(best_test_loss, test_loss)) 591 | net.save_parameters(os.path.join(checkpoint_dir, checkpoint_name)) 592 | best_test_loss = test_loss 593 | 594 | if e % print_every_n == 0: 595 | name1, val1 = cls_metric.get() 596 | name2, val2 = box_metric.get() 597 | print("Epoch {0}, train_loss {1:.6f}, test_loss {2:.6f}, test {3}={4:.6f}, {5}={6:.6f}".format(e, train_loss, test_loss, name1, val1, name2, val2)) 598 | -------------------------------------------------------------------------------- /dataset/iamdataset/subject/testset.txt: -------------------------------------------------------------------------------- 1 | m01-049-00 2 | m01-049-01 3 | m01-049-02 4 | m01-049-03 5 | m01-049-04 6 | m01-049-05 7 | m01-049-06 8 | m01-049-07 9 | m01-049-08 10 | m01-049-09 11 | m01-049-10 12 | m01-049-11 13 | m01-060-00 14 | m01-060-01 15 | m01-060-02 16 | m01-060-03 17 | m01-060-04 18 | m01-060-05 19 | m01-060-06 20 | m01-079-00 21 | m01-079-01 22 | m01-079-02 23 | m01-079-03 24 | m01-079-04 25 | m01-079-05 26 | m01-084-00 27 | m01-084-01 28 | m01-084-02 29 | m01-084-03 30 | m01-084-04 31 | m01-084-05 32 | m01-084-06 33 | m01-084-07 34 | m01-090-00 35 | m01-090-01 36 | m01-090-02 37 | m01-090-03 38 | m01-090-04 39 | m01-090-05 40 | m01-090-06 41 | m01-090-07 42 | m01-095-00 43 | m01-095-01 44 | m01-095-02 45 | m01-095-03 46 | m01-095-04 47 | m01-095-05 48 | m01-095-06 49 | m01-095-07 50 | m01-104-00 51 | m01-104-01 52 | m01-104-02 53 | m01-104-03 54 | m01-104-04 55 | m01-104-05 56 | m01-104-06 57 | m01-104-07 58 | m01-121-00 59 | m01-121-01 60 | m01-121-02 61 | m01-121-03 62 | m01-121-04 63 | m01-121-05 64 | m01-121-06 65 | m01-121-07 66 | m01-110-00 67 | m01-110-01 68 | m01-110-02 69 | m01-110-03 70 | m01-110-04 71 | m01-110-05 72 | m01-110-06 73 | m01-110-07 74 | m01-110-08 75 | m01-110-09 76 | m01-110-10 77 | m01-110-11 78 | m01-131-00 79 | m01-131-01 80 | m01-131-02 81 | m01-131-03 82 | m01-131-04 83 | m01-131-05 84 | m01-115-00 85 | m01-115-01 86 | m01-115-02 87 | m01-115-03 88 | m01-115-04 89 | m01-115-05 90 | m01-115-06 91 | m01-115-07 92 | m01-125-00 93 | m01-125-01 94 | m01-125-02 95 | m01-125-03 96 | m01-125-04 97 | m01-125-05 98 | m01-125-06 99 | m01-125-07 100 | m01-125-08 101 | m01-125-09 102 | m01-125-10 103 | m01-136-00 104 | m01-136-01 105 | m01-136-02 106 | m01-136-03 107 | m01-136-04 108 | m01-136-05 109 | m01-136-06 110 | m01-136-07 111 | m01-136-08 112 | m01-136-09 113 | m01-136-10 114 | m01-149-00 115 | m01-149-01 116 | m01-149-02 117 | m01-149-03 118 | m01-149-04 119 | m01-149-05 120 | m01-149-06 121 | m01-149-07 122 | m01-160-00 123 | m01-160-01 124 | m01-160-02 125 | m01-160-03 126 | m01-160-04 127 | m01-160-05 128 | m01-160-06 129 | m02-048-00 130 | m02-048-01 131 | m02-048-02 132 | m02-048-03 133 | m02-048-04 134 | m02-048-05 135 | m02-048-06 136 | m02-048-07 137 | m02-048-08 138 | m02-048-09 139 | m02-048-10 140 | m02-052-00 141 | m02-052-01 142 | m02-052-02 143 | m02-052-03 144 | m02-052-04 145 | m02-052-05 146 | m02-052-06 147 | m02-055-00 148 | m02-055-01 149 | m02-055-02 150 | m02-055-03 151 | m02-055-04 152 | m02-055-05 153 | m02-055-06 154 | m02-055-07 155 | m02-055-08 156 | m02-059-00 157 | m02-059-01 158 | m02-059-02 159 | m02-059-03 160 | m02-059-04 161 | m02-059-05 162 | m02-066-00 163 | m02-066-01 164 | m02-066-02 165 | m02-066-03 166 | m02-066-04 167 | m02-066-05 168 | m02-069-00 169 | m02-069-01 170 | m02-069-02 171 | m02-069-03 172 | m02-069-04 173 | m02-069-05 174 | m02-069-06 175 | m02-069-07 176 | m02-069-08 177 | m02-072-00 178 | m02-072-01 179 | m02-072-02 180 | m02-072-03 181 | m02-072-04 182 | m02-072-05 183 | m02-072-06 184 | m02-075-00 185 | m02-075-01 186 | m02-075-02 187 | m02-075-03 188 | m02-075-04 189 | m02-075-05 190 | m02-075-06 191 | m02-106-00 192 | m02-106-01 193 | m02-106-02 194 | m02-106-03 195 | m02-106-04 196 | m02-106-05 197 | m02-106-06 198 | m02-106-07 199 | m02-106-08 200 | m02-106-09 201 | m02-080-01 202 | m02-080-02 203 | m02-080-03 204 | m02-080-04 205 | m02-080-05 206 | m02-080-06 207 | m02-080-07 208 | m02-080-08 209 | m02-083-00 210 | m02-083-01 211 | m02-083-02 212 | m02-083-03 213 | m02-083-04 214 | m02-083-05 215 | m02-083-06 216 | m02-083-07 217 | m02-083-08 218 | m02-083-09 219 | m02-083-10 220 | m02-083-11 221 | m02-087-00 222 | m02-087-01 223 | m02-087-02 224 | m02-087-03 225 | m02-087-04 226 | m02-087-05 227 | m02-087-06 228 | m02-090-00 229 | m02-090-01 230 | m02-090-02 231 | m02-090-03 232 | m02-090-04 233 | m02-090-05 234 | m02-090-06 235 | m02-090-07 236 | m02-090-08 237 | m02-090-09 238 | m02-090-10 239 | m02-095-00 240 | m02-095-01 241 | m02-095-02 242 | m02-095-03 243 | m02-095-04 244 | m02-095-05 245 | m02-095-06 246 | m02-095-07 247 | m02-095-08 248 | m02-102-00 249 | m02-102-01 250 | m02-102-02 251 | m02-102-03 252 | m02-102-04 253 | m02-102-05 254 | m02-102-06 255 | m02-102-07 256 | m02-102-08 257 | m02-102-09 258 | m02-109-00 259 | m02-109-01 260 | m02-109-02 261 | m02-109-03 262 | m02-109-04 263 | m02-109-05 264 | m02-109-06 265 | m02-109-07 266 | m02-112-00 267 | m02-112-01 268 | m02-112-02 269 | m02-112-03 270 | m02-112-04 271 | m02-112-05 272 | m03-006-00 273 | m03-006-01 274 | m03-006-02 275 | m03-006-03 276 | m03-006-04 277 | m03-006-05 278 | m03-006-06 279 | m03-006-07 280 | m03-006-08 281 | m03-006-09 282 | m03-013-00 283 | m03-013-01 284 | m03-013-02 285 | m03-013-03 286 | m03-013-04 287 | m03-013-05 288 | m03-020-00 289 | m03-020-01 290 | m03-020-02 291 | m03-020-03 292 | m03-020-04 293 | m03-020-05 294 | m03-033-00 295 | m03-033-01 296 | m03-033-02 297 | m03-033-03 298 | m03-033-04 299 | m03-033-05 300 | m03-033-06 301 | m03-062-00 302 | m03-062-01 303 | m03-062-02 304 | m03-062-03 305 | m03-062-04 306 | m03-062-05 307 | m03-062-06 308 | m03-062-07 309 | m03-062-08 310 | m03-095-00 311 | m03-095-01 312 | m03-095-02 313 | m03-095-03 314 | m03-095-04 315 | m03-095-05 316 | m03-095-06 317 | m03-095-07 318 | m03-095-08 319 | m03-110-00 320 | m03-110-01 321 | m03-110-02 322 | m03-110-03 323 | m03-110-04 324 | m03-110-05 325 | m03-110-06 326 | m03-110-07 327 | m03-110-08 328 | m03-110-09 329 | m03-114-00 330 | m03-114-01 331 | m03-114-02 332 | m03-114-03 333 | m03-114-04 334 | m03-114-05 335 | m03-114-06 336 | m03-114-07 337 | m03-114-08 338 | m03-114-09 339 | m03-118-00 340 | m03-118-01 341 | m03-118-02 342 | m03-118-03 343 | m03-118-04 344 | m03-118-05 345 | m04-000-00 346 | m04-000-01 347 | m04-000-02 348 | m04-000-03 349 | m04-000-04 350 | m04-000-05 351 | m04-000-06 352 | m04-000-07 353 | m04-007-00 354 | m04-007-01 355 | m04-007-02 356 | m04-007-03 357 | m04-007-04 358 | m04-007-05 359 | m04-007-06 360 | m04-007-07 361 | m04-007-08 362 | m04-012-00 363 | m04-012-01 364 | m04-012-02 365 | m04-012-03 366 | m04-012-04 367 | m04-012-05 368 | m04-012-06 369 | m04-012-07 370 | m04-012-08 371 | m04-012-09 372 | m04-019-00 373 | m04-019-01 374 | m04-019-02 375 | m04-019-03 376 | m04-019-04 377 | m04-019-05 378 | m04-019-06 379 | m04-019-07 380 | m04-024-00 381 | m04-024-01 382 | m04-024-02 383 | m04-024-03 384 | m04-024-04 385 | m04-024-05 386 | m04-024-06 387 | m04-024-07 388 | m04-030-00 389 | m04-030-01 390 | m04-030-02 391 | m04-030-03 392 | m04-030-04 393 | m04-038-00 394 | m04-038-01 395 | m04-038-02 396 | m04-038-03 397 | m04-038-04 398 | m04-043-00 399 | m04-043-01 400 | m04-043-02 401 | m04-043-03 402 | m04-043-04 403 | m04-061-00 404 | m04-061-01 405 | m04-061-02 406 | m04-061-03 407 | m04-061-04 408 | m04-072-00 409 | m04-072-01 410 | m04-072-02 411 | m04-072-03 412 | m04-072-04 413 | m04-072-05 414 | m04-072-06 415 | m04-072-07 416 | m04-072-08 417 | m04-072-09 418 | m04-078-00 419 | m04-078-01 420 | m04-078-02 421 | m04-078-03 422 | m04-078-04 423 | m04-078-05 424 | m04-078-06 425 | m04-078-07 426 | m04-081-00 427 | m04-081-01 428 | m04-081-02 429 | m04-081-03 430 | m04-081-04 431 | m04-081-05 432 | m04-081-06 433 | m04-081-07 434 | m04-081-08 435 | m04-081-09 436 | m04-081-10 437 | m04-093-00 438 | m04-093-01 439 | m04-093-02 440 | m04-093-03 441 | m04-093-04 442 | m04-093-05 443 | m04-093-06 444 | m04-093-07 445 | m04-093-08 446 | m04-093-09 447 | m04-100-00 448 | m04-100-01 449 | m04-100-02 450 | m04-100-03 451 | m04-100-04 452 | m04-100-05 453 | m04-100-06 454 | m04-100-07 455 | m04-107-00 456 | m04-107-01 457 | m04-107-02 458 | m04-107-03 459 | m04-107-04 460 | m04-107-05 461 | m04-107-06 462 | m04-107-07 463 | m04-107-08 464 | m04-107-09 465 | m04-113-00 466 | m04-113-01 467 | m04-113-02 468 | m04-113-03 469 | m04-113-04 470 | m04-113-05 471 | m04-113-06 472 | m04-113-07 473 | m04-113-08 474 | m04-113-09 475 | m04-113-10 476 | m04-123-00 477 | m04-123-01 478 | m04-123-02 479 | m04-123-03 480 | m04-123-04 481 | m04-123-05 482 | m04-123-06 483 | m04-123-07 484 | m04-123-08 485 | m04-123-09 486 | m04-123-10 487 | m04-131-00 488 | m04-131-01 489 | m04-131-02 490 | m04-131-03 491 | m04-131-04 492 | m04-131-05 493 | m04-131-06 494 | m04-131-07 495 | m04-131-08 496 | m04-138-00 497 | m04-138-01 498 | m04-138-02 499 | m04-138-03 500 | m04-138-04 501 | m04-138-05 502 | m04-138-06 503 | m04-138-07 504 | m04-138-08 505 | m04-138-09 506 | m04-145-00 507 | m04-145-01 508 | m04-145-02 509 | m04-145-03 510 | m04-145-04 511 | m04-145-05 512 | m04-145-06 513 | m04-152-00 514 | m04-152-01 515 | m04-152-02 516 | m04-152-03 517 | m04-152-04 518 | m04-152-05 519 | m04-152-06 520 | m04-152-07 521 | m04-164-00 522 | m04-164-01 523 | m04-164-02 524 | m04-164-03 525 | m04-164-04 526 | m04-164-05 527 | m04-164-06 528 | m04-180-00 529 | m04-180-01 530 | m04-180-02 531 | m04-180-03 532 | m04-180-04 533 | m04-180-05 534 | m04-180-06 535 | m04-251-00 536 | m04-251-01 537 | m04-190-00 538 | m04-190-01 539 | m04-190-02 540 | m04-190-03 541 | m04-190-04 542 | m04-190-05 543 | m04-190-06 544 | m04-190-07 545 | m04-190-08 546 | m04-190-09 547 | m04-200-00 548 | m04-200-01 549 | m04-200-02 550 | m04-200-03 551 | m04-200-04 552 | m04-200-05 553 | m04-200-06 554 | m04-209-00 555 | m04-209-01 556 | m04-209-02 557 | m04-209-03 558 | m04-209-04 559 | m04-209-05 560 | m04-216-00 561 | m04-216-01 562 | m04-216-02 563 | m04-216-03 564 | m04-216-04 565 | m04-216-05 566 | m04-222-00 567 | m04-222-01 568 | m04-222-02 569 | m04-222-03 570 | m04-222-04 571 | m04-222-05 572 | m04-222-06 573 | m04-231-00 574 | m04-231-01 575 | m04-231-02 576 | m04-231-03 577 | m04-231-04 578 | m04-231-05 579 | m04-238-00 580 | m04-238-01 581 | m04-238-02 582 | m04-238-03 583 | m04-238-04 584 | m04-238-05 585 | m04-246-00 586 | m04-246-01 587 | m04-246-02 588 | m04-246-03 589 | m04-246-04 590 | m04-246-05 591 | m04-246-06 592 | n04-000-00 593 | n04-000-01 594 | n04-000-02 595 | n04-000-03 596 | n04-000-04 597 | n04-000-05 598 | n04-009-00 599 | n04-009-01 600 | n04-009-02 601 | n04-009-03 602 | n04-009-04 603 | n04-009-05 604 | n04-009-06 605 | m06-019-00 606 | m06-019-01 607 | m06-019-02 608 | m06-019-03 609 | m06-019-04 610 | m06-019-05 611 | m06-019-06 612 | n06-148-00 613 | n06-148-01 614 | n06-148-02 615 | n06-148-03 616 | n06-148-04 617 | n06-148-05 618 | n06-148-06 619 | n06-148-07 620 | n06-148-08 621 | n06-156-00 622 | n06-156-01 623 | n06-156-02 624 | n06-156-03 625 | n06-156-04 626 | n06-156-05 627 | n06-156-06 628 | n06-156-07 629 | n06-163-00 630 | n06-163-01 631 | n06-163-02 632 | n06-163-03 633 | n06-163-04 634 | n06-163-05 635 | n06-163-06 636 | n06-163-07 637 | n06-163-08 638 | n06-163-09 639 | n06-169-00 640 | n06-169-01 641 | n06-169-02 642 | n06-169-03 643 | n06-169-04 644 | n06-169-05 645 | n06-169-06 646 | n06-169-07 647 | n06-175-00 648 | n06-175-01 649 | n06-175-02 650 | n06-175-03 651 | n06-175-04 652 | n06-175-05 653 | n06-175-06 654 | n06-175-07 655 | n06-175-08 656 | n06-182-00 657 | n06-182-01 658 | n06-182-02 659 | n06-182-03 660 | n06-182-04 661 | n06-182-05 662 | n06-182-06 663 | n06-182-07 664 | n06-186-00 665 | n06-186-01 666 | n06-186-02 667 | n06-186-03 668 | n06-186-04 669 | n06-186-05 670 | n06-186-06 671 | n06-186-07 672 | n06-194-00 673 | n06-194-01 674 | n06-194-02 675 | n06-194-03 676 | n06-194-04 677 | n06-194-05 678 | n06-194-06 679 | n06-194-07 680 | n06-201-00 681 | n06-201-01 682 | n06-201-02 683 | n06-201-03 684 | n06-201-04 685 | n06-201-05 686 | n06-201-06 687 | m06-031-00 688 | m06-031-01 689 | m06-031-02 690 | m06-031-03 691 | m06-031-04 692 | m06-031-05 693 | m06-031-06 694 | m06-042-00 695 | m06-042-01 696 | m06-042-02 697 | m06-042-03 698 | m06-042-04 699 | m06-048-00 700 | m06-048-01 701 | m06-048-02 702 | m06-048-03 703 | m06-048-04 704 | m06-048-05 705 | m06-048-06 706 | m06-048-07 707 | m06-056-00 708 | m06-056-01 709 | m06-056-02 710 | m06-056-03 711 | m06-056-04 712 | m06-056-05 713 | m06-067-00 714 | m06-067-01 715 | m06-067-02 716 | m06-067-03 717 | m06-067-04 718 | m06-067-05 719 | m06-067-06 720 | m06-076-00 721 | m06-076-01 722 | m06-076-02 723 | m06-076-03 724 | m06-076-04 725 | m06-076-05 726 | m06-083-00 727 | m06-083-01 728 | m06-083-02 729 | m06-083-03 730 | m06-083-04 731 | m06-083-05 732 | m06-083-06 733 | m06-091-00 734 | m06-091-01 735 | m06-091-02 736 | m06-091-03 737 | m06-091-04 738 | m06-091-05 739 | m06-091-06 740 | m06-098-00 741 | m06-098-01 742 | m06-098-02 743 | m06-098-03 744 | m06-098-04 745 | m06-098-05 746 | m06-106-00 747 | m06-106-01 748 | m06-106-02 749 | m06-106-03 750 | m06-106-04 751 | m06-106-05 752 | m06-106-06 753 | m06-106-07 754 | m06-106-08 755 | n01-000-00 756 | n01-000-01 757 | n01-000-02 758 | n01-000-03 759 | n01-000-04 760 | n01-000-05 761 | n01-000-06 762 | n01-000-07 763 | n01-009-00 764 | n01-009-01 765 | n01-009-02 766 | n01-009-03 767 | n01-009-04 768 | n01-009-05 769 | n01-009-06 770 | n01-009-07 771 | n01-004-00 772 | n01-004-01 773 | n01-004-02 774 | n01-004-03 775 | n01-004-04 776 | n01-004-05 777 | n01-004-06 778 | n01-020-00 779 | n01-020-01 780 | n01-020-02 781 | n01-020-03 782 | n01-020-04 783 | n01-020-05 784 | n01-020-06 785 | n01-020-07 786 | n01-020-08 787 | n01-031-00 788 | n01-031-01 789 | n01-031-02 790 | n01-031-03 791 | n01-031-04 792 | n01-031-05 793 | n01-031-06 794 | n01-031-07 795 | n01-031-08 796 | n01-045-00 797 | n01-045-01 798 | n01-045-02 799 | n01-045-03 800 | n01-045-04 801 | n01-045-05 802 | n01-045-06 803 | n01-045-07 804 | n01-045-08 805 | n01-045-09 806 | n01-036-00 807 | n01-036-01 808 | n01-036-02 809 | n01-036-03 810 | n01-036-04 811 | n01-036-05 812 | n01-052-00 813 | n01-052-01 814 | n01-052-02 815 | n01-052-03 816 | n01-052-04 817 | n01-052-05 818 | n01-052-06 819 | n01-052-07 820 | n01-052-08 821 | n01-052-09 822 | n01-052-10 823 | n01-052-11 824 | n01-052-12 825 | n01-057-00 826 | n01-057-01 827 | n01-057-02 828 | n01-057-03 829 | n01-057-04 830 | n01-057-05 831 | n01-057-06 832 | n01-057-07 833 | n01-057-08 834 | n02-000-00 835 | n02-000-01 836 | n02-000-02 837 | n02-000-03 838 | n02-000-04 839 | n02-000-05 840 | n02-000-06 841 | n02-016-00 842 | n02-016-01 843 | n02-016-02 844 | n02-016-03 845 | n02-016-04 846 | n02-016-05 847 | n02-016-06 848 | n02-016-07 849 | n02-004-00 850 | n02-004-01 851 | n02-004-02 852 | n02-004-03 853 | n02-004-04 854 | n02-004-05 855 | n02-004-06 856 | n02-004-07 857 | n02-004-08 858 | n02-009-00 859 | n02-009-01 860 | n02-009-02 861 | n02-009-03 862 | n02-009-04 863 | n02-009-05 864 | n02-009-06 865 | n02-028-00 866 | n02-028-01 867 | n02-028-02 868 | n02-028-03 869 | n02-028-04 870 | n02-028-05 871 | n02-028-06 872 | n02-028-07 873 | n02-028-08 874 | n02-049-00 875 | n02-049-01 876 | n02-049-02 877 | n02-049-03 878 | n02-049-04 879 | n02-033-00 880 | n02-033-01 881 | n02-033-02 882 | n02-033-03 883 | n02-033-04 884 | n02-033-05 885 | n02-033-06 886 | n02-033-07 887 | n02-033-08 888 | n02-037-00 889 | n02-037-01 890 | n02-037-02 891 | n02-037-03 892 | n02-037-04 893 | n02-037-05 894 | n02-037-06 895 | n02-037-07 896 | n02-037-08 897 | n02-040-00 898 | n02-040-01 899 | n02-040-02 900 | n02-040-03 901 | n02-040-04 902 | n02-040-05 903 | n02-040-06 904 | n02-040-07 905 | n02-045-00 906 | n02-045-01 907 | n02-045-02 908 | n02-045-03 909 | n02-045-04 910 | n02-045-05 911 | n02-045-06 912 | n02-045-07 913 | n02-054-00 914 | n02-054-01 915 | n02-054-02 916 | n02-054-03 917 | n02-054-04 918 | n02-054-05 919 | n02-054-06 920 | n02-054-07 921 | n02-054-08 922 | n02-054-09 923 | n02-062-00 924 | n02-062-01 925 | n02-062-02 926 | n02-062-03 927 | n02-062-04 928 | n02-062-05 929 | n02-062-06 930 | n02-062-07 931 | n02-082a-00 932 | n02-082a-01 933 | n02-082a-02 934 | n02-082a-03 935 | n02-082a-04 936 | n02-082a-05 937 | n02-082a-06 938 | n02-098-00 939 | n02-098-01 940 | n02-098-02 941 | n02-098-03 942 | n02-098-04 943 | n02-098-05 944 | n02-098-06 945 | n02-098-07 946 | p03-057-00 947 | p03-057-01 948 | p03-057-02 949 | p03-057-03 950 | p03-057-04 951 | p03-057-05 952 | p03-057-06 953 | p03-057-07 954 | p03-057-08 955 | p03-087-00 956 | p03-087-01 957 | p03-087-02 958 | p03-087-03 959 | p03-087-04 960 | p03-087-05 961 | p03-087-06 962 | p03-087-07 963 | p03-087-08 964 | p03-087-09 965 | p03-087-10 966 | p03-096-00 967 | p03-096-01 968 | p03-096-02 969 | p03-096-03 970 | p03-096-04 971 | p03-096-05 972 | p03-096-06 973 | p03-096-07 974 | p03-096-08 975 | p03-103-00 976 | p03-103-01 977 | p03-103-02 978 | p03-103-03 979 | p03-103-04 980 | p03-103-05 981 | p03-103-06 982 | p03-103-07 983 | p03-112-00 984 | p03-112-01 985 | p03-112-02 986 | p03-112-03 987 | p03-112-04 988 | p03-112-05 989 | p03-112-06 990 | p03-112-07 991 | p03-112-08 992 | p03-112-09 993 | n02-151-00 994 | n02-151-01 995 | n02-151-02 996 | n02-151-03 997 | n02-151-04 998 | n02-151-05 999 | n02-151-06 1000 | n02-151-07 1001 | n02-151-08 1002 | n02-151-09 1003 | n02-151-10 1004 | n02-154-00 1005 | n02-154-01 1006 | n02-154-02 1007 | n02-154-03 1008 | n02-154-04 1009 | n02-154-05 1010 | n02-157-00 1011 | n02-157-01 1012 | n02-157-02 1013 | n02-157-03 1014 | n02-157-04 1015 | n02-157-05 1016 | n02-157-06 1017 | n02-157-07 1018 | n02-157-08 1019 | n03-038-00 1020 | n03-038-01 1021 | n03-038-02 1022 | n03-038-03 1023 | n03-038-04 1024 | n03-038-05 1025 | n03-064-00 1026 | n03-064-01 1027 | n03-064-02 1028 | n03-064-03 1029 | n03-064-04 1030 | n03-064-05 1031 | n03-066-00 1032 | n03-066-01 1033 | n03-066-02 1034 | n03-066-03 1035 | n03-066-04 1036 | n03-066-05 1037 | n03-066-06 1038 | n03-066-07 1039 | n03-079-00 1040 | n03-079-01 1041 | n03-079-02 1042 | n03-079-03 1043 | n03-079-04 1044 | n03-079-05 1045 | n03-082-00 1046 | n03-082-01 1047 | n03-082-02 1048 | n03-082-03 1049 | n03-082-04 1050 | n03-082-05 1051 | n03-082-06 1052 | n03-091-00 1053 | n03-091-01 1054 | n03-091-02 1055 | n03-091-03 1056 | n03-091-04 1057 | n03-091-05 1058 | n03-091-06 1059 | n03-091-07 1060 | n03-097-00 1061 | n03-097-01 1062 | n03-097-02 1063 | n03-097-03 1064 | n03-097-04 1065 | n03-097-05 1066 | n03-097-06 1067 | n03-097-07 1068 | n03-103-00 1069 | n03-103-01 1070 | n03-103-02 1071 | n03-103-03 1072 | n03-103-04 1073 | n03-103-05 1074 | n03-103-06 1075 | n03-103-07 1076 | n03-103-08 1077 | n03-103-09 1078 | n03-106-00 1079 | n03-106-01 1080 | n03-106-02 1081 | n03-106-03 1082 | n03-106-04 1083 | n03-106-05 1084 | n03-106-06 1085 | n03-106-07 1086 | n03-106-08 1087 | n03-106-09 1088 | n03-106-10 1089 | n03-106-11 1090 | n03-113-00 1091 | n03-113-01 1092 | n03-113-02 1093 | n03-113-03 1094 | n03-113-04 1095 | n03-113-05 1096 | n03-113-06 1097 | n03-113-07 1098 | n03-113-08 1099 | n03-113-09 1100 | n03-120-00 1101 | n03-120-01 1102 | n03-120-02 1103 | n03-120-03 1104 | n03-120-04 1105 | n03-120-05 1106 | n03-120-06 1107 | n03-120-07 1108 | n03-120-08 1109 | n03-126-00 1110 | n03-126-01 1111 | n03-126-02 1112 | n03-126-03 1113 | n03-126-04 1114 | n04-015-00 1115 | n04-015-01 1116 | n04-015-02 1117 | n04-015-03 1118 | n04-015-04 1119 | n04-015-05 1120 | n04-015-06 1121 | n04-022-00 1122 | n04-022-01 1123 | n04-022-02 1124 | n04-022-03 1125 | n04-022-04 1126 | n04-022-05 1127 | n04-022-06 1128 | n04-022-07 1129 | n04-031-00 1130 | n04-031-01 1131 | n04-031-02 1132 | n04-031-03 1133 | n04-031-04 1134 | n04-031-05 1135 | n04-031-06 1136 | n04-039-00 1137 | n04-039-01 1138 | n04-039-02 1139 | n04-039-03 1140 | n04-039-04 1141 | n04-039-05 1142 | n04-044-00 1143 | n04-044-01 1144 | n04-044-02 1145 | n04-044-03 1146 | n04-044-04 1147 | n04-044-05 1148 | n04-044-06 1149 | n04-044-07 1150 | n04-044-08 1151 | n04-044-09 1152 | n04-048-00 1153 | n04-048-01 1154 | n04-048-02 1155 | n04-048-03 1156 | n04-048-04 1157 | n04-048-05 1158 | n04-048-06 1159 | n04-048-08 1160 | n04-052-00 1161 | n04-052-01 1162 | n04-052-02 1163 | n04-052-03 1164 | n04-052-04 1165 | n04-052-05 1166 | n04-052-06 1167 | n04-052-07 1168 | n04-052-08 1169 | n04-060-00 1170 | n04-060-01 1171 | n04-060-02 1172 | n04-060-03 1173 | n04-060-04 1174 | n04-060-05 1175 | n04-060-06 1176 | n04-068-00 1177 | n04-068-01 1178 | n04-068-02 1179 | n04-068-03 1180 | n04-068-04 1181 | n04-068-05 1182 | n04-068-06 1183 | n04-068-07 1184 | n04-075-00 1185 | n04-075-01 1186 | n04-075-02 1187 | n04-075-03 1188 | n04-075-04 1189 | n04-075-05 1190 | n04-075-06 1191 | n04-084-00 1192 | n04-084-01 1193 | n04-084-02 1194 | n04-084-03 1195 | n04-084-04 1196 | n04-084-05 1197 | n04-084-06 1198 | n04-092-00 1199 | n04-092-01 1200 | n04-092-02 1201 | n04-092-03 1202 | n04-092-04 1203 | n04-092-05 1204 | n04-092-06 1205 | n04-092-07 1206 | n04-100-00 1207 | n04-100-01 1208 | n04-100-02 1209 | n04-100-03 1210 | n04-100-04 1211 | n04-100-05 1212 | n04-100-06 1213 | n04-107-00 1214 | n04-107-01 1215 | n04-107-02 1216 | n04-107-03 1217 | n04-107-04 1218 | n04-107-05 1219 | n04-107-06 1220 | n04-107-07 1221 | n04-114-00 1222 | n04-114-01 1223 | n04-114-02 1224 | n04-114-03 1225 | n04-114-04 1226 | n04-114-05 1227 | n04-114-06 1228 | n04-114-07 1229 | n04-130-00 1230 | n04-130-01 1231 | n04-130-02 1232 | n04-130-03 1233 | n04-130-04 1234 | n04-130-05 1235 | n04-139-00 1236 | n04-139-01 1237 | n04-139-02 1238 | n04-139-03 1239 | n04-139-04 1240 | n04-139-05 1241 | n04-139-06 1242 | n04-139-07 1243 | n04-149-00 1244 | n04-149-01 1245 | n04-149-02 1246 | n04-149-03 1247 | n04-149-04 1248 | n04-149-05 1249 | n04-149-06 1250 | n04-156-00 1251 | n04-156-01 1252 | n04-156-02 1253 | n04-156-03 1254 | n04-156-04 1255 | n04-156-05 1256 | n04-156-06 1257 | n04-156-07 1258 | n04-163-00 1259 | n04-163-01 1260 | n04-163-02 1261 | n04-163-03 1262 | n04-163-04 1263 | n04-163-05 1264 | n04-163-06 1265 | n04-163-07 1266 | n04-171-00 1267 | n04-171-01 1268 | n04-171-02 1269 | n04-171-03 1270 | n04-171-04 1271 | n04-171-05 1272 | n04-171-06 1273 | n04-183-00 1274 | n04-183-01 1275 | n04-183-02 1276 | n04-183-03 1277 | n04-183-04 1278 | n04-183-05 1279 | n04-183-06 1280 | n04-190-00 1281 | n04-190-01 1282 | n04-190-02 1283 | n04-190-03 1284 | n04-190-04 1285 | n04-190-05 1286 | n04-195-00 1287 | n04-195-01 1288 | n04-195-02 1289 | n04-195-03 1290 | n04-195-04 1291 | n04-195-05 1292 | n04-202-00 1293 | n04-202-01 1294 | n04-202-02 1295 | n04-202-03 1296 | n04-202-04 1297 | n04-202-05 1298 | n04-202-06 1299 | n04-202-07 1300 | n04-202-08 1301 | n04-209-00 1302 | n04-209-01 1303 | n04-209-02 1304 | n04-209-03 1305 | n04-209-04 1306 | n04-209-05 1307 | n04-209-06 1308 | n04-213-00 1309 | n04-213-01 1310 | n04-213-02 1311 | n04-213-03 1312 | n04-213-04 1313 | n04-213-05 1314 | n04-213-06 1315 | n04-218-00 1316 | n04-218-01 1317 | n04-218-02 1318 | n04-218-03 1319 | n04-218-04 1320 | n04-218-05 1321 | n04-218-06 1322 | n04-218-07 1323 | n04-218-08 1324 | n06-074-00 1325 | n06-074-01 1326 | n06-074-02 1327 | n06-074-03 1328 | n06-074-04 1329 | n06-074-05 1330 | n06-074-06 1331 | n06-082-00 1332 | n06-082-01 1333 | n06-082-02 1334 | n06-082-03 1335 | n06-082-04 1336 | n06-082-05 1337 | n06-082-06 1338 | n06-092-00 1339 | n06-092-01 1340 | n06-092-02 1341 | n06-092-03 1342 | n06-092-04 1343 | n06-092-05 1344 | n06-092-06 1345 | n06-092-07 1346 | n06-100-00 1347 | n06-100-01 1348 | n06-100-02 1349 | n06-100-03 1350 | n06-100-04 1351 | n06-100-05 1352 | n06-100-06 1353 | n06-100-07 1354 | n06-100-08 1355 | n06-111-00 1356 | n06-111-01 1357 | n06-111-02 1358 | n06-111-03 1359 | n06-111-04 1360 | n06-111-05 1361 | n06-111-06 1362 | n06-111-07 1363 | n06-119-00 1364 | n06-119-01 1365 | n06-119-02 1366 | n06-119-03 1367 | n06-119-04 1368 | n06-119-05 1369 | n06-123-00 1370 | n06-123-01 1371 | n06-123-02 1372 | n06-123-03 1373 | n06-123-04 1374 | n06-123-05 1375 | n06-123-06 1376 | n06-123-07 1377 | n06-123-08 1378 | n06-123-09 1379 | n06-128-00 1380 | n06-128-01 1381 | n06-128-02 1382 | n06-128-03 1383 | n06-128-04 1384 | n06-128-05 1385 | n06-128-06 1386 | n06-128-07 1387 | n06-133-00 1388 | n06-133-01 1389 | n06-133-02 1390 | n06-133-03 1391 | n06-133-04 1392 | n06-133-05 1393 | n06-133-06 1394 | n06-133-07 1395 | n06-133-08 1396 | n06-140-00 1397 | n06-140-01 1398 | n06-140-02 1399 | n06-140-03 1400 | n06-140-04 1401 | n06-140-05 1402 | n06-140-06 1403 | p01-147-00 1404 | p01-147-01 1405 | p01-147-02 1406 | p01-147-03 1407 | p01-147-04 1408 | p01-147-05 1409 | p01-147-06 1410 | p01-147-07 1411 | p01-147-08 1412 | p01-147-09 1413 | p01-147-10 1414 | p01-155-00 1415 | p01-155-01 1416 | p01-155-02 1417 | p01-155-03 1418 | p01-155-04 1419 | p01-155-05 1420 | p01-155-06 1421 | p01-155-07 1422 | p01-155-08 1423 | p01-155-09 1424 | p01-168-00 1425 | p01-168-01 1426 | p01-168-02 1427 | p01-168-03 1428 | p01-168-04 1429 | p01-168-05 1430 | p01-168-06 1431 | p01-174-00 1432 | p01-174-01 1433 | p01-174-02 1434 | p01-174-03 1435 | p01-174-04 1436 | p01-174-05 1437 | p01-174-06 1438 | p01-174-07 1439 | p01-174-08 1440 | p02-000-00 1441 | p02-000-01 1442 | p02-000-02 1443 | p02-000-03 1444 | p02-000-04 1445 | p02-000-05 1446 | p02-008-00 1447 | p02-008-01 1448 | p02-008-02 1449 | p02-008-03 1450 | p02-008-04 1451 | p02-008-05 1452 | p02-008-06 1453 | p02-008-07 1454 | p02-008-08 1455 | p02-008-09 1456 | p02-008-10 1457 | p02-017-00 1458 | p02-017-01 1459 | p02-017-02 1460 | p02-017-03 1461 | p02-017-04 1462 | p02-017-05 1463 | p02-017-06 1464 | p02-022-00 1465 | p02-022-01 1466 | p02-022-02 1467 | p02-022-03 1468 | p02-022-04 1469 | p02-022-05 1470 | p02-022-06 1471 | p02-022-07 1472 | p02-022-08 1473 | p02-027-00 1474 | p02-027-01 1475 | p02-027-02 1476 | p02-027-03 1477 | p02-027-04 1478 | p02-069-00 1479 | p02-069-01 1480 | p02-069-02 1481 | p02-069-03 1482 | p02-069-04 1483 | p02-069-05 1484 | p02-069-06 1485 | p02-069-08 1486 | p02-069-09 1487 | p02-090-00 1488 | p02-090-01 1489 | p02-090-02 1490 | p02-090-03 1491 | p02-090-04 1492 | p02-090-05 1493 | p02-090-06 1494 | p02-090-07 1495 | p02-090-08 1496 | p02-076-00 1497 | p02-076-01 1498 | p02-076-02 1499 | p02-076-03 1500 | p02-076-04 1501 | p02-076-05 1502 | p02-076-06 1503 | p02-076-07 1504 | p02-076-08 1505 | p02-076-09 1506 | p02-076-10 1507 | p02-081-00 1508 | p02-081-01 1509 | p02-081-02 1510 | p02-081-03 1511 | p02-081-04 1512 | p02-081-05 1513 | p02-081-06 1514 | p02-081-07 1515 | p02-081-08 1516 | p02-081-09 1517 | p02-101-00 1518 | p02-101-01 1519 | p02-101-02 1520 | p02-101-03 1521 | p02-101-04 1522 | p02-101-05 1523 | p02-101-06 1524 | p02-105-00 1525 | p02-105-01 1526 | p02-105-02 1527 | p02-105-03 1528 | p02-105-04 1529 | p02-105-05 1530 | p02-105-06 1531 | p02-105-07 1532 | p02-109-00 1533 | p02-109-02 1534 | p02-109-03 1535 | p02-109-04 1536 | p02-109-05 1537 | p02-109-06 1538 | p02-109-07 1539 | p02-109-08 1540 | p02-115-00 1541 | p02-115-01 1542 | p02-115-02 1543 | p02-115-03 1544 | p02-115-04 1545 | p02-115-05 1546 | p02-115-06 1547 | p02-115-07 1548 | p02-115-08 1549 | p02-121-00 1550 | p02-121-01 1551 | p02-121-02 1552 | p02-121-03 1553 | p02-121-04 1554 | p02-121-05 1555 | p02-127-00 1556 | p02-127-01 1557 | p02-127-02 1558 | p02-127-03 1559 | p02-127-04 1560 | p02-127-05 1561 | p02-131-00 1562 | p02-131-01 1563 | p02-131-02 1564 | p02-131-03 1565 | p02-131-04 1566 | p02-131-05 1567 | p02-131-06 1568 | p02-131-07 1569 | p02-135-00 1570 | p02-135-01 1571 | p02-135-02 1572 | p02-135-03 1573 | p02-135-04 1574 | p02-135-05 1575 | p02-135-06 1576 | p02-135-07 1577 | p02-135-08 1578 | p02-139-00 1579 | p02-139-01 1580 | p02-139-02 1581 | p02-139-03 1582 | p02-139-04 1583 | p02-144-00 1584 | p02-144-01 1585 | p02-144-02 1586 | p02-144-03 1587 | p02-144-04 1588 | p02-144-05 1589 | p02-144-06 1590 | p02-144-07 1591 | p02-144-08 1592 | p02-144-09 1593 | p02-144-10 1594 | p02-150-00 1595 | p02-150-01 1596 | p02-150-02 1597 | p02-150-03 1598 | p02-150-04 1599 | p02-150-05 1600 | p02-150-06 1601 | p02-150-07 1602 | p02-155-00 1603 | p03-004-00 1604 | p03-004-01 1605 | p03-004-02 1606 | p03-004-03 1607 | p03-004-04 1608 | p03-004-05 1609 | p03-004-06 1610 | p03-004-07 1611 | p03-004-08 1612 | p03-004-09 1613 | p03-004-10 1614 | p03-009-00 1615 | p03-009-01 1616 | p03-009-02 1617 | p03-009-03 1618 | p03-009-04 1619 | p03-009-05 1620 | p03-009-06 1621 | p03-012-00 1622 | p03-012-01 1623 | p03-012-02 1624 | p03-012-03 1625 | p03-012-04 1626 | p03-012-05 1627 | p03-012-06 1628 | p03-012-07 1629 | p03-012-08 1630 | p03-023-00 1631 | p03-023-01 1632 | p03-023-02 1633 | p03-023-03 1634 | p03-023-04 1635 | p03-027-00 1636 | p03-027-01 1637 | p03-027-02 1638 | p03-027-03 1639 | p03-027-04 1640 | p03-027-05 1641 | p03-027-06 1642 | p03-029-00 1643 | p03-029-01 1644 | p03-029-02 1645 | p03-029-03 1646 | p03-029-04 1647 | p03-029-05 1648 | p03-029-06 1649 | p03-029-07 1650 | p03-029-08 1651 | p03-029-09 1652 | p03-033-00 1653 | p03-033-01 1654 | p03-033-02 1655 | p03-033-03 1656 | p03-033-04 1657 | p03-040-00 1658 | p03-040-01 1659 | p03-040-02 1660 | p03-040-03 1661 | p03-040-04 1662 | p03-040-05 1663 | p03-040-06 1664 | p03-040-07 1665 | p03-047-00 1666 | p03-047-01 1667 | p03-047-02 1668 | p03-047-03 1669 | p03-047-04 1670 | p03-047-05 1671 | p03-047-06 1672 | p03-047-07 1673 | p03-047-08 1674 | p03-047-09 1675 | p03-069-00 1676 | p03-069-01 1677 | p03-069-02 1678 | p03-069-03 1679 | p03-069-04 1680 | p03-069-05 1681 | p03-069-06 1682 | p03-072-00 1683 | p03-072-01 1684 | p03-072-02 1685 | p03-072-03 1686 | p03-072-04 1687 | p03-072-05 1688 | p03-072-06 1689 | p03-080-00 1690 | p03-080-01 1691 | p03-080-02 1692 | p03-080-03 1693 | p03-080-04 1694 | p03-080-05 1695 | p03-080-06 1696 | p03-121-00 1697 | p03-121-01 1698 | p03-121-02 1699 | p03-121-03 1700 | p03-121-04 1701 | p03-121-05 1702 | p03-121-06 1703 | p03-135-00 1704 | p03-135-01 1705 | p03-135-02 1706 | p03-135-03 1707 | p03-135-04 1708 | p03-135-05 1709 | p03-142-00 1710 | p03-142-01 1711 | p03-142-02 1712 | p03-142-03 1713 | p03-142-04 1714 | p03-142-05 1715 | p03-142-06 1716 | p03-151-00 1717 | p03-151-01 1718 | p03-151-02 1719 | p03-151-03 1720 | p03-151-04 1721 | p03-151-05 1722 | p03-151-06 1723 | p03-151-07 1724 | p03-158-00 1725 | p03-158-01 1726 | p03-158-02 1727 | p03-158-03 1728 | p03-158-04 1729 | p03-158-05 1730 | p03-158-06 1731 | p03-163-00 1732 | p03-163-01 1733 | p03-163-02 1734 | p03-163-03 1735 | p03-163-04 1736 | p03-163-05 1737 | p03-163-06 1738 | p03-163-07 1739 | p03-173-00 1740 | p03-173-01 1741 | p03-173-02 1742 | p03-173-03 1743 | p03-173-04 1744 | p03-173-05 1745 | p03-173-06 1746 | p03-173-07 1747 | p03-173-08 1748 | p03-181-00 1749 | p03-181-01 1750 | p03-181-02 1751 | p03-181-03 1752 | p03-181-04 1753 | p03-181-05 1754 | p03-181-06 1755 | p03-181-07 1756 | p03-181-08 1757 | p03-181-09 1758 | p03-185-00 1759 | p03-185-01 1760 | p03-185-02 1761 | p03-185-03 1762 | p03-185-04 1763 | p03-185-05 1764 | p03-185-06 1765 | p03-185-07 1766 | p03-189-00 1767 | p03-189-01 1768 | p03-189-02 1769 | p03-189-03 1770 | p06-030-00 1771 | p06-030-01 1772 | p06-030-02 1773 | p06-030-03 1774 | p06-030-04 1775 | p06-030-05 1776 | p06-030-06 1777 | p06-030-07 1778 | p06-030-08 1779 | p06-042-00 1780 | p06-042-01 1781 | p06-042-02 1782 | p06-042-03 1783 | p06-042-04 1784 | p06-042-05 1785 | p06-042-06 1786 | p06-042-07 1787 | p06-042-08 1788 | p06-042-09 1789 | p06-042-10 1790 | p06-047-00 1791 | p06-047-01 1792 | p06-047-02 1793 | p06-047-03 1794 | p06-047-04 1795 | p06-047-05 1796 | p06-047-06 1797 | p06-047-07 1798 | p06-047-08 1799 | p06-047-09 1800 | p06-047-10 1801 | p06-052-00 1802 | p06-052-01 1803 | p06-052-02 1804 | p06-052-03 1805 | p06-052-04 1806 | p06-052-05 1807 | p06-052-06 1808 | p06-052-07 1809 | p06-052-08 1810 | p06-058-00 1811 | p06-058-01 1812 | p06-058-02 1813 | p06-058-03 1814 | p06-058-04 1815 | p06-058-05 1816 | p06-058-06 1817 | p06-058-07 1818 | p06-058-08 1819 | p06-058-09 1820 | p06-058-10 1821 | p06-069-00 1822 | p06-069-01 1823 | p06-069-02 1824 | p06-069-03 1825 | p06-069-04 1826 | p06-069-05 1827 | p06-069-06 1828 | p06-069-07 1829 | p06-069-08 1830 | p06-069-09 1831 | p06-088-00 1832 | p06-088-01 1833 | p06-088-02 1834 | p06-088-03 1835 | p06-088-04 1836 | p06-088-05 1837 | p06-088-06 1838 | p06-088-07 1839 | p06-088-08 1840 | p06-088-09 1841 | p06-096-00 1842 | p06-096-01 1843 | p06-096-02 1844 | p06-096-03 1845 | p06-096-04 1846 | p06-096-05 1847 | p06-096-06 1848 | p06-096-07 1849 | p06-096-08 1850 | p06-096-09 1851 | p06-096-10 1852 | p06-104-00 1853 | p06-104-01 1854 | p06-104-02 1855 | p06-104-03 1856 | p06-104-04 1857 | p06-104-05 1858 | p06-104-06 1859 | p06-104-07 1860 | p06-104-08 1861 | p06-104-09 1862 | --------------------------------------------------------------------------------