From ba5444e576db3ed46e31e2dbc0d8bd13f420d617 Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Mon, 23 Jan 2023 12:40:08 +0100 Subject: [PATCH 01/69] partial implementation of new xml parser --- kraken/lib/xml.py | 434 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 430 insertions(+), 4 deletions(-) diff --git a/kraken/lib/xml.py b/kraken/lib/xml.py index 608cd7dc5..14a4bf450 100644 --- a/kraken/lib/xml.py +++ b/kraken/lib/xml.py @@ -22,7 +22,7 @@ from itertools import groupby from lxml import etree from PIL import Image -from typing import Union, Dict, Any, Sequence, Tuple +from typing import Union, Dict, Any, Sequence, Tuple, Literal from os import PathLike from collections import defaultdict @@ -52,8 +52,8 @@ # same for ALTO alto_regions = {'TextBlock': 'text', - 'IllustrationType': 'illustration', - 'GraphicalElementType': 'graphic', + 'Illustration': 'illustration', + 'GraphicalElement': 'graphic', 'ComposedBlock': 'composed'} @@ -423,7 +423,7 @@ def _parse_pointstype(coords: str) -> Sequence[Tuple[float, float]]: if boundary == page_boundary and rtype == 'text': logger.info('Skipping TextBlock with same size as page image.') continue - region_data[rtype].append(boundary) + region_data[rtype].append({'id': region.get('ID'), 'boundary': boundary}) data['regions'] = region_data tag_set = set(('default',)) @@ -476,3 +476,429 @@ def _parse_pointstype(coords: str) -> Sequence[Tuple[float, float]]: else: data['tags'] = False return data + +class XMLPage(object): + + type: Literal['baselines', 'bbox'] == 'baselines' + base_dir: Optional[Literal['L', 'R']] = None + imagename: pathlib.Path = None + _order: Dict[str, Dict[str, Any]] = None + has_tags: bool = False + _tag_set: Optional[Dict] = None + has_splits: bool = False + _split_set: Optional[List] = None + + def __init__(self, + filename: Union[str, pathlib.Path], + filetype: Literal['xml', 'alto', 'page'] = 'xml'): + super().__init__() + self.filename = Path(filename) + self.filetype = filetype + + if filetype == 'xml': + self._parse_xml() + elif filetype == 'alto': + self._parse_alto() + elif filetype == 'page': + self._parse_page() + + self._regions = {} + self._baselines = {} + self._orders = {'line_implicit': {'order': [], 'is_total': True, 'description': 'Implicit line order derived from element sequence'}, + 'region_implicit': {'order': [], 'is_total': True, 'description': 'Implicit region order derived from element sequence'}} + + def _parse_alto(self): + with open(self.filename, 'rb') as fp: + base_directory = self.filename.parent + try: + doc = etree.parse(fp) + except etree.XMLSyntaxError as e: + raise KrakenInputException('Parsing {} failed: {}'.format(self.filename, e)) + image = doc.find('.//{*}fileName') + if image is None or not image.text: + raise KrakenInputException('No valid image filename found in ALTO file {self.filename}') + self.imagename = base_directory.joinpath(image.text) + + lines = doc.findall('.//{*}TextLine') + # find all image regions in order + regions = [] + for el in doc.iterfind('./{*}Layout/{*}Page/{*}PrintSpace/{*}*'): + for block_type in alto_regions.keys(): + if el.tag.endswith(block_type): + regions.append(el) + # find overall dimensions to filter out dummy TextBlocks + ps = doc.find('./{*}Layout/{*}Page/{*}PrintSpace') + x_min = int(float(ps.get('HPOS'))) + y_min = int(float(ps.get('VPOS'))) + width = int(float(ps.get('WIDTH'))) + height = int(float(ps.get('HEIGHT'))) + page_boundary = [(x_min, y_min), + (x_min, y_min + height), + (x_min + width, y_min + height), + (x_min + width, y_min)] + + # parse tagrefs + cls_map = {} + tags = doc.find('.//{*}Tags') + if tags is not None: + for x in ['StructureTag', 'LayoutTag', 'OtherTag']: + for tag in tags.findall('./{{*}}{}'.format(x)): + cls_map[tag.get('ID')] = (x[:-3].lower(), tag.get('LABEL')) + # parse region type and coords + region_data = defaultdict(list) + for region in regions: + # try to find shape object + coords = region.find('./{*}Shape/{*}Polygon') + if coords is not None: + boundary = _parse_pointstype(coords.get('POINTS')) + elif (region.get('HPOS') is not None and region.get('VPOS') is not None and + region.get('WIDTH') is not None and region.get('HEIGHT') is not None): + # use rectangular definition + x_min = int(float(region.get('HPOS'))) + y_min = int(float(region.get('VPOS'))) + width = int(float(region.get('WIDTH'))) + height = int(float(region.get('HEIGHT'))) + boundary = [(x_min, y_min), + (x_min, y_min + height), + (x_min + width, y_min + height), + (x_min + width, y_min)] + else: + continue + rtype = region.get('TYPE') + # fall back to default region type if nothing is given + tagrefs = region.get('TAGREFS') + if tagrefs is not None and rtype is None: + for tagref in tagrefs.split(): + ttype, rtype = cls_map.get(tagref, (None, None)) + if rtype is not None and ttype: + break + if rtype is None: + rtype = alto_regions[region.tag.split('}')[-1]] + if boundary == page_boundary and rtype == 'text': + logger.info('Skipping TextBlock with same size as page image.') + continue + region_data[rtype].append({'id': region.get('ID'), 'boundary': boundary}) + # register implicit reading order + self._orders['region_implicit']['order'].append(region.get('ID')) + self.regions = region_data + + self._tag_set = set(('default',)) + self.lines = [] + for line in lines: + if line.get('BASELINE') is None: + logger.info('TextLine {} without baseline'.format(line.get('ID'))) + continue + pol = line.find('./{*}Shape/{*}Polygon') + boundary = None + if pol is not None: + try: + boundary = self._parse_alto_pointstype(pol.get('POINTS')) + except ValueError: + logger.info('TextLine {} without polygon'.format(line.get('ID'))) + else: + logger.info('TextLine {} without polygon'.format(line.get('ID'))) + + baseline = None + try: + baseline = self._parse_alto_pointstype(line.get('BASELINE')) + except ValueError: + logger.info('TextLine {} without baseline'.format(line.get('ID'))) + + text = '' + for el in line.xpath(".//*[local-name() = 'String'] | .//*[local-name() = 'SP']"): + text += el.get('CONTENT') if el.get('CONTENT') else ' ' + # find line type + tags = {'type': 'default'} + split_type = None + tagrefs = line.get('TAGREFS') + if tagrefs is not None: + for tagref in tagrefs.split(): + ttype, ltype = cls_map.get(tagref, (None, None)) + if ltype is not None: + self._tag_set.add(ltype) + if ttype == 'other': + tags['type'] = ltype + else: + tags[ttype] = ltype + if ltype in ['train', 'validation', 'test']: + split_type = ltype + self.lines.append({'id': line.get('ID'), + 'baseline': baseline, + 'boundary': boundary, + 'text': text, + 'tags': tags, + 'split': split_type}) + # register implicit reading order + self._orders['line_implicit']['order'].append(line.get('ID')) + + if len(self._tag_set) > 1: + self.has_tags = True + else: + self.has_tags = False + + # parse explicit reading orders if they exist + ro_el = doc.find('.//{*}ReadingOrder') + if ro_el is not None: + reading_orders = ro_el.getchildren() + # UnorderedGroup at top-level => treated as multiple reading orders + if len(reading_orders) == 1 and reading_orders[0].tag.endswith('UnorderedGroup'): + reading_orders = reading_orders.getchildren() + else: + reading_orders = [reading_orders] + def _parse_group(el): + _ro = [] + if el.tag.endswith('UnorderedGroup'): + _ro.append([_parse_group(x) for x in el.iterchildren()]) + is_total = False + elif el.tag.endswith('OrderedGroup'): + _ro.extend(_parse_group(x) for x in el.iterchildren()) + else: + return el.get('REF') + return _ro + + for ro in reading_orders: + is_total = True + joint_order = _parse_group(ro) + tag = ro.get('TAGREFS') + self._orders[ro.get('ID')] = {'order': joint_order, + 'is_total': is_total, + 'description': cls_map[tag] if tag and tag in cls_map else ''} + + def _parse_page(self): + with open(self.filename, 'rb') as fp: + base_directory = self.filename.parent + + try: + doc = etree.parse(fp) + except etree.XMLSyntaxError as e: + raise KrakenInputException('Parsing {} failed: {}'.format(self.filename, e)) + image = doc.find('.//{*}Page') + if image is None or image.get('imageFilename') is None: + raise KrakenInputException('No valid image filename found in PageXML file {}'.format(self.filename)) + try: + self.base_dir = {'left-to-right': 'L', + 'right-to-left': 'R', + 'top-to-bottom': 'L', + 'bottom-to-top': 'R', + None: None}[image.get('readingDirection')] + except KeyError: + logger.warning(f'Invalid value {image.get("readingDirection")} encountered in page-level reading direction.') + lines = doc.findall('.//{*}TextLine') + self.imagename = base_dir.joinpath(image.get('imageFilename')) + # find all image regions + regions = [] + for x in page_regions.keys(): + regions.extend(doc.findall('.//{{*}}{}'.format(x))) + # parse region type and coords + region_data = defaultdict(list) + tr_region_order = [] + for region in regions: + coords = region.find('{*}Coords') + if coords is not None and not coords.get('points').isspace() and len(coords.get('points')): + try: + coords = _parse_coords(coords.get('points')) + except Exception: + logger.warning('Region {} without coordinates'.format(region.get('id'))) + continue + else: + logger.warning('Region {} without coordinates'.format(region.get('id'))) + continue + rtype = region.get('type') + # parse transkribus-style custom field if possible + custom_str = region.get('custom') + if not rtype and custom_str: + cs = _parse_page_custom(custom_str) + if 'structure' in cs and 'type' in cs['structure']: + rtype = cs['structure']['type'] + # transkribus-style reading order + if 'readingOrder' in cs and 'index'in cs['readingOrder']: + tr_region_order.append((region.get('id'), int(cs['readingOrder']['index']))) + # fall back to default region type if nothing is given + if not rtype: + rtype = page_regions[region.tag.split('}')[-1]] + region_data[rtype].append({'id': region.get('id'), 'boundary': coords}) + # register implicit reading order + self._orders['region_implicit']['order'].append(region.get('id')) + # add transkribus-style region order + self._order['region_transkribus'] = {'order': [x[1] for x in sorted(tr_region_order, key=lambda k: k[0])], + 'is_total': True if len(set(map(lambda x: x[0], tr_region_order))) == len(tr_region_order) else False, + 'description': 'Explicit region order from `custom` attribute'} + + self.regions = region_data + + # parse line information + self._tag_set = set(('default',)) + tmp_transkribus_line_order = defaultdict(list) + valid_tr_lo = True + for line in lines: + pol = line.find('./{*}Coords') + boundary = None + if pol is not None and not pol.get('points').isspace() and len(pol.get('points')): + try: + boundary = self._parse_coords(pol.get('points')) + except Exception: + logger.info('TextLine {} without polygon'.format(line.get('id'))) + else: + logger.info('TextLine {} without polygon'.format(line.get('id'))) + base = line.find('./{*}Baseline') + baseline = None + if base is not None and not base.get('points').isspace() and len(base.get('points')): + try: + baseline = self._parse_coords(base.get('points')) + except Exception: + logger.info('TextLine {} without baseline'.format(line.get('id'))) + continue + else: + logger.info('TextLine {} without baseline'.format(line.get('id'))) + continue + text = '' + manual_transcription = line.find('./{*}TextEquiv') + if manual_transcription is not None: + transcription = manual_transcription + else: + transcription = line + for el in transcription.findall('.//{*}Unicode'): + if el.text: + text += el.text + # retrieve line tags if custom string is set and contains + tags = {'type': 'default'} + split_type = None + custom_str = line.get('custom') + if custom_str: + cs = _parse_page_custom(custom_str) + if 'structure' in cs and 'type' in cs['structure']: + tags['type'] = cs['structure']['type'] + self._tag_set.add(tags['type']) + # retrieve data split if encoded in custom string. + if 'split' in cs and 'type' in cs['split'] and cs['split']['type'] in ['train', 'validation', 'test']: + split_type = cs['split']['type'] + tags['split'] = split_type + self._tag_set.add(split_type) + if 'readingOrder' in cs and 'index' in cs['readingOrder']: + # look up region index from parent + reg_cus = _parse_page_custom(line.getparent().get('custom')) + if 'readingOrder' not in reg_cus or 'index' not in reg_cus['readingOrder']: + logger.warning('Incomplete `custom` attribute reading order found.') + valid_tr_lo = False + else: + tmp_transkribus_line_order[int(reg_cus['readingOrder']['index'])].append((int(cs['readingOrder']['index']), line.get('id'))) + + self.lines.append({'id': line.get('id'), + 'baseline': baseline, + 'boundary': boundary, + 'text': text, + 'split': split_type, + 'tags': tags}) + # register implicit reading order + self._orders['line_implicit']['order'].append(line.get('id')) + if tmp_transkribus_line_order: + # sort by regions + tmp_reg_order = sorted(((k, v) for k, v in tmp_transkribus_line_order.items()), key=lambda k: k[0]) + # flatten + tr_line_order = [] + for _, lines in tmp_reg_order: + tr_line_order.extend([x[1] for x in sorted(lines, key=lambda k: k[0])]) + self._order['line_transkribus'] = {'order': tr_line_order, + 'is_total': True, + 'description': 'Explicit line order from `custom` attribute'} + + # parse explicit reading orders if they exist + ro_el = doc.find('.//{*}ReadingOrder') + if ro_el is not None: + reading_orders = ro_el.getchildren() + # UnorderedGroup at top-level => treated as multiple reading orders + if len(reading_orders) == 1 and reading_orders[0].tag.endswith('UnorderedGroup'): + reading_orders = reading_orders.getchildren() + else: + reading_orders = [reading_orders] + def _parse_group(el): + _ro = [] + if el.tag.endswith('UnorderedGroup'): + _ro.append([_parse_group(x) for x in el.iterchildren()]) + is_total = False + elif el.tag.endswith('OrderedGroup'): + _ro.extend(_parse_group(x) for x in el.iterchildren()) + else: + return el.get('regionRef') + return _ro + + for ro in reading_orders: + is_total = True + self._orders[ro.get('id')] = {'order': _parse_group(ro), + 'is_total': is_total, + 'description': ro.get('caption') if ro.get('caption') else ''} + + + if len(self._tag_set) > 1: + self.has_tags = True + else: + self.has_tags = False + + @property + def regions(self): + return self._regions + + @property + def baselines(self): + return self._baselines + + def get_baselines_by_region(self, region): + pass + + def get_baselines_by_tag(self, key, value): + pass + + def get_baselines_by_split(self, split: Literal['train', 'validation', 'test']): + pass + + @property + def tags(self): + return self._tag_set + + @property + def splits(self): + return self._split_set + + @staticmethod + def _parse_alto_pointstype(coords: str) -> Sequence[Tuple[float, float]]: + """ + ALTO's PointsType is underspecified so a variety of serializations are valid: + + x0, y0 x1, y1 ... + x0 y0 x1 y1 ... + (x0, y0) (x1, y1) ... + (x0 y0) (x1 y1) ... + + Returns: + A list of tuples [(x0, y0), (x1, y1), ...] + """ + float_re = re.compile(r'[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?') + points = [float(point.group()) for point in float_re.finditer(coords)] + if len(points) % 2: + raise ValueError(f'Odd number of points in points sequence: {points}') + pts = zip(points[::2], points[1::2]) + return [k for k, g in groupby(pts)] + + @staticmethod + def _parse_page_custom(s): + o = {} + s = s.strip() + l_chunks = [l_chunk for l_chunk in s.split('}') if l_chunk.strip()] + if l_chunks: + for chunk in l_chunks: + tag, vals = chunk.split('{') + tag_vals = {} + vals = [val.strip() for val in vals.split(';') if val.strip()] + for val in vals: + key, *val = val.split(':') + tag_vals[key] = ":".join(val) + o[tag.strip()] = tag_vals + return o + + @staticmethod + def _parse_page_coords(coords): + points = [x for x in coords.split(' ')] + points = [int(c) for point in points for c in point.split(',')] + pts = zip(points[::2], points[1::2]) + return [k for k, g in groupby(pts)] + From c36c721e23626c5461b915ba5257f96ffee1bbf6 Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Thu, 26 Jan 2023 15:08:11 +0100 Subject: [PATCH 02/69] skip ROs in ALTO with sub-line elements --- kraken/lib/xml.py | 170 +++++++++++++++++++++++++++------------------- 1 file changed, 101 insertions(+), 69 deletions(-) diff --git a/kraken/lib/xml.py b/kraken/lib/xml.py index 14a4bf450..280b2cf7b 100644 --- a/kraken/lib/xml.py +++ b/kraken/lib/xml.py @@ -22,12 +22,11 @@ from itertools import groupby from lxml import etree from PIL import Image -from typing import Union, Dict, Any, Sequence, Tuple, Literal +from typing import Union, Dict, Any, Sequence, Tuple, Literal, Optional, List from os import PathLike from collections import defaultdict from kraken.lib.segmentation import calculate_polygonal_environment -from kraken.lib.exceptions import KrakenInputException logger = logging.getLogger(__name__) @@ -89,7 +88,7 @@ def preparse_xml_data(filenames: Sequence[Union[str, PathLike]], for fn in filenames: try: data = parse_fn(fn) - except KrakenInputException as e: + except ValueError as e: logger.warning(e) continue try: @@ -482,7 +481,7 @@ class XMLPage(object): type: Literal['baselines', 'bbox'] == 'baselines' base_dir: Optional[Literal['L', 'R']] = None imagename: pathlib.Path = None - _order: Dict[str, Dict[str, Any]] = None + _orders: Dict[str, Dict[str, Any]] = None has_tags: bool = False _tag_set: Optional[Dict] = None has_splits: bool = False @@ -495,6 +494,11 @@ def __init__(self, self.filename = Path(filename) self.filetype = filetype + self._regions = {} + self._baselines = {} + self._orders = {'line_implicit': {'order': [], 'is_total': True, 'description': 'Implicit line order derived from element sequence'}, + 'region_implicit': {'order': [], 'is_total': True, 'description': 'Implicit region order derived from element sequence'}} + if filetype == 'xml': self._parse_xml() elif filetype == 'alto': @@ -502,10 +506,18 @@ def __init__(self, elif filetype == 'page': self._parse_page() - self._regions = {} - self._baselines = {} - self._orders = {'line_implicit': {'order': [], 'is_total': True, 'description': 'Implicit line order derived from element sequence'}, - 'region_implicit': {'order': [], 'is_total': True, 'description': 'Implicit region order derived from element sequence'}} + def _parse_xml(self): + with open(self.filename, 'rb') as fp: + try: + doc = etree.parse(fp) + except etree.XMLSyntaxError as e: + raise ValueError(f'Parsing {self.filename} failed: {e}') + if doc.getroot().tag.endswith('alto'): + return self._parse_alto() + elif doc.getroot().tag.endswith('PcGts'): + return self._parse_page() + else: + raise ValueError(f'Unknown XML format in {self.filename}') def _parse_alto(self): with open(self.filename, 'rb') as fp: @@ -513,10 +525,10 @@ def _parse_alto(self): try: doc = etree.parse(fp) except etree.XMLSyntaxError as e: - raise KrakenInputException('Parsing {} failed: {}'.format(self.filename, e)) + raise ValueError('Parsing {} failed: {}'.format(self.filename, e)) image = doc.find('.//{*}fileName') if image is None or not image.text: - raise KrakenInputException('No valid image filename found in ALTO file {self.filename}') + raise ValueError('No valid image filename found in ALTO file {self.filename}') self.imagename = base_directory.joinpath(image.text) lines = doc.findall('.//{*}TextLine') @@ -550,7 +562,7 @@ def _parse_alto(self): # try to find shape object coords = region.find('./{*}Shape/{*}Polygon') if coords is not None: - boundary = _parse_pointstype(coords.get('POINTS')) + boundary = self._parse_alto_pointstype(coords.get('POINTS')) elif (region.get('HPOS') is not None and region.get('VPOS') is not None and region.get('WIDTH') is not None and region.get('HEIGHT') is not None): # use rectangular definition @@ -580,7 +592,7 @@ def _parse_alto(self): region_data[rtype].append({'id': region.get('ID'), 'boundary': boundary}) # register implicit reading order self._orders['region_implicit']['order'].append(region.get('ID')) - self.regions = region_data + self._regions = region_data self._tag_set = set(('default',)) self.lines = [] @@ -639,30 +651,42 @@ def _parse_alto(self): # parse explicit reading orders if they exist ro_el = doc.find('.//{*}ReadingOrder') if ro_el is not None: - reading_orders = ro_el.getchildren() - # UnorderedGroup at top-level => treated as multiple reading orders - if len(reading_orders) == 1 and reading_orders[0].tag.endswith('UnorderedGroup'): - reading_orders = reading_orders.getchildren() - else: - reading_orders = [reading_orders] - def _parse_group(el): - _ro = [] - if el.tag.endswith('UnorderedGroup'): - _ro.append([_parse_group(x) for x in el.iterchildren()]) - is_total = False - elif el.tag.endswith('OrderedGroup'): - _ro.extend(_parse_group(x) for x in el.iterchildren()) - else: - return el.get('REF') - return _ro - - for ro in reading_orders: - is_total = True - joint_order = _parse_group(ro) - tag = ro.get('TAGREFS') - self._orders[ro.get('ID')] = {'order': joint_order, - 'is_total': is_total, - 'description': cls_map[tag] if tag and tag in cls_map else ''} + reading_orders = ro_el.getchildren() + # UnorderedGroup at top-level => treated as multiple reading orders + if len(reading_orders) == 1 and reading_orders[0].tag.endswith('UnorderedGroup'): + reading_orders = reading_orders[0].getchildren() + else: + reading_orders = [reading_orders] + def _parse_group(el): + _ro = [] + if el.tag.endswith('UnorderedGroup'): + _ro.append([_parse_group(x) for x in el.iterchildren()]) + is_total = False + elif el.tag.endswith('OrderedGroup'): + _ro.extend(_parse_group(x) for x in el.iterchildren()) + else: + ref = el.get('REF') + res = doc.find(f'.//{{*}}*[@ID="{ref}"]') + if res is None: + logger.warning(f'Nonexistant element with ID {ref} in reading order. Skipping RO {ro.get("ID")}.') + is_valid = False + return _ro + tag = res.tag.split('}')[-1] + if tag not in alto_regions.keys() and tag != 'TextLine': + logger.warning(f'Sub-line element with ID {ref} in reading order. Skipping RO {ro.get("ID")}.') + is_valid = False + return _ro + + for ro in reading_orders: + is_total = True + is_valid = True + joint_order = _parse_group(ro) + if is_valid: + tag = ro.get('TAGREFS') + self._orders[ro.get('ID')] = {'order': joint_order, + 'is_total': is_total, + 'description': cls_map[tag] if tag and tag in cls_map else ''} + self.filetype = 'alto' def _parse_page(self): with open(self.filename, 'rb') as fp: @@ -671,10 +695,10 @@ def _parse_page(self): try: doc = etree.parse(fp) except etree.XMLSyntaxError as e: - raise KrakenInputException('Parsing {} failed: {}'.format(self.filename, e)) + raise ValueError(f'Parsing {self.filename} failed: {e}') image = doc.find('.//{*}Page') if image is None or image.get('imageFilename') is None: - raise KrakenInputException('No valid image filename found in PageXML file {}'.format(self.filename)) + raise ValueError(f'No valid image filename found in PageXML file {self.filename}') try: self.base_dir = {'left-to-right': 'L', 'right-to-left': 'R', @@ -684,19 +708,17 @@ def _parse_page(self): except KeyError: logger.warning(f'Invalid value {image.get("readingDirection")} encountered in page-level reading direction.') lines = doc.findall('.//{*}TextLine') - self.imagename = base_dir.joinpath(image.get('imageFilename')) + self.imagename = base_directory.joinpath(image.get('imageFilename')) # find all image regions - regions = [] - for x in page_regions.keys(): - regions.extend(doc.findall('.//{{*}}{}'.format(x))) + regions = [reg for reg in image.iterfind('./{*}*')] # parse region type and coords region_data = defaultdict(list) tr_region_order = [] for region in regions: - coords = region.find('{*}Coords') + coords = region.find('./{*}Coords') if coords is not None and not coords.get('points').isspace() and len(coords.get('points')): try: - coords = _parse_coords(coords.get('points')) + coords = self._parse_page_coords(coords.get('points')) except Exception: logger.warning('Region {} without coordinates'.format(region.get('id'))) continue @@ -706,9 +728,9 @@ def _parse_page(self): rtype = region.get('type') # parse transkribus-style custom field if possible custom_str = region.get('custom') - if not rtype and custom_str: - cs = _parse_page_custom(custom_str) - if 'structure' in cs and 'type' in cs['structure']: + if custom_str: + cs = self._parse_page_custom(custom_str) + if not rtype and 'structure' in cs and 'type' in cs['structure']: rtype = cs['structure']['type'] # transkribus-style reading order if 'readingOrder' in cs and 'index'in cs['readingOrder']: @@ -720,11 +742,11 @@ def _parse_page(self): # register implicit reading order self._orders['region_implicit']['order'].append(region.get('id')) # add transkribus-style region order - self._order['region_transkribus'] = {'order': [x[1] for x in sorted(tr_region_order, key=lambda k: k[0])], - 'is_total': True if len(set(map(lambda x: x[0], tr_region_order))) == len(tr_region_order) else False, - 'description': 'Explicit region order from `custom` attribute'} + self._orders['region_transkribus'] = {'order': [x[0] for x in sorted(tr_region_order, key=lambda k: k[1])], + 'is_total': True if len(set(map(lambda x: x[0], tr_region_order))) == len(tr_region_order) else False, + 'description': 'Explicit region order from `custom` attribute'} - self.regions = region_data + self._regions = region_data # parse line information self._tag_set = set(('default',)) @@ -744,7 +766,7 @@ def _parse_page(self): baseline = None if base is not None and not base.get('points').isspace() and len(base.get('points')): try: - baseline = self._parse_coords(base.get('points')) + baseline = self._parse_page_coords(base.get('points')) except Exception: logger.info('TextLine {} without baseline'.format(line.get('id'))) continue @@ -765,7 +787,7 @@ def _parse_page(self): split_type = None custom_str = line.get('custom') if custom_str: - cs = _parse_page_custom(custom_str) + cs = self._parse_page_custom(custom_str) if 'structure' in cs and 'type' in cs['structure']: tags['type'] = cs['structure']['type'] self._tag_set.add(tags['type']) @@ -776,19 +798,19 @@ def _parse_page(self): self._tag_set.add(split_type) if 'readingOrder' in cs and 'index' in cs['readingOrder']: # look up region index from parent - reg_cus = _parse_page_custom(line.getparent().get('custom')) + reg_cus = self._parse_page_custom(line.getparent().get('custom')) if 'readingOrder' not in reg_cus or 'index' not in reg_cus['readingOrder']: logger.warning('Incomplete `custom` attribute reading order found.') valid_tr_lo = False else: tmp_transkribus_line_order[int(reg_cus['readingOrder']['index'])].append((int(cs['readingOrder']['index']), line.get('id'))) - self.lines.append({'id': line.get('id'), - 'baseline': baseline, - 'boundary': boundary, - 'text': text, - 'split': split_type, - 'tags': tags}) + self._baselines[line.get('id')] = {'baseline': baseline, + 'boundary': boundary, + 'text': text, + 'split': split_type, + 'tags': tags} + # register implicit reading order self._orders['line_implicit']['order'].append(line.get('id')) if tmp_transkribus_line_order: @@ -798,9 +820,9 @@ def _parse_page(self): tr_line_order = [] for _, lines in tmp_reg_order: tr_line_order.extend([x[1] for x in sorted(lines, key=lambda k: k[0])]) - self._order['line_transkribus'] = {'order': tr_line_order, - 'is_total': True, - 'description': 'Explicit line order from `custom` attribute'} + self._orders['line_transkribus'] = {'order': tr_line_order, + 'is_total': True, + 'description': 'Explicit line order from `custom` attribute'} # parse explicit reading orders if they exist ro_el = doc.find('.//{*}ReadingOrder') @@ -828,11 +850,12 @@ def _parse_group(el): 'is_total': is_total, 'description': ro.get('caption') if ro.get('caption') else ''} + if len(self._tag_set) > 1: + self.has_tags = True + else: + self.has_tags = False - if len(self._tag_set) > 1: - self.has_tags = True - else: - self.has_tags = False + self.filetype = 'page' @property def regions(self): @@ -842,14 +865,18 @@ def regions(self): def baselines(self): return self._baselines + @property + def reading_orders(self): + return self._orders + def get_baselines_by_region(self, region): pass def get_baselines_by_tag(self, key, value): - pass + return {k: v for k, v in self._baselines.items() if v['tags'].get(key) == value} def get_baselines_by_split(self, split: Literal['train', 'validation', 'test']): - pass + return {k: v for k, v in self._baselines.items() if v['tags'].get(key) == split} @property def tags(self): @@ -902,3 +929,8 @@ def _parse_page_coords(coords): pts = zip(points[::2], points[1::2]) return [k for k, g in groupby(pts)] + def __str__(self): + return f'XMLPage {self.filename} (format: {self.filetype}, image: {self.imagename})' + + def __repr__(self): + return f'XMLPage(filename={self.filename}, filetype={self.filetype})' From b1977055e69f3a1a6f444839971d89665299828b Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Mon, 30 Jan 2023 11:47:13 +0100 Subject: [PATCH 03/69] wip --- kraken/lib/xml.py | 300 ++++++++++++++++++++++++++-------------------- 1 file changed, 171 insertions(+), 129 deletions(-) diff --git a/kraken/lib/xml.py b/kraken/lib/xml.py index 280b2cf7b..2d6f72e29 100644 --- a/kraken/lib/xml.py +++ b/kraken/lib/xml.py @@ -495,7 +495,7 @@ def __init__(self, self.filetype = filetype self._regions = {} - self._baselines = {} + self._lines = {} self._orders = {'line_implicit': {'order': [], 'is_total': True, 'description': 'Implicit line order derived from element sequence'}, 'region_implicit': {'order': [], 'is_total': True, 'description': 'Implicit region order derived from element sequence'}} @@ -531,7 +531,6 @@ def _parse_alto(self): raise ValueError('No valid image filename found in ALTO file {self.filename}') self.imagename = base_directory.joinpath(image.text) - lines = doc.findall('.//{*}TextLine') # find all image regions in order regions = [] for el in doc.iterfind('./{*}Layout/{*}Page/{*}PrintSpace/{*}*'): @@ -556,6 +555,9 @@ def _parse_alto(self): for x in ['StructureTag', 'LayoutTag', 'OtherTag']: for tag in tags.findall('./{{*}}{}'.format(x)): cls_map[tag.get('ID')] = (x[:-3].lower(), tag.get('LABEL')) + + self._tag_set = set(('default',)) + # parse region type and coords region_data = defaultdict(list) for region in regions: @@ -574,8 +576,6 @@ def _parse_alto(self): (x_min, y_min + height), (x_min + width, y_min + height), (x_min + width, y_min)] - else: - continue rtype = region.get('TYPE') # fall back to default region type if nothing is given tagrefs = region.get('TAGREFS') @@ -586,62 +586,61 @@ def _parse_alto(self): break if rtype is None: rtype = alto_regions[region.tag.split('}')[-1]] - if boundary == page_boundary and rtype == 'text': - logger.info('Skipping TextBlock with same size as page image.') - continue - region_data[rtype].append({'id': region.get('ID'), 'boundary': boundary}) + region_id = region.get('ID') + region_data[rtype].append({'id': region_id, 'boundary': boundary}) # register implicit reading order - self._orders['region_implicit']['order'].append(region.get('ID')) - self._regions = region_data + self._orders['region_implicit']['order'].append(region_id) - self._tag_set = set(('default',)) - self.lines = [] - for line in lines: - if line.get('BASELINE') is None: - logger.info('TextLine {} without baseline'.format(line.get('ID'))) - continue - pol = line.find('./{*}Shape/{*}Polygon') - boundary = None - if pol is not None: + # parse lines in region + for line in region.iterfind('./{*}TextLine'): + if line.get('BASELINE') is None: + logger.info('TextLine {} without baseline'.format(line.get('ID'))) + continue + pol = line.find('./{*}Shape/{*}Polygon') + boundary = None + if pol is not None: + try: + boundary = self._parse_alto_pointstype(pol.get('POINTS')) + except ValueError: + logger.info('TextLine {} without polygon'.format(line.get('ID'))) + else: + logger.info('TextLine {} without polygon'.format(line.get('ID'))) + + baseline = None try: - boundary = self._parse_alto_pointstype(pol.get('POINTS')) + baseline = self._parse_alto_pointstype(line.get('BASELINE')) except ValueError: - logger.info('TextLine {} without polygon'.format(line.get('ID'))) - else: - logger.info('TextLine {} without polygon'.format(line.get('ID'))) + logger.info('TextLine {} without baseline'.format(line.get('ID'))) + + text = '' + for el in line.xpath(".//*[local-name() = 'String'] | .//*[local-name() = 'SP']"): + text += el.get('CONTENT') if el.get('CONTENT') else ' ' + # find line type + tags = {'type': 'default'} + split_type = None + tagrefs = line.get('TAGREFS') + if tagrefs is not None: + for tagref in tagrefs.split(): + ttype, ltype = cls_map.get(tagref, (None, None)) + if ltype is not None: + self._tag_set.add(ltype) + if ttype == 'other': + tags['type'] = ltype + else: + tags[ttype] = ltype + if ltype in ['train', 'validation', 'test']: + split_type = ltype + self._lines[line.get('ID')] = {'baseline': baseline, + 'boundary': boundary, + 'text': text, + 'tags': tags, + 'split': split_type, + 'region': region_id} + # register implicit reading order + self._orders['line_implicit']['order'].append(line.get('ID')) + + self._regions = region_data - baseline = None - try: - baseline = self._parse_alto_pointstype(line.get('BASELINE')) - except ValueError: - logger.info('TextLine {} without baseline'.format(line.get('ID'))) - - text = '' - for el in line.xpath(".//*[local-name() = 'String'] | .//*[local-name() = 'SP']"): - text += el.get('CONTENT') if el.get('CONTENT') else ' ' - # find line type - tags = {'type': 'default'} - split_type = None - tagrefs = line.get('TAGREFS') - if tagrefs is not None: - for tagref in tagrefs.split(): - ttype, ltype = cls_map.get(tagref, (None, None)) - if ltype is not None: - self._tag_set.add(ltype) - if ttype == 'other': - tags['type'] = ltype - else: - tags[ttype] = ltype - if ltype in ['train', 'validation', 'test']: - split_type = ltype - self.lines.append({'id': line.get('ID'), - 'baseline': baseline, - 'boundary': boundary, - 'text': text, - 'tags': tags, - 'split': split_type}) - # register implicit reading order - self._orders['line_implicit']['order'].append(line.get('ID')) if len(self._tag_set) > 1: self.has_tags = True @@ -707,13 +706,17 @@ def _parse_page(self): None: None}[image.get('readingDirection')] except KeyError: logger.warning(f'Invalid value {image.get("readingDirection")} encountered in page-level reading direction.') - lines = doc.findall('.//{*}TextLine') self.imagename = base_directory.joinpath(image.get('imageFilename')) # find all image regions regions = [reg for reg in image.iterfind('./{*}*')] # parse region type and coords region_data = defaultdict(list) tr_region_order = [] + + self._tag_set = set(('default',)) + tmp_transkribus_line_order = defaultdict(list) + valid_tr_lo = True + for region in regions: coords = region.find('./{*}Coords') if coords is not None and not coords.get('points').isspace() and len(coords.get('points')): @@ -721,10 +724,10 @@ def _parse_page(self): coords = self._parse_page_coords(coords.get('points')) except Exception: logger.warning('Region {} without coordinates'.format(region.get('id'))) - continue + coords = None else: logger.warning('Region {} without coordinates'.format(region.get('id'))) - continue + coords = None rtype = region.get('type') # parse transkribus-style custom field if possible custom_str = region.get('custom') @@ -741,78 +744,78 @@ def _parse_page(self): region_data[rtype].append({'id': region.get('id'), 'boundary': coords}) # register implicit reading order self._orders['region_implicit']['order'].append(region.get('id')) - # add transkribus-style region order - self._orders['region_transkribus'] = {'order': [x[0] for x in sorted(tr_region_order, key=lambda k: k[1])], - 'is_total': True if len(set(map(lambda x: x[0], tr_region_order))) == len(tr_region_order) else False, - 'description': 'Explicit region order from `custom` attribute'} - - self._regions = region_data - # parse line information - self._tag_set = set(('default',)) - tmp_transkribus_line_order = defaultdict(list) - valid_tr_lo = True - for line in lines: - pol = line.find('./{*}Coords') - boundary = None - if pol is not None and not pol.get('points').isspace() and len(pol.get('points')): - try: - boundary = self._parse_coords(pol.get('points')) - except Exception: + # parse line information + for line in region.iterfind('./{*}TextLine'): + pol = line.find('./{*}Coords') + boundary = None + if pol is not None and not pol.get('points').isspace() and len(pol.get('points')): + try: + boundary = self._parse_page_coords(pol.get('points')) + except Exception: + logger.info('TextLine {} without polygon'.format(line.get('id'))) + else: logger.info('TextLine {} without polygon'.format(line.get('id'))) - else: - logger.info('TextLine {} without polygon'.format(line.get('id'))) - base = line.find('./{*}Baseline') - baseline = None - if base is not None and not base.get('points').isspace() and len(base.get('points')): - try: - baseline = self._parse_page_coords(base.get('points')) - except Exception: + base = line.find('./{*}Baseline') + baseline = None + if base is not None and not base.get('points').isspace() and len(base.get('points')): + try: + baseline = self._parse_page_coords(base.get('points')) + except Exception: + logger.info('TextLine {} without baseline'.format(line.get('id'))) + continue + else: logger.info('TextLine {} without baseline'.format(line.get('id'))) continue - else: - logger.info('TextLine {} without baseline'.format(line.get('id'))) - continue - text = '' - manual_transcription = line.find('./{*}TextEquiv') - if manual_transcription is not None: - transcription = manual_transcription - else: - transcription = line - for el in transcription.findall('.//{*}Unicode'): - if el.text: - text += el.text - # retrieve line tags if custom string is set and contains - tags = {'type': 'default'} - split_type = None - custom_str = line.get('custom') - if custom_str: - cs = self._parse_page_custom(custom_str) - if 'structure' in cs and 'type' in cs['structure']: - tags['type'] = cs['structure']['type'] - self._tag_set.add(tags['type']) - # retrieve data split if encoded in custom string. - if 'split' in cs and 'type' in cs['split'] and cs['split']['type'] in ['train', 'validation', 'test']: - split_type = cs['split']['type'] - tags['split'] = split_type - self._tag_set.add(split_type) - if 'readingOrder' in cs and 'index' in cs['readingOrder']: - # look up region index from parent - reg_cus = self._parse_page_custom(line.getparent().get('custom')) - if 'readingOrder' not in reg_cus or 'index' not in reg_cus['readingOrder']: - logger.warning('Incomplete `custom` attribute reading order found.') - valid_tr_lo = False - else: - tmp_transkribus_line_order[int(reg_cus['readingOrder']['index'])].append((int(cs['readingOrder']['index']), line.get('id'))) + text = '' + manual_transcription = line.find('./{*}TextEquiv') + if manual_transcription is not None: + transcription = manual_transcription + else: + transcription = line + for el in transcription.findall('.//{*}Unicode'): + if el.text: + text += el.text + # retrieve line tags if custom string is set and contains + tags = {'type': 'default'} + split_type = None + custom_str = line.get('custom') + if custom_str: + cs = self._parse_page_custom(custom_str) + if 'structure' in cs and 'type' in cs['structure']: + tags['type'] = cs['structure']['type'] + self._tag_set.add(tags['type']) + # retrieve data split if encoded in custom string. + if 'split' in cs and 'type' in cs['split'] and cs['split']['type'] in ['train', 'validation', 'test']: + split_type = cs['split']['type'] + tags['split'] = split_type + self._tag_set.add(split_type) + if 'readingOrder' in cs and 'index' in cs['readingOrder']: + # look up region index from parent + reg_cus = self._parse_page_custom(line.getparent().get('custom')) + if 'readingOrder' not in reg_cus or 'index' not in reg_cus['readingOrder']: + logger.warning('Incomplete `custom` attribute reading order found.') + valid_tr_lo = False + else: + tmp_transkribus_line_order[int(reg_cus['readingOrder']['index'])].append((int(cs['readingOrder']['index']), line.get('id'))) - self._baselines[line.get('id')] = {'baseline': baseline, + self._lines[line.get('id')] = {'baseline': baseline, 'boundary': boundary, 'text': text, 'split': split_type, - 'tags': tags} + 'tags': tags, + 'region': region.get('id')} + + # register implicit reading order + self._orders['line_implicit']['order'].append(line.get('id')) + + # add transkribus-style region order + self._orders['region_transkribus'] = {'order': [x[0] for x in sorted(tr_region_order, key=lambda k: k[1])], + 'is_total': True if len(set(map(lambda x: x[0], tr_region_order))) == len(tr_region_order) else False, + 'description': 'Explicit region order from `custom` attribute'} + + self._regions = region_data - # register implicit reading order - self._orders['line_implicit']['order'].append(line.get('id')) if tmp_transkribus_line_order: # sort by regions tmp_reg_order = sorted(((k, v) for k, v in tmp_transkribus_line_order.items()), key=lambda k: k[0]) @@ -862,21 +865,60 @@ def regions(self): return self._regions @property - def baselines(self): - return self._baselines + def lines(self): + return self._lines @property def reading_orders(self): return self._orders - def get_baselines_by_region(self, region): - pass + def get_sorted_lines(self, ro='line_implicit'): + """ + Returns ordered baselines from particular reading order. + """ + if ro not in self.reading_orders: + raise ValueError(f'Unknown reading order {ro}') + def _traverse_ro(el): + _ro = [] + if isinstance(el, list): + _ro.append([_traverse_ro(x) for x in el]) + else: + # if line directly append to ro + if el in self.lines: + return self.lines[el] + # substitute lines if region in RO + elif el in [reg['id'] for regs in doc.regions.values() for reg in regs]: + _ro.extend(self.get_lines_by_region(el)) + else: + raise ValueError(f'Invalid reading order {ro}') + return _ro + + _ro = self.reading_orders[ro] + return _traverse_ro(_ro['order']) + + def get_sorted_regions(self, ro='region_implicit'): + """ + Returns ordered regions from particular reading order. + """ + + + def get_sorted_lines_by_region(self, region, ro='line_implicit'): + """ + Returns ordered lines in region. + """ + if self.reading_orders[ro]['is_total'] is False: + raise ValueError('Fetching lines by region of a non-total order is not supported') + lines = [(id, line) for id, line in self._lines.items() if line['region'] == region] + for line in lines: + if line[0] not in self.reading_orders[ro]['order']: + raise ValueError('Fetching lines by region is only possible for flat orders') + return sorted(lines, key=lambda k: self.reading_orders[ro]['order'].index(k[0])) - def get_baselines_by_tag(self, key, value): - return {k: v for k, v in self._baselines.items() if v['tags'].get(key) == value} + def get_lines_by_tag(self, key, value): + return {k: v for k, v in self._lines.items() if v['tags'].get(key) == value} - def get_baselines_by_split(self, split: Literal['train', 'validation', 'test']): - return {k: v for k, v in self._baselines.items() if v['tags'].get(key) == split} + def get_lines_by_split(self, split: Literal['train', 'validation', 'test']): + return {k: v for k, v in self._lines.items() if v['tags'].get(key) == split} @property def tags(self): From 65ad0ea118eda06f35fb82cd2218781d83284f1a Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Mon, 6 Feb 2023 17:13:44 +0100 Subject: [PATCH 04/69] non-working xml parsing tests --- tests/test_xml.py | 60 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 tests/test_xml.py diff --git a/tests/test_xml.py b/tests/test_xml.py new file mode 100644 index 000000000..cee436ad8 --- /dev/null +++ b/tests/test_xml.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- +import json +import unittest +import tempfile +import numpy as np + +from pathlib import Path +from pytest import raises + +from kraken.lib import xml + +thisfile = Path(__file__).resolve().parent +resources = thisfile / 'resources' + +class TestXMLParser(unittest.TestCase): + """ + Tests XML (ALTO/PAGE) parsing + """ + def setUp(self): + self.page_doc = resources / 'cPAS-2000.xml' + self.alto_doc = resources / 'bsb00084914_00007.xml' + + def test_page_parsing(self): + """ + Test parsing of PAGE XML files with reading order. + """ + doc = xml.XMLPage(self.page_doc, filetype='page') + self.assertEqual(len(doc.baselines), 97) + self.assertEqual(len([item for x in doc.regions.values() for item in x]), 4) + self.assertEqual( + + def test_alto_parsing(self): + """ + Test parsing of ALTO XML files with reading order. + """ + doc = xml.XMLPage(self.alto_doc, filetype='alto') + + def test_auto_parsing(self): + """ + Test parsing of PAGE and ALTO XML files with auto-format determination. + """ + doc = xml.XMLPage(self.page_doc, filetype='xml') + self.assertEqual(doc.filetype, 'page') + doc = xml.XMLPage(self.alto_doc, filetype='xml') + self.assertEqual(doc.filetype, 'alto') + + def test_failure_page_alto_parsing(self): + """ + Test that parsing ALTO files with PAGE as format fails. + """ + with raises(ValueError): + xml.XMLPage(self.alto_doc, filetype='page') + + def test_failure_alto_page_parsing(self): + """ + Test that parsing PAGE files with ALTO as format fails. + """ + with raises(ValueError): + xml.XMLPage(self.page_doc, filetype='alto') + From 2a1be8be8e115388351d5459f539a1d40e2e548f Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Wed, 22 Feb 2023 15:47:01 +0100 Subject: [PATCH 05/69] Fix ALTO region order parsing --- kraken/lib/xml.py | 43 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/kraken/lib/xml.py b/kraken/lib/xml.py index 2d6f72e29..2f478cc54 100644 --- a/kraken/lib/xml.py +++ b/kraken/lib/xml.py @@ -17,6 +17,8 @@ """ import re import logging + +from os import PathLike from pathlib import Path from itertools import groupby @@ -480,7 +482,7 @@ class XMLPage(object): type: Literal['baselines', 'bbox'] == 'baselines' base_dir: Optional[Literal['L', 'R']] = None - imagename: pathlib.Path = None + imagename: PathLike = None _orders: Dict[str, Dict[str, Any]] = None has_tags: bool = False _tag_set: Optional[Dict] = None @@ -488,7 +490,7 @@ class XMLPage(object): _split_set: Optional[List] = None def __init__(self, - filename: Union[str, pathlib.Path], + filename: Union[str, PathLike], filetype: Literal['xml', 'alto', 'page'] = 'xml'): super().__init__() self.filename = Path(filename) @@ -653,13 +655,16 @@ def _parse_alto(self): reading_orders = ro_el.getchildren() # UnorderedGroup at top-level => treated as multiple reading orders if len(reading_orders) == 1 and reading_orders[0].tag.endswith('UnorderedGroup'): - reading_orders = reading_orders[0].getchildren() + reading_orders = reading_orders[0].getchildren() else: reading_orders = [reading_orders] + def _parse_group(el): + nonlocal is_valid + _ro = [] if el.tag.endswith('UnorderedGroup'): - _ro.append([_parse_group(x) for x in el.iterchildren()]) + _ro = [_parse_group(x) for x in el.iterchildren()] is_total = False elif el.tag.endswith('OrderedGroup'): _ro.extend(_parse_group(x) for x in el.iterchildren()) @@ -667,13 +672,15 @@ def _parse_group(el): ref = el.get('REF') res = doc.find(f'.//{{*}}*[@ID="{ref}"]') if res is None: - logger.warning(f'Nonexistant element with ID {ref} in reading order. Skipping RO {ro.get("ID")}.') + logger.warning(f'Nonexistent element with ID {ref} in reading order. Skipping RO {ro.get("ID")}.') is_valid = False return _ro tag = res.tag.split('}')[-1] if tag not in alto_regions.keys() and tag != 'TextLine': logger.warning(f'Sub-line element with ID {ref} in reading order. Skipping RO {ro.get("ID")}.') is_valid = False + return _ro + return ref return _ro for ro in reading_orders: @@ -839,7 +846,7 @@ def _parse_page(self): def _parse_group(el): _ro = [] if el.tag.endswith('UnorderedGroup'): - _ro.append([_parse_group(x) for x in el.iterchildren()]) + _ro = [_parse_group(x) for x in el.iterchildren()] is_total = False elif el.tag.endswith('OrderedGroup'): _ro.extend(_parse_group(x) for x in el.iterchildren()) @@ -887,8 +894,8 @@ def _traverse_ro(el): if el in self.lines: return self.lines[el] # substitute lines if region in RO - elif el in [reg['id'] for regs in doc.regions.values() for reg in regs]: - _ro.extend(self.get_lines_by_region(el)) + elif el in [reg['id'] for regs in self.regions.values() for reg in regs]: + _ro.extend(self.get_sorted_lines_by_region(el)) else: raise ValueError(f'Invalid reading order {ro}') return _ro @@ -900,12 +907,32 @@ def get_sorted_regions(self, ro='region_implicit'): """ Returns ordered regions from particular reading order. """ + if ro not in self.reading_orders: + raise ValueError(f'Unknown reading order {ro}') + regions = {reg['id']: key for key, regs in self.regions.items() for reg in regs} + + def _traverse_ro(el): + _ro = [] + if isinstance(el, list): + _ro.append([_traverse_ro(x) for x in el]) + else: + # if region directly append to ro + if el in regions.keys(): + return [reg for reg in self.regions[regions[el]] if reg['id'] == el][0] + else: + raise ValueError(f'Invalid reading order {ro}') + return _ro + + _ro = self.reading_orders[ro] + return _traverse_ro(_ro['order']) def get_sorted_lines_by_region(self, region, ro='line_implicit'): """ Returns ordered lines in region. """ + if ro not in self.reading_orders: + raise ValueError(f'Unknown reading order {ro}') if self.reading_orders[ro]['is_total'] is False: raise ValueError('Fetching lines by region of a non-total order is not supported') lines = [(id, line) for id, line in self._lines.items() if line['region'] == region] From bb4999ee7d7241d3f8daa86a6531ce8c47940a60 Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Fri, 3 Mar 2023 12:45:38 +0100 Subject: [PATCH 06/69] working training code for RO --- kraken/lib/dataset/__init__.py | 1 + kraken/lib/dataset/ro.py | 140 ++++++++++++++++++++++++++++++ kraken/lib/default_specs.py | 23 +++++ kraken/lib/ro/__init__.py | 19 ++++ kraken/lib/ro/layers.py | 27 ++++++ kraken/lib/ro/model.py | 154 +++++++++++++++++++++++++++++++++ kraken/lib/ro/util.py | 66 ++++++++++++++ kraken/lib/xml.py | 8 +- 8 files changed, 434 insertions(+), 4 deletions(-) create mode 100644 kraken/lib/dataset/ro.py create mode 100644 kraken/lib/ro/__init__.py create mode 100644 kraken/lib/ro/layers.py create mode 100644 kraken/lib/ro/model.py create mode 100644 kraken/lib/ro/util.py diff --git a/kraken/lib/dataset/__init__.py b/kraken/lib/dataset/__init__.py index 960ef8499..5388cfe6b 100644 --- a/kraken/lib/dataset/__init__.py +++ b/kraken/lib/dataset/__init__.py @@ -17,4 +17,5 @@ """ from .recognition import ArrowIPCRecognitionDataset, PolygonGTDataset, GroundTruthDataset # NOQA from .segmentation import BaselineSet # NOQA +from .ro import ROSet #NOQA from .utils import ImageInputTransforms, collate_sequences, global_align, compute_confusions # NOQA diff --git a/kraken/lib/dataset/ro.py b/kraken/lib/dataset/ro.py new file mode 100644 index 000000000..0b81912d1 --- /dev/null +++ b/kraken/lib/dataset/ro.py @@ -0,0 +1,140 @@ +# +# Copyright 2015 Benjamin Kiessling +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express +# or implied. See the License for the specific language governing +# permissions and limitations under the License. +""" +Utility functions for data loading and training of VGSL networks. +""" +import json +import torch +import traceback +import numpy as np +import torch.nn.functional as F +import shapely.geometry as geom + +from math import factorial +from os import path, PathLike +from PIL import Image +from shapely.ops import split +from itertools import groupby +from torchvision import transforms +from collections import defaultdict +from torch.utils.data import Dataset +from typing import Dict, List, Tuple, Sequence, Callable, Any, Union, Literal, Optional + +from kraken.lib.xml import parse_alto, parse_page, parse_xml, XMLPage + +from kraken.lib.exceptions import KrakenInputException + +__all__ = ['BaselineSet'] + +import logging + +logger = logging.getLogger(__name__) + + +class ROSet(Dataset): + """ + Dataset for training a reading order determination model. + """ + def __init__(self, files: Sequence[Union[PathLike, str]] = None, + mode: Optional[Literal['alto', 'page', 'xml']] = 'path', + ro_type: Literal['region', 'line'] = 'line', + ro_id: str = 'line_implicit', + class_mapping: Optional[Dict[str, int]] = None): + """ + Samples pairs lines/regions from XML files for training a reading order + model . + + Args: + mode: Either alto, page, xml, None. In alto, page, and xml + mode the baseline paths and image data is retrieved from an + ALTO/PageXML file. In `None` mode data is iteratively added + through the `add` method. + ro_id: ID of the reading order to sample from. + """ + super().__init__() + + self._num_pairs = 0 + if class_mapping: + self.class_mapping = class_mapping + self.num_classes = len(class_mapping) + 1 + else: + self.num_classes = 1 + self.class_mapping = {} + + self.data = [] + + if mode in ['alto', 'page', 'xml']: + for file in files: + try: + doc = XMLPage(file, filetype=mode) + for tag in doc.tags: + if tag not in self.class_mapping: + self.class_mapping[tag] = self.num_classes + self.num_classes += 1 + except KrakenInputException as e: + files.pop(file) + logger.warning(e) + continue + for file in files: + try: + doc = XMLPage(file, filetype=mode) + if ro_type == 'line': + order = doc.get_sorted_lines(ro_id) + elif ro_type == 'region': + order = doc.get_sorted_regions(ro_id) + else: + raise ValueError(f'Invalid RO type {ro_type}') + # traverse RO and substitute features. + h,w = Image.open(doc.imagename).size + sorted_lines = [] + for line in order: + line_coords = np.array(line['baseline']) / (w, h) + line_center = np.mean(line_coords, axis=0) + cl = torch.zeros(self.num_classes, dtype=torch.float) + # if class is not in class mapping default to None class (idx 0) + cl[self.class_mapping.get(line['tags']['type'], 0)] = 1 + line_data = {'type': line['tags']['type'], + 'features': torch.cat((cl, # one hot encoded line type + torch.tensor(line_center, dtype=torch.float), # line center + torch.tensor(line_coords[0, :], dtype=torch.float), # start_point coord + torch.tensor(line_coords[-1, :], dtype=torch.float), # end point coord) + )) + } + sorted_lines.append(line_data) + self.data.append(sorted_lines) + self._num_pairs += int(factorial(len(sorted_lines))/factorial(len(sorted_lines)-2)) + + except KrakenInputException as e: + logger.warning(e) + continue + else: + raise Exception('invalid dataset mode') + + def __getitem__(self, idx): + lines = [] + while len(lines) < 2: + lines = self.data[torch.randint(len(self.data), (1,))[0]] + idx0, idx1 = 0, 0 + while idx0 == idx1: + idx0, idx1 = torch.randint(len(lines), (2,)) + x = torch.cat((lines[idx0]['features'], lines[idx1]['features'])) + y = torch.tensor(0 if idx0 >= idx1 else 1, dtype=torch.float) + return {'sample': x, 'target': y} + + def get_feature_dim(self): + return 2 * self.num_classes + 12 + + def __len__(self): + return self._num_pairs diff --git a/kraken/lib/default_specs.py b/kraken/lib/default_specs.py index 4830ee1fb..bc92f6979 100644 --- a/kraken/lib/default_specs.py +++ b/kraken/lib/default_specs.py @@ -19,6 +19,29 @@ SEGMENTATION_SPEC = '[1,1800,0,3 Cr7,7,64,2,2 Gn32 Cr3,3,128,2,2 Gn32 Cr3,3,128 Gn32 Cr3,3,256 Gn32 Cr3,3,256 Gn32 Lbx32 Lby32 Cr1,1,32 Gn32 Lby32 Lbx32]' # NOQA RECOGNITION_SPEC = '[1,120,0,1 Cr3,13,32 Do0.1,2 Mp2,2 Cr3,13,32 Do0.1,2 Mp2,2 Cr3,9,64 Do0.1,2 Mp2,2 Cr3,9,64 Do0.1,2 S1(1x0)1,3 Lbx200 Do0.1,2 Lbx200 Do0.1,2 Lbx200 Do]' # NOQA +READING_ORDER_HYPER_PARAMS = {'lrate': 0.001, + 'freq': 1.0, + 'batch_size': 15000, + 'epochs': 3000, + 'lag': 300, + 'quit': 'early', + 'optimizer': 'Adam', + 'momentum': 0.9, + 'weight_decay': 0.01, + 'schedule': 'cosine', + 'completed_epochs': 0, + # lr scheduler params + # step/exp decay + 'step_size': 10, + 'gamma': 0.1, + # reduce on plateau + 'rop_factor': 0.1, + 'rop_patience': 5, + # cosine + 'cos_t_max': 100, + 'warmup': 0, + } + RECOGNITION_PRETRAIN_HYPER_PARAMS = {'pad': 16, 'freq': 1.0, 'batch_size': 64, diff --git a/kraken/lib/ro/__init__.py b/kraken/lib/ro/__init__.py new file mode 100644 index 000000000..4e370b855 --- /dev/null +++ b/kraken/lib/ro/__init__.py @@ -0,0 +1,19 @@ +# +# Copyright 2023 Benjamin Kiessling +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express +# or implied. See the License for the specific language governing +# permissions and limitations under the License. +""" +Tools for trainable reading order. +""" + +from .model import ROModel # NOQA diff --git a/kraken/lib/ro/layers.py b/kraken/lib/ro/layers.py new file mode 100644 index 000000000..c86cb0711 --- /dev/null +++ b/kraken/lib/ro/layers.py @@ -0,0 +1,27 @@ +""" +Layers for VGSL models +""" +from torch import nn + +# all tensors are ordered NCHW, the "feature" dimension is C, so the output of +# an LSTM will be put into C same as the filters of a CNN. + +__all__ = ['MLP'] + + +class MLP(nn.Module): + """ + A simple 2 layer MLP for reading order determination. + """ + def __init__(self, feature_size: int, hidden_size: int): + super(MLP, self).__init__() + self.fc1 = nn.Linear(feature_size, hidden_size) + self.relu = nn.ReLU() + self.fc2 = nn.Linear(hidden_size, 1) + self.sigmoid = nn.Sigmoid() + + def forward(self, x): + x = self.fc1(x) + x = self.relu(x) + x = self.fc2(x) + return self.sigmoid(x) diff --git a/kraken/lib/ro/model.py b/kraken/lib/ro/model.py new file mode 100644 index 000000000..63b0bf349 --- /dev/null +++ b/kraken/lib/ro/model.py @@ -0,0 +1,154 @@ +# +# Copyright 2023 Benjamin Kiessling +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express +# or implied. See the License for the specific language governing +# permissions and limitations under the License. +""" +Pytorch-lightning modules for reading order training. + +Adapted from: +""" +import re +import math +import torch +import logging +import numpy as np +import torch.nn.functional as F +import pytorch_lightning as pl + +from os import PathLike +from typing import Dict, Optional, Sequence, Union, Any, Literal + +from kraken.lib import vgsl, default_specs, layers +from kraken.lib.dataset import ROSet +from kraken.lib.train import _configure_optimizer_and_lr_scheduler +from kraken.lib.ro.layers import MLP + +from torch.utils.data import DataLoader, random_split, Subset + + +logger = logging.getLogger(__name__) + + +class ROModel(pl.LightningModule): + def __init__(self, + hyper_params: Dict[str, Any] = None, + output: str = 'model', + model: Optional[Union[PathLike, str]] = None, + training_data: Union[Sequence[Union[PathLike, str]], Sequence[Dict[str, Any]]] = None, + evaluation_data: Optional[Union[Sequence[Union[PathLike, str]], Sequence[Dict[str, Any]]]] = None, + partition: Optional[float] = 0.9, + num_workers: int = 1, + format_type: Literal['alto', 'page', 'xml'] = 'xml', + load_hyper_parameters: bool = False): + """ + A LightningModule encapsulating the unsupervised pretraining setup for + a text recognition model. + + Setup parameters (load, training_data, evaluation_data, ....) are + named, model hyperparameters (everything in + `kraken.lib.default_specs.RECOGNITION_HYPER_PARAMS`) are in in the + `hyper_params` argument. + + Args: + hyper_params (dict): Hyperparameter dictionary containing all fields + from + kraken.lib.default_specs.RECOGNITION_PRETRAIN_HYPER_PARAMS + **kwargs: Setup parameters, i.e. CLI parameters of the train() command. + """ + super().__init__() + hyper_params_ = default_specs.READING_ORDER_HYPER_PARAMS + if model: + logger.info(f'Loading existing model from {model} ') + self.nn = vgsl.TorchVGSLModel.load_model(model) + + if self.nn.model_type not in [None, 'segmentation']: + raise ValueError(f'Model {model} is of type {self.nn.model_type} while `segmentation` is expected.') + + if load_hyper_parameters: + hp = self.nn.hyper_params + else: + hp = {} + hyper_params_.update(hp) + else: + self.ro_net = None + + if hyper_params: + hyper_params_.update(hyper_params) + self.save_hyperparameters(hyper_params_) + + if not evaluation_data: + np.random.shuffle(training_data) + training_data = training_data[:int(partition*len(training_data))] + evaluation_data = training_data[int(partition*len(training_data)):] + self.train_set = ROSet(training_data, mode=format_type) + self.val_set = ROSet(evaluation_data, mode=format_type, class_mapping=self.train_set.class_mapping) + + if len(self.train_set) == 0 or len(self.val_set) == 0: + raise ValueError('No valid training data was provided to the train ' + 'command. Please add valid XML, line, or binary data.') + + logger.info(f'Training set {len(self.train_set)} lines, validation set ' + f'{len(self.val_set)} lines') + + self.model = model + self.output = output + self.criterion = torch.nn.BCELoss() + + self.num_workers = num_workers + + self.best_epoch = 0 + self.best_metric = math.inf + + logger.info(f'Creating new RO model') + self.ro_net = torch.jit.script(MLP(self.train_set.get_feature_dim(), 128)) + + if 'file_system' in torch.multiprocessing.get_all_sharing_strategies(): + logger.debug('Setting multiprocessing tensor sharing strategy to file_system') + torch.multiprocessing.set_sharing_strategy('file_system') + + logger.info('Encoding training set') + + def forward(self, x): + return self.ro_net(x) + + def validation_step(self, batch, batch_idx): + x, y = batch['sample'], batch['target'] + yhat = self.ro_net(x) + loss = self.criterion(yhat.squeeze(), y) + self.log('loss', loss) + return loss + + def training_step(self, batch, batch_idx): + x, y = batch['sample'], batch['target'] + yhat = self.ro_net(x) + loss = self.criterion(yhat.squeeze(), y) + self.log('loss', loss) + return loss + + def configure_optimizers(self): + return _configure_optimizer_and_lr_scheduler(self.hparams, + self.ro_net.parameters(), + len_train_set=len(self.train_set), + loss_tracking_mode='min') + + def train_dataloader(self): + return DataLoader(self.train_set, + batch_size=self.hparams.batch_size, + num_workers=self.num_workers, + pin_memory=True) + + def val_dataloader(self): + return DataLoader(self.val_set, + batch_size=self.hparams.batch_size, + num_workers=self.num_workers, + pin_memory=True) diff --git a/kraken/lib/ro/util.py b/kraken/lib/ro/util.py new file mode 100644 index 000000000..57fea354b --- /dev/null +++ b/kraken/lib/ro/util.py @@ -0,0 +1,66 @@ +# Copyright (c) Facebook, Inc. and its affiliates. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +from typing import Sequence, Union + +import torch +import random +import numpy as np + + +def positive_integers_with_sum(n, total): + ls = [0] + rv = [] + while len(ls) < n: + c = random.randint(0, total) + ls.append(c) + ls = sorted(ls) + ls.append(total) + for i in range(1, len(ls)): + rv.append(ls[i] - ls[i-1]) + return rv + + +def compute_masks(mask_prob: int, + mask_width: int, + num_neg_samples: int, + seq_lens: Union[torch.Tensor, Sequence[int]]): + """ + Samples num_mask non-overlapping random masks of length mask_width in + sequence of length seq_len. + + Args: + mask_prob: Probability of each individual token being chosen as start + of a masked sequence. Overall number of masks num_masks is + mask_prob * sum(seq_lens) / mask_width. + mask_width: width of each mask + num_neg_samples: Number of samples from unmasked sequence parts (gets + multiplied by num_mask) + seq_lens: sequence lengths + + Returns: + An index array containing 1 for masked bits, 2 for negative samples, + the number of masks, and the actual number of negative samples. + """ + mask_samples = np.zeros(sum(seq_lens)) + num_masks = int(mask_prob * sum(seq_lens.numpy()) // mask_width) + num_neg_samples = num_masks * num_neg_samples + num_masks += num_neg_samples + + indices = [x+mask_width for x in positive_integers_with_sum(num_masks, sum(seq_lens)-num_masks*mask_width)] + start = 0 + mask_slices = [] + for i in indices: + i_start = random.randint(start, i+start-mask_width) + mask_slices.append(slice(i_start, i_start+mask_width)) + start += i + + neg_idx = random.sample(range(len(mask_slices)), num_neg_samples) + neg_slices = [mask_slices.pop(idx) for idx in sorted(neg_idx, reverse=True)] + + mask_samples[np.r_[tuple(mask_slices)]] = 1 + mask_samples[np.r_[tuple(neg_slices)]] = 2 + + return mask_samples, num_masks - num_neg_samples, num_neg_samples diff --git a/kraken/lib/xml.py b/kraken/lib/xml.py index 2f478cc54..ebfd5c877 100644 --- a/kraken/lib/xml.py +++ b/kraken/lib/xml.py @@ -725,6 +725,8 @@ def _parse_page(self): valid_tr_lo = True for region in regions: + if not any([True if region.tag.endswith(k) else False for k in page_regions.keys()]): + continue coords = region.find('./{*}Coords') if coords is not None and not coords.get('points').isspace() and len(coords.get('points')): try: @@ -841,8 +843,6 @@ def _parse_page(self): # UnorderedGroup at top-level => treated as multiple reading orders if len(reading_orders) == 1 and reading_orders[0].tag.endswith('UnorderedGroup'): reading_orders = reading_orders.getchildren() - else: - reading_orders = [reading_orders] def _parse_group(el): _ro = [] if el.tag.endswith('UnorderedGroup'): @@ -888,7 +888,7 @@ def get_sorted_lines(self, ro='line_implicit'): def _traverse_ro(el): _ro = [] if isinstance(el, list): - _ro.append([_traverse_ro(x) for x in el]) + _ro = [_traverse_ro(x) for x in el] else: # if line directly append to ro if el in self.lines: @@ -915,7 +915,7 @@ def get_sorted_regions(self, ro='region_implicit'): def _traverse_ro(el): _ro = [] if isinstance(el, list): - _ro.append([_traverse_ro(x) for x in el]) + _ro = [_traverse_ro(x) for x in el] else: # if region directly append to ro if el in regions.keys(): From b06ebe7086a40227bd48880d8107d86bb2ed2045 Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Fri, 3 Mar 2023 13:32:00 +0100 Subject: [PATCH 07/69] more training code --- docs/ketos.rst | 2 +- kraken/ketos/__init__.py | 2 + kraken/ketos/pretrain.py | 6 +- kraken/ketos/recognition.py | 6 +- kraken/ketos/ro.py | 249 +++++++++++++++++++++++++++++++++++ kraken/ketos/segmentation.py | 6 +- kraken/lib/dataset/ro.py | 18 ++- kraken/lib/default_specs.py | 3 +- kraken/lib/models.py | 2 +- kraken/lib/progress.py | 1 - kraken/lib/ro/model.py | 22 +++- 11 files changed, 291 insertions(+), 26 deletions(-) create mode 100644 kraken/ketos/ro.py diff --git a/docs/ketos.rst b/docs/ketos.rst index c3bd2926a..c96481390 100644 --- a/docs/ketos.rst +++ b/docs/ketos.rst @@ -142,7 +142,7 @@ option action -F, \--savefreq Model save frequency in epochs during training -q, \--quit Stop condition for training. Set to `early` - for early stopping (default) or `dumb` for fixed + for early stopping (default) or `fixed` for fixed number of epochs. -N, \--epochs Number of epochs to train for. \--min-epochs Minimum number of epochs to train for when using early stopping. diff --git a/kraken/ketos/__init__.py b/kraken/ketos/__init__.py index 83e56e82c..4b7087dc4 100644 --- a/kraken/ketos/__init__.py +++ b/kraken/ketos/__init__.py @@ -34,6 +34,7 @@ from .repo import publish from .segmentation import segtrain, segtest from .transcription import extract, transcription +from .ro import rotrain APP_NAME = 'kraken' @@ -76,6 +77,7 @@ def cli(ctx, verbose, seed, deterministic): cli.add_command(segtrain) cli.add_command(segtest) cli.add_command(publish) +cli.add_command(rotrain) # deprecated commands cli.add_command(line_generator) diff --git a/kraken/ketos/pretrain.py b/kraken/ketos/pretrain.py index 7be3cc2f0..aeebecca0 100644 --- a/kraken/ketos/pretrain.py +++ b/kraken/ketos/pretrain.py @@ -56,8 +56,8 @@ show_default=True, default=RECOGNITION_PRETRAIN_HYPER_PARAMS['quit'], type=click.Choice(['early', - 'dumb']), - help='Stop condition for training. Set to `early` for early stooping or `dumb` for fixed number of epochs') + 'fixed']), + help='Stop condition for training. Set to `early` for early stooping or `fixed` for fixed number of epochs') @click.option('-N', '--epochs', show_default=True, @@ -275,7 +275,7 @@ def pretrain(ctx, batch_size, pad, output, spec, load, freq, quit, epochs, trainer = KrakenTrainer(accelerator=accelerator, devices=device, precision=precision, - max_epochs=hyper_params['epochs'] if hyper_params['quit'] == 'dumb' else -1, + max_epochs=hyper_params['epochs'] if hyper_params['quit'] == 'fixed' else -1, min_epochs=hyper_params['min_epochs'], enable_progress_bar=True if not ctx.meta['verbose'] else False, deterministic=ctx.meta['deterministic'], diff --git a/kraken/ketos/recognition.py b/kraken/ketos/recognition.py index 781fe9f47..2d8eaf86b 100644 --- a/kraken/ketos/recognition.py +++ b/kraken/ketos/recognition.py @@ -55,8 +55,8 @@ show_default=True, default=RECOGNITION_HYPER_PARAMS['quit'], type=click.Choice(['early', - 'dumb']), - help='Stop condition for training. Set to `early` for early stooping or `dumb` for fixed number of epochs') + 'fixed']), + help='Stop condition for training. Set to `early` for early stooping or `fixed` for fixed number of epochs') @click.option('-N', '--epochs', show_default=True, @@ -302,7 +302,7 @@ def train(ctx, batch_size, pad, output, spec, append, load, freq, quit, epochs, trainer = KrakenTrainer(accelerator=accelerator, devices=device, precision=precision, - max_epochs=hyper_params['epochs'] if hyper_params['quit'] == 'dumb' else -1, + max_epochs=hyper_params['epochs'] if hyper_params['quit'] == 'fixed' else -1, min_epochs=hyper_params['min_epochs'], freeze_backbone=hyper_params['freeze_backbone'], enable_progress_bar=True if not ctx.meta['verbose'] else False, diff --git a/kraken/ketos/ro.py b/kraken/ketos/ro.py new file mode 100644 index 000000000..9d1222077 --- /dev/null +++ b/kraken/ketos/ro.py @@ -0,0 +1,249 @@ +# +# Copyright 2022 Benjamin Kiessling +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express +# or implied. See the License for the specific language governing +# permissions and limitations under the License. +""" +kraken.ketos.ro +~~~~~~~~~~~~~~~ + +Command line driver for reading order training, evaluation, and handling. +""" +import click +import pathlib +import logging + +from PIL import Image +from typing import Dict + +from kraken.lib.progress import KrakenProgressBar +from kraken.lib.exceptions import KrakenInputException +from kraken.lib.default_specs import READING_ORDER_HYPER_PARAMS + +from kraken.ketos.util import _validate_manifests, _expand_gt, message, to_ptl_device + +logging.captureWarnings(True) +logger = logging.getLogger('kraken') + +# raise default max image size to 20k * 20k pixels +Image.MAX_IMAGE_PIXELS = 20000 ** 2 + +@click.command('rotrain') +@click.pass_context +@click.option('-o', '--output', show_default=True, type=click.Path(), default='model', help='Output model file') +@click.option('-i', '--load', show_default=True, type=click.Path(exists=True, + readable=True), help='Load existing file to continue training') +@click.option('-F', '--freq', show_default=True, default=READING_ORDER_HYPER_PARAMS['freq'], type=click.FLOAT, + help='Model saving and report generation frequency in epochs ' + 'during training. If frequency is >1 it must be an integer, ' + 'i.e. running validation every n-th epoch.') +@click.option('-q', + '--quit', + show_default=True, + default=READING_ORDER_HYPER_PARAMS['quit'], + type=click.Choice(['early', + 'fixed']), + help='Stop condition for training. Set to `early` for early stopping or `fixed` for fixed number of epochs') +@click.option('-N', + '--epochs', + show_default=True, + default=READING_ORDER_HYPER_PARAMS['epochs'], + help='Number of epochs to train for') +@click.option('--min-epochs', + show_default=True, + default=['min_epochs'], + help='Minimal number of epochs to train for when using early stopping.') +@click.option('--lag', + show_default=True, + default=READING_ORDER_HYPER_PARAMS['lag'], + help='Number of evaluations (--report frequence) to wait before stopping training without improvement') +@click.option('--min-delta', + show_default=True, + default=READING_ORDER_HYPER_PARAMS['min_delta'], + type=click.FLOAT, + help='Minimum improvement between epochs to reset early stopping. By default it scales the delta by the best loss') +@click.option('-d', '--device', show_default=True, default='cpu', help='Select device to use (cpu, cuda:0, cuda:1, ...)') +@click.option('--precision', default='32', type=click.Choice(['32', '16']), help='set tensor precision') +@click.option('--optimizer', + show_default=True, + default=READING_ORDER_HYPER_PARAMS['optimizer'], + type=click.Choice(['Adam', + 'SGD', + 'RMSprop', + 'Lamb']), + help='Select optimizer') +@click.option('-r', '--lrate', show_default=True, default=READING_ORDER_HYPER_PARAMS['lrate'], help='Learning rate') +@click.option('-m', '--momentum', show_default=True, default=READING_ORDER_HYPER_PARAMS['momentum'], help='Momentum') +@click.option('-w', '--weight-decay', show_default=True, + default=READING_ORDER_HYPER_PARAMS['weight_decay'], help='Weight decay') +@click.option('--warmup', show_default=True, type=float, + default=READING_ORDER_HYPER_PARAMS['warmup'], help='Number of samples to ramp up to `lrate` initial learning rate.') +@click.option('--schedule', + show_default=True, + type=click.Choice(['constant', + '1cycle', + 'exponential', + 'cosine', + 'step', + 'reduceonplateau']), + default=READING_ORDER_HYPER_PARAMS['schedule'], + help='Set learning rate scheduler. For 1cycle, cycle length is determined by the `--step-size` option.') +@click.option('-g', + '--gamma', + show_default=True, + default=READING_ORDER_HYPER_PARAMS['gamma'], + help='Decay factor for exponential, step, and reduceonplateau learning rate schedules') +@click.option('-ss', + '--step-size', + show_default=True, + default=READING_ORDER_HYPER_PARAMS['step_size'], + help='Number of validation runs between learning rate decay for exponential and step LR schedules') +@click.option('--sched-patience', + show_default=True, + default=READING_ORDER_HYPER_PARAMS['rop_patience'], + help='Minimal number of validation runs between LR reduction for reduceonplateau LR schedule.') +@click.option('--cos-max', + show_default=True, + default=READING_ORDER_HYPER_PARAMS['cos_t_max'], + help='Epoch of minimal learning rate for cosine LR scheduler.') +@click.option('-p', '--partition', show_default=True, default=0.9, + help='Ground truth data partition ratio between train/validation set') +@click.option('-t', '--training-files', show_default=True, default=None, multiple=True, + callback=_validate_manifests, type=click.File(mode='r', lazy=True), + help='File(s) with additional paths to training data') +@click.option('-e', '--evaluation-files', show_default=True, default=None, multiple=True, + callback=_validate_manifests, type=click.File(mode='r', lazy=True), + help='File(s) with paths to evaluation data. Overrides the `-p` parameter') +@click.option('--workers', show_default=True, default=1, help='Number of OpenMP threads and workers when running on CPU.') +@click.option('--load-hyper-parameters/--no-load-hyper-parameters', show_default=True, default=False, + help='When loading an existing model, retrieve hyper-parameters from the model') +@click.option('-f', '--format-type', type=click.Choice(['xml', 'alto', 'page']), default='xml', + help='Sets the training data format. In ALTO and PageXML mode all ' + 'data is extracted from xml files containing both baselines and a ' + 'link to source images.') +@click.option('--logger', 'pl_logger', show_default=True, type=click.Choice(['tensorboard']), default=None, + help='Logger used by PyTorch Lightning to track metrics such as loss and accuracy.') +@click.option('--log-dir', show_default=True, type=click.Path(exists=True, dir_okay=True, writable=True), + help='Path to directory where the logger will store the logs. If not set, a directory will be created in the current working directory.') +@click.option('--level', show_default=True, type=click.Choice(['baselines', 'regions']), default='baselines', + help='Selects level to train reading order model on.') +@click.option('--reading-order', show_default=True, default=None, + help='Select reading order to train. Defaults to `line_implicit`/`region_implicit`') +@click.argument('ground_truth', nargs=-1, callback=_expand_gt, type=click.Path(exists=False, dir_okay=False)) +def rotrain(ctx, output, load, freq, quit, epochs, min_epochs, lag, + min_delta, device, precision, optimizer, lrate, momentum, + weight_decay, warmup, schedule, gamma, step_size, sched_patience, + cos_max, partition, training_files, evaluation_files, workers, + load_hyper_parameters, format_type, pl_logger, log_dir, level, + reading_order, ground_truth): + """ + Trains a baseline labeling model for layout analysis + """ + import shutil + + from kraken.lib.train import KrakenTrainer + from kraken.lib.ro import ROModel + + if not (0 <= freq <= 1) and freq % 1.0 != 0: + raise click.BadOptionUsage('freq', 'freq needs to be either in the interval [0,1.0] or a positive integer.') + + if pl_logger == 'tensorboard': + try: + import tensorboard + except ImportError: + raise click.BadOptionUsage('logger', 'tensorboard logger needs the `tensorboard` package installed.') + + if log_dir is None: + log_dir = pathlib.Path.cwd() + + logger.info('Building ground truth set from {} document images'.format(len(ground_truth) + len(training_files))) + + # populate hyperparameters from command line args + hyper_params = READING_ORDER_HYPER_PARAMS.copy() + hyper_params.update({'freq': freq, + 'quit': quit, + 'epochs': epochs, + 'min_epochs': min_epochs, + 'lag': lag, + 'min_delta': min_delta, + 'optimizer': optimizer, + 'lrate': lrate, + 'momentum': momentum, + 'weight_decay': weight_decay, + 'warmup': warmup, + 'schedule': schedule, + 'gamma': gamma, + 'step_size': step_size, + 'rop_patience': sched_patience, + 'cos_t_max': cos_max, + 'pl_logger': pl_logger,}) + + # disable automatic partition when given evaluation set explicitly + if evaluation_files: + partition = 1 + ground_truth = list(ground_truth) + + # merge training_files into ground_truth list + if training_files: + ground_truth.extend(training_files) + + if len(ground_truth) == 0: + raise click.UsageError('No training data was provided to the train command. Use `-t` or the `ground_truth` argument.') + + try: + accelerator, device = to_ptl_device(device) + except Exception as e: + raise click.BadOptionUsage('device', str(e)) + + if hyper_params['freq'] > 1: + val_check_interval = {'check_val_every_n_epoch': int(hyper_params['freq'])} + else: + val_check_interval = {'val_check_interval': hyper_params['freq']} + + model = ROModel(hyper_params, + output=output, + model=load, + training_data=ground_truth, + evaluation_data=evaluation_files, + partition=partition, + num_workers=workers, + load_hyper_parameters=load_hyper_parameters, + format_type=format_type, + level=level, + reading_order=reading_order) + + message(f'Training RO on following {level} types:') + for k, v in model.train_set.dataset.class_mapping.items(): + message(f' {k}\t{v}') + + if len(model.train_set) == 0: + raise click.UsageError('No valid training data was provided to the train command. Use `-t` or the `ground_truth` argument.') + + trainer = KrakenTrainer(accelerator=accelerator, + devices=device, + max_epochs=hyper_params['epochs'] if hyper_params['quit'] == 'fixed' else -1, + min_epochs=hyper_params['min_epochs'], + enable_progress_bar=True if not ctx.meta['verbose'] else False, + deterministic=ctx.meta['deterministic'], + precision=int(precision), + pl_logger=pl_logger, + log_dir=log_dir, + **val_check_interval) + + trainer.fit(model) + + if quit == 'early': + message('Moving best model {0}_{1}.mlmodel ({2}) to {0}_best.mlmodel'.format( + output, model.best_epoch, model.best_metric)) + logger.info('Moving best model {0}_{1}.mlmodel ({2}) to {0}_best.mlmodel'.format( + output, model.best_epoch, model.best_metric)) + shutil.copy(f'{output}_{model.best_epoch}.mlmodel', f'{output}_best.mlmodel') diff --git a/kraken/ketos/segmentation.py b/kraken/ketos/segmentation.py index 54afa9ae5..dfcb7c739 100644 --- a/kraken/ketos/segmentation.py +++ b/kraken/ketos/segmentation.py @@ -76,8 +76,8 @@ def _validate_merging(ctx, param, value): show_default=True, default=SEGMENTATION_HYPER_PARAMS['quit'], type=click.Choice(['early', - 'dumb']), - help='Stop condition for training. Set to `early` for early stopping or `dumb` for fixed number of epochs') + 'fixed']), + help='Stop condition for training. Set to `early` for early stopping or `fixed` for fixed number of epochs') @click.option('-N', '--epochs', show_default=True, @@ -339,7 +339,7 @@ def segtrain(ctx, output, spec, line_width, pad, load, freq, quit, epochs, trainer = KrakenTrainer(accelerator=accelerator, devices=device, precision=precision, - max_epochs=hyper_params['epochs'] if hyper_params['quit'] == 'dumb' else -1, + max_epochs=hyper_params['epochs'] if hyper_params['quit'] == 'fixed' else -1, min_epochs=hyper_params['min_epochs'], enable_progress_bar=True if not ctx.meta['verbose'] else False, deterministic=ctx.meta['deterministic'], diff --git a/kraken/lib/dataset/ro.py b/kraken/lib/dataset/ro.py index 0b81912d1..88bb32b73 100644 --- a/kraken/lib/dataset/ro.py +++ b/kraken/lib/dataset/ro.py @@ -49,8 +49,8 @@ class ROSet(Dataset): """ def __init__(self, files: Sequence[Union[PathLike, str]] = None, mode: Optional[Literal['alto', 'page', 'xml']] = 'path', - ro_type: Literal['region', 'line'] = 'line', - ro_id: str = 'line_implicit', + level: Literal['regions', 'baselines'] = 'baselines', + ro_id: Optional[str] = None, class_mapping: Optional[Dict[str, int]] = None): """ Samples pairs lines/regions from XML files for training a reading order @@ -61,11 +61,13 @@ def __init__(self, files: Sequence[Union[PathLike, str]] = None, mode the baseline paths and image data is retrieved from an ALTO/PageXML file. In `None` mode data is iteratively added through the `add` method. - ro_id: ID of the reading order to sample from. + ro_id: ID of the reading order to sample from. Defaults to + `line_implicit`/`region_implicit`. """ super().__init__() self._num_pairs = 0 + self.failed_samples = [] if class_mapping: self.class_mapping = class_mapping self.num_classes = len(class_mapping) + 1 @@ -90,12 +92,16 @@ def __init__(self, files: Sequence[Union[PathLike, str]] = None, for file in files: try: doc = XMLPage(file, filetype=mode) - if ro_type == 'line': + if level == 'baselines': + if not ro_id: + ro_id = 'line_implicit' order = doc.get_sorted_lines(ro_id) - elif ro_type == 'region': + elif level == 'regions': + if not ro_id: + ro_id = 'region_implicit' order = doc.get_sorted_regions(ro_id) else: - raise ValueError(f'Invalid RO type {ro_type}') + raise ValueError(f'Invalid RO type {level}') # traverse RO and substitute features. h,w = Image.open(doc.imagename).size sorted_lines = [] diff --git a/kraken/lib/default_specs.py b/kraken/lib/default_specs.py index bc92f6979..af08fd1e5 100644 --- a/kraken/lib/default_specs.py +++ b/kraken/lib/default_specs.py @@ -24,6 +24,7 @@ 'batch_size': 15000, 'epochs': 3000, 'lag': 300, + 'min_delta': None, 'quit': 'early', 'optimizer': 'Adam', 'momentum': 0.9, @@ -107,7 +108,7 @@ SEGMENTATION_HYPER_PARAMS = {'line_width': 8, 'padding': (0, 0), 'freq': 1.0, - 'quit': 'dumb', + 'quit': 'fixed', 'epochs': 50, 'min_epochs': 0, 'lag': 10, diff --git a/kraken/lib/models.py b/kraken/lib/models.py index c8e08dcfb..ac1219b23 100644 --- a/kraken/lib/models.py +++ b/kraken/lib/models.py @@ -217,6 +217,6 @@ def validate_hyper_parameters(hyper_params): """ Validate some model's hyper parameters and modify them in place if need be. """ - if (hyper_params['quit'] == 'dumb' and hyper_params['completed_epochs'] >= hyper_params['epochs']): + if (hyper_params['quit'] == 'fixed' and hyper_params['completed_epochs'] >= hyper_params['epochs']): logger.warning('Maximum epochs reached (might be loaded from given model), starting again from 0.') hyper_params['completed_epochs'] = 0 diff --git a/kraken/lib/progress.py b/kraken/lib/progress.py index bb2e30b3e..0284fa865 100644 --- a/kraken/lib/progress.py +++ b/kraken/lib/progress.py @@ -155,4 +155,3 @@ class RichProgressBarTheme: time: Union[str, Style] = DEFAULT_STYLES['progress.elapsed'] processing_speed: Union[str, Style] = DEFAULT_STYLES['progress.data.speed'] metrics: Union[str, Style] = DEFAULT_STYLES['progress.description'] - diff --git a/kraken/lib/ro/model.py b/kraken/lib/ro/model.py index 63b0bf349..e5a482acd 100644 --- a/kraken/lib/ro/model.py +++ b/kraken/lib/ro/model.py @@ -49,7 +49,9 @@ def __init__(self, partition: Optional[float] = 0.9, num_workers: int = 1, format_type: Literal['alto', 'page', 'xml'] = 'xml', - load_hyper_parameters: bool = False): + load_hyper_parameters: bool = False, + level: Literal['baselines', 'regions'] = 'baselines', + reading_order: Optional[str] = None): """ A LightningModule encapsulating the unsupervised pretraining setup for a text recognition model. @@ -90,8 +92,17 @@ def __init__(self, np.random.shuffle(training_data) training_data = training_data[:int(partition*len(training_data))] evaluation_data = training_data[int(partition*len(training_data)):] - self.train_set = ROSet(training_data, mode=format_type) - self.val_set = ROSet(evaluation_data, mode=format_type, class_mapping=self.train_set.class_mapping) + train_set = ROSet(training_data, + mode=format_type, + level=level, + ro_id=reading_order) + self.train_set = Subset(train_set, range(len(train_set))) + val_set = ROSet(evaluation_data, + mode=format_type, + class_mapping=train_set.class_mapping, + level=level, + ro_id=reading_order) + self.val_set = Subset(val_set, range(len(val_set))) if len(self.train_set) == 0 or len(self.val_set) == 0: raise ValueError('No valid training data was provided to the train ' @@ -106,11 +117,8 @@ def __init__(self, self.num_workers = num_workers - self.best_epoch = 0 - self.best_metric = math.inf - logger.info(f'Creating new RO model') - self.ro_net = torch.jit.script(MLP(self.train_set.get_feature_dim(), 128)) + self.ro_net = torch.jit.script(MLP(train_set.get_feature_dim(), 128)) if 'file_system' in torch.multiprocessing.get_all_sharing_strategies(): logger.debug('Setting multiprocessing tensor sharing strategy to file_system') From 145516b1d0926260ffaed9982b1d515c67f0c7b6 Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Fri, 3 Mar 2023 14:19:11 +0100 Subject: [PATCH 08/69] remove metric ignore code in progress bar --- kraken/ketos/pretrain.py | 2 +- kraken/ketos/ro.py | 1 - kraken/lib/ro/model.py | 42 ++++++++++++++++++++++------------------ 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/kraken/ketos/pretrain.py b/kraken/ketos/pretrain.py index aeebecca0..1c4bc3d0e 100644 --- a/kraken/ketos/pretrain.py +++ b/kraken/ketos/pretrain.py @@ -279,7 +279,7 @@ def pretrain(ctx, batch_size, pad, output, spec, load, freq, quit, epochs, min_epochs=hyper_params['min_epochs'], enable_progress_bar=True if not ctx.meta['verbose'] else False, deterministic=ctx.meta['deterministic'], - pb_ignored_metrics=(), + failed_sample_threshold=failed_sample_threshold, **val_check_interval) trainer.fit(model, datamodule=data_module) diff --git a/kraken/ketos/ro.py b/kraken/ketos/ro.py index 9d1222077..d4ff1a409 100644 --- a/kraken/ketos/ro.py +++ b/kraken/ketos/ro.py @@ -211,7 +211,6 @@ def rotrain(ctx, output, load, freq, quit, epochs, min_epochs, lag, model = ROModel(hyper_params, output=output, - model=load, training_data=ground_truth, evaluation_data=evaluation_files, partition=partition, diff --git a/kraken/lib/ro/model.py b/kraken/lib/ro/model.py index e5a482acd..f8e607291 100644 --- a/kraken/lib/ro/model.py +++ b/kraken/lib/ro/model.py @@ -26,7 +26,9 @@ import pytorch_lightning as pl from os import PathLike -from typing import Dict, Optional, Sequence, Union, Any, Literal +from dataclasses import dataclass, field +from torch.nn import Module +from typing import Dict, Optional, Sequence, Union, Any, Literal, List from kraken.lib import vgsl, default_specs, layers from kraken.lib.dataset import ROSet @@ -38,12 +40,26 @@ logger = logging.getLogger(__name__) +@dataclass +class DummyVGSLModel: + hyper_params: Dict[str, int] = field(default_factory=dict) + user_metadata: Dict[str, List] = field(default_factory=dict) + one_channel_mode: Literal['1', 'L'] = '1' + ptl_module: Module = None + model_type: str = 'unknown' + + def __post_init__(self): + self.hyper_params: Dict[str, int] = {'completed_epochs': 0} + self.user_metadata: Dict[str, List] = {'accuracy': [], 'metrics': []} + + def save_model(self, filename): + self.ptl_module.save_checkpoint(filename) + class ROModel(pl.LightningModule): def __init__(self, hyper_params: Dict[str, Any] = None, output: str = 'model', - model: Optional[Union[PathLike, str]] = None, training_data: Union[Sequence[Union[PathLike, str]], Sequence[Dict[str, Any]]] = None, evaluation_data: Optional[Union[Sequence[Union[PathLike, str]], Sequence[Dict[str, Any]]]] = None, partition: Optional[float] = 0.9, @@ -69,20 +85,6 @@ def __init__(self, """ super().__init__() hyper_params_ = default_specs.READING_ORDER_HYPER_PARAMS - if model: - logger.info(f'Loading existing model from {model} ') - self.nn = vgsl.TorchVGSLModel.load_model(model) - - if self.nn.model_type not in [None, 'segmentation']: - raise ValueError(f'Model {model} is of type {self.nn.model_type} while `segmentation` is expected.') - - if load_hyper_parameters: - hp = self.nn.hyper_params - else: - hp = {} - hyper_params_.update(hp) - else: - self.ro_net = None if hyper_params: hyper_params_.update(hyper_params) @@ -111,7 +113,6 @@ def __init__(self, logger.info(f'Training set {len(self.train_set)} lines, validation set ' f'{len(self.val_set)} lines') - self.model = model self.output = output self.criterion = torch.nn.BCELoss() @@ -124,7 +125,7 @@ def __init__(self, logger.debug('Setting multiprocessing tensor sharing strategy to file_system') torch.multiprocessing.set_sharing_strategy('file_system') - logger.info('Encoding training set') + self.nn = DummyVGSLModel(ptl_module=self) def forward(self, x): return self.ro_net(x) @@ -133,7 +134,7 @@ def validation_step(self, batch, batch_idx): x, y = batch['sample'], batch['target'] yhat = self.ro_net(x) loss = self.criterion(yhat.squeeze(), y) - self.log('loss', loss) + self.log('val_metric', loss) return loss def training_step(self, batch, batch_idx): @@ -160,3 +161,6 @@ def val_dataloader(self): batch_size=self.hparams.batch_size, num_workers=self.num_workers, pin_memory=True) + + def save_checkpoint(self, filename): + self.trainer.save_checkpoint(filename) From 46d2c96a401891f6952695f4b30853da7a83a55a Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Fri, 3 Mar 2023 15:17:14 +0100 Subject: [PATCH 09/69] make checkpoint loading work for RO training --- kraken/ketos/ro.py | 29 ++++++---- kraken/lib/ro/model.py | 26 ++++++--- kraken/lib/train.py | 123 +++++++++++++++++++++++------------------ 3 files changed, 107 insertions(+), 71 deletions(-) diff --git a/kraken/ketos/ro.py b/kraken/ketos/ro.py index d4ff1a409..b9ae52f55 100644 --- a/kraken/ketos/ro.py +++ b/kraken/ketos/ro.py @@ -209,16 +209,25 @@ def rotrain(ctx, output, load, freq, quit, epochs, min_epochs, lag, else: val_check_interval = {'val_check_interval': hyper_params['freq']} - model = ROModel(hyper_params, - output=output, - training_data=ground_truth, - evaluation_data=evaluation_files, - partition=partition, - num_workers=workers, - load_hyper_parameters=load_hyper_parameters, - format_type=format_type, - level=level, - reading_order=reading_order) + if load: + model = ROModel.load_from_checkpoint(load, + training_data=ground_truth, + evaluation_data=evaluation_files, + partition=partition, + num_workers=workers, + load_hyper_parameters=load_hyper_parameters, + format_type=format_type) + else: + model = ROModel(hyper_params, + output=output, + training_data=ground_truth, + evaluation_data=evaluation_files, + partition=partition, + num_workers=workers, + load_hyper_parameters=load_hyper_parameters, + format_type=format_type, + level=level, + reading_order=reading_order) message(f'Training RO on following {level} types:') for k, v in model.train_set.dataset.class_mapping.items(): diff --git a/kraken/lib/ro/model.py b/kraken/lib/ro/model.py index f8e607291..c878f4a83 100644 --- a/kraken/lib/ro/model.py +++ b/kraken/lib/ro/model.py @@ -84,11 +84,9 @@ def __init__(self, **kwargs: Setup parameters, i.e. CLI parameters of the train() command. """ super().__init__() - hyper_params_ = default_specs.READING_ORDER_HYPER_PARAMS - + self.hyper_params = default_specs.READING_ORDER_HYPER_PARAMS if hyper_params: - hyper_params_.update(hyper_params) - self.save_hyperparameters(hyper_params_) + self.hyper_params.update(hyper_params) if not evaluation_data: np.random.shuffle(training_data) @@ -118,6 +116,9 @@ def __init__(self, self.num_workers = num_workers + self.best_epoch = -1 + self.best_metric = torch.inf + logger.info(f'Creating new RO model') self.ro_net = torch.jit.script(MLP(train_set.get_feature_dim(), 128)) @@ -127,6 +128,8 @@ def __init__(self, self.nn = DummyVGSLModel(ptl_module=self) + self.save_hyperparameters() + def forward(self, x): return self.ro_net(x) @@ -137,6 +140,15 @@ def validation_step(self, batch, batch_idx): self.log('val_metric', loss) return loss + def validation_epoch_end(self, outputs): + val_metric = np.mean([x.cpu() for x in outputs]) + if val_metric < self.best_metric: + logger.debug(f'Updating best metric from {self.best_metric} ({self.best_epoch}) to {val_metric} ({self.current_epoch})') + self.best_epoch = self.current_epoch + self.best_metric = val_metric + logger.info(f'validation run: val_metric {val_metric}') + self.log('val_metric', val_metric, on_step=False, on_epoch=True, prog_bar=True, logger=True) + def training_step(self, batch, batch_idx): x, y = batch['sample'], batch['target'] yhat = self.ro_net(x) @@ -145,20 +157,20 @@ def training_step(self, batch, batch_idx): return loss def configure_optimizers(self): - return _configure_optimizer_and_lr_scheduler(self.hparams, + return _configure_optimizer_and_lr_scheduler(self.hparams.hyper_params, self.ro_net.parameters(), len_train_set=len(self.train_set), loss_tracking_mode='min') def train_dataloader(self): return DataLoader(self.train_set, - batch_size=self.hparams.batch_size, + batch_size=self.hyper_params['batch_size'], num_workers=self.num_workers, pin_memory=True) def val_dataloader(self): return DataLoader(self.val_set, - batch_size=self.hparams.batch_size, + batch_size=self.hyper_params['batch_size'], num_workers=self.num_workers, pin_memory=True) diff --git a/kraken/lib/train.py b/kraken/lib/train.py index be9b70a31..5f4af6e17 100644 --- a/kraken/lib/train.py +++ b/kraken/lib/train.py @@ -246,7 +246,8 @@ def __init__(self, if hyper_params: hyper_params_.update(hyper_params) - self.save_hyperparameters(hyper_params_) + self.hyper_params = hyper_params_ + self.save_hyperparameters() self.reorder = reorder self.append = append @@ -415,11 +416,11 @@ def _build_dataset(self, DatasetClass, training_data, **kwargs): - dataset = DatasetClass(normalization=self.hparams.normalization, - whitespace_normalization=self.hparams.normalize_whitespace, + dataset = DatasetClass(normalization=self.hparams.hyper_params['normalization'], + whitespace_normalization=self.hparams.hyper_params['normalize_whitespace'], reorder=self.reorder, im_transforms=self.transforms, - augmentation=self.hparams.augment, + augmentation=self.hparams.hyper_params['augment'], **kwargs) if (self.num_workers and self.num_workers > 1) and self.format_type != 'binary': @@ -434,7 +435,7 @@ def _build_dataset(self, dataset.add(**im) except KrakenInputException as e: logger.warning(str(e)) - if self.format_type == 'binary' and self.hparams.normalization: + if self.format_type == 'binary' and self.hparams.hyper_params['normalization']: logger.debug('Rebuilding dataset using unicode normalization') dataset.rebuild_alphabet() return dataset @@ -591,7 +592,7 @@ def setup(self, stage: Optional[str] = None): if self.format_type != 'path' and self.nn.seg_type == 'bbox': logger.warning('Neural network has been trained on bounding box image information but training set is polygonal.') - self.nn.hyper_params = self.hparams + self.nn.hyper_params = self.hparams.hyper_params self.nn.model_type = 'recognition' if not self.nn.seg_type: @@ -605,7 +606,7 @@ def setup(self, stage: Optional[str] = None): def train_dataloader(self): return DataLoader(self.train_set, - batch_size=self.hparams.batch_size, + batch_size=self.hparams.hyper_params['batch_size'], num_workers=self.num_workers, pin_memory=True, shuffle=True, @@ -614,7 +615,7 @@ def train_dataloader(self): def val_dataloader(self): return DataLoader(self.val_set, shuffle=False, - batch_size=self.hparams.batch_size, + batch_size=self.hparams.hyper_params['batch_size'], num_workers=self.num_workers, pin_memory=True, collate_fn=collate_sequences, @@ -622,11 +623,12 @@ def val_dataloader(self): def configure_callbacks(self): callbacks = [] - if self.hparams.quit == 'early': + if self.hparams.hyper_params['quit'] == 'early': callbacks.append(EarlyStopping(monitor='val_accuracy', mode='max', - patience=self.hparams.lag, + patience=self.hparams.hyper_params['lag'], stopping_threshold=1.0)) + return callbacks # configuration of optimizers and learning rate schedulers @@ -636,7 +638,7 @@ def configure_callbacks(self): # batch-wise learning rate warmup. In lr_scheduler_step() calls to the # scheduler are then only performed at the end of the epoch. def configure_optimizers(self): - return _configure_optimizer_and_lr_scheduler(self.hparams, + return _configure_optimizer_and_lr_scheduler(self.hparams.hyper_params, self.nn.nn.parameters(), len_train_set=len(self.train_set), loss_tracking_mode='max') @@ -648,13 +650,13 @@ def optimizer_step(self, epoch, batch_idx, optimizer, optimizer_closure): # linear warmup between 0 and the initial learning rate `lrate` in `warmup` # steps. - if self.hparams.warmup and self.trainer.global_step < self.hparams.warmup: - lr_scale = min(1.0, float(self.trainer.global_step + 1) / self.hparams.warmup) + if self.hparams.hyper_params.warmup and self.trainer.global_step < self.hparams.hyper_params.warmup: + lr_scale = min(1.0, float(self.trainer.global_step + 1) / self.hparams.hyper_params.warmup) for pg in optimizer.param_groups: - pg["lr"] = lr_scale * self.hparams.lrate + pg["lr"] = lr_scale * self.hparams.hyper_params.lrate - def lr_scheduler_step(self, scheduler, metric): - if not self.hparams.warmup or self.trainer.global_step >= self.hparams.warmup: + def lr_scheduler_step(self, scheduler, optimizer_idx, metric): + if not self.hparams.hyper_params.warmup or self.trainer.global_step >= self.hparams.hyper_params.warmup: # step OneCycleLR each batch if not in warmup phase if isinstance(scheduler, lr_scheduler.OneCycleLR): scheduler.step() @@ -759,7 +761,8 @@ def __init__(self, hyper_params_.update(hyper_params) validate_hyper_parameters(hyper_params_) - self.save_hyperparameters(hyper_params_) + self.hyper_params = hyper_params_ + self.save_hyperparameters() if not training_data: raise ValueError('No training data provided. Please add some.') @@ -768,7 +771,7 @@ def __init__(self, height, width, channels, - self.hparams.padding, + self.hparams.hyper_params.padding, valid_norm=False, force_binarization=force_binarization) @@ -795,10 +798,10 @@ def __init__(self, merge_baselines = None train_set = BaselineSet(training_data, - line_width=self.hparams.line_width, + line_width=self.hparams.hyper_params.line_width, im_transforms=transforms, mode=format_type, - augmentation=self.hparams.augment, + augmentation=self.hparams.hyper_params.augment, valid_baselines=valid_baselines, merge_baselines=merge_baselines, valid_regions=valid_regions, @@ -810,7 +813,7 @@ def __init__(self, if evaluation_data: val_set = BaselineSet(evaluation_data, - line_width=self.hparams.line_width, + line_width=self.hparams.hyper_params.line_width, im_transforms=transforms, mode=format_type, augmentation=False, @@ -1035,10 +1038,10 @@ def val_dataloader(self): def configure_callbacks(self): callbacks = [] - if self.hparams.quit == 'early': + if self.hparams.hyper_params['quit'] == 'early': callbacks.append(EarlyStopping(monitor='val_mean_iu', mode='max', - patience=self.hparams.lag, + patience=self.hparams.hyper_params['lag'], stopping_threshold=1.0)) return callbacks @@ -1050,7 +1053,7 @@ def configure_callbacks(self): # batch-wise learning rate warmup. In lr_scheduler_step() calls to the # scheduler are then only performed at the end of the epoch. def configure_optimizers(self): - return _configure_optimizer_and_lr_scheduler(self.hparams, + return _configure_optimizer_and_lr_scheduler(self.hparams.hyper_params, self.nn.nn.parameters(), len_train_set=len(self.train_set), loss_tracking_mode='max') @@ -1061,13 +1064,13 @@ def optimizer_step(self, epoch, batch_idx, optimizer, optimizer_closure): # linear warmup between 0 and the initial learning rate `lrate` in `warmup` # steps. - if self.hparams.warmup and self.trainer.global_step < self.hparams.warmup: - lr_scale = min(1.0, float(self.trainer.global_step + 1) / self.hparams.warmup) + if self.hparams.hyper_params['warmup'] and self.trainer.global_step < self.hparams.hyper_params['warmup']: + lr_scale = min(1.0, float(self.trainer.global_step + 1) / self.hparams.hyper_params['warmup']) for pg in optimizer.param_groups: - pg["lr"] = lr_scale * self.hparams.lrate + pg["lr"] = lr_scale * self.hparams.hyper_params['lrate'] - def lr_scheduler_step(self, scheduler, metric): - if not self.hparams.warmup or self.trainer.global_step >= self.hparams.warmup: + def lr_scheduler_step(self, scheduler, optimizer_idx, metric): + if not self.hparams.hyper_params['warmup'] or self.trainer.global_step >= self.hparams.hyper_params['warmup']: # step OneCycleLR each batch if not in warmup phase if isinstance(scheduler, lr_scheduler.OneCycleLR): scheduler.step() @@ -1077,51 +1080,63 @@ def lr_scheduler_step(self, scheduler, metric): def _configure_optimizer_and_lr_scheduler(hparams, params, len_train_set=None, loss_tracking_mode='max'): + optimizer = hparams.get("optimizer") + lrate = hparams.get("lrate") + momentum = hparams.get("momentum") + weight_decay = hparams.get("weight_decay") + schedule = hparams.get("schedule") + gamma = hparams.get("gamma") + step_size = hparams.get("step_size") + rop_factor = hparams.get("rop_factor") + rop_patience = hparams.get("rop_patience") + epochs = hparams.get("epochs") + completed_epochs = hparams.get("completed_epochs") + # XXX: Warmup is not configured here because it needs to be manually done in optimizer_step() - logger.debug(f'Constructing {hparams.optimizer} optimizer (lr: {hparams.lrate}, momentum: {hparams.momentum})') - if hparams.optimizer == 'Adam': - optim = torch.optim.Adam(params, lr=hparams.lrate, weight_decay=hparams.weight_decay) + logger.debug(f'Constructing {optimizer} optimizer (lr: {lrate}, momentum: {momentum})') + if optimizer == 'Adam': + optim = torch.optim.Adam(params, lr=lrate, weight_decay=weight_decay) else: - optim = getattr(torch.optim, hparams.optimizer)(params, - lr=hparams.lrate, - momentum=hparams.momentum, - weight_decay=hparams.weight_decay) + optim = getattr(torch.optim, optimizer)(params, + lr=lrate, + momentum=momentum, + weight_decay=weight_decay) lr_sched = {} - if hparams.schedule == 'exponential': - lr_sched = {'scheduler': lr_scheduler.ExponentialLR(optim, hparams.gamma, last_epoch=hparams.completed_epochs-1), + if schedule == 'exponential': + lr_sched = {'scheduler': lr_scheduler.ExponentialLR(optim, gamma, last_epoch=completed_epochs-1), 'interval': 'step'} - elif hparams.schedule == 'cosine': - lr_sched = {'scheduler': lr_scheduler.CosineAnnealingLR(optim, hparams.gamma, last_epoch=hparams.completed_epochs-1), + elif schedule == 'cosine': + lr_sched = {'scheduler': lr_scheduler.CosineAnnealingLR(optim, gamma, last_epoch=completed_epochs-1), 'interval': 'step'} - elif hparams.schedule == 'step': - lr_sched = {'scheduler': lr_scheduler.StepLR(optim, hparams.step_size, hparams.gamma, last_epoch=hparams.completed_epochs-1), + elif schedule == 'step': + lr_sched = {'scheduler': lr_scheduler.StepLR(optim, step_size, gamma, last_epoch=completed_epochs-1), 'interval': 'step'} - elif hparams.schedule == 'reduceonplateau': + elif schedule == 'reduceonplateau': lr_sched = {'scheduler': lr_scheduler.ReduceLROnPlateau(optim, mode=loss_tracking_mode, - factor=hparams.rop_factor, - patience=hparams.rop_patience), + factor=rop_factor, + patience=rop_patience), 'interval': 'step'} - elif hparams.schedule == '1cycle': - if hparams.epochs <= 0: + elif schedule == '1cycle': + if epochs <= 0: raise ValueError('1cycle learning rate scheduler selected but ' 'number of epochs is less than 0 ' - f'({hparams.epochs}).') - last_epoch = hparams.completed_epochs*len_train_set if hparams.completed_epochs else -1 + f'({epochs}).') + last_epoch = completed_epochs*len_train_set if completed_epochs else -1 lr_sched = {'scheduler': lr_scheduler.OneCycleLR(optim, - max_lr=hparams.lrate, - epochs=hparams.epochs, + max_lr=lrate, + epochs=epochs, steps_per_epoch=len_train_set, last_epoch=last_epoch), 'interval': 'step'} - elif hparams.schedule != 'constant': - raise ValueError(f'Unsupported learning rate scheduler {hparams.schedule}.') + elif schedule != 'constant': + raise ValueError(f'Unsupported learning rate scheduler {schedule}.') ret = {'optimizer': optim} if lr_sched: ret['lr_scheduler'] = lr_sched - if hparams.schedule == 'reduceonplateau': + if schedule == 'reduceonplateau': lr_sched['monitor'] = 'val_metric' lr_sched['strict'] = False lr_sched['reduce_on_plateau'] = True From c13b72c22a9eb07d4887ae3687628c5a5db482ca Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Fri, 3 Mar 2023 16:07:28 +0100 Subject: [PATCH 10/69] add batch_size arg to ro cli again --- kraken/ketos/ro.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/kraken/ketos/ro.py b/kraken/ketos/ro.py index b9ae52f55..a58ce8d89 100644 --- a/kraken/ketos/ro.py +++ b/kraken/ketos/ro.py @@ -39,6 +39,8 @@ @click.command('rotrain') @click.pass_context +@click.option('-B', '--batch-size', show_default=True, type=click.INT, + default=RECOGNITION_PRETRAIN_HYPER_PARAMS['batch_size'], help='batch sample size') @click.option('-o', '--output', show_default=True, type=click.Path(), default='model', help='Output model file') @click.option('-i', '--load', show_default=True, type=click.Path(exists=True, readable=True), help='Load existing file to continue training') @@ -139,7 +141,7 @@ @click.option('--reading-order', show_default=True, default=None, help='Select reading order to train. Defaults to `line_implicit`/`region_implicit`') @click.argument('ground_truth', nargs=-1, callback=_expand_gt, type=click.Path(exists=False, dir_okay=False)) -def rotrain(ctx, output, load, freq, quit, epochs, min_epochs, lag, +def rotrain(ctx, batch_size, output, load, freq, quit, epochs, min_epochs, lag, min_delta, device, precision, optimizer, lrate, momentum, weight_decay, warmup, schedule, gamma, step_size, sched_patience, cos_max, partition, training_files, evaluation_files, workers, @@ -169,7 +171,8 @@ def rotrain(ctx, output, load, freq, quit, epochs, min_epochs, lag, # populate hyperparameters from command line args hyper_params = READING_ORDER_HYPER_PARAMS.copy() - hyper_params.update({'freq': freq, + hyper_params.update({'batch_size': batch_size, + 'freq': freq, 'quit': quit, 'epochs': epochs, 'min_epochs': min_epochs, From 92dfb074d841eba8d29351a7d61f0bfef3d63c19 Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Fri, 3 Mar 2023 16:08:44 +0100 Subject: [PATCH 11/69] arrgh --- kraken/ketos/ro.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kraken/ketos/ro.py b/kraken/ketos/ro.py index a58ce8d89..27e4851be 100644 --- a/kraken/ketos/ro.py +++ b/kraken/ketos/ro.py @@ -40,7 +40,7 @@ @click.command('rotrain') @click.pass_context @click.option('-B', '--batch-size', show_default=True, type=click.INT, - default=RECOGNITION_PRETRAIN_HYPER_PARAMS['batch_size'], help='batch sample size') + default=READING_ORDER_HYPER_PARAMS['batch_size'], help='batch sample size') @click.option('-o', '--output', show_default=True, type=click.Path(), default='model', help='Output model file') @click.option('-i', '--load', show_default=True, type=click.Path(exists=True, readable=True), help='Load existing file to continue training') From e1a932b95d7086d396b03339abcfefdd3563537d Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Fri, 3 Mar 2023 16:27:11 +0100 Subject: [PATCH 12/69] sketch of neural RO decoder --- kraken/lib/segmentation.py | 79 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/kraken/lib/segmentation.py b/kraken/lib/segmentation.py index 6f6459ac5..784cb4ceb 100644 --- a/kraken/lib/segmentation.py +++ b/kraken/lib/segmentation.py @@ -817,6 +817,85 @@ def is_in_region(line, region) -> bool: return region.contains(l_obj) +def neural_reading_order(lines: Sequence[Tuple[List[Tuple[int, int]], List[Tuple[int, int]]]], + im_size: Tuple[int, int], + model): + """ + Given a list of baselines and regions, calculates the correct reading order + and applies it to the input. + + Args: + lines: List of tuples containing the baseline and its polygonization. + model: torch Module for + + Returns: + A reordered input. + """ + # construct all possible pairs + h, w = im_size + features = [] + for i in lines: + for j in lines: + if i == j and len(children) != 1: + continue + line_coords_i = np.array(i) / (w, h) + line_center_i = np.mean(line_coords_i, axis=0) + line_coords_j = np.array(j) / (w, h) + line_center_j = np.mean(line_coords_j, axis=0) + features.append(torch.cat((cl_i, + torch.tensor(line_center_i, dtype=torch.float), # lin + torch.tensor(line_coords_i[0, :], dtype=torch.float), + torch.tensor(line_coords_i[-1, :], dtype=torch.float), + cl_j, + torch.tensor(line_center_j, dtype=torch.float), # lin + torch.tensor(line_coords_j[0, :], dtype=torch.float), + torch.tensor(line_coords_j[-1, :], dtype=torch.float)))) + features = torch.cat(features) + output = model(features) + order = torch.zeros((len(lines), len(lines))) + idx = 0 + for i in enumerate(lines): + for j in enumerate(lines): + order[i, j] = output[idx] + idx += 1 + # decode order relation matrix + path = _greedy_order_decoder(order) + return ordered_lines + + +def _greedy_order_decoder(P): + """ + A greedy decoder of order-relation matrix. For each position in the + reading order we select the most probable one, then move to the next + position. Most probable for position: + z^{\star}_t = \argmax_{(s,\nu) \ni z^{\star}} + \prod_{(s',\nu') \in z^\star}{\tilde{P}(Y=1\mid s',s)} + \times \prod_{\substack{(s'',\nu'') \ni z^\star\\ + s'' \ne s}}{\tilde{P}(r=0\mid s'',s)}, 1\le t \le n + """ + A = P + torch.finfo(torch.float).eps + N = P.shape[0] + A = (A + (1-A).T)/2 + for i in range(A.shape[0]): + A[i,i] = torch.finfo(torch.float).eps + best_path = [] + #--- use log(p(R\mid s',s)) to shift multiplication to sum + lP = torch.log(A) + for i in range(N): + lP[i,i] = 0 + for t in range(N): + #print(lP) + #print("----------------------") + for i in range(N): + idx = torch.argmax(lP.sum(axis=1)) + if idx not in best_path: + best_path.append(idx) + lP[idx,:] = lP[:,idx] + lP[:,idx] = 0 + break + return best_path + + def scale_regions(regions: Sequence[Tuple[List[int], List[int]]], scale: Union[float, Tuple[float, float]]) -> Sequence[Tuple[List, List]]: """ From 3507a8ea83de1332adafbe062f1d7cb64e8e2e5d Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Mon, 6 Mar 2023 23:19:51 +0100 Subject: [PATCH 13/69] Use spearman footrule distance as evaluation metric for RO training --- kraken/lib/dataset/__init__.py | 2 +- kraken/lib/dataset/ro.py | 107 +++++++++++++++++++++++++++++- kraken/lib/ro/model.py | 115 ++++++++++++++++++++++++++------- kraken/lib/segmentation.py | 15 ++--- kraken/lib/train.py | 10 +-- 5 files changed, 206 insertions(+), 43 deletions(-) diff --git a/kraken/lib/dataset/__init__.py b/kraken/lib/dataset/__init__.py index 5388cfe6b..c6710d24c 100644 --- a/kraken/lib/dataset/__init__.py +++ b/kraken/lib/dataset/__init__.py @@ -17,5 +17,5 @@ """ from .recognition import ArrowIPCRecognitionDataset, PolygonGTDataset, GroundTruthDataset # NOQA from .segmentation import BaselineSet # NOQA -from .ro import ROSet #NOQA +from .ro import PairWiseROSet, PageWiseROSet #NOQA from .utils import ImageInputTransforms, collate_sequences, global_align, compute_confusions # NOQA diff --git a/kraken/lib/dataset/ro.py b/kraken/lib/dataset/ro.py index 88bb32b73..8bf7d6b72 100644 --- a/kraken/lib/dataset/ro.py +++ b/kraken/lib/dataset/ro.py @@ -43,9 +43,11 @@ logger = logging.getLogger(__name__) -class ROSet(Dataset): +class PairWiseROSet(Dataset): """ Dataset for training a reading order determination model. + + Returns random pairs of lines from the same page. """ def __init__(self, files: Sequence[Union[PathLike, str]] = None, mode: Optional[Literal['alto', 'page', 'xml']] = 'path', @@ -144,3 +146,106 @@ def get_feature_dim(self): def __len__(self): return self._num_pairs + + +class PageWiseROSet(Dataset): + """ + Dataset for training a reading order determination model. + + Returns all lines from the same page. + """ + def __init__(self, files: Sequence[Union[PathLike, str]] = None, + mode: Optional[Literal['alto', 'page', 'xml']] = 'path', + level: Literal['regions', 'baselines'] = 'baselines', + ro_id: Optional[str] = None, + class_mapping: Optional[Dict[str, int]] = None): + """ + Samples pairs lines/regions from XML files for training a reading order + model . + + Args: + mode: Either alto, page, xml, None. In alto, page, and xml + mode the baseline paths and image data is retrieved from an + ALTO/PageXML file. In `None` mode data is iteratively added + through the `add` method. + ro_id: ID of the reading order to sample from. Defaults to + `line_implicit`/`region_implicit`. + """ + super().__init__() + + self.failed_samples = [] + if class_mapping: + self.class_mapping = class_mapping + self.num_classes = len(class_mapping) + 1 + else: + self.num_classes = 1 + self.class_mapping = {} + + self.data = [] + + if mode in ['alto', 'page', 'xml']: + for file in files: + try: + doc = XMLPage(file, filetype=mode) + for tag in doc.tags: + if tag not in self.class_mapping: + self.class_mapping[tag] = self.num_classes + self.num_classes += 1 + except KrakenInputException as e: + files.pop(file) + logger.warning(e) + continue + for file in files: + try: + doc = XMLPage(file, filetype=mode) + if level == 'baselines': + if not ro_id: + ro_id = 'line_implicit' + order = doc.get_sorted_lines(ro_id) + elif level == 'regions': + if not ro_id: + ro_id = 'region_implicit' + order = doc.get_sorted_regions(ro_id) + else: + raise ValueError(f'Invalid RO type {level}') + # traverse RO and substitute features. + h,w = Image.open(doc.imagename).size + sorted_lines = [] + for line in order: + line_coords = np.array(line['baseline']) / (w, h) + line_center = np.mean(line_coords, axis=0) + cl = torch.zeros(self.num_classes, dtype=torch.float) + # if class is not in class mapping default to None class (idx 0) + cl[self.class_mapping.get(line['tags']['type'], 0)] = 1 + line_data = {'type': line['tags']['type'], + 'features': torch.cat((cl, # one hot encoded line type + torch.tensor(line_center, dtype=torch.float), # line center + torch.tensor(line_coords[0, :], dtype=torch.float), # start_point coord + torch.tensor(line_coords[-1, :], dtype=torch.float), # end point coord) + )) + } + sorted_lines.append(line_data) + self.data.append(sorted_lines) + except KrakenInputException as e: + logger.warning(e) + continue + else: + raise Exception('invalid dataset mode') + + def __getitem__(self, idx): + xs = [] + ys = [] + for i in range(len(self.data[idx])): + for j in range(len(self.data[idx])): + if i == j and len(self.data[idx]) != 1: + continue + xs.append(torch.cat((self.data[idx][i]['features'], + self.data[idx][j]['features']))) + ys.append(torch.tensor(0 if i >= j else 1, dtype=torch.float)) + return {'sample': torch.stack(xs), 'target': torch.stack(ys), 'num_lines': len(self.data[idx])} + + def get_feature_dim(self): + return 2 * self.num_classes + 12 + + def __len__(self): + return len(self.data) diff --git a/kraken/lib/ro/model.py b/kraken/lib/ro/model.py index c878f4a83..8cbcabf58 100644 --- a/kraken/lib/ro/model.py +++ b/kraken/lib/ro/model.py @@ -26,13 +26,17 @@ import pytorch_lightning as pl from os import PathLike +from torch.optim import lr_scheduler from dataclasses import dataclass, field from torch.nn import Module from typing import Dict, Optional, Sequence, Union, Any, Literal, List +from pytorch_lightning.callbacks import EarlyStopping, LearningRateMonitor + from kraken.lib import vgsl, default_specs, layers -from kraken.lib.dataset import ROSet +from kraken.lib.dataset import PairWiseROSet, PageWiseROSet from kraken.lib.train import _configure_optimizer_and_lr_scheduler +from kraken.lib.segmentation import _greedy_order_decoder from kraken.lib.ro.layers import MLP from torch.utils.data import DataLoader, random_split, Subset @@ -56,6 +60,10 @@ def save_model(self, filename): self.ptl_module.save_checkpoint(filename) +def spearman_footrule_distance(s, t): + return (s - t).abs().sum() / (0.5 * (len(s) ** 2 - (len(s) % 2))) + + class ROModel(pl.LightningModule): def __init__(self, hyper_params: Dict[str, Any] = None, @@ -92,16 +100,16 @@ def __init__(self, np.random.shuffle(training_data) training_data = training_data[:int(partition*len(training_data))] evaluation_data = training_data[int(partition*len(training_data)):] - train_set = ROSet(training_data, - mode=format_type, - level=level, - ro_id=reading_order) + train_set = PairWiseROSet(training_data, + mode=format_type, + level=level, + ro_id=reading_order) self.train_set = Subset(train_set, range(len(train_set))) - val_set = ROSet(evaluation_data, - mode=format_type, - class_mapping=train_set.class_mapping, - level=level, - ro_id=reading_order) + val_set = PageWiseROSet(evaluation_data, + mode=format_type, + class_mapping=train_set.class_mapping, + level=level, + ro_id=reading_order) self.val_set = Subset(val_set, range(len(val_set))) if len(self.train_set) == 0 or len(self.val_set) == 0: @@ -134,20 +142,34 @@ def forward(self, x): return self.ro_net(x) def validation_step(self, batch, batch_idx): - x, y = batch['sample'], batch['target'] - yhat = self.ro_net(x) - loss = self.criterion(yhat.squeeze(), y) - self.log('val_metric', loss) - return loss + xs, ys, num_lines = batch['sample'], batch['target'], batch['num_lines'] + yhat = self.ro_net(xs).squeeze() + order = torch.zeros((num_lines, num_lines)) + idx = 0 + for i in range(num_lines): + for j in range(num_lines): + if i != j: + order[i, j] = yhat[idx] + idx += 1 + path = _greedy_order_decoder(order) + spearman_dist = spearman_footrule_distance(torch.tensor(range(num_lines)), path) + self.log('val_spearman', spearman_dist) + loss = self.criterion(yhat, ys.squeeze()) + self.log('val_loss', loss) + return {'val_spearman': spearman_dist, 'val_loss': loss} def validation_epoch_end(self, outputs): - val_metric = np.mean([x.cpu() for x in outputs]) + val_metric = np.mean([x['val_spearman'].cpu() for x in outputs]) + val_loss = np.mean([x['val_loss'].cpu() for x in outputs]) + if val_metric < self.best_metric: logger.debug(f'Updating best metric from {self.best_metric} ({self.best_epoch}) to {val_metric} ({self.current_epoch})') self.best_epoch = self.current_epoch self.best_metric = val_metric - logger.info(f'validation run: val_metric {val_metric}') - self.log('val_metric', val_metric, on_step=False, on_epoch=True, prog_bar=True, logger=True) + logger.info(f'validation run: val_spearman {val_metric} val_loss {val_loss}') + self.log('val_spearman', val_metric, on_step=False, on_epoch=True, prog_bar=True, logger=True) + self.log('val_metric', val_metric, on_step=False, on_epoch=True, prog_bar=False, logger=True) + self.log('val_loss', val_loss, on_step=False, on_epoch=True, prog_bar=True, logger=True) def training_step(self, batch, batch_idx): x, y = batch['sample'], batch['target'] @@ -156,12 +178,6 @@ def training_step(self, batch, batch_idx): self.log('loss', loss) return loss - def configure_optimizers(self): - return _configure_optimizer_and_lr_scheduler(self.hparams.hyper_params, - self.ro_net.parameters(), - len_train_set=len(self.train_set), - loss_tracking_mode='min') - def train_dataloader(self): return DataLoader(self.train_set, batch_size=self.hyper_params['batch_size'], @@ -170,9 +186,58 @@ def train_dataloader(self): def val_dataloader(self): return DataLoader(self.val_set, - batch_size=self.hyper_params['batch_size'], + batch_size=1, num_workers=self.num_workers, pin_memory=True) def save_checkpoint(self, filename): self.trainer.save_checkpoint(filename) + + def configure_callbacks(self): + callbacks = [] + if self.hparams.hyper_params['quit'] == 'early': + callbacks.append(EarlyStopping(monitor='val_metric', + mode='min', + patience=self.hparams.hyper_params['lag'], + stopping_threshold=0.0)) + if self.hparams.hyper_params['pl_logger']: + callbacks.append(LearningRateMonitor(logging_interval='step')) + return callbacks + + # configuration of optimizers and learning rate schedulers + # -------------------------------------------------------- + # + # All schedulers are created internally with a frequency of step to enable + # batch-wise learning rate warmup. In lr_scheduler_step() calls to the + # scheduler are then only performed at the end of the epoch. + def configure_optimizers(self): + return _configure_optimizer_and_lr_scheduler(self.hparams.hyper_params, + self.ro_net.parameters(), + len_train_set=len(self.train_set), + loss_tracking_mode='min') + + def optimizer_step(self, epoch, batch_idx, optimizer, optimizer_idx, + optimizer_closure, on_tpu=False, using_native_amp=False, + using_lbfgs=False): + # update params + optimizer.step(closure=optimizer_closure) + + # linear warmup between 0 and the initial learning rate `lrate` in `warmup` + # steps. + if self.hparams.hyper_params['warmup'] and self.trainer.global_step < self.hparams.hyper_params['warmup']: + lr_scale = min(1.0, float(self.trainer.global_step + 1) / self.hparams.hyper_params['warmup']) + for pg in optimizer.param_groups: + pg["lr"] = lr_scale * self.hparams.hyper_params['lrate'] + + def lr_scheduler_step(self, scheduler, optimizer_idx, metric): + if not self.hparams.hyper_params['warmup'] or self.trainer.global_step >= self.hparams.hyper_params['warmup']: + # step OneCycleLR each batch if not in warmup phase + if isinstance(scheduler, lr_scheduler.OneCycleLR): + scheduler.step() + # step every other scheduler epoch-wise + elif self.trainer.is_last_batch: + if metric is None: + scheduler.step() + else: + scheduler.step(metric) + diff --git a/kraken/lib/segmentation.py b/kraken/lib/segmentation.py index 784cb4ceb..96aeb5f26 100644 --- a/kraken/lib/segmentation.py +++ b/kraken/lib/segmentation.py @@ -16,6 +16,7 @@ Processing for baseline segmenter output """ import PIL +import torch import logging import numpy as np import shapely.geometry as geom @@ -33,7 +34,7 @@ from skimage import draw, filters from skimage.graph import MCP_Connect -from skimage.filters import apply_hysteresis_threshold, sobel +from skimage.filters import sobel from skimage.measure import approximate_polygon, subdivide_polygon, regionprops, label from skimage.morphology import skeletonize from skimage.transform import PiecewiseAffineTransform, SimilarityTransform, AffineTransform, warp @@ -50,7 +51,6 @@ logger = logging.getLogger('kraken') __all__ = ['reading_order', - 'denoising_hysteresis_thresh', 'vectorize_lines', 'calculate_polygonal_environment', 'polygonal_reading_order', @@ -131,11 +131,6 @@ def _visit(k): return L -def denoising_hysteresis_thresh(im, low, high, sigma): - im = gaussian_filter(im, sigma) - return apply_hysteresis_threshold(im, low, high) - - def moore_neighborhood(current, backtrack): operations = np.array([[-1, 0], [-1, 1], [0, 1], [1, 1], [1, 0], [1, -1], [0, -1], [-1, -1]]) @@ -879,13 +874,11 @@ def _greedy_order_decoder(P): for i in range(A.shape[0]): A[i,i] = torch.finfo(torch.float).eps best_path = [] - #--- use log(p(R\mid s',s)) to shift multiplication to sum + # use log(p(R\mid s',s)) to shift multiplication to sum lP = torch.log(A) for i in range(N): lP[i,i] = 0 for t in range(N): - #print(lP) - #print("----------------------") for i in range(N): idx = torch.argmax(lP.sum(axis=1)) if idx not in best_path: @@ -893,7 +886,7 @@ def _greedy_order_decoder(P): lP[idx,:] = lP[:,idx] lP[:,idx] = 0 break - return best_path + return torch.tensor(best_path) def scale_regions(regions: Sequence[Tuple[List[int], List[int]]], diff --git a/kraken/lib/train.py b/kraken/lib/train.py index 5f4af6e17..253075253 100644 --- a/kraken/lib/train.py +++ b/kraken/lib/train.py @@ -650,13 +650,13 @@ def optimizer_step(self, epoch, batch_idx, optimizer, optimizer_closure): # linear warmup between 0 and the initial learning rate `lrate` in `warmup` # steps. - if self.hparams.hyper_params.warmup and self.trainer.global_step < self.hparams.hyper_params.warmup: - lr_scale = min(1.0, float(self.trainer.global_step + 1) / self.hparams.hyper_params.warmup) + if self.hparams.hyper_params['warmup'] and self.trainer.global_step < self.hparams.hyper_params['warmup']: + lr_scale = min(1.0, float(self.trainer.global_step + 1) / self.hparams.hyper_params['warmup']) for pg in optimizer.param_groups: - pg["lr"] = lr_scale * self.hparams.hyper_params.lrate + pg["lr"] = lr_scale * self.hparams.hyper_params['lrate'] def lr_scheduler_step(self, scheduler, optimizer_idx, metric): - if not self.hparams.hyper_params.warmup or self.trainer.global_step >= self.hparams.hyper_params.warmup: + if not self.hparams.hyper_params['warmup'] or self.trainer.global_step >= self.hparams.hyper_params['warmup']: # step OneCycleLR each batch if not in warmup phase if isinstance(scheduler, lr_scheduler.OneCycleLR): scheduler.step() @@ -889,7 +889,7 @@ def on_validation_epoch_end(self): self.log('val_mean_acc', mean_accuracy, on_step=False, on_epoch=True, prog_bar=True, logger=True) self.log('val_mean_iu', mean_iu, on_step=False, on_epoch=True, prog_bar=True, logger=True) self.log('val_freq_iu', freq_iu, on_step=False, on_epoch=True, prog_bar=True, logger=True) - self.log('val_metric', mean_iu, on_step=False, on_epoch=True, prog_bar=True, logger=True) + self.log('val_metric', mean_iu, on_step=False, on_epoch=True, prog_bar=False, logger=True) self.val_px_accuracy.reset() self.val_mean_accuracy.reset() From 72caf7b404127c485fcf569421c62e8102121689 Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Mon, 6 Mar 2023 23:39:13 +0100 Subject: [PATCH 14/69] use original implementation hidden size --- kraken/lib/ro/model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kraken/lib/ro/model.py b/kraken/lib/ro/model.py index 8cbcabf58..1187a242b 100644 --- a/kraken/lib/ro/model.py +++ b/kraken/lib/ro/model.py @@ -128,7 +128,7 @@ def __init__(self, self.best_metric = torch.inf logger.info(f'Creating new RO model') - self.ro_net = torch.jit.script(MLP(train_set.get_feature_dim(), 128)) + self.ro_net = torch.jit.script(MLP(train_set.get_feature_dim(), train_set.get_feature_dim() * 2)) if 'file_system' in torch.multiprocessing.get_all_sharing_strategies(): logger.debug('Setting multiprocessing tensor sharing strategy to file_system') From 583d36fe2f6eb862cbeaf6b1152857f0416e8b10 Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Wed, 8 Mar 2023 16:41:50 +0100 Subject: [PATCH 15/69] compute loss with logits --- kraken/lib/ro/layers.py | 4 +--- kraken/lib/ro/model.py | 13 +++++++------ 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/kraken/lib/ro/layers.py b/kraken/lib/ro/layers.py index c86cb0711..93c284f9a 100644 --- a/kraken/lib/ro/layers.py +++ b/kraken/lib/ro/layers.py @@ -18,10 +18,8 @@ def __init__(self, feature_size: int, hidden_size: int): self.fc1 = nn.Linear(feature_size, hidden_size) self.relu = nn.ReLU() self.fc2 = nn.Linear(hidden_size, 1) - self.sigmoid = nn.Sigmoid() def forward(self, x): x = self.fc1(x) x = self.relu(x) - x = self.fc2(x) - return self.sigmoid(x) + return self.fc2(x) diff --git a/kraken/lib/ro/model.py b/kraken/lib/ro/model.py index 1187a242b..6dec0817f 100644 --- a/kraken/lib/ro/model.py +++ b/kraken/lib/ro/model.py @@ -120,7 +120,7 @@ def __init__(self, f'{len(self.val_set)} lines') self.output = output - self.criterion = torch.nn.BCELoss() + self.criterion = torch.nn.BCEWithLogitsLoss() self.num_workers = num_workers @@ -139,11 +139,12 @@ def __init__(self, self.save_hyperparameters() def forward(self, x): - return self.ro_net(x) + return F.sigmoid(self.ro_net(x)) def validation_step(self, batch, batch_idx): xs, ys, num_lines = batch['sample'], batch['target'], batch['num_lines'] - yhat = self.ro_net(xs).squeeze() + logits = self.ro_net(xs).squeeze() + yhat = F.sigmoid(logits) order = torch.zeros((num_lines, num_lines)) idx = 0 for i in range(num_lines): @@ -154,7 +155,7 @@ def validation_step(self, batch, batch_idx): path = _greedy_order_decoder(order) spearman_dist = spearman_footrule_distance(torch.tensor(range(num_lines)), path) self.log('val_spearman', spearman_dist) - loss = self.criterion(yhat, ys.squeeze()) + loss = self.criterion(probits, ys.squeeze()) self.log('val_loss', loss) return {'val_spearman': spearman_dist, 'val_loss': loss} @@ -173,8 +174,8 @@ def validation_epoch_end(self, outputs): def training_step(self, batch, batch_idx): x, y = batch['sample'], batch['target'] - yhat = self.ro_net(x) - loss = self.criterion(yhat.squeeze(), y) + logits = self.ro_net(x) + loss = self.criterion(logits.squeeze(), y) self.log('loss', loss) return loss From 845335c5ee630d70ccf4c842d5fdfb22c5241313 Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Wed, 8 Mar 2023 16:42:54 +0100 Subject: [PATCH 16/69] logits not probits --- kraken/lib/ro/model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kraken/lib/ro/model.py b/kraken/lib/ro/model.py index 6dec0817f..3dff4c828 100644 --- a/kraken/lib/ro/model.py +++ b/kraken/lib/ro/model.py @@ -155,7 +155,7 @@ def validation_step(self, batch, batch_idx): path = _greedy_order_decoder(order) spearman_dist = spearman_footrule_distance(torch.tensor(range(num_lines)), path) self.log('val_spearman', spearman_dist) - loss = self.criterion(probits, ys.squeeze()) + loss = self.criterion(logits, ys.squeeze()) self.log('val_loss', loss) return {'val_spearman': spearman_dist, 'val_loss': loss} From 7af9a721ffe4ad03b1f286648bb58936f8a1c324 Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Thu, 9 Mar 2023 12:32:02 +0100 Subject: [PATCH 17/69] s/h,w/w,h/g Fix normalization factors for RO datasets --- kraken/lib/dataset/ro.py | 4 ++-- kraken/lib/segmentation.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/kraken/lib/dataset/ro.py b/kraken/lib/dataset/ro.py index 8bf7d6b72..b7c6d681d 100644 --- a/kraken/lib/dataset/ro.py +++ b/kraken/lib/dataset/ro.py @@ -105,7 +105,7 @@ def __init__(self, files: Sequence[Union[PathLike, str]] = None, else: raise ValueError(f'Invalid RO type {level}') # traverse RO and substitute features. - h,w = Image.open(doc.imagename).size + w, h = Image.open(doc.imagename).size sorted_lines = [] for line in order: line_coords = np.array(line['baseline']) / (w, h) @@ -209,7 +209,7 @@ def __init__(self, files: Sequence[Union[PathLike, str]] = None, else: raise ValueError(f'Invalid RO type {level}') # traverse RO and substitute features. - h,w = Image.open(doc.imagename).size + w, h = Image.open(doc.imagename).size sorted_lines = [] for line in order: line_coords = np.array(line['baseline']) / (w, h) diff --git a/kraken/lib/segmentation.py b/kraken/lib/segmentation.py index 96aeb5f26..c5c6e5e4b 100644 --- a/kraken/lib/segmentation.py +++ b/kraken/lib/segmentation.py @@ -821,7 +821,7 @@ def neural_reading_order(lines: Sequence[Tuple[List[Tuple[int, int]], List[Tuple Args: lines: List of tuples containing the baseline and its polygonization. - model: torch Module for + model: torch Module for Returns: A reordered input. From c51e28d56b43364826b52e6181e3198d1e00b3a7 Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Thu, 23 Mar 2023 11:28:25 +0100 Subject: [PATCH 18/69] some small new parser fixes --- kraken/lib/xml.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kraken/lib/xml.py b/kraken/lib/xml.py index ebfd5c877..16f253e57 100644 --- a/kraken/lib/xml.py +++ b/kraken/lib/xml.py @@ -480,7 +480,7 @@ def _parse_pointstype(coords: str) -> Sequence[Tuple[float, float]]: class XMLPage(object): - type: Literal['baselines', 'bbox'] == 'baselines' + type: Literal['baselines', 'bbox'] = 'baselines' base_dir: Optional[Literal['L', 'R']] = None imagename: PathLike = None _orders: Dict[str, Dict[str, Any]] = None @@ -945,7 +945,7 @@ def get_lines_by_tag(self, key, value): return {k: v for k, v in self._lines.items() if v['tags'].get(key) == value} def get_lines_by_split(self, split: Literal['train', 'validation', 'test']): - return {k: v for k, v in self._lines.items() if v['tags'].get(key) == split} + return {k: v for k, v in self._lines.items() if v['tags'].get('split') == split} @property def tags(self): From 898a1edfb44f528078a599f4f5a5895b2c2dfaac Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Thu, 23 Mar 2023 11:30:05 +0100 Subject: [PATCH 19/69] more small fixes in lib/segmentation.py --- kraken/lib/segmentation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kraken/lib/segmentation.py b/kraken/lib/segmentation.py index c5c6e5e4b..3f3bee7e7 100644 --- a/kraken/lib/segmentation.py +++ b/kraken/lib/segmentation.py @@ -831,7 +831,7 @@ def neural_reading_order(lines: Sequence[Tuple[List[Tuple[int, int]], List[Tuple features = [] for i in lines: for j in lines: - if i == j and len(children) != 1: + if i == j and len(lines) != 1: continue line_coords_i = np.array(i) / (w, h) line_center_i = np.mean(line_coords_i, axis=0) @@ -855,7 +855,7 @@ def neural_reading_order(lines: Sequence[Tuple[List[Tuple[int, int]], List[Tuple idx += 1 # decode order relation matrix path = _greedy_order_decoder(order) - return ordered_lines + return path def _greedy_order_decoder(P): From 7be2aa02b1f1ef4333ababb1b4110ddc10591ed5 Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Mon, 3 Apr 2023 13:20:42 +0200 Subject: [PATCH 20/69] decoder work --- kraken/lib/segmentation.py | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/kraken/lib/segmentation.py b/kraken/lib/segmentation.py index 3f3bee7e7..5e1f0b96b 100644 --- a/kraken/lib/segmentation.py +++ b/kraken/lib/segmentation.py @@ -51,6 +51,7 @@ logger = logging.getLogger('kraken') __all__ = ['reading_order', + 'neural_reading_order', 'vectorize_lines', 'calculate_polygonal_environment', 'polygonal_reading_order', @@ -833,9 +834,14 @@ def neural_reading_order(lines: Sequence[Tuple[List[Tuple[int, int]], List[Tuple for j in lines: if i == j and len(lines) != 1: continue - line_coords_i = np.array(i) / (w, h) + num_classes = len(model.class_mapping) + 1 + cl_i = torch.zeros(num_classes, dtype=torch.float) + cl_j = torch.zeros(num_classes, dtype=torch.float) + cl_i[model.class_mapping.get(i['tags']['type'], 0)] = 1 + cl_j[model.class_mapping.get(j['tags']['type'], 0)] = 1 + line_coords_i = np.array(i['baseline']) / (w, h) line_center_i = np.mean(line_coords_i, axis=0) - line_coords_j = np.array(j) / (w, h) + line_coords_j = np.array(j['baseline']) / (w, h) line_center_j = np.mean(line_coords_j, axis=0) features.append(torch.cat((cl_i, torch.tensor(line_center_i, dtype=torch.float), # lin @@ -853,7 +859,7 @@ def neural_reading_order(lines: Sequence[Tuple[List[Tuple[int, int]], List[Tuple for j in enumerate(lines): order[i, j] = output[idx] idx += 1 - # decode order relation matrix + # decode order relation matrix path = _greedy_order_decoder(order) return path @@ -863,28 +869,30 @@ def _greedy_order_decoder(P): A greedy decoder of order-relation matrix. For each position in the reading order we select the most probable one, then move to the next position. Most probable for position: - z^{\star}_t = \argmax_{(s,\nu) \ni z^{\star}} - \prod_{(s',\nu') \in z^\star}{\tilde{P}(Y=1\mid s',s)} - \times \prod_{\substack{(s'',\nu'') \ni z^\star\\ - s'' \ne s}}{\tilde{P}(r=0\mid s'',s)}, 1\le t \le n + + .. math:: + z^{\\star}_t = \\argmax_{(s,\\nu) \\ni z^{\\star}} + \\prod_{(s',\\nu') \\in z^\\star}{\\tilde{P}(Y=1\\mid s',s)} + \\times \\prod_{\\substack{(s'',\\nu'') \\ni z^\\star\\ + s'' \\ne s}}{\\tilde{P}(r=0\\mid s'',s)}, 1\\le t \\le n """ A = P + torch.finfo(torch.float).eps N = P.shape[0] A = (A + (1-A).T)/2 for i in range(A.shape[0]): - A[i,i] = torch.finfo(torch.float).eps + A[i, i] = torch.finfo(torch.float).eps best_path = [] # use log(p(R\mid s',s)) to shift multiplication to sum lP = torch.log(A) for i in range(N): - lP[i,i] = 0 + lP[i, i] = 0 for t in range(N): for i in range(N): idx = torch.argmax(lP.sum(axis=1)) if idx not in best_path: best_path.append(idx) - lP[idx,:] = lP[:,idx] - lP[:,idx] = 0 + lP[idx, :] = lP[:, idx] + lP[:, idx] = 0 break return torch.tensor(best_path) From 1c283a99fc57a06a63c1598fb216ed1abebe2c00 Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Mon, 3 Apr 2023 13:20:59 +0200 Subject: [PATCH 21/69] add cls mapping to model params --- kraken/lib/ro/model.py | 1 + 1 file changed, 1 insertion(+) diff --git a/kraken/lib/ro/model.py b/kraken/lib/ro/model.py index 3dff4c828..6bfc4f07f 100644 --- a/kraken/lib/ro/model.py +++ b/kraken/lib/ro/model.py @@ -105,6 +105,7 @@ def __init__(self, level=level, ro_id=reading_order) self.train_set = Subset(train_set, range(len(train_set))) + self.class_mapping = train_set.class_mapping val_set = PageWiseROSet(evaluation_data, mode=format_type, class_mapping=train_set.class_mapping, From 15820b4aab86d8aeec32b4af89dd8d0b0795f9f4 Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Tue, 4 Apr 2023 13:06:29 +0200 Subject: [PATCH 22/69] coreml serialization of RO model --- kraken/lib/ro/layers.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/kraken/lib/ro/layers.py b/kraken/lib/ro/layers.py index 93c284f9a..fb63aad46 100644 --- a/kraken/lib/ro/layers.py +++ b/kraken/lib/ro/layers.py @@ -23,3 +23,36 @@ def forward(self, x): x = self.fc1(x) x = self.relu(x) return self.fc2(x) + + def deserialize(self, name: str, spec) -> None: + """ + Sets the weights of an initialized module from a CoreML protobuf spec. + """ + # extract 1st linear projection parameters + lin = [x for x in spec.neuralNetwork.layers if x.name == '{}_mlp_lin_1'.format(name)][0].innerProduct + weights = torch.Tensor(lin.weights.floatValue).resize_as_(self.fc1.weight.data) + bias = torch.Tensor(lin.bias.floatValue) + self.fc1.weight = torch.nn.Parameter(weights) + self.fc1.bias = torch.nn.Parameter(bias) + # extract 2nd linear projection parameters + lin = [x for x in spec.neuralNetwork.layers if x.name == '{}_mlp_lin_2'.format(name)][0].innerProduct + weights = torch.Tensor(lin.weights.floatValue).resize_as_(self.fc2.weight.data) + bias = torch.Tensor(lin.bias.floatValue) + self.fc2.weight = torch.nn.Parameter(weights) + self.fc2.bias = torch.nn.Parameter(bias) + + def serialize(self, name: str, input: str, builder): + """ + Serializes the module using a NeuralNetworkBuilder. + """ + builder.add_inner_product(f'{name}_mlp_lin_1', self.fc1.weight.data.numpy(), + self.fc1.bias.data.numpy(), + self.feature_size, self.hidden_size, + has_bias=True, input_name=input, output_name=f'{name}_mlp_lin_1') + builder.add_activation(f'{name}_mlp_lin_1_relu', 'RELU', f'{name}_mlp_lin_1', f'{name}_mlp_lin_1_relu') + builder.add_inner_product(f'{name}_mlp_lin_1', self.fc2.weight.data.numpy(), + self.fc2.bias.data.numpy(), + self.hidden_size, 1, + has_bias=True, input_name=f'{name}_mlp_lin_1_relu', output_name=f'{name}_mlp_lin_2') + return name + From b04492831ade4e4139edd16775881878c2e332b7 Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Tue, 4 Apr 2023 13:28:48 +0200 Subject: [PATCH 23/69] some linter cleanup --- kraken/lib/xml.py | 49 +++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/kraken/lib/xml.py b/kraken/lib/xml.py index 16f253e57..7b78082b3 100644 --- a/kraken/lib/xml.py +++ b/kraken/lib/xml.py @@ -26,9 +26,9 @@ from PIL import Image from typing import Union, Dict, Any, Sequence, Tuple, Literal, Optional, List -from os import PathLike from collections import defaultdict from kraken.lib.segmentation import calculate_polygonal_environment +from kraken.lib.exceptions import KrakenInputException logger = logging.getLogger(__name__) @@ -478,6 +478,7 @@ def _parse_pointstype(coords: str) -> Sequence[Tuple[float, float]]: data['tags'] = False return data + class XMLPage(object): type: Literal['baselines', 'bbox'] = 'baselines' @@ -545,10 +546,6 @@ def _parse_alto(self): y_min = int(float(ps.get('VPOS'))) width = int(float(ps.get('WIDTH'))) height = int(float(ps.get('HEIGHT'))) - page_boundary = [(x_min, y_min), - (x_min, y_min + height), - (x_min + width, y_min + height), - (x_min + width, y_min)] # parse tagrefs cls_map = {} @@ -643,7 +640,6 @@ def _parse_alto(self): self._regions = region_data - if len(self._tag_set) > 1: self.has_tags = True else: @@ -839,26 +835,28 @@ def _parse_page(self): # parse explicit reading orders if they exist ro_el = doc.find('.//{*}ReadingOrder') if ro_el is not None: - reading_orders = ro_el.getchildren() - # UnorderedGroup at top-level => treated as multiple reading orders - if len(reading_orders) == 1 and reading_orders[0].tag.endswith('UnorderedGroup'): + reading_orders = ro_el.getchildren() + # UnorderedGroup at top-level => treated as multiple reading orders + if len(reading_orders) == 1 and reading_orders[0].tag.endswith('UnorderedGroup'): reading_orders = reading_orders.getchildren() - def _parse_group(el): - _ro = [] - if el.tag.endswith('UnorderedGroup'): - _ro = [_parse_group(x) for x in el.iterchildren()] - is_total = False - elif el.tag.endswith('OrderedGroup'): - _ro.extend(_parse_group(x) for x in el.iterchildren()) - else: - return el.get('regionRef') - return _ro - - for ro in reading_orders: - is_total = True - self._orders[ro.get('id')] = {'order': _parse_group(ro), - 'is_total': is_total, - 'description': ro.get('caption') if ro.get('caption') else ''} + + def _parse_group(el): + + _ro = [] + if el.tag.endswith('UnorderedGroup'): + _ro = [_parse_group(x) for x in el.iterchildren()] + is_total = False + elif el.tag.endswith('OrderedGroup'): + _ro.extend(_parse_group(x) for x in el.iterchildren()) + else: + return el.get('regionRef') + return _ro + + for ro in reading_orders: + is_total = True + self._orders[ro.get('id')] = {'order': _parse_group(ro), + 'is_total': is_total, + 'description': ro.get('caption') if ro.get('caption') else ''} if len(self._tag_set) > 1: self.has_tags = True @@ -885,6 +883,7 @@ def get_sorted_lines(self, ro='line_implicit'): """ if ro not in self.reading_orders: raise ValueError(f'Unknown reading order {ro}') + def _traverse_ro(el): _ro = [] if isinstance(el, list): From 1326d09394ec7b20e795526aea6400e4f976a3c0 Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Tue, 4 Apr 2023 13:29:17 +0200 Subject: [PATCH 24/69] remove unused failed_sample_threshold --- kraken/ketos/pretrain.py | 1 - 1 file changed, 1 deletion(-) diff --git a/kraken/ketos/pretrain.py b/kraken/ketos/pretrain.py index 1c4bc3d0e..ea5a14e19 100644 --- a/kraken/ketos/pretrain.py +++ b/kraken/ketos/pretrain.py @@ -279,7 +279,6 @@ def pretrain(ctx, batch_size, pad, output, spec, load, freq, quit, epochs, min_epochs=hyper_params['min_epochs'], enable_progress_bar=True if not ctx.meta['verbose'] else False, deterministic=ctx.meta['deterministic'], - failed_sample_threshold=failed_sample_threshold, **val_check_interval) trainer.fit(model, datamodule=data_module) From 98faf22582e65322557b99b6a66cc32b722c22ab Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Tue, 4 Apr 2023 13:30:16 +0200 Subject: [PATCH 25/69] more import fixes --- kraken/lib/dataset/ro.py | 2 +- kraken/lib/ro/layers.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/kraken/lib/dataset/ro.py b/kraken/lib/dataset/ro.py index b7c6d681d..31dee62ec 100644 --- a/kraken/lib/dataset/ro.py +++ b/kraken/lib/dataset/ro.py @@ -36,7 +36,7 @@ from kraken.lib.exceptions import KrakenInputException -__all__ = ['BaselineSet'] +__all__ = ['PairWiseROSet', 'PageWiseROSet'] import logging diff --git a/kraken/lib/ro/layers.py b/kraken/lib/ro/layers.py index fb63aad46..7c15c0868 100644 --- a/kraken/lib/ro/layers.py +++ b/kraken/lib/ro/layers.py @@ -1,6 +1,7 @@ """ Layers for VGSL models """ +import torch from torch import nn # all tensors are ordered NCHW, the "feature" dimension is C, so the output of From f36310bc1f24e1a7c2be1b93dcc9662aa966f34a Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Tue, 4 Apr 2023 14:26:48 +0200 Subject: [PATCH 26/69] syntax fix xml tests --- tests/test_xml.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_xml.py b/tests/test_xml.py index cee436ad8..611959d2e 100644 --- a/tests/test_xml.py +++ b/tests/test_xml.py @@ -27,7 +27,6 @@ def test_page_parsing(self): doc = xml.XMLPage(self.page_doc, filetype='page') self.assertEqual(len(doc.baselines), 97) self.assertEqual(len([item for x in doc.regions.values() for item in x]), 4) - self.assertEqual( def test_alto_parsing(self): """ From a7f1140c5af14084a3c363a21838da410cff856e Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Tue, 4 Apr 2023 14:39:17 +0200 Subject: [PATCH 27/69] xml parsing tests files --- tests/resources/bsb00084914_00007.xml | 1074 +++++++++++++++++++++++++ tests/resources/cPAS-2000.xml | 410 ++++++++++ 2 files changed, 1484 insertions(+) create mode 100644 tests/resources/bsb00084914_00007.xml create mode 100644 tests/resources/cPAS-2000.xml diff --git a/tests/resources/bsb00084914_00007.xml b/tests/resources/bsb00084914_00007.xml new file mode 100644 index 000000000..311751ad1 --- /dev/null +++ b/tests/resources/bsb00084914_00007.xml @@ -0,0 +1,1074 @@ + + + + pixel + + bsb00084914_00007.jpg + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/resources/cPAS-2000.xml b/tests/resources/cPAS-2000.xml new file mode 100644 index 000000000..d9f844121 --- /dev/null +++ b/tests/resources/cPAS-2000.xml @@ -0,0 +1,410 @@ + + + + TRP + 2018-12-24T11:28:19+07:00 + 2019-02-05T09:16:48Z + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 778c20b0b7d7919aef5f1767169ab7d9a2dd7eaf Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Sun, 16 Apr 2023 13:17:33 +0200 Subject: [PATCH 28/69] lightning 2.0 changes to RO code --- kraken/lib/progress.py | 2 +- kraken/lib/ro/model.py | 21 ++++++++++++--------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/kraken/lib/progress.py b/kraken/lib/progress.py index 0284fa865..344864d01 100644 --- a/kraken/lib/progress.py +++ b/kraken/lib/progress.py @@ -128,7 +128,7 @@ def _init_progress(self, trainer): def _get_train_description(self, current_epoch: int) -> str: return f"stage {current_epoch}/" \ - f"{self.trainer.max_epochs if self.trainer.model.hparams['quit'] == 'fixed' else '∞'}" + f"{self.trainer.max_epochs if self.trainer.model.hparams.hyper_params['quit'] == 'fixed' else '∞'}" @dataclass class RichProgressBarTheme: diff --git a/kraken/lib/ro/model.py b/kraken/lib/ro/model.py index 6bfc4f07f..b066bf565 100644 --- a/kraken/lib/ro/model.py +++ b/kraken/lib/ro/model.py @@ -137,6 +137,9 @@ def __init__(self, self.nn = DummyVGSLModel(ptl_module=self) + self.val_losses = [] + self.val_spearman = [] + self.save_hyperparameters() def forward(self, x): @@ -157,12 +160,14 @@ def validation_step(self, batch, batch_idx): spearman_dist = spearman_footrule_distance(torch.tensor(range(num_lines)), path) self.log('val_spearman', spearman_dist) loss = self.criterion(logits, ys.squeeze()) - self.log('val_loss', loss) - return {'val_spearman': spearman_dist, 'val_loss': loss} + self.val_losses.append(loss.cpu()) + self.val_spearman.append(spearman_dist.cpu()) - def validation_epoch_end(self, outputs): - val_metric = np.mean([x['val_spearman'].cpu() for x in outputs]) - val_loss = np.mean([x['val_loss'].cpu() for x in outputs]) + def on_validation_epoch_end(self): + val_metric = np.mean(self.val_spearman) + val_loss = np.mean(self.val_losses) + self.val_spearman.clear() + self.val_losses.clear() if val_metric < self.best_metric: logger.debug(f'Updating best metric from {self.best_metric} ({self.best_epoch}) to {val_metric} ({self.current_epoch})') @@ -218,9 +223,7 @@ def configure_optimizers(self): len_train_set=len(self.train_set), loss_tracking_mode='min') - def optimizer_step(self, epoch, batch_idx, optimizer, optimizer_idx, - optimizer_closure, on_tpu=False, using_native_amp=False, - using_lbfgs=False): + def optimizer_step(self, epoch, batch_idx, optimizer, optimizer_closure): # update params optimizer.step(closure=optimizer_closure) @@ -231,7 +234,7 @@ def optimizer_step(self, epoch, batch_idx, optimizer, optimizer_idx, for pg in optimizer.param_groups: pg["lr"] = lr_scale * self.hparams.hyper_params['lrate'] - def lr_scheduler_step(self, scheduler, optimizer_idx, metric): + def lr_scheduler_step(self, scheduler, metric): if not self.hparams.hyper_params['warmup'] or self.trainer.global_step >= self.hparams.hyper_params['warmup']: # step OneCycleLR each batch if not in warmup phase if isinstance(scheduler, lr_scheduler.OneCycleLR): From c733b85b333198213b354c494ab01b889c7ddc98 Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Sun, 16 Apr 2023 15:42:16 +0200 Subject: [PATCH 29/69] Code for adding RO models to segmentation models --- kraken/ketos/ro.py | 32 ++++++++++++++++++++++++++++++++ kraken/lib/ro/layers.py | 29 ++++++++++++++++++++++------- kraken/lib/ro/model.py | 2 +- kraken/lib/vgsl.py | 21 ++++++++++++++++++++- 4 files changed, 75 insertions(+), 9 deletions(-) diff --git a/kraken/ketos/ro.py b/kraken/ketos/ro.py index 27e4851be..2854ea7fd 100644 --- a/kraken/ketos/ro.py +++ b/kraken/ketos/ro.py @@ -258,3 +258,35 @@ def rotrain(ctx, batch_size, output, load, freq, quit, epochs, min_epochs, lag, logger.info('Moving best model {0}_{1}.mlmodel ({2}) to {0}_best.mlmodel'.format( output, model.best_epoch, model.best_metric)) shutil.copy(f'{output}_{model.best_epoch}.mlmodel', f'{output}_best.mlmodel') + + +@click.command('roadd') +@click.pass_context +@click.option('-o', '--output', show_default=True, type=click.Path(), default='combined_seg.mlmodel', help='Combined output model file') +@click.option('-r', '--ro-model', show_default=True, type=click.Path(exists=True, readable=True), help='Reading order model to load into segmentation model') +@click.option('-i', '--seg-model', show_default=True, type=click.Path(exists=True, readable=True), help='Segmentation model to load') +def rotrain(ctx, output, ro_model, seg_model): + """ + Combines a reading order model with a segmentation model. + """ + from kraken.lib import vgsl + from kraken.lib.ro import ROModel + from kraken.lib.train import KrakenTrainer + + message(f'Adding {ro_model} reading order model to {seg_model}.') + ro_net = ROModel.load_from_checkpoint(ro_model) + message('Line classes known to RO model:') + for k, v in ro_net.class_mapping.items(): + message(f' {k}\t{v}') + seg_net = vgsl.TorchVGSLModel.load_model(seg_model) + if seg_net.model_type != 'segmentation': + raise click.UsageError(f'Model {seg_model} is invalid {seg_net.model_type} model (expected `segmentation`).') + message('Line classes known to segmentation model:') + for k, v in seg_net.user_metadata['class_mapping']['baselines'].items(): + message(f' {k}\t{v}') + if ro_net.class_mapping.keys() != seg_net.user_metadata['class_mapping']['baselines'].keys(): + raise click.UsageError(f'Model {seg_model} and {ro_model} class mappings mismatch.') + + seg_net.aux_layers = {'ro_model': ro_net.ro_net} + message(f'Saving combined model to {output}') + seg_net.save_model(output) diff --git a/kraken/lib/ro/layers.py b/kraken/lib/ro/layers.py index 7c15c0868..87fffc078 100644 --- a/kraken/lib/ro/layers.py +++ b/kraken/lib/ro/layers.py @@ -4,6 +4,8 @@ import torch from torch import nn +from typing import Tuple + # all tensors are ordered NCHW, the "feature" dimension is C, so the output of # an LSTM will be put into C same as the filters of a CNN. @@ -19,24 +21,38 @@ def __init__(self, feature_size: int, hidden_size: int): self.fc1 = nn.Linear(feature_size, hidden_size) self.relu = nn.ReLU() self.fc2 = nn.Linear(hidden_size, 1) + self.feature_size = feature_size + self.hidden_size = hidden_size def forward(self, x): x = self.fc1(x) x = self.relu(x) return self.fc2(x) + def get_shape(self, input: Tuple[int, int, int, int]) -> Tuple[int, int, int, int]: + """ + Calculates the output shape from input 4D tuple NCHW. + """ + return input + + def get_spec(self, name) -> "VGSLBlock": + """ + Generates a VGSL spec block from the layer instance. + """ + return f'[1,0,0,1 RO{{{name}}}{self.feature_size},{self.hidden_size}]' + def deserialize(self, name: str, spec) -> None: """ Sets the weights of an initialized module from a CoreML protobuf spec. """ # extract 1st linear projection parameters - lin = [x for x in spec.neuralNetwork.layers if x.name == '{}_mlp_lin_1'.format(name)][0].innerProduct + lin = [x for x in spec.neuralNetwork.layers if x.name == '{}_mlp_lin_0'.format(name)][0].innerProduct weights = torch.Tensor(lin.weights.floatValue).resize_as_(self.fc1.weight.data) bias = torch.Tensor(lin.bias.floatValue) self.fc1.weight = torch.nn.Parameter(weights) self.fc1.bias = torch.nn.Parameter(bias) # extract 2nd linear projection parameters - lin = [x for x in spec.neuralNetwork.layers if x.name == '{}_mlp_lin_2'.format(name)][0].innerProduct + lin = [x for x in spec.neuralNetwork.layers if x.name == '{}_mlp_lin_1'.format(name)][0].innerProduct weights = torch.Tensor(lin.weights.floatValue).resize_as_(self.fc2.weight.data) bias = torch.Tensor(lin.bias.floatValue) self.fc2.weight = torch.nn.Parameter(weights) @@ -46,14 +62,13 @@ def serialize(self, name: str, input: str, builder): """ Serializes the module using a NeuralNetworkBuilder. """ - builder.add_inner_product(f'{name}_mlp_lin_1', self.fc1.weight.data.numpy(), + builder.add_inner_product(f'{name}_mlp_lin_0', self.fc1.weight.data.numpy(), self.fc1.bias.data.numpy(), self.feature_size, self.hidden_size, - has_bias=True, input_name=input, output_name=f'{name}_mlp_lin_1') - builder.add_activation(f'{name}_mlp_lin_1_relu', 'RELU', f'{name}_mlp_lin_1', f'{name}_mlp_lin_1_relu') + has_bias=True, input_name=input, output_name=f'{name}_mlp_lin_0') + builder.add_activation(f'{name}_mlp_lin_0_relu', 'RELU', f'{name}_mlp_lin_0', f'{name}_mlp_lin_0_relu') builder.add_inner_product(f'{name}_mlp_lin_1', self.fc2.weight.data.numpy(), self.fc2.bias.data.numpy(), self.hidden_size, 1, - has_bias=True, input_name=f'{name}_mlp_lin_1_relu', output_name=f'{name}_mlp_lin_2') + has_bias=True, input_name=f'{name}_mlp_lin_0_relu', output_name=f'{name}_mlp_lin_1') return name - diff --git a/kraken/lib/ro/model.py b/kraken/lib/ro/model.py index b066bf565..4fa957144 100644 --- a/kraken/lib/ro/model.py +++ b/kraken/lib/ro/model.py @@ -129,7 +129,7 @@ def __init__(self, self.best_metric = torch.inf logger.info(f'Creating new RO model') - self.ro_net = torch.jit.script(MLP(train_set.get_feature_dim(), train_set.get_feature_dim() * 2)) + self.ro_net = MLP(train_set.get_feature_dim(), train_set.get_feature_dim() * 2) if 'file_system' in torch.multiprocessing.get_all_sharing_strategies(): logger.debug('Setting multiprocessing tensor sharing strategy to file_system') diff --git a/kraken/lib/vgsl.py b/kraken/lib/vgsl.py index 9f745ceb4..ad762da77 100644 --- a/kraken/lib/vgsl.py +++ b/kraken/lib/vgsl.py @@ -137,7 +137,7 @@ def __init__(self, spec: str) -> None: self.build_dropout, self.build_maxpool, self.build_conv, self.build_output, self.build_reshape, self.build_wav2vec2, self.build_groupnorm, self.build_series, - self.build_parallel] + self.build_parallel, self.build_ro] self.codec = None # type: Optional[PytorchCodec] self.criterion = None # type: Any self.nn = layers.MultiParamSequential() @@ -577,6 +577,25 @@ def build_wav2vec2(self, f'{mask_prob}, negative samples {num_negatives}') return fn.get_shape(input), [VGSLBlock(blocks[idx], m.group('type'), m.group('name'), self.idx)], fn + def build_ro(self, + input: Tuple[int, int, int, int], + blocks: List[str], + idx: int) -> Union[Tuple[None, None, None], Tuple[Tuple[int, int, int, int], str, Callable]]: + """ + Builds a RO determination layer. + """ + pattern = re.compile(r'(?PRO)(?P{\w+})(?P\d+),(?P\d+)') + m = pattern.match(blocks[idx]) + if not m: + return None, None, None + feature_size = int(m.group('feature_size')) + hidden_size = int(m.group('hidden_size')) + from kraken.lib import ro + fn = ro.layers.MLP(feature_size, hidden_size) + self.idx += 1 + logger.debug(f'{self.idx}\t\tro\tfeatures {feature_size}, hidden_size {hidden_size}') + return fn.get_shape(input), [VGSLBlock(blocks[idx], m.group('type'), m.group('name'), self.idx)], fn + def build_conv(self, input: Tuple[int, int, int, int], blocks: List[str], From d4947afa40c0dfab016919023371a83c9bcd3191 Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Sun, 16 Apr 2023 18:41:09 +0200 Subject: [PATCH 30/69] Working inference --- kraken/blla.py | 29 +++++++++++++++++++++++++---- kraken/ketos/__init__.py | 3 ++- kraken/ketos/ro.py | 3 ++- kraken/lib/ro/layers.py | 1 + kraken/lib/segmentation.py | 33 +++++++++++++++++++++------------ 5 files changed, 51 insertions(+), 18 deletions(-) diff --git a/kraken/blla.py b/kraken/blla.py index 84d7fafca..19b3fa0ba 100644 --- a/kraken/blla.py +++ b/kraken/blla.py @@ -29,6 +29,7 @@ import torch.nn.functional as F import torchvision.transforms as tf +from functools import partial from typing import Optional, Dict, Callable, Union, List, Any, Tuple from scipy.ndimage import gaussian_filter @@ -38,6 +39,7 @@ from kraken.lib.util import is_bitonal, get_im_str from kraken.lib.exceptions import KrakenInputException, KrakenInvalidModelException from kraken.lib.segmentation import (polygonal_reading_order, + neural_reading_order, vectorize_lines, vectorize_regions, scale_polygonal_lines, calculate_polygonal_environment, @@ -73,8 +75,6 @@ def compute_segmentation_map(im: PIL.Image.Image, Raises: KrakenInputException: When given an invalid mask. """ - im_str = get_im_str(im) - logger.info(f'Segmenting {im_str}') if model.input[1] == 1 and model.one_channel_mode == '1' and not is_bitonal(im): logger.warning('Running binary model on non-binary input image ' @@ -250,7 +250,7 @@ def vec_lines(heatmap: torch.Tensor, def segment(im: PIL.Image.Image, text_direction: str = 'horizontal-lr', mask: Optional[np.ndarray] = None, - reading_order_fn: Callable = polygonal_reading_order, + reading_order_fn: Optional[Callable] = None, model: Union[List[vgsl.TorchVGSLModel], vgsl.TorchVGSLModel] = None, device: str = 'cpu', raise_on_error: bool = False, @@ -271,7 +271,15 @@ def segment(im: PIL.Image.Image, detection. reading_order_fn: Function to determine the reading order. Has to accept a list of tuples (baselines, polygon) and a - text direction (`lr` or `rl`). + text direction (`lr` or `rl`). If None is given it + defaults to either + :func:`kraken.lib.segmentation.polygonal_reading_order` + or + :func:`kraken.lib.segmentation.neural_reading_order` + depending on the presence of a neural reading order + net in the segmentation model. If multiple + segmentation models are given and more than one + contains an RO net the first one will be used. model: One or more TorchVGSLModel containing a segmentation model. If none is given a default model will be loaded. device: The target device to run the neural network on. @@ -313,6 +321,18 @@ def segment(im: PIL.Image.Image, if isinstance(model, vgsl.TorchVGSLModel): model = [model] + # determine which reading order function to use + if not reading_order_fn: + reading_order_fn = polygonal_reading_order + for x in model: + if 'ro_model' in x.aux_layers: + logger.info(f'Using reading order model found in segmentation model {x}.') + reading_order_fn = partial(neural_reading_order, + model=x.aux_layers['ro_model'], + im_size=im.size, + class_mapping=x.user_metadata['ro_class_mapping']) + break + for nn in model: if nn.model_type != 'segmentation': raise KrakenInvalidModelException(f'Invalid model type {nn.model_type} for {nn}') @@ -340,6 +360,7 @@ def segment(im: PIL.Image.Image, # convert back to net scale suppl_obj = scale_regions(suppl_obj, 1/rets['scale']) line_regs = scale_regions(line_regs, 1/rets['scale']) + lines = vec_lines(**rets, regions=line_regs, reading_order_fn=reading_order_fn, diff --git a/kraken/ketos/__init__.py b/kraken/ketos/__init__.py index 4b7087dc4..a8ddfe9a2 100644 --- a/kraken/ketos/__init__.py +++ b/kraken/ketos/__init__.py @@ -34,7 +34,7 @@ from .repo import publish from .segmentation import segtrain, segtest from .transcription import extract, transcription -from .ro import rotrain +from .ro import rotrain, roadd APP_NAME = 'kraken' @@ -78,6 +78,7 @@ def cli(ctx, verbose, seed, deterministic): cli.add_command(segtest) cli.add_command(publish) cli.add_command(rotrain) +cli.add_command(roadd) # deprecated commands cli.add_command(line_generator) diff --git a/kraken/ketos/ro.py b/kraken/ketos/ro.py index 2854ea7fd..1dcc0856f 100644 --- a/kraken/ketos/ro.py +++ b/kraken/ketos/ro.py @@ -265,7 +265,7 @@ def rotrain(ctx, batch_size, output, load, freq, quit, epochs, min_epochs, lag, @click.option('-o', '--output', show_default=True, type=click.Path(), default='combined_seg.mlmodel', help='Combined output model file') @click.option('-r', '--ro-model', show_default=True, type=click.Path(exists=True, readable=True), help='Reading order model to load into segmentation model') @click.option('-i', '--seg-model', show_default=True, type=click.Path(exists=True, readable=True), help='Segmentation model to load') -def rotrain(ctx, output, ro_model, seg_model): +def roadd(ctx, output, ro_model, seg_model): """ Combines a reading order model with a segmentation model. """ @@ -288,5 +288,6 @@ def rotrain(ctx, output, ro_model, seg_model): raise click.UsageError(f'Model {seg_model} and {ro_model} class mappings mismatch.') seg_net.aux_layers = {'ro_model': ro_net.ro_net} + seg_net.user_metadata['ro_class_mapping'] = ro_net.class_mapping message(f'Saving combined model to {output}') seg_net.save_model(output) diff --git a/kraken/lib/ro/layers.py b/kraken/lib/ro/layers.py index 87fffc078..a18f3de1e 100644 --- a/kraken/lib/ro/layers.py +++ b/kraken/lib/ro/layers.py @@ -23,6 +23,7 @@ def __init__(self, feature_size: int, hidden_size: int): self.fc2 = nn.Linear(hidden_size, 1) self.feature_size = feature_size self.hidden_size = hidden_size + self.class_mapping = None def forward(self, x): x = self.fc1(x) diff --git a/kraken/lib/segmentation.py b/kraken/lib/segmentation.py index 5e1f0b96b..104dd00b9 100644 --- a/kraken/lib/segmentation.py +++ b/kraken/lib/segmentation.py @@ -20,6 +20,7 @@ import logging import numpy as np import shapely.geometry as geom +import torch.nn.functional as F from collections import defaultdict @@ -814,8 +815,11 @@ def is_in_region(line, region) -> bool: def neural_reading_order(lines: Sequence[Tuple[List[Tuple[int, int]], List[Tuple[int, int]]]], - im_size: Tuple[int, int], - model): + text_direction: str = 'lr', + regions: Optional[Sequence[List[Tuple[int, int]]]] = None, + im_size: Tuple[int, int] = None, + model: 'TorchVGSLModel' = None, + class_mapping: Dict[str, int] = None) -> Sequence[Tuple[List[Tuple[int, int]], List[Tuple[int, int]]]]: """ Given a list of baselines and regions, calculates the correct reading order and applies it to the input. @@ -834,14 +838,14 @@ def neural_reading_order(lines: Sequence[Tuple[List[Tuple[int, int]], List[Tuple for j in lines: if i == j and len(lines) != 1: continue - num_classes = len(model.class_mapping) + 1 + num_classes = len(class_mapping) + 1 cl_i = torch.zeros(num_classes, dtype=torch.float) cl_j = torch.zeros(num_classes, dtype=torch.float) - cl_i[model.class_mapping.get(i['tags']['type'], 0)] = 1 - cl_j[model.class_mapping.get(j['tags']['type'], 0)] = 1 - line_coords_i = np.array(i['baseline']) / (w, h) + cl_i[class_mapping.get(i[0], 0)] = 1 + cl_j[class_mapping.get(j[0], 0)] = 1 + line_coords_i = np.array(i[1]) / (w, h) line_center_i = np.mean(line_coords_i, axis=0) - line_coords_j = np.array(j['baseline']) / (w, h) + line_coords_j = np.array(j[1]) / (w, h) line_center_j = np.mean(line_coords_j, axis=0) features.append(torch.cat((cl_i, torch.tensor(line_center_i, dtype=torch.float), # lin @@ -851,17 +855,22 @@ def neural_reading_order(lines: Sequence[Tuple[List[Tuple[int, int]], List[Tuple torch.tensor(line_center_j, dtype=torch.float), # lin torch.tensor(line_coords_j[0, :], dtype=torch.float), torch.tensor(line_coords_j[-1, :], dtype=torch.float)))) - features = torch.cat(features) - output = model(features) + features = torch.stack(features) + output = F.sigmoid(model(features)) + order = torch.zeros((len(lines), len(lines))) idx = 0 - for i in enumerate(lines): - for j in enumerate(lines): + for i in range(len(lines)): + for j in range(len(lines)): + if i == j and len(lines) != 1: + continue order[i, j] = output[idx] idx += 1 # decode order relation matrix path = _greedy_order_decoder(order) - return path + # reorder lines + lines = [lines[idx] for idx in path] + return lines def _greedy_order_decoder(P): From 9aca246f875f48f7bae8641de3a0d82cf469ef50 Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Fri, 21 Apr 2023 11:40:25 +0200 Subject: [PATCH 31/69] Segmentation data class in blla/pageseg --- kraken/blla.py | 133 +++++++++++++++++++++---------------- kraken/kraken.py | 26 ++++---- kraken/lib/segmentation.py | 47 ++++++++----- kraken/lib/xml.py | 2 +- kraken/pageseg.py | 11 +-- kraken/serialization.py | 12 ++-- kraken/transcribe.py | 2 - 7 files changed, 131 insertions(+), 102 deletions(-) diff --git a/kraken/blla.py b/kraken/blla.py index 19b3fa0ba..767ec6867 100644 --- a/kraken/blla.py +++ b/kraken/blla.py @@ -29,8 +29,7 @@ import torch.nn.functional as F import torchvision.transforms as tf -from functools import partial -from typing import Optional, Dict, Callable, Union, List, Any, Tuple +from typing import Optional, Dict, Callable, Union, List, Any, Tuple, Literal from scipy.ndimage import gaussian_filter from skimage.filters import sobel @@ -38,7 +37,8 @@ from kraken.lib import vgsl, dataset from kraken.lib.util import is_bitonal, get_im_str from kraken.lib.exceptions import KrakenInputException, KrakenInvalidModelException -from kraken.lib.segmentation import (polygonal_reading_order, +from kraken.lib.segmentation import (Segmentation, + polygonal_reading_order, neural_reading_order, vectorize_lines, vectorize_regions, scale_polygonal_lines, @@ -163,7 +163,6 @@ def vec_lines(heatmap: torch.Tensor, cls_map: Dict[str, Dict[str, int]], scale: float, text_direction: str = 'horizontal-lr', - reading_order_fn: Callable = polygonal_reading_order, regions: List[np.ndarray] = None, scal_im: np.ndarray = None, suppl_obj: List[np.ndarray] = None, @@ -181,7 +180,6 @@ def vec_lines(heatmap: torch.Tensor, scale: Scaling factor between heatmap and unscaled input image. text_direction: Text directions used as hints in the reading order algorithm. - reading_order_fn: Reading order calculation function. regions: Regions to be used as boundaries during polygonization and atomic blocks during reading order determination for lines contained within. @@ -242,15 +240,13 @@ def vec_lines(heatmap: torch.Tensor, logger.debug('Scaling vectorized lines') sc = scale_polygonal_lines([x[1:] for x in lines], scale) lines = list(zip([x[0] for x in lines], [x[0] for x in sc], [x[1] for x in sc])) - logger.debug('Reordering baselines') - lines = reading_order_fn(lines=lines, regions=regions, text_direction=text_direction[-2:]) return [{'tags': {'type': bl_type}, 'baseline': bl, 'boundary': pl} for bl_type, bl, pl in lines] def segment(im: PIL.Image.Image, - text_direction: str = 'horizontal-lr', + text_direction: Literal['horizontal-lr', 'horizontal-rl', 'vertical-lr', 'vertical-rl'] = 'horizontal-lr', mask: Optional[np.ndarray] = None, - reading_order_fn: Optional[Callable] = None, + reading_order_fn: Callable = polygonal_reading_order, model: Union[List[vgsl.TorchVGSLModel], vgsl.TorchVGSLModel] = None, device: str = 'cpu', raise_on_error: bool = False, @@ -271,15 +267,7 @@ def segment(im: PIL.Image.Image, detection. reading_order_fn: Function to determine the reading order. Has to accept a list of tuples (baselines, polygon) and a - text direction (`lr` or `rl`). If None is given it - defaults to either - :func:`kraken.lib.segmentation.polygonal_reading_order` - or - :func:`kraken.lib.segmentation.neural_reading_order` - depending on the presence of a neural reading order - net in the segmentation model. If multiple - segmentation models are given and more than one - contains an RO net the first one will be used. + text direction (`lr` or `rl`). model: One or more TorchVGSLModel containing a segmentation model. If none is given a default model will be loaded. device: The target device to run the neural network on. @@ -288,31 +276,36 @@ def segment(im: PIL.Image.Image, autocast: Runs the model with automatic mixed precision Returns: - A dictionary containing the text direction and under the key 'lines' a - list of reading order sorted baselines (polylines) and their respective - polygonal boundaries. The last and first point of each boundary polygon - are connected. + A :class:`kraken.lib.blla.Segmentation` class containing reading order + sorted baselines (polylines) and their respective polygonal boundaries. + The format of the line and region records is shown below. The last and + first point of each boundary polygon are connected. .. code-block:: :force: - {'text_direction': '$dir', - 'type': 'baseline', - 'lines': [ - {'baseline': [[x0, y0], [x1, y1], ..., [x_n, y_n]], 'boundary': [[x0, y0, x1, y1], ... [x_m, y_m]]}, - {'baseline': [[x0, ...]], 'boundary': [[x0, ...]]} - ] - 'regions': [ - {'region': [[x0, y0], [x1, y1], ..., [x_n, y_n]], 'type': 'image'}, - {'region': [[x0, ...]], 'type': 'text'} - ] - } + 'lines': [ + {'baseline': [[x0, y0], [x1, y1], ..., [x_n, y_n]], 'boundary': [[x0, y0, x1, y1], ... [x_m, y_m]]}, + {'baseline': [[x0, ...]], 'boundary': [[x0, ...]]} + ] + 'regions': [ + {'region': [[x0, y0], [x1, y1], ..., [x_n, y_n]], 'type': 'image'}, + {'region': [[x0, ...]], 'type': 'text'} + ] Raises: KrakenInvalidModelException: if the given model is not a valid segmentation model. KrakenInputException: if the mask is not bitonal or does not match the image size. + + Notes: + Multi-model operation is most useful for combining one or more region + detection models and one text line model. Detected lines from all + models are simply combined without any merging or duplicate detection + so the chance of the same line appearing multiple times in the output + are high. In addition, neural reading order determination is disabled + when more than one model outputs lines. """ if model is None: logger.info('No segmentation model given. Loading default model.') @@ -321,18 +314,6 @@ def segment(im: PIL.Image.Image, if isinstance(model, vgsl.TorchVGSLModel): model = [model] - # determine which reading order function to use - if not reading_order_fn: - reading_order_fn = polygonal_reading_order - for x in model: - if 'ro_model' in x.aux_layers: - logger.info(f'Using reading order model found in segmentation model {x}.') - reading_order_fn = partial(neural_reading_order, - model=x.aux_layers['ro_model'], - im_size=im.size, - class_mapping=x.user_metadata['ro_class_mapping']) - break - for nn in model: if nn.model_type != 'segmentation': raise KrakenInvalidModelException(f'Invalid model type {nn.model_type} for {nn}') @@ -342,6 +323,12 @@ def segment(im: PIL.Image.Image, im_str = get_im_str(im) logger.info(f'Segmenting {im_str}') + lines = [] + order = None + regions = {} + multi_lines = False + # flag to indicate that multiple models produced line output -> disable + # neural reading order for net in model: if 'topline' in net.user_metadata: loc = {None: 'center', @@ -349,11 +336,12 @@ def segment(im: PIL.Image.Image, False: 'bottom'}[net.user_metadata['topline']] logger.debug(f'Baseline location: {loc}') rets = compute_segmentation_map(im, mask, net, device, autocast=autocast) - regions = vec_regions(**rets) + _regions = vec_regions(**rets) + # flatten regions for line ordering/fetch bounding regions line_regs = [] suppl_obj = [] - for cls, regs in regions.items(): + for cls, regs in _regions.items(): line_regs.extend(regs) if rets['bounding_regions'] is not None and cls in rets['bounding_regions']: suppl_obj.extend(regs) @@ -361,21 +349,48 @@ def segment(im: PIL.Image.Image, suppl_obj = scale_regions(suppl_obj, 1/rets['scale']) line_regs = scale_regions(line_regs, 1/rets['scale']) - lines = vec_lines(**rets, - regions=line_regs, - reading_order_fn=reading_order_fn, - text_direction=text_direction, - suppl_obj=suppl_obj, - topline=net.user_metadata['topline'] if 'topline' in net.user_metadata else False, - raise_on_error=raise_on_error) + _lines = vec_lines(**rets, + regions=line_regs, + text_direction=text_direction, + suppl_obj=suppl_obj, + topline=net.user_metadata['topline'] if 'topline' in net.user_metadata else False, + raise_on_error=raise_on_error) + + if 'ro_model' in net.aux_layers: + logger.info(f'Using reading order model found in segmentation model {net}.') + _order = neural_reading_order(lines=_lines, + regions=regions, + text_direction=text_direction[-2:], + model=net.aux_layers['ro_model'], + im_size=im.size, + class_mapping=net.user_metadata['ro_class_mapping']) + else: + _order = None + + if _lines and lines or multi_lines: + multi_lines = True + order = None + logger.warning('Multiple models produced line output. This is ' + 'likely unintended. Suppressing neural reading ' + 'order.') + else: + order = _order + + lines.extend(_lines) + + # reorder lines + logger.debug(f'Reordering baselines with main RO function {reading_order_fn}.') + basic_lo = reading_order_fn(lines=lines, regions=regions, text_direction=text_direction[-2:]) + lines = [lines[idx] for idx in basic_lo] if len(rets['cls_map']['baselines']) > 1: script_detection = True else: script_detection = False - return {'text_direction': text_direction, - 'type': 'baselines', - 'lines': lines, - 'regions': regions, - 'script_detection': script_detection} + return Segmentation(text_direction=text_direction, + type='baselines', + lines=lines, + regions=regions, + script_detection=script_detection, + line_orders=[order]) diff --git a/kraken/kraken.py b/kraken/kraken.py index 6788cdeef..a45e47f75 100644 --- a/kraken/kraken.py +++ b/kraken/kraken.py @@ -21,6 +21,7 @@ import os import warnings import logging +import dataclasses import pkg_resources from typing import Dict, Union, List, cast, Any, IO, Callable @@ -57,15 +58,9 @@ def message(msg: str, **styles) -> None: def get_input_parser(type_str: str) -> Callable[[str], Dict[str, Any]]: - if type_str == 'alto': - from kraken.lib.xml import parse_alto - return parse_alto - elif type_str == 'page': - from kraken.lib.xml import parse_page - return parse_page - elif type_str == 'xml': - from kraken.lib.xml import parse_xml - return parse_xml + if type_str in ['alto', 'page', 'xml']: + from kraken.lib.xml import XMLPage + return XMLPage elif type_str == 'image': return Image.open @@ -78,7 +73,7 @@ def binarizer(threshold, zoom, escale, border, perc, range, low, high, input, ou ctx = click.get_current_context() if ctx.meta['first_process']: if ctx.meta['input_format_type'] != 'image': - input = get_input_parser(ctx.meta['input_format_type'])(input)['image'] + input = get_input_parser(ctx.meta['input_format_type'])(input).imagename ctx.meta['first_process'] = False else: raise click.UsageError('Binarization has to be the initial process.') @@ -131,7 +126,7 @@ def segmenter(legacy, model, text_direction, scale, maxcolseps, black_colseps, if ctx.meta['first_process']: if ctx.meta['input_format_type'] != 'image': - input = get_input_parser(ctx.meta['input_format_type'])(input)['image'] + input = get_input_parser(ctx.meta['input_format_type'])(input).imagename ctx.meta['first_process'] = False if 'base_image' not in ctx.meta: @@ -179,7 +174,7 @@ def segmenter(legacy, model, text_direction, scale, maxcolseps, black_colseps, else: with click.open_file(output, 'w') as fp: fp = cast(IO[Any], fp) - json.dump(res, fp) + json.dump(dataclasses.asdict(res), fp) message('\u2713', fg='green') @@ -203,7 +198,12 @@ def recognizer(model, pad, no_segmentation, bidi_reordering, tags_ignore, input, if doc['base_dir'] and bidi_reordering is True: message(f'Setting base text direction for BiDi reordering to {doc["base_dir"]} (from XML input file)') bidi_reordering = doc['base_dir'] - bounds = doc + bounds = {'text_direction': 'horizontal-lr', + 'tags': True, + 'lines': doc.get_sorted_lines(), + 'regions': doc.get_sorted_regions(), + 'type': 'baselines', + 'image': doc.imagename} try: im = Image.open(ctx.meta['base_image']) except IOError as e: diff --git a/kraken/lib/segmentation.py b/kraken/lib/segmentation.py index 104dd00b9..20a14e973 100644 --- a/kraken/lib/segmentation.py +++ b/kraken/lib/segmentation.py @@ -22,6 +22,7 @@ import shapely.geometry as geom import torch.nn.functional as F +from dataclasses import dataclass from collections import defaultdict from PIL import Image @@ -40,7 +41,7 @@ from skimage.morphology import skeletonize from skimage.transform import PiecewiseAffineTransform, SimilarityTransform, AffineTransform, warp -from typing import List, Tuple, Union, Dict, Any, Sequence, Optional +from typing import List, Tuple, Union, Dict, Any, Sequence, Optional, Literal from kraken.lib import default_specs from kraken.lib.exceptions import KrakenInputException @@ -59,10 +60,21 @@ 'scale_polygonal_lines', 'scale_regions', 'compute_polygon_section', - 'extract_polygons'] + 'extract_polygons', + 'Segmentation'] -def reading_order(lines: Sequence[Tuple[slice, slice]], text_direction: str = 'lr') -> np.ndarray: +@dataclass +class Segmentation: + type: Literal['baselines', 'bbox'] + text_direction: Literal['horizontal-lr', 'horizontal-rl', 'vertical-lr', 'vertical-rl'] + script_detection: bool + lines: List + regions: Dict[str, List] + line_orders: List[List[int]] + + +def reading_order(lines: Sequence[Tuple[slice, slice]], text_direction: Literal['lr', 'rl'] = 'lr') -> np.ndarray: """Given the list of lines (a list of 2D slices), computes the partial reading order. The output is a binary 2D array such that order[i,j] is true if line i comes before line j @@ -737,9 +749,9 @@ def calculate_polygonal_environment(im: PIL.Image.Image = None, return polygons -def polygonal_reading_order(lines: Sequence[Tuple[List[Tuple[int, int]], List[Tuple[int, int]]]], - text_direction: str = 'lr', - regions: Optional[Sequence[List[Tuple[int, int]]]] = None) -> Sequence[Tuple[List[Tuple[int, int]], List[Tuple[int, int]]]]: +def polygonal_reading_order(lines: Sequence[Dict], + text_direction: Literal['lr', 'rl'] = 'lr', + regions: Optional[Sequence[List[Tuple[int, int]]]] = None) -> Sequence[int]: """ Given a list of baselines and regions, calculates the correct reading order and applies it to the input. @@ -752,8 +764,10 @@ def polygonal_reading_order(lines: Sequence[Tuple[List[Tuple[int, int]], List[Tu Can be 'lr' or 'rl' Returns: - A reordered input. + The indices of the ordered input. """ + lines = [(line['tags']['type'], line['baseline'], line['boundary']) for line in lines] + bounds = [] if regions is not None: r = [geom.Polygon(reg) for reg in regions] @@ -789,13 +803,13 @@ def polygonal_reading_order(lines: Sequence[Tuple[List[Tuple[int, int]], List[Tu lsort = topsort(order) sidz = sorted(indizes.keys()) lsort = [sidz[i] for i in lsort] - ordered_lines = [] + ordered_idxs = [] for i in lsort: if indizes[i][0] == 'line': - ordered_lines.append(indizes[i][1]) + ordered_idxs.append(i) else: - ordered_lines.extend(lines[x] for x in intra_region_order[indizes[i][1]]) - return ordered_lines + ordered_idxs.extend(intra_region_order[indizes[i][1]]) + return ordered_idxs def is_in_region(line, region) -> bool: @@ -814,12 +828,12 @@ def is_in_region(line, region) -> bool: return region.contains(l_obj) -def neural_reading_order(lines: Sequence[Tuple[List[Tuple[int, int]], List[Tuple[int, int]]]], +def neural_reading_order(lines: Sequence[Dict], text_direction: str = 'lr', regions: Optional[Sequence[List[Tuple[int, int]]]] = None, im_size: Tuple[int, int] = None, model: 'TorchVGSLModel' = None, - class_mapping: Dict[str, int] = None) -> Sequence[Tuple[List[Tuple[int, int]], List[Tuple[int, int]]]]: + class_mapping: Dict[str, int] = None) -> Sequence[int]: """ Given a list of baselines and regions, calculates the correct reading order and applies it to the input. @@ -829,8 +843,9 @@ def neural_reading_order(lines: Sequence[Tuple[List[Tuple[int, int]], List[Tuple model: torch Module for Returns: - A reordered input. + The indices of the ordered input. """ + lines = [(line['tags']['type'], line['baseline'], line['boundary']) for line in lines] # construct all possible pairs h, w = im_size features = [] @@ -868,9 +883,7 @@ def neural_reading_order(lines: Sequence[Tuple[List[Tuple[int, int]], List[Tuple idx += 1 # decode order relation matrix path = _greedy_order_decoder(order) - # reorder lines - lines = [lines[idx] for idx in path] - return lines + return path def _greedy_order_decoder(P): diff --git a/kraken/lib/xml.py b/kraken/lib/xml.py index 7b78082b3..135c50560 100644 --- a/kraken/lib/xml.py +++ b/kraken/lib/xml.py @@ -32,7 +32,7 @@ logger = logging.getLogger(__name__) -__all__ = ['parse_xml', 'parse_page', 'parse_alto', 'preparse_xml_data'] +__all__ = ['XMLPage', 'parse_xml', 'parse_page', 'parse_alto', 'preparse_xml_data'] # fallback mapping between PAGE region types and tags page_regions = {'TextRegion': 'text', diff --git a/kraken/pageseg.py b/kraken/pageseg.py index 3f52ba339..355af4f5d 100644 --- a/kraken/pageseg.py +++ b/kraken/pageseg.py @@ -29,7 +29,7 @@ from kraken.lib import morph, sl from kraken.lib.util import pil2array, is_bitonal, get_im_str from kraken.lib.exceptions import KrakenInputException -from kraken.lib.segmentation import reading_order, topsort +from kraken.lib.segmentation import reading_order, topsort, Segmentation __all__ = ['segment'] @@ -424,6 +424,9 @@ def segment(im, pad = (pad, pad) lines = [(max(x[0]-pad[0], 0), x[1], min(x[2]+pad[1], im.size[0]), x[3]) for x in lines] - return {'text_direction': text_direction, - 'boxes': rotate_lines(lines, 360-angle, offset).tolist(), - 'script_detection': False} + return Segmentation(text_direction=text_direction, + type='bbox', + regions=None, + line_orders=None, + lines=rotate_lines(lines, 360-angle, offset).tolist(), + script_detection=False) diff --git a/kraken/serialization.py b/kraken/serialization.py index c741923da..da644c948 100644 --- a/kraken/serialization.py +++ b/kraken/serialization.py @@ -25,7 +25,7 @@ from kraken.rpred import BaselineOCRRecord, BBoxOCRRecord, ocr_record from kraken.lib.util import make_printable -from kraken.lib.segmentation import is_in_region +from kraken.lib.segmentation import is_in_region, Segmentation from typing import Union, List, Tuple, Iterable, Optional, Sequence, Dict, Any, Literal @@ -246,7 +246,7 @@ def _load_template(name): return tmpl.render(page=page, metadata=metadata) -def serialize_segmentation(segresult: Dict[str, Any], +def serialize_segmentation(segresult: Segmentation, image_name: Union[PathLike, str] = None, image_size: Tuple[int, int] = (0, 0), template: Union[PathLike, str] = 'alto', @@ -266,18 +266,18 @@ def serialize_segmentation(segresult: Dict[str, Any], Returns: (str) rendered template. """ - if 'type' in segresult and segresult['type'] == 'baselines': - records = [BaselineOCRRecord('', (), (), bl) for bl in segresult['lines']] + if segresult.type == 'baselines': + records = [BaselineOCRRecord('', (), (), bl) for bl in segresult.lines] else: records = [] - for line in segresult['boxes']: + for line in segresult.lines: xmin, xmax = min(line[::2]), max(line[::2]) ymin, ymax = min(line[1::2]), max(line[1::2]) records.append(BBoxOCRRecord('', (), (), ((xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin)))) return serialize(records, image_name=image_name, image_size=image_size, - regions=segresult['regions'] if 'regions' in segresult else None, + regions=segresult.regions, template=template, template_source=template_source, processing_steps=processing_steps) diff --git a/kraken/transcribe.py b/kraken/transcribe.py index e9a8067b8..5b39ee2f7 100644 --- a/kraken/transcribe.py +++ b/kraken/transcribe.py @@ -18,8 +18,6 @@ from kraken.lib.exceptions import KrakenInputException from kraken.lib.util import get_im_str -from typing import List - from jinja2 import Environment, PackageLoader from io import BytesIO From cbc67cd5925236419b992e091908f89226dc1d58 Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Fri, 21 Apr 2023 12:02:33 +0200 Subject: [PATCH 32/69] Switch over rpred.* to Segmentation class --- kraken/rpred.py | 99 +++++++++++++++++++++++-------------------------- 1 file changed, 46 insertions(+), 53 deletions(-) diff --git a/kraken/rpred.py b/kraken/rpred.py index ae80ffa63..33375cc12 100644 --- a/kraken/rpred.py +++ b/kraken/rpred.py @@ -30,7 +30,7 @@ from kraken.lib.util import get_im_str, is_bitonal from kraken.lib.models import TorchSeqRecognizer -from kraken.lib.segmentation import extract_polygons, compute_polygon_section +from kraken.lib.segmentation import extract_polygons, compute_polygon_section, Segmentation from kraken.lib.exceptions import KrakenInputException from kraken.lib.dataset import ImageInputTransforms @@ -401,7 +401,7 @@ class mm_rpred(object): def __init__(self, nets: Dict[str, TorchSeqRecognizer], im: Image.Image, - bounds: dict, + bounds: Segmentation, pad: int = 16, bidi_reordering: Union[bool, str] = True, tags_ignore: Optional[List[str]] = None) -> Generator[ocr_record, None, None]: @@ -413,20 +413,18 @@ def __init__(self, these lines. Args: - nets (dict): A dict mapping tag values to TorchSegRecognizer - objects. Recommended to be an defaultdict. - im (PIL.Image.Image): Image to extract text from - bounds (dict): A dictionary containing a 'boxes' entry - with a list of lists of coordinates (script, (x0, y0, - x1, y1)) of a text line in the image and an entry - 'text_direction' containing - 'horizontal-lr/rl/vertical-lr/rl'. - pad (int): Extra blank padding to the left and right of text line - bidi_reordering (bool|str): Reorder classes in the ocr_record according to - the Unicode bidirectional algorithm for - correct display. Set to L|R to - override default text direction. - tags_ignore (list): List of tag values to ignore during recognition + nets: A dict mapping tag values to TorchSegRecognizer objects. + Recommended to be an defaultdict. + im: Image to extract text from + bounds: A Segmentation data class containing either bounding box or + baseline type segmentation. + pad: Extra blank padding to the left and right of text line + bidi_reordering: Reorder classes in the ocr_record according to the + Unicode bidirectional algorithm for correct + display. Set to L|R to override default text + direction. + tags_ignore: List of tag values to ignore during recognition + Yields: An ocr_record containing the recognized text, absolute character positions, and confidence values for each character. @@ -445,36 +443,35 @@ def __init__(self, if not tags_ignore: tags_ignore = [] - if ('type' in bounds and bounds['type'] not in seg_types) or len(seg_types) > 1: + if bounds.type not in seg_types or len(seg_types) > 1: logger.warning(f'Recognizers with segmentation types {seg_types} will be ' - f'applied to segmentation of type {bounds["type"] if "type" in bounds else None}. ' + f'applied to segmentation of type {bounds.type}. ' f'This will likely result in severely degraded performace') one_channel_modes = set(recognizer.nn.one_channel_mode for recognizer in nets.values()) if '1' in one_channel_modes and len(one_channel_modes) > 1: raise KrakenInputException('Mixing binary and non-binary recognition models is not supported.') elif '1' in one_channel_modes and not is_bitonal(im): logger.warning('Running binary models on non-binary input image ' - '(mode {}). This will result in severely degraded ' - 'performance'.format(im.mode)) - if 'type' in bounds and bounds['type'] == 'baselines': + f'(mode {im.mode}). This will result in severely degraded ' + 'performance') + + self.len = len(bounds.lines) + self.line_iter = iter(bounds.lines) + + if bounds.type == 'baselines': valid_norm = False - self.len = len(bounds['lines']) - self.seg_key = 'lines' self.next_iter = self._recognize_baseline_line - self.line_iter = iter(bounds['lines']) tags = set() - for x in bounds['lines']: + for x in bounds.lines: tags.update(x['tags'].values()) else: valid_norm = True - self.len = len(bounds['boxes']) self.seg_key = 'boxes' self.next_iter = self._recognize_box_line - self.line_iter = iter(bounds['boxes']) - tags = set(x[0] for line in bounds['boxes'] for x in line) + tags = set(x[0] for line in bounds.lines for x in line) im_str = get_im_str(im) - logger.info('Running {} multi-script recognizers on {} with {} lines'.format(len(nets), im_str, self.len)) + logger.info(f'Running {len(nets)} multi-script recognizers on {im_str} with {self.len} lines') filtered_tags = [] miss = [] @@ -486,12 +483,12 @@ def __init__(self, tags = filtered_tags if miss: - raise KrakenInputException('Missing models for tags {}'.format(set(miss))) + raise KrakenInputException(f'Missing models for tags {set(miss)}') # build dictionary for line preprocessing self.ts = {} for tag in tags: - logger.debug('Loading line transforms for {}'.format(tag)) + logger.debug(f'Loading line transforms for {tag}') network = nets[tag] batch, channels, height, width = network.nn.input self.ts[tag] = ImageInputTransforms(batch, height, width, channels, (pad, 0), valid_norm) @@ -554,7 +551,7 @@ def _recognize_box_line(self, line): conf = [] for _, start, end, c in preds: - if self.bounds['text_direction'].startswith('horizontal'): + if self.bounds.text_direction.startswith('horizontal'): xmin = coords[0] + self._scale_val(start, 0, self.box.size[0]) xmax = coords[0] + self._scale_val(end, 0, self.box.size[0]) pos.append([[xmin, coords[1]], [xmin, coords[3]], [xmax, coords[3]], [xmax, coords[1]]]) @@ -631,7 +628,7 @@ def _recognize_baseline_line(self, line): def __next__(self): bound = self.bounds - bound[self.seg_key] = [next(self.line_iter)] + setattr(bound, self.seg_key, [next(self.line_iter)]) return self.next_iter(bound) def __iter__(self): @@ -646,39 +643,35 @@ def _scale_val(self, val, min_val, max_val): def rpred(network: TorchSeqRecognizer, im: Image.Image, - bounds: dict, + bounds: Segmentation, pad: int = 16, bidi_reordering: Union[bool, str] = True) -> Generator[ocr_record, None, None]: """ Uses a TorchSeqRecognizer and a segmentation to recognize text Args: - network (kraken.lib.models.TorchSeqRecognizer): A TorchSegRecognizer - object - im (PIL.Image.Image): Image to extract text from - bounds (dict): A dictionary containing a 'boxes' entry with a list of - coordinates (x0, y0, x1, y1) of a text line in the image - and an entry 'text_direction' containing - 'horizontal-lr/rl/vertical-lr/rl'. - pad (int): Extra blank padding to the left and right of text line. - Auto-disabled when expected network inputs are incompatible - with padding. - bidi_reordering (bool|str): Reorder classes in the ocr_record according to - the Unicode bidirectional algorithm for correct - display. Set to L|R to change base text - direction. + network: A TorchSegRecognizer object + im: Image to extract text from + bounds: A Segmentation class instance containing either a baseline or bbox segmentation. + pad: Extra blank padding to the left and right of text line. + Auto-disabled when expected network inputs are incompatible with + padding. + bidi_reordering: Reorder classes in the ocr_record according to the + Unicode bidirectional algorithm for correct display. + Set to L|R to change base text direction. + Yields: An ocr_record containing the recognized text, absolute character positions, and confidence values for each character. """ bounds = copy.deepcopy(bounds) - if 'boxes' in bounds: - boxes = bounds['boxes'] + if bounds.type == 'bbox': + boxes = bounds.lines rewrite_boxes = [] for box in boxes: rewrite_boxes.append([('default', box)]) - bounds['boxes'] = rewrite_boxes - bounds['script_detection'] = True + bounds.lines = rewrite_boxes + bounds.script_detection = True return mm_rpred(defaultdict(lambda: network), im, bounds, pad, bidi_reordering) @@ -693,4 +686,4 @@ def _resolve_tags_to_model(tags: Sequence[Dict[str, str]], return tag, model_map[tag] if default: return next(tags.values()), default - raise KrakenInputException('No model for tags {}'.format(tags)) + raise KrakenInputException(f'No model for tags {tags}') From 14f98c69715aaa5dcb33cf5f75a31b0aa2e3a404 Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Mon, 24 Apr 2023 11:44:36 +0200 Subject: [PATCH 33/69] Use XMLPage in dataset/ro.py --- kraken/lib/dataset/ro.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kraken/lib/dataset/ro.py b/kraken/lib/dataset/ro.py index 31dee62ec..e6c6bf678 100644 --- a/kraken/lib/dataset/ro.py +++ b/kraken/lib/dataset/ro.py @@ -32,7 +32,7 @@ from torch.utils.data import Dataset from typing import Dict, List, Tuple, Sequence, Callable, Any, Union, Literal, Optional -from kraken.lib.xml import parse_alto, parse_page, parse_xml, XMLPage +from kraken.lib.xml import XMLPage from kraken.lib.exceptions import KrakenInputException From d4193e01e5bcd8216b5d99b80e1816a6dba207ec Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Mon, 24 Apr 2023 11:45:55 +0200 Subject: [PATCH 34/69] XMLPage in dataset/segmentation.py --- kraken/lib/dataset/segmentation.py | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/kraken/lib/dataset/segmentation.py b/kraken/lib/dataset/segmentation.py index 075507cbf..faa0536ce 100644 --- a/kraken/lib/dataset/segmentation.py +++ b/kraken/lib/dataset/segmentation.py @@ -33,7 +33,7 @@ from skimage.draw import polygon -from kraken.lib.xml import parse_alto, parse_page, parse_xml +from kraken.lib.xml import XMLPage from kraken.lib.exceptions import KrakenInputException @@ -103,32 +103,25 @@ def __init__(self, imgs: Sequence[Union[PathLike, str]] = None, self.valid_baselines = valid_baselines self.valid_regions = valid_regions if mode in ['alto', 'page', 'xml']: - if mode == 'alto': - fn = parse_alto - elif mode == 'page': - fn = parse_page - elif mode == 'xml': - fn = parse_xml im_paths = [] self.targets = [] for img in imgs: try: - data = fn(img) - im_paths.append(data['image']) + data = XMLPage(img) + im_paths.append(data.imagename) lines = defaultdict(list) - for line in data['lines']: + for line in data.get_sorted_lines(): if valid_baselines is None or set(line['tags'].values()).intersection(valid_baselines): tags = set(line['tags'].values()).intersection(valid_baselines) if valid_baselines else line['tags'].values() for tag in tags: lines[self.mbl_dict.get(tag, tag)].append(line['baseline']) self.class_stats['baselines'][self.mbl_dict.get(tag, tag)] += 1 regions = defaultdict(list) - for k, v in data['regions'].items(): + for k, v in data.regions.items(): if valid_regions is None or k in valid_regions: regions[self.mreg_dict.get(k, k)].extend(v) self.class_stats['regions'][self.mreg_dict.get(k, k)] += len(v) - data['regions'] = regions - self.targets.append({'baselines': lines, 'regions': data['regions']}) + self.targets.append({'baselines': lines, 'regions': regions}) except KrakenInputException as e: logger.warning(e) continue From 9b34c09f14f79054c37b4d0a04adb727fccf43e4 Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Tue, 25 Apr 2023 12:48:22 +0200 Subject: [PATCH 35/69] extract_polygons with dataclass --- kraken/lib/segmentation.py | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/kraken/lib/segmentation.py b/kraken/lib/segmentation.py index 20a14e973..d821d9ce6 100644 --- a/kraken/lib/segmentation.py +++ b/kraken/lib/segmentation.py @@ -67,6 +67,7 @@ @dataclass class Segmentation: type: Literal['baselines', 'bbox'] + imagename: str text_direction: Literal['horizontal-lr', 'horizontal-rl', 'vertical-lr', 'vertical-rl'] script_detection: bool lines: List @@ -1046,29 +1047,20 @@ def compute_polygon_section(baseline: Sequence[Tuple[int, int]], return tuple(o) -def extract_polygons(im: Image.Image, bounds: Dict[str, Any]) -> Image.Image: +def extract_polygons(im: Image.Image, bounds: Segmentation) -> Image.Image: """ Yields the subimages of image im defined in the list of bounding polygons with baselines preserving order. Args: im: Input image - bounds: A list of dicts in baseline:: - - {'type': 'baselines', - 'lines': [{'baseline': [[x_0, y_0], ... [x_n, y_n]], - 'boundary': [[x_0, y_0], ... [x_n, y_n]]}, - ....] - } - - or bounding box format:: - - {'boxes': [[x_0, y_0, x_1, y_1], ...], 'text_direction': 'horizontal-lr'} + bounds: A Segmentation class containing a boundig box or baseline + segmentation. Yields: The extracted subimage """ - if 'type' in bounds and bounds['type'] == 'baselines': + if bounds.type == 'baselines': # select proper interpolation scheme depending on shape if im.mode == '1': order = 0 @@ -1077,7 +1069,7 @@ def extract_polygons(im: Image.Image, bounds: Dict[str, Any]) -> Image.Image: order = 1 im = np.array(im) - for line in bounds['lines']: + for line in bounds.lines: if line['boundary'] is None: raise KrakenInputException('No boundary given for line') pl = np.array(line['boundary']) @@ -1166,11 +1158,11 @@ def extract_polygons(im: Image.Image, bounds: Dict[str, Any]) -> Image.Image: i = Image.fromarray(o.astype('uint8')) yield i.crop(i.getbbox()), line else: - if bounds['text_direction'].startswith('vertical'): + if bounds.text_direction.startswith('vertical'): angle = 90 else: angle = 0 - for box in bounds['boxes']: + for box in bounds.lines: if isinstance(box, tuple): box = list(box) if (box < [0, 0, 0, 0] or box[::2] >= [im.size[0], im.size[0]] or From 09591567e60f8fa0776748af33fbb52c11d8e9b1 Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Thu, 11 May 2023 13:08:50 +0200 Subject: [PATCH 36/69] Add new container classes --- kraken/containers.py | 417 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 417 insertions(+) create mode 100644 kraken/containers.py diff --git a/kraken/containers.py b/kraken/containers.py new file mode 100644 index 000000000..5110ad659 --- /dev/null +++ b/kraken/containers.py @@ -0,0 +1,417 @@ + +import PIL.Image + +from typing import Literal, List, Dict, Sequence, Union, Optional, Tuple +from dataclasses import dataclass, asdict +from abc import ABC, abstractmethod + + +@dataclass +class BaselineLine: + """ + """ + id: str + baseline: List[Tuple[int, int]] + boundary: List[Tuple[int, int]] + text: Optional[str] = None + base_dir: Optional[Literal['L', 'R']] = None + type: str = 'baselines' + image: Optional[PIL.Image.Image] = None + + +@dataclass +class BBoxLine: + """ + """ + id: str + bbox: Tuple[Tuple[int, int], + Tuple[int, int], + Tuple[int, int], + Tuple[int, int]] + text: Optional[str] = None + base_dir: Optional[Literal['L', 'R']] = None + type: str = 'bbox' + image: Optional[PIL.Image.Image] = None + + +@dataclass +class Segmentation: + """ + + """ + type: Literal['baselines', 'bbox'] + imagename: str + text_direction: Literal['horizontal-lr', 'horizontal-rl', 'vertical-lr', 'vertical-rl'] + script_detection: bool + lines: Sequence[Union[BaselineLine, BBoxLine]] + regions: Dict[str, List] + line_orders: List[List[int]] + + +class ocr_record(ABC): + """ + A record object containing the recognition result of a single line + """ + base_dir = None + + def __init__(self, + prediction: str, + cuts: Sequence[Union[Tuple[int, int], Tuple[Tuple[int, int], + Tuple[int, int], + Tuple[int, int], + Tuple[int, int]]]], + confidences: Sequence[float], + display_order: bool = True) -> None: + self._prediction = prediction + self._cuts = cuts + self._confidences = confidences + self._display_order = display_order + + @property + @abstractmethod + def type(self): + pass + + def __len__(self) -> int: + return len(self._prediction) + + def __str__(self) -> str: + return self._prediction + + @property + def prediction(self) -> str: + return self._prediction + + @property + def cuts(self) -> Sequence: + return self._cuts + + @property + def confidences(self) -> List[float]: + return self._confidences + + def __iter__(self): + self.idx = -1 + return self + + @abstractmethod + def __next__(self) -> Tuple[str, + Union[Sequence[Tuple[int, int]], + Tuple[Tuple[int, int], + Tuple[int, int], + Tuple[int, int], + Tuple[int, int]]], + float]: + pass + + @abstractmethod + def __getitem__(self, key: Union[int, slice]): + pass + + @abstractmethod + def display_order(self, base_dir) -> 'ocr_record': + pass + + @abstractmethod + def logical_order(self, base_dir) -> 'ocr_record': + pass + + +class BaselineOCRRecord(ocr_record, BaselineLine): + """ + A record object containing the recognition result of a single line in + baseline format. + + Attributes: + type: 'baselines' to indicate a baseline record + prediction: The text predicted by the network as one continuous string. + cuts: The absolute bounding polygons for each code point in prediction + as a list of tuples [(x0, y0), (x1, y2), ...]. + confidences: A list of floats indicating the confidence value of each + code point. + + Notes: + When slicing the record the behavior of the cuts is changed from + earlier versions of kraken. Instead of returning per-character bounding + polygons a single polygons section of the line bounding polygon + starting at the first and extending to the last code point emitted by + the network is returned. This aids numerical stability when computing + aggregated bounding polygons such as for words. Individual code point + bounding polygons are still accessible through the `cuts` attribute or + by iterating over the record code point by code point. + """ + type = 'baselines' + + def __init__(self, prediction: str, + cuts: Sequence[Tuple[int, int]], + confidences: Sequence[float], + line: BaselineLine, + base_dir: Optional[Literal['L', 'R']] = None, + display_order: bool = True) -> None: + if line.type != 'baselines': + raise TypeError('Invalid argument type (non-baseline line)') + BaselineLine.__init__(self, **asdict(line)) + self._line_base_dir = self.base_dir + self.base_dir = base_dir + ocr_record.__init__(self, prediction, cuts, confidences, display_order) + + def __repr__(self) -> str: + return f'pred: {self.prediction} baseline: {self.baseline} boundary: {self.boundary} confidences: {self.confidences}' + + def __next__(self) -> Tuple[str, int, float]: + if self.idx + 1 < len(self): + self.idx += 1 + return (self.prediction[self.idx], + compute_polygon_section(self.baseline, + self.line, + self.cuts[self.idx][0], + self.cuts[self.idx][1]), + self.confidences[self.idx]) + else: + raise StopIteration + + def _get_raw_item(self, key: int): + if key < 0: + key += len(self) + if key >= len(self): + raise IndexError('Index (%d) is out of range' % key) + return (self.prediction[key], + self._cuts[key], + self.confidences[key]) + + def __getitem__(self, key: Union[int, slice]): + if isinstance(key, slice): + recs = [self._get_raw_item(i) for i in range(*key.indices(len(self)))] + prediction = ''.join([x[0] for x in recs]) + flat_offsets = sum((tuple(x[1]) for x in recs), ()) + cut = compute_polygon_section(self.baseline, + self.line, + min(flat_offsets), + max(flat_offsets)) + confidence = np.mean([x[2] for x in recs]) + return (prediction, cut, confidence) + elif isinstance(key, int): + pred, cut, confidence = self._get_raw_item(key) + return (pred, + compute_polygon_section(self.baseline, self.line, cut[0], cut[1]), + confidence) + else: + raise TypeError('Invalid argument type') + + @property + def cuts(self) -> Sequence[Tuple[int, int]]: + return tuple([compute_polygon_section(self.baseline, self.line, cut[0], cut[1]) for cut in self._cuts]) + + def logical_order(self, base_dir: Optional[str] = None) -> 'BaselineOCRRecord': + """ + Returns the OCR record in Unicode logical order, i.e. in the order the + characters in the line would be read by a human. + + Args: + base_dir: An optional string defining the base direction (also + called paragraph direction) for the BiDi algorithm. Valid + values are 'L' or 'R'. If None is given the default + auto-resolution will be used. + """ + if self._display_order: + return self._reorder(base_dir) + else: + return self + + def display_order(self, base_dir: Optional[str] = None) -> 'BaselineOCRRecord': + """ + Returns the OCR record in Unicode display order, i.e. ordered from left + to right inside the line. + + Args: + base_dir: An optional string defining the base direction (also + called paragraph direction) for the BiDi algorithm. Valid + values are 'L' or 'R'. If None is given the default + auto-resolution will be used. + """ + if self._display_order: + return self + else: + return self._reorder(base_dir) + + def _reorder(self, base_dir: Optional[str] = None) -> 'BaselineOCRRecord': + """ + Reorder the record using the BiDi algorithm. + """ + storage = bd.get_empty_storage() + + if base_dir not in ('L', 'R'): + base_level = bd.get_base_level(self._prediction) + else: + base_level = {'L': 0, 'R': 1}[base_dir] + + storage['base_level'] = base_level + storage['base_dir'] = ('L', 'R')[base_level] + bd.get_embedding_levels(self._prediction, storage) + bd.explicit_embed_and_overrides(storage) + bd.resolve_weak_types(storage) + bd.resolve_neutral_types(storage, False) + bd.resolve_implicit_levels(storage, False) + for i, j in enumerate(zip(self._prediction, self._cuts, self._confidences)): + storage['chars'][i]['record'] = j + bd.reorder_resolved_levels(storage, False) + bd.apply_mirroring(storage, False) + prediction = '' + cuts = [] + confidences = [] + for ch in storage['chars']: + # code point may have been mirrored + prediction = prediction + ch['ch'] + cuts.append(ch['record'][1]) + confidences.append(ch['record'][2]) + line = BaselineLine(id=self.id, + baseline=self.baseline, + boundary=self.boundary, + text=self.text, + base_dir=self._line_base_dir, + image=self.image) + rec = BaselineOCRRecord(prediction=prediction, + cuts=cuts, + confidences=confidences, + line=line, + base_dir=base_dir, + display_order=not self._display_order) + return rec + + +class BBoxOCRRecord(ocr_record, BBoxLine): + """ + A record object containing the recognition result of a single line in + bbox format. + """ + type = 'bbox' + + def __init__(self, prediction: str, + cuts: Sequence[Tuple[Tuple[int, int], + Tuple[int, int], + Tuple[int, int], + Tuple[int, int]]], + confidences: Sequence[float], + line: BBoxLine, + base_dir: Optional['L', 'R'], + display_order: bool = True) -> None: + if line.type != 'bbox': + raise TypeError('Invalid argument type (non-bbox line)') + BBoxLine.__init__(self, **asdict(line)) + self._line_base_dir = self.base_dir + self.base_dir = base_dir + ocr_record.__init__(self, prediction, cuts, confidences, display_order) + + def __repr__(self) -> str: + return f'pred: {self.prediction} line: {self.line} confidences: {self.confidences}' + + def __next__(self) -> Tuple[str, int, float]: + if self.idx + 1 < len(self): + self.idx += 1 + return (self.prediction[self.idx], + self.cuts[self.idx], + self.confidences[self.idx]) + else: + raise StopIteration + + def _get_raw_item(self, key: int): + if key < 0: + key += len(self) + if key >= len(self): + raise IndexError('Index (%d) is out of range' % key) + return (self.prediction[key], + self.cuts[key], + self.confidences[key]) + + def __getitem__(self, key: Union[int, slice]): + if isinstance(key, slice): + recs = [self._get_raw_item(i) for i in range(*key.indices(len(self)))] + prediction = ''.join([x[0] for x in recs]) + box = [x[1] for x in recs] + flat_box = [point for pol in box for point in pol] + flat_box = [x for point in flat_box for x in point] + min_x, max_x = min(flat_box[::2]), max(flat_box[::2]) + min_y, max_y = min(flat_box[1::2]), max(flat_box[1::2]) + cut = ((min_x, min_y), (max_x, min_y), (max_x, max_y), (min_x, max_y)) + confidence = np.mean([x[2] for x in recs]) + return (prediction, cut, confidence) + elif isinstance(key, int): + return self._get_raw_item(key) + else: + raise TypeError('Invalid argument type') + + def logical_order(self, base_dir: Optional[Literal['L', 'R']] = None) -> 'BBoxOCRRecord': + """ + Returns the OCR record in Unicode logical order, i.e. in the order the + characters in the line would be read by a human. + + Args: + base_dir: An optional string defining the base direction (also + called paragraph direction) for the BiDi algorithm. Valid + values are 'L' or 'R'. If None is given the default + auto-resolution will be used. + """ + if self._display_order: + return self._reorder(base_dir) + else: + return self + + def display_order(self, base_dir: Optional[Literal['L', 'R']] = None) -> 'BBoxOCRRecord': + """ + Returns the OCR record in Unicode display order, i.e. ordered from left + to right inside the line. + + Args: + base_dir: An optional string defining the base direction (also + called paragraph direction) for the BiDi algorithm. Valid + values are 'L' or 'R'. If None is given the default + auto-resolution will be used. + """ + if self._display_order: + return self + else: + return self._reorder(base_dir) + + def _reorder(self, base_dir: Optional[Literal['L', 'R']] = None) -> 'BBoxOCRRecord': + storage = bd.get_empty_storage() + + if base_dir not in ('L', 'R'): + base_level = bd.get_base_level(self.prediction) + else: + base_level = {'L': 0, 'R': 1}[base_dir] + + storage['base_level'] = base_level + storage['base_dir'] = ('L', 'R')[base_level] + + bd.get_embedding_levels(self.prediction, storage) + bd.explicit_embed_and_overrides(storage) + bd.resolve_weak_types(storage) + bd.resolve_neutral_types(storage, False) + bd.resolve_implicit_levels(storage, False) + for i, j in enumerate(zip(self.prediction, self.cuts, self.confidences)): + storage['chars'][i]['record'] = j + bd.reorder_resolved_levels(storage, False) + bd.apply_mirroring(storage, False) + prediction = '' + cuts = [] + confidences = [] + for ch in storage['chars']: + # code point may have been mirrored + prediction = prediction + ch['ch'] + cuts.append(ch['record'][1]) + confidences.append(ch['record'][2]) + # carry over whole line information + line = BBoxLine(id=self.id, + bbox=self.bbox, + text=self.text, + base_dir=self._line_base_dir, + image=self.image) + rec = BBoxOCRRecord(prediction=prediction, + cuts=cuts, + confidences=confidences, + line=line, + base_dir=base_dir, + display_order=not self._display_order) + return rec + + From 5afb82847354576b71dddff123bfba952b5a387e Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Thu, 11 May 2023 13:09:13 +0200 Subject: [PATCH 37/69] better _to_ptl_device --- kraken/ketos/util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kraken/ketos/util.py b/kraken/ketos/util.py index e71b53505..009190027 100644 --- a/kraken/ketos/util.py +++ b/kraken/ketos/util.py @@ -54,7 +54,7 @@ def message(msg, **styles): def to_ptl_device(device: str) -> Tuple[str, Optional[List[int]]]: - if any([device == x for x in ['cpu', 'mps']]): + if device in ['cpu', 'mps']]): return device, 'auto' elif any([device.startswith(x) for x in ['tpu', 'cuda', 'hpu', 'ipu']]): dev, idx = device.split(':') From 24a0b85bf318ab921f85935b7b3b502db63e13d2 Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Thu, 11 May 2023 13:10:14 +0200 Subject: [PATCH 38/69] strip container classes from rpred --- kraken/rpred.py | 356 +----------------------------------------------- 1 file changed, 2 insertions(+), 354 deletions(-) diff --git a/kraken/rpred.py b/kraken/rpred.py index 33375cc12..40bcba765 100644 --- a/kraken/rpred.py +++ b/kraken/rpred.py @@ -28,6 +28,7 @@ from collections import defaultdict from typing import List, Tuple, Optional, Generator, Union, Dict, Sequence +from kraken.containers import BaselineOCRRecord, BBoxOCRRecord, ocr_record from kraken.lib.util import get_im_str, is_bitonal from kraken.lib.models import TorchSeqRecognizer from kraken.lib.segmentation import extract_polygons, compute_polygon_section, Segmentation @@ -36,364 +37,11 @@ import copy -__all__ = ['ocr_record', 'BaselineOCRRecord', 'BBoxOCRRecord', 'mm_rpred', 'rpred'] +__all__ = ['mm_rpred', 'rpred'] logger = logging.getLogger(__name__) -class ocr_record(ABC): - """ - A record object containing the recognition result of a single line - """ - base_dir = None - - def __init__(self, - prediction: str, - cuts: Sequence[Union[Tuple[int, int], Tuple[Tuple[int, int], - Tuple[int, int], - Tuple[int, int], - Tuple[int, int]]]], - confidences: Sequence[float], - display_order: bool = True) -> None: - self._prediction = prediction - self._cuts = cuts - self._confidences = confidences - self._display_order = display_order - - @property - @abstractmethod - def type(self): - pass - - def __len__(self) -> int: - return len(self._prediction) - - def __str__(self) -> str: - return self._prediction - - @property - def prediction(self) -> str: - return self._prediction - - @property - def cuts(self) -> Sequence: - return self._cuts - - @property - def confidences(self) -> List[float]: - return self._confidences - - def __iter__(self): - self.idx = -1 - return self - - @abstractmethod - def __next__(self) -> Tuple[str, - Union[Sequence[Tuple[int, int]], - Tuple[Tuple[int, int], - Tuple[int, int], - Tuple[int, int], - Tuple[int, int]]], - float]: - pass - - @abstractmethod - def __getitem__(self, key: Union[int, slice]): - pass - - @abstractmethod - def display_order(self, base_dir) -> 'ocr_record': - pass - - @abstractmethod - def logical_order(self, base_dir) -> 'ocr_record': - pass - - -class BaselineOCRRecord(ocr_record): - """ - A record object containing the recognition result of a single line in - baseline format. - - Attributes: - type: 'baselines' to indicate a baseline record - prediction: The text predicted by the network as one continuous string. - cuts: The absolute bounding polygons for each code point in prediction - as a list of tuples [(x0, y0), (x1, y2), ...]. - confidences: A list of floats indicating the confidence value of each - code point. - - Notes: - When slicing the record the behavior of the cuts is changed from - earlier versions of kraken. Instead of returning per-character bounding - polygons a single polygons section of the line bounding polygon - starting at the first and extending to the last code point emitted by - the network is returned. This aids numerical stability when computing - aggregated bounding polygons such as for words. Individual code point - bounding polygons are still accessible through the `cuts` attribute or - by iterating over the record code point by code point. - """ - type = 'baselines' - - def __init__(self, prediction: str, - cuts: Sequence[Tuple[int, int]], - confidences: Sequence[float], - line: Dict[str, List], - display_order: bool = True) -> None: - super().__init__(prediction, cuts, confidences, display_order) - if 'baseline' not in line: - raise TypeError('Invalid argument type (non-baseline line)') - self.tags = None if 'tags' not in line else line['tags'] - self.line = line['boundary'] - self.baseline = line['baseline'] - - def __repr__(self) -> str: - return f'pred: {self.prediction} baseline: {self.baseline} boundary: {self.line} confidences: {self.confidences}' - - def __next__(self) -> Tuple[str, int, float]: - if self.idx + 1 < len(self): - self.idx += 1 - return (self.prediction[self.idx], - compute_polygon_section(self.baseline, - self.line, - self.cuts[self.idx][0], - self.cuts[self.idx][1]), - self.confidences[self.idx]) - else: - raise StopIteration - - def _get_raw_item(self, key: int): - if key < 0: - key += len(self) - if key >= len(self): - raise IndexError('Index (%d) is out of range' % key) - return (self.prediction[key], - self._cuts[key], - self.confidences[key]) - - def __getitem__(self, key: Union[int, slice]): - if isinstance(key, slice): - recs = [self._get_raw_item(i) for i in range(*key.indices(len(self)))] - prediction = ''.join([x[0] for x in recs]) - flat_offsets = sum((tuple(x[1]) for x in recs), ()) - cut = compute_polygon_section(self.baseline, - self.line, - min(flat_offsets), - max(flat_offsets)) - confidence = np.mean([x[2] for x in recs]) - return (prediction, cut, confidence) - - elif isinstance(key, int): - pred, cut, confidence = self._get_raw_item(key) - return (pred, - compute_polygon_section(self.baseline, self.line, cut[0], cut[1]), - confidence) - else: - raise TypeError('Invalid argument type') - - @property - def cuts(self) -> Sequence[Tuple[int, int]]: - return tuple([compute_polygon_section(self.baseline, self.line, cut[0], cut[1]) for cut in self._cuts]) - - def logical_order(self, base_dir: Optional[str] = None) -> 'BaselineOCRRecord': - """ - Returns the OCR record in Unicode logical order, i.e. in the order the - characters in the line would be read by a human. - - Args: - base_dir: An optional string defining the base direction (also - called paragraph direction) for the BiDi algorithm. Valid - values are 'L' or 'R'. If None is given the default - auto-resolution will be used. - """ - if self._display_order: - return self._reorder(base_dir) - else: - return self - - def display_order(self, base_dir: Optional[str] = None) -> 'BaselineOCRRecord': - """ - Returns the OCR record in Unicode display order, i.e. ordered from left - to right inside the line. - - Args: - base_dir: An optional string defining the base direction (also - called paragraph direction) for the BiDi algorithm. Valid - values are 'L' or 'R'. If None is given the default - auto-resolution will be used. - """ - if self._display_order: - return self - else: - return self._reorder(base_dir) - - def _reorder(self, base_dir: Optional[str] = None) -> 'BaselineOCRRecord': - """ - Reorder the record using the BiDi algorithm. - """ - storage = bd.get_empty_storage() - - if base_dir not in ('L', 'R'): - base_level = bd.get_base_level(self._prediction) - else: - base_level = {'L': 0, 'R': 1}[base_dir] - - storage['base_level'] = base_level - storage['base_dir'] = ('L', 'R')[base_level] - - bd.get_embedding_levels(self._prediction, storage) - bd.explicit_embed_and_overrides(storage) - bd.resolve_weak_types(storage) - bd.resolve_neutral_types(storage, False) - bd.resolve_implicit_levels(storage, False) - for i, j in enumerate(zip(self._prediction, self._cuts, self._confidences)): - storage['chars'][i]['record'] = j - bd.reorder_resolved_levels(storage, False) - bd.apply_mirroring(storage, False) - prediction = '' - cuts = [] - confidences = [] - for ch in storage['chars']: - # code point may have been mirrored - prediction = prediction + ch['ch'] - cuts.append(ch['record'][1]) - confidences.append(ch['record'][2]) - line = {'boundary': self.line, 'baseline': self.baseline} - rec = BaselineOCRRecord(prediction, cuts, confidences, line) - rec.tags = self.tags - rec.base_dir = base_dir - rec._display_order = not self._display_order - return rec - - -class BBoxOCRRecord(ocr_record): - """ - A record object containing the recognition result of a single line in - bbox format. - """ - type = 'box' - - def __init__(self, prediction: str, - cuts: Sequence[Tuple[Tuple[int, int], - Tuple[int, int], - Tuple[int, int], - Tuple[int, int]]], - confidences: Sequence[float], - line: Tuple[Tuple[int, int], - Tuple[int, int], - Tuple[int, int], - Tuple[int, int]], - display_order: bool = True) -> None: - super().__init__(prediction, cuts, confidences, display_order) - if 'baseline' in line: - raise TypeError('Invalid argument type (baseline line)') - self.line = line - - def __repr__(self) -> str: - return f'pred: {self.prediction} line: {self.line} confidences: {self.confidences}' - - def __next__(self) -> Tuple[str, int, float]: - if self.idx + 1 < len(self): - self.idx += 1 - return (self.prediction[self.idx], - self.cuts[self.idx], - self.confidences[self.idx]) - else: - raise StopIteration - - def _get_raw_item(self, key: int): - if key < 0: - key += len(self) - if key >= len(self): - raise IndexError('Index (%d) is out of range' % key) - return (self.prediction[key], - self.cuts[key], - self.confidences[key]) - - def __getitem__(self, key: Union[int, slice]): - if isinstance(key, slice): - recs = [self._get_raw_item(i) for i in range(*key.indices(len(self)))] - prediction = ''.join([x[0] for x in recs]) - box = [x[1] for x in recs] - flat_box = [point for pol in box for point in pol] - flat_box = [x for point in flat_box for x in point] - min_x, max_x = min(flat_box[::2]), max(flat_box[::2]) - min_y, max_y = min(flat_box[1::2]), max(flat_box[1::2]) - cut = ((min_x, min_y), (max_x, min_y), (max_x, max_y), (min_x, max_y)) - confidence = np.mean([x[2] for x in recs]) - return (prediction, cut, confidence) - elif isinstance(key, int): - return self._get_raw_item(key) - else: - raise TypeError('Invalid argument type') - - def logical_order(self, base_dir: Optional[str] = None) -> 'BBoxOCRRecord': - """ - Returns the OCR record in Unicode logical order, i.e. in the order the - characters in the line would be read by a human. - - Args: - base_dir: An optional string defining the base direction (also - called paragraph direction) for the BiDi algorithm. Valid - values are 'L' or 'R'. If None is given the default - auto-resolution will be used. - """ - if self._display_order: - return self._reorder(base_dir) - else: - return self - - def display_order(self, base_dir: Optional[str] = None) -> 'BBoxOCRRecord': - """ - Returns the OCR record in Unicode display order, i.e. ordered from left - to right inside the line. - - Args: - base_dir: An optional string defining the base direction (also - called paragraph direction) for the BiDi algorithm. Valid - values are 'L' or 'R'. If None is given the default - auto-resolution will be used. - """ - if self._display_order: - return self - else: - return self._reorder(base_dir) - - def _reorder(self, base_dir: Optional[str] = None) -> 'BBoxOCRRecord': - storage = bd.get_empty_storage() - - if base_dir not in ('L', 'R'): - base_level = bd.get_base_level(self.prediction) - else: - base_level = {'L': 0, 'R': 1}[base_dir] - - storage['base_level'] = base_level - storage['base_dir'] = ('L', 'R')[base_level] - - bd.get_embedding_levels(self.prediction, storage) - bd.explicit_embed_and_overrides(storage) - bd.resolve_weak_types(storage) - bd.resolve_neutral_types(storage, False) - bd.resolve_implicit_levels(storage, False) - for i, j in enumerate(zip(self.prediction, self.cuts, self.confidences)): - storage['chars'][i]['record'] = j - bd.reorder_resolved_levels(storage, False) - bd.apply_mirroring(storage, False) - prediction = '' - cuts = [] - confidences = [] - for ch in storage['chars']: - # code point may have been mirrored - prediction = prediction + ch['ch'] - cuts.append(ch['record'][1]) - confidences.append(ch['record'][2]) - # carry over whole line information - rec = BBoxOCRRecord(prediction, cuts, confidences, self.line) - rec.base_dir = base_dir - rec._display_order = not self._display_order - return rec - - class mm_rpred(object): """ Multi-model version of kraken.rpred.rpred From 66127aaf6e1ad3b55f1cac82e9caf9e061fddea6 Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Thu, 11 May 2023 13:19:47 +0200 Subject: [PATCH 39/69] Use new container classes in XMLPage --- kraken/containers.py | 7 + kraken/lib/xml.py | 452 ++----------------------------------------- 2 files changed, 24 insertions(+), 435 deletions(-) diff --git a/kraken/containers.py b/kraken/containers.py index 5110ad659..a07848f92 100644 --- a/kraken/containers.py +++ b/kraken/containers.py @@ -5,6 +5,13 @@ from dataclasses import dataclass, asdict from abc import ABC, abstractmethod +__all__ = ['BaselineLine', + 'BBoxLine', + 'Segmentation', + 'ocr_record', + 'BaselineOCRRecord', + 'BBoxOCRRecord'] + @dataclass class BaselineLine: diff --git a/kraken/lib/xml.py b/kraken/lib/xml.py index 135c50560..ec55772e6 100644 --- a/kraken/lib/xml.py +++ b/kraken/lib/xml.py @@ -27,12 +27,14 @@ from typing import Union, Dict, Any, Sequence, Tuple, Literal, Optional, List from collections import defaultdict +from kraken.containers import BaselineLine from kraken.lib.segmentation import calculate_polygonal_environment from kraken.lib.exceptions import KrakenInputException logger = logging.getLogger(__name__) -__all__ = ['XMLPage', 'parse_xml', 'parse_page', 'parse_alto', 'preparse_xml_data'] +__all__ = ['XMLPage'] + # fallback mapping between PAGE region types and tags page_regions = {'TextRegion': 'text', @@ -58,427 +60,6 @@ 'ComposedBlock': 'composed'} -def preparse_xml_data(filenames: Sequence[Union[str, PathLike]], - format_type: str = 'xml', - repolygonize: bool = False) -> Dict[str, Any]: - """ - Loads training data from a set of xml files. - - Extracts line information from Page/ALTO xml files for training of - recognition models. - - Args: - filenames: List of XML files. - format_type: Either `page`, `alto` or `xml` for autodetermination. - repolygonize: (Re-)calculates polygon information using the kraken - algorithm. - - Returns: - A list of dicts {'text': text, 'baseline': [[x0, y0], ...], 'boundary': - [[x0, y0], ...], 'image': PIL.Image}. - """ - training_pairs = [] - if format_type == 'xml': - parse_fn = parse_xml - elif format_type == 'alto': - parse_fn = parse_alto - elif format_type == 'page': - parse_fn = parse_page - else: - raise ValueError(f'invalid format {format_type} for preparse_xml_data') - - for fn in filenames: - try: - data = parse_fn(fn) - except ValueError as e: - logger.warning(e) - continue - try: - with open(data['image'], 'rb') as fp: - Image.open(fp) - except FileNotFoundError as e: - logger.warning(f'Could not open file {e.filename} in {fn}') - continue - if repolygonize: - logger.info('repolygonizing {} lines in {}'.format(len(data['lines']), data['image'])) - data['lines'] = _repolygonize(data['image'], data['lines']) - for line in data['lines']: - training_pairs.append({'image': data['image'], **line}) - return training_pairs - - -def _repolygonize(im: Image.Image, lines: Sequence[Dict[str, Any]]): - """ - Helper function taking an output of the lib.xml parse_* functions and - recalculating the contained polygonization. - - Args: - im (Image.Image): Input image - lines (list): List of dicts [{'boundary': [[x0, y0], ...], 'baseline': [[x0, y0], ...], 'text': 'abcvsd'}, {...] - - Returns: - A data structure `lines` with a changed polygonization. - """ - im = Image.open(im).convert('L') - polygons = calculate_polygonal_environment(im, [x['baseline'] for x in lines]) - return [{'boundary': polygon, - 'baseline': orig['baseline'], - 'text': orig['text'], - 'script': orig['script']} for orig, polygon in zip(lines, polygons)] - - -def parse_xml(filename: Union[str, PathLike]) -> Dict[str, Any]: - """ - Parses either a PageXML or ALTO file with autodetermination of the file - format. - - Args: - filename: path to an XML file. - - Returns: - A dict:: - - {'image': impath, - 'lines': [{'boundary': [[x0, y0], ...], - 'baseline': [[x0, y0], ...], - 'text': apdjfqpf', - 'tags': {'type': 'default', ...}}, - ... - {...}], - 'regions': {'region_type_0': [[[x0, y0], ...], ...], ...}} - """ - with open(filename, 'rb') as fp: - try: - doc = etree.parse(fp) - except etree.XMLSyntaxError as e: - raise KrakenInputException(f'Parsing {filename} failed: {e}') - if doc.getroot().tag.endswith('alto'): - return parse_alto(filename) - elif doc.getroot().tag.endswith('PcGts'): - return parse_page(filename) - else: - raise KrakenInputException(f'Unknown XML format in {filename}') - - -def parse_page(filename: Union[str, PathLike]) -> Dict[str, Any]: - """ - Parses a PageXML file, returns the baselines defined in it, and loads the - referenced image. - - Args: - filename: path to a PageXML file. - - Returns: - A dict:: - - {'image': impath, - 'lines': [{'boundary': [[x0, y0], ...], - 'baseline': [[x0, y0], ...], - 'text': apdjfqpf', - 'tags': {'type': 'default', ...}}, - ... - {...}], - 'regions': {'region_type_0': [[[x0, y0], ...], ...], ...}} - """ - def _parse_page_custom(s): - o = {} - s = s.strip() - l_chunks = [l_chunk for l_chunk in s.split('}') if l_chunk.strip()] - if l_chunks: - for chunk in l_chunks: - tag, vals = chunk.split('{') - tag_vals = {} - vals = [val.strip() for val in vals.split(';') if val.strip()] - for val in vals: - key, *val = val.split(':') - tag_vals[key] = ":".join(val) - o[tag.strip()] = tag_vals - return o - - def _parse_coords(coords): - points = [x for x in coords.split(' ')] - points = [int(c) for point in points for c in point.split(',')] - pts = zip(points[::2], points[1::2]) - return [k for k, g in groupby(pts)] - - with open(filename, 'rb') as fp: - base_dir = Path(filename).parent - try: - doc = etree.parse(fp) - except etree.XMLSyntaxError as e: - raise KrakenInputException('Parsing {} failed: {}'.format(filename, e)) - image = doc.find('.//{*}Page') - if image is None or image.get('imageFilename') is None: - raise KrakenInputException('No valid image filename found in PageXML file {}'.format(filename)) - try: - base_direction = {'left-to-right': 'L', - 'right-to-left': 'R', - 'top-to-bottom': 'L', - 'bottom-to-top': 'R', - None: None}[image.get('readingDirection')] - except KeyError: - logger.warning(f'Invalid value {image.get("readingDirection")} encountered in page-level reading direction.') - base_direction = None - lines = doc.findall('.//{*}TextLine') - data = {'image': base_dir.joinpath(image.get('imageFilename')), - 'lines': [], - 'type': 'baselines', - 'base_dir': base_direction, - 'regions': {}} - # find all image regions - regions = [] - for x in page_regions.keys(): - regions.extend(doc.findall('.//{{*}}{}'.format(x))) - # parse region type and coords - region_data = defaultdict(list) - for region in regions: - coords = region.find('{*}Coords') - if coords is not None and not coords.get('points').isspace() and len(coords.get('points')): - try: - coords = _parse_coords(coords.get('points')) - except Exception: - logger.warning('Region {} without coordinates'.format(region.get('id'))) - continue - else: - logger.warning('Region {} without coordinates'.format(region.get('id'))) - continue - rtype = region.get('type') - # parse transkribus-style custom field if possible - custom_str = region.get('custom') - if not rtype and custom_str: - cs = _parse_page_custom(custom_str) - if 'structure' in cs and 'type' in cs['structure']: - rtype = cs['structure']['type'] - # fall back to default region type if nothing is given - if not rtype: - rtype = page_regions[region.tag.split('}')[-1]] - region_data[rtype].append(coords) - - data['regions'] = region_data - - # parse line information - tag_set = set(('default',)) - for line in lines: - pol = line.find('./{*}Coords') - boundary = None - if pol is not None and not pol.get('points').isspace() and len(pol.get('points')): - try: - boundary = _parse_coords(pol.get('points')) - except Exception: - logger.info('TextLine {} without polygon'.format(line.get('id'))) - else: - logger.info('TextLine {} without polygon'.format(line.get('id'))) - base = line.find('./{*}Baseline') - baseline = None - if base is not None and not base.get('points').isspace() and len(base.get('points')): - try: - baseline = _parse_coords(base.get('points')) - except Exception: - logger.info('TextLine {} without baseline'.format(line.get('id'))) - continue - else: - logger.info('TextLine {} without baseline'.format(line.get('id'))) - continue - text = '' - manual_transcription = line.find('./{*}TextEquiv') - if manual_transcription is not None: - transcription = manual_transcription - else: - transcription = line - for el in transcription.findall('.//{*}Unicode'): - if el.text: - text += el.text - # retrieve line tags if custom string is set and contains - tags = {'type': 'default'} - split_type = None - custom_str = line.get('custom') - if custom_str: - cs = _parse_page_custom(custom_str) - if 'structure' in cs and 'type' in cs['structure']: - tags['type'] = cs['structure']['type'] - tag_set.add(tags['type']) - # retrieve data split if encoded in custom string. - if 'split' in cs and 'type' in cs['split'] and cs['split']['type'] in ['train', 'validation', 'test']: - split_type = cs['split']['type'] - tags['split'] = split_type - tag_set.add(split_type) - - data['lines'].append({'baseline': baseline, - 'boundary': boundary, - 'text': text, - 'split': split_type, - 'tags': tags}) - if len(tag_set) > 1: - data['script_detection'] = True - else: - data['script_detection'] = False - return data - - -def parse_alto(filename: Union[str, PathLike]) -> Dict[str, Any]: - """ - Parses an ALTO file, returns the baselines defined in it, and loads the - referenced image. - - Args: - filename: path to an ALTO file. - - Returns: - A dict:: - - {'image': impath, - 'lines': [{'boundary': [[x0, y0], ...], - 'baseline': [[x0, y0], ...], - 'text': apdjfqpf', - 'tags': {'type': 'default', ...}}, - ... - {...}], - 'regions': {'region_type_0': [[[x0, y0], ...], ...], ...}} - """ - def _parse_pointstype(coords: str) -> Sequence[Tuple[float, float]]: - """ - ALTO's PointsType is underspecified so a variety of serializations are valid: - - x0, y0 x1, y1 ... - x0 y0 x1 y1 ... - (x0, y0) (x1, y1) ... - (x0 y0) (x1 y1) ... - - Returns: - A list of tuples [(x0, y0), (x1, y1), ...] - """ - float_re = re.compile(r'[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?') - points = [float(point.group()) for point in float_re.finditer(coords)] - if len(points) % 2: - raise ValueError(f'Odd number of points in points sequence: {points}') - pts = zip(points[::2], points[1::2]) - return [k for k, g in groupby(pts)] - - with open(filename, 'rb') as fp: - base_dir = Path(filename).parent - try: - doc = etree.parse(fp) - except etree.XMLSyntaxError as e: - raise KrakenInputException('Parsing {} failed: {}'.format(filename, e)) - image = doc.find('.//{*}fileName') - if image is None or not image.text: - raise KrakenInputException('No valid filename found in ALTO file') - lines = doc.findall('.//{*}TextLine') - data = {'image': base_dir.joinpath(image.text), - 'lines': [], - 'type': 'baselines', - 'base_dir': None, - 'regions': {}} - # find all image regions - regions = [] - for x in alto_regions.keys(): - regions.extend(doc.findall('./{{*}}Layout/{{*}}Page/{{*}}PrintSpace/{{*}}{}'.format(x))) - # find overall dimensions to filter out dummy TextBlocks - ps = doc.find('./{*}Layout/{*}Page/{*}PrintSpace') - x_min = int(float(ps.get('HPOS'))) - y_min = int(float(ps.get('VPOS'))) - width = int(float(ps.get('WIDTH'))) - height = int(float(ps.get('HEIGHT'))) - page_boundary = [(x_min, y_min), - (x_min, y_min + height), - (x_min + width, y_min + height), - (x_min + width, y_min)] - - # parse tagrefs - cls_map = {} - tags = doc.find('.//{*}Tags') - if tags is not None: - for x in ['StructureTag', 'LayoutTag', 'OtherTag']: - for tag in tags.findall('./{{*}}{}'.format(x)): - cls_map[tag.get('ID')] = (x[:-3].lower(), tag.get('LABEL')) - # parse region type and coords - region_data = defaultdict(list) - for region in regions: - # try to find shape object - coords = region.find('./{*}Shape/{*}Polygon') - if coords is not None: - boundary = _parse_pointstype(coords.get('POINTS')) - elif (region.get('HPOS') is not None and region.get('VPOS') is not None and - region.get('WIDTH') is not None and region.get('HEIGHT') is not None): - # use rectangular definition - x_min = int(float(region.get('HPOS'))) - y_min = int(float(region.get('VPOS'))) - width = int(float(region.get('WIDTH'))) - height = int(float(region.get('HEIGHT'))) - boundary = [(x_min, y_min), - (x_min, y_min + height), - (x_min + width, y_min + height), - (x_min + width, y_min)] - else: - continue - rtype = region.get('TYPE') - # fall back to default region type if nothing is given - tagrefs = region.get('TAGREFS') - if tagrefs is not None and rtype is None: - for tagref in tagrefs.split(): - ttype, rtype = cls_map.get(tagref, (None, None)) - if rtype is not None and ttype: - break - if rtype is None: - rtype = alto_regions[region.tag.split('}')[-1]] - if boundary == page_boundary and rtype == 'text': - logger.info('Skipping TextBlock with same size as page image.') - continue - region_data[rtype].append({'id': region.get('ID'), 'boundary': boundary}) - data['regions'] = region_data - - tag_set = set(('default',)) - for line in lines: - if line.get('BASELINE') is None: - logger.info('TextLine {} without baseline'.format(line.get('ID'))) - continue - pol = line.find('./{*}Shape/{*}Polygon') - boundary = None - if pol is not None: - try: - boundary = _parse_pointstype(pol.get('POINTS')) - except ValueError: - logger.info('TextLine {} without polygon'.format(line.get('ID'))) - else: - logger.info('TextLine {} without polygon'.format(line.get('ID'))) - - baseline = None - try: - baseline = _parse_pointstype(line.get('BASELINE')) - except ValueError: - logger.info('TextLine {} without baseline'.format(line.get('ID'))) - - text = '' - for el in line.xpath(".//*[local-name() = 'String'] | .//*[local-name() = 'SP']"): - text += el.get('CONTENT') if el.get('CONTENT') else ' ' - # find line type - tags = {'type': 'default'} - split_type = None - tagrefs = line.get('TAGREFS') - if tagrefs is not None: - for tagref in tagrefs.split(): - ttype, ltype = cls_map.get(tagref, (None, None)) - if ltype is not None: - tag_set.add(ltype) - if ttype == 'other': - tags['type'] = ltype - else: - tags[ttype] = ltype - if ltype in ['train', 'validation', 'test']: - split_type = ltype - data['lines'].append({'baseline': baseline, - 'boundary': boundary, - 'text': text, - 'tags': tags, - 'split': split_type}) - - if len(tag_set) > 1: - data['tags'] = True - else: - data['tags'] = False - return data - - class XMLPage(object): type: Literal['baselines', 'bbox'] = 'baselines' @@ -629,12 +210,13 @@ def _parse_alto(self): tags[ttype] = ltype if ltype in ['train', 'validation', 'test']: split_type = ltype - self._lines[line.get('ID')] = {'baseline': baseline, - 'boundary': boundary, - 'text': text, - 'tags': tags, - 'split': split_type, - 'region': region_id} + self._lines[line.get('ID')] = BaselineLine(id=line.get('ID'), + baseline=baseline, + boundary=boundary, + text=text, + tags=tags, + split=split_type, + region=region_id) # register implicit reading order self._orders['line_implicit']['order'].append(line.get('ID')) @@ -804,13 +386,13 @@ def _parse_page(self): else: tmp_transkribus_line_order[int(reg_cus['readingOrder']['index'])].append((int(cs['readingOrder']['index']), line.get('id'))) - self._lines[line.get('id')] = {'baseline': baseline, - 'boundary': boundary, - 'text': text, - 'split': split_type, - 'tags': tags, - 'region': region.get('id')} - + self._lines[line.get('id')] = BaselineLine(id=line.get('id'), + baseline=baseline, + boundary=boundary, + text=text, + tags=tags, + split=split_type, + region=region.get('id')) # register implicit reading order self._orders['line_implicit']['order'].append(line.get('id')) From 9a787019fffa3eedbbb30cf6dd3250b097af0507 Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Tue, 16 May 2023 13:51:13 +0200 Subject: [PATCH 40/69] Use new container classes in blla.segment --- kraken/blla.py | 46 +++++++++++++++++++++++++++----------- kraken/containers.py | 30 +++++++++++++++++++++---- kraken/lib/segmentation.py | 16 ++----------- 3 files changed, 61 insertions(+), 31 deletions(-) diff --git a/kraken/blla.py b/kraken/blla.py index 767ec6867..442b34079 100644 --- a/kraken/blla.py +++ b/kraken/blla.py @@ -21,6 +21,7 @@ """ import PIL +import uuid import torch import logging import numpy as np @@ -37,8 +38,7 @@ from kraken.lib import vgsl, dataset from kraken.lib.util import is_bitonal, get_im_str from kraken.lib.exceptions import KrakenInputException, KrakenInvalidModelException -from kraken.lib.segmentation import (Segmentation, - polygonal_reading_order, +from kraken.lib.segmentation import (polygonal_reading_order, neural_reading_order, vectorize_lines, vectorize_regions, scale_polygonal_lines, @@ -134,7 +134,7 @@ def compute_segmentation_map(im: PIL.Image.Image, 'scal_im': scal_im} -def vec_regions(heatmap: torch.Tensor, cls_map: Dict, scale: float, **kwargs) -> Dict[str, List[List[Tuple[int, int]]]]: +def vec_regions(heatmap: torch.Tensor, cls_map: Dict, scale: float, **kwargs) -> Dict[str, List[Region]]: """ Computes regions from a stack of heatmaps, a class mapping, and scaling factor. @@ -154,8 +154,8 @@ def vec_regions(heatmap: torch.Tensor, cls_map: Dict, scale: float, **kwargs) -> for region_type, idx in cls_map['regions'].items(): logger.debug(f'Vectorizing regions of type {region_type}') regions[region_type] = vectorize_regions(heatmap[idx]) - for reg_id, regs in regions.items(): - regions[reg_id] = scale_regions(regs, scale) + for reg_type, regs in regions.items(): + regions[reg_type] = [Region(id=uuid.uuid4(), boundary=x, tags={'type': reg_type}) for x in scale_regions(regs, scale)] return regions @@ -218,6 +218,7 @@ def vec_lines(heatmap: torch.Tensor, lines = [] reg_pols = [geom.Polygon(x) for x in regions] + line_regs = [] for bl_idx in range(len(baselines)): bl = baselines[bl_idx] mid_point = geom.LineString(bl[1]).interpolate(0.5, normalized=True) @@ -226,7 +227,6 @@ def vec_lines(heatmap: torch.Tensor, for reg_idx, reg_pol in enumerate(reg_pols): if reg_pol.contains(mid_point): suppl_obj.append(regions[reg_idx]) - pol = calculate_polygonal_environment( baselines=[bl[1]], im_feats=im_feats, @@ -239,7 +239,7 @@ def vec_lines(heatmap: torch.Tensor, logger.debug('Scaling vectorized lines') sc = scale_polygonal_lines([x[1:] for x in lines], scale) - lines = list(zip([x[0] for x in lines], [x[0] for x in sc], [x[1] for x in sc])) + lines = list(zip([x[0] for x in lines], [x[0] for x in sc], [x[1] for x in sc], line_regs)) return [{'tags': {'type': bl_type}, 'baseline': bl, 'boundary': pl} for bl_type, bl, pl in lines] @@ -250,7 +250,7 @@ def segment(im: PIL.Image.Image, model: Union[List[vgsl.TorchVGSLModel], vgsl.TorchVGSLModel] = None, device: str = 'cpu', raise_on_error: bool = False, - autocast: bool = False) -> Dict[str, Any]: + autocast: bool = False) -> Segmentation: r""" Segments a page into text lines using the baseline segmenter. @@ -337,6 +337,10 @@ def segment(im: PIL.Image.Image, logger.debug(f'Baseline location: {loc}') rets = compute_segmentation_map(im, mask, net, device, autocast=autocast) _regions = vec_regions(**rets) + for reg_key, reg_val in vec_regions(**rets).items(): + if reg_key not in regions: + regions[reg_key] = [] + regions[reg_key].extend(reg_val) # flatten regions for line ordering/fetch bounding regions line_regs = [] @@ -346,8 +350,8 @@ def segment(im: PIL.Image.Image, if rets['bounding_regions'] is not None and cls in rets['bounding_regions']: suppl_obj.extend(regs) # convert back to net scale - suppl_obj = scale_regions(suppl_obj, 1/rets['scale']) - line_regs = scale_regions(line_regs, 1/rets['scale']) + suppl_obj = scale_regions([x.boundary for x in suppl_obj], 1/rets['scale']) + line_regs = scale_regions([x.boundary for x in line_regs], 1/rets['scale']) _lines = vec_lines(**rets, regions=line_regs, @@ -359,7 +363,7 @@ def segment(im: PIL.Image.Image, if 'ro_model' in net.aux_layers: logger.info(f'Using reading order model found in segmentation model {net}.') _order = neural_reading_order(lines=_lines, - regions=regions, + regions=_regions, text_direction=text_direction[-2:], model=net.aux_layers['ro_model'], im_size=im.size, @@ -388,9 +392,25 @@ def segment(im: PIL.Image.Image, else: script_detection = False + # create objects and assign IDs + blls = [] + reg_idx = 0 + _shp_regs = {} + for reg_type, rgs in regions.items(): + for reg in rgs: + _shp_regs[reg.id] = geom.Polygon(reg.boundary) + + for idx, line in enumerate(lines): + line_regs = [] + for reg_id, reg in _shp_regs.items(): + mid_point = geom.LineString(line[1]).interpolate(0.5, normalized=True) + if reg.contains(mid_point): + line_regs.append(reg_id) + blls.append(BaselineLine(id=f'line_{idx}', baseline=line[1], boundary=line[2], tags={'type': line[0]}, regions=line_regs)) + return Segmentation(text_direction=text_direction, type='baselines', - lines=lines, - regions=regions, + lines=blls, + regions=_regs, script_detection=script_detection, line_orders=[order]) diff --git a/kraken/containers.py b/kraken/containers.py index a07848f92..3c12f2f0b 100644 --- a/kraken/containers.py +++ b/kraken/containers.py @@ -24,7 +24,9 @@ class BaselineLine: base_dir: Optional[Literal['L', 'R']] = None type: str = 'baselines' image: Optional[PIL.Image.Image] = None - + tags: Optional[Dict[str, str]] = None + split: Optional[Literal['train', 'validation', 'test'] = None + regions: Optional[List[str]] = None @dataclass class BBoxLine: @@ -39,6 +41,20 @@ class BBoxLine: base_dir: Optional[Literal['L', 'R']] = None type: str = 'bbox' image: Optional[PIL.Image.Image] = None + tags: Optional[Dict[str, str]] = None + split: Optional[Literal['train', 'validation', 'test'] = None + regions: Optional[List[str]] = None + + +@dataclass +class Region: + """ + + """ + id: str + boundary: List[Tuple[int, int]] + image: Optional[PIL.Image.Image] = None + tags: Optional[Dict[str, str]] = None @dataclass @@ -51,7 +67,7 @@ class Segmentation: text_direction: Literal['horizontal-lr', 'horizontal-rl', 'vertical-lr', 'vertical-rl'] script_detection: bool lines: Sequence[Union[BaselineLine, BBoxLine]] - regions: Dict[str, List] + regions: Dict[str, List[Region]] line_orders: List[List[int]] @@ -276,7 +292,10 @@ def _reorder(self, base_dir: Optional[str] = None) -> 'BaselineOCRRecord': boundary=self.boundary, text=self.text, base_dir=self._line_base_dir, - image=self.image) + image=self.image, + tags=self.tags, + split=self.split, + region=self.region) rec = BaselineOCRRecord(prediction=prediction, cuts=cuts, confidences=confidences, @@ -412,7 +431,10 @@ def _reorder(self, base_dir: Optional[Literal['L', 'R']] = None) -> 'BBoxOCRReco bbox=self.bbox, text=self.text, base_dir=self._line_base_dir, - image=self.image) + image=self.image, + tags=self.tags, + split=self.split, + region=self.region) rec = BBoxOCRRecord(prediction=prediction, cuts=cuts, confidences=confidences, diff --git a/kraken/lib/segmentation.py b/kraken/lib/segmentation.py index d821d9ce6..8f6b9ccff 100644 --- a/kraken/lib/segmentation.py +++ b/kraken/lib/segmentation.py @@ -22,7 +22,6 @@ import shapely.geometry as geom import torch.nn.functional as F -from dataclasses import dataclass from collections import defaultdict from PIL import Image @@ -43,6 +42,7 @@ from typing import List, Tuple, Union, Dict, Any, Sequence, Optional, Literal +from kraken.containers import Segmentation, BaselineLine, BBoxLine from kraken.lib import default_specs from kraken.lib.exceptions import KrakenInputException @@ -60,19 +60,7 @@ 'scale_polygonal_lines', 'scale_regions', 'compute_polygon_section', - 'extract_polygons', - 'Segmentation'] - - -@dataclass -class Segmentation: - type: Literal['baselines', 'bbox'] - imagename: str - text_direction: Literal['horizontal-lr', 'horizontal-rl', 'vertical-lr', 'vertical-rl'] - script_detection: bool - lines: List - regions: Dict[str, List] - line_orders: List[List[int]] + 'extract_polygons'] def reading_order(lines: Sequence[Tuple[slice, slice]], text_direction: Literal['lr', 'rl'] = 'lr') -> np.ndarray: From 83b17fae9f40ec12692f5f39533dcf3f8b742c0d Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Tue, 16 May 2023 13:55:51 +0200 Subject: [PATCH 41/69] BBoxLine/Segmentation in legacy segmenter --- kraken/pageseg.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/kraken/pageseg.py b/kraken/pageseg.py index 355af4f5d..8bf96b64c 100644 --- a/kraken/pageseg.py +++ b/kraken/pageseg.py @@ -29,7 +29,7 @@ from kraken.lib import morph, sl from kraken.lib.util import pil2array, is_bitonal, get_im_str from kraken.lib.exceptions import KrakenInputException -from kraken.lib.segmentation import reading_order, topsort, Segmentation +from kraken.lib.segmentation import reading_order, topsort, Segmentation, BBoxLine __all__ = ['segment'] @@ -423,10 +423,11 @@ def segment(im, if isinstance(pad, int): pad = (pad, pad) lines = [(max(x[0]-pad[0], 0), x[1], min(x[2]+pad[1], im.size[0]), x[3]) for x in lines] + lines = [BBoxLine(id=uuid.uuid4(), bbox=line) for line in rotate_lines(lines, 360-angle, offset).tolist()] return Segmentation(text_direction=text_direction, type='bbox', regions=None, line_orders=None, - lines=rotate_lines(lines, 360-angle, offset).tolist(), + lines=lines, script_detection=False) From c390e68c6f4455d739e3895f379aa1645cad236f Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Tue, 16 May 2023 13:56:12 +0200 Subject: [PATCH 42/69] UUIDs in blla lines --- kraken/blla.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kraken/blla.py b/kraken/blla.py index 442b34079..84ce62086 100644 --- a/kraken/blla.py +++ b/kraken/blla.py @@ -400,13 +400,13 @@ def segment(im: PIL.Image.Image, for reg in rgs: _shp_regs[reg.id] = geom.Polygon(reg.boundary) - for idx, line in enumerate(lines): + for line in lines: line_regs = [] for reg_id, reg in _shp_regs.items(): mid_point = geom.LineString(line[1]).interpolate(0.5, normalized=True) if reg.contains(mid_point): line_regs.append(reg_id) - blls.append(BaselineLine(id=f'line_{idx}', baseline=line[1], boundary=line[2], tags={'type': line[0]}, regions=line_regs)) + blls.append(BaselineLine(id=uuid.uuid4(), baseline=line[1], boundary=line[2], tags={'type': line[0]}, regions=line_regs)) return Segmentation(text_direction=text_direction, type='baselines', From b9753de1dd181b493c810f229cb1823b493fdd60 Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Tue, 16 May 2023 14:48:56 +0200 Subject: [PATCH 43/69] Segmentation/BBoxLine/BaselineLine containers in rpred --- kraken/rpred.py | 171 +++++++++++++++++++++++------------------------- 1 file changed, 83 insertions(+), 88 deletions(-) diff --git a/kraken/rpred.py b/kraken/rpred.py index 40bcba765..3354b56b6 100644 --- a/kraken/rpred.py +++ b/kraken/rpred.py @@ -109,14 +109,13 @@ def __init__(self, if bounds.type == 'baselines': valid_norm = False self.next_iter = self._recognize_baseline_line - tags = set() - for x in bounds.lines: - tags.update(x['tags'].values()) else: valid_norm = True - self.seg_key = 'boxes' self.next_iter = self._recognize_box_line - tags = set(x[0] for line in bounds.lines for x in line) + + tags = set() + for x in bounds.lines: + tags.update(x.tags.values()) im_str = get_im_str(im) logger.info(f'Running {len(nets)} multi-script recognizers on {im_str} with {self.len} lines') @@ -149,70 +148,75 @@ def __init__(self, self.tags_ignore = tags_ignore def _recognize_box_line(self, line): - flat_box = [point for box in line['boxes'][0] for point in box[1]] + flat_box = [point for box in line.bbox for point in box] xmin, xmax = min(flat_box[::2]), max(flat_box[::2]) ymin, ymax = min(flat_box[1::2]), max(flat_box[1::2]) line_bbox = ((xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin)) prediction = '' cuts = [] confidences = [] - for tag, (box, coords) in zip(map(lambda x: x[0], line['boxes'][0]), - extract_polygons(self.im, {'text_direction': line['text_direction'], - 'boxes': map(lambda x: x[1], line['boxes'][0])})): - self.box = box - # skip if tag is set to ignore - if self.tags_ignore is not None and tag in self.tags_ignore: - logger.warning(f'Ignoring {tag} line segment.') - continue - # check if boxes are non-zero in any dimension - if 0 in box.size: - logger.warning(f'bbox {coords} with zero dimension. Emitting empty record.') - return BBoxOCRRecord('', (), (), coords) - # try conversion into tensor - try: - logger.debug('Preparing run.') - line = self.ts[tag](box) - except Exception: - logger.warning(f'Conversion of line {coords} failed. Emitting empty record..') - return BBoxOCRRecord('', (), (), coords) - - # check if line is non-zero - if line.max() == line.min(): - logger.warning('Empty run. Emitting empty record.') - return BBoxOCRRecord('', (), (), coords) - - _, net = self._resolve_tags_to_model({'type': tag}, self.nets) - - logger.debug(f'Forward pass with model {tag}.') - preds = net.predict(line.unsqueeze(0))[0] - - # calculate recognized LSTM locations of characters - logger.debug('Convert to absolute coordinates') - # calculate recognized LSTM locations of characters - # scale between network output and network input - self.net_scale = line.shape[2]/net.outputs.shape[2] - # scale between network input and original line - self.in_scale = box.size[0]/(line.shape[2]-2*self.pad) - - pred = ''.join(x[0] for x in preds) - pos = [] - conf = [] - - for _, start, end, c in preds: - if self.bounds.text_direction.startswith('horizontal'): - xmin = coords[0] + self._scale_val(start, 0, self.box.size[0]) - xmax = coords[0] + self._scale_val(end, 0, self.box.size[0]) - pos.append([[xmin, coords[1]], [xmin, coords[3]], [xmax, coords[3]], [xmax, coords[1]]]) - else: - ymin = coords[1] + self._scale_val(start, 0, self.box.size[1]) - ymax = coords[1] + self._scale_val(end, 0, self.box.size[1]) - pos.append([[coords[0], ymin], [coords[2], ymin], [coords[2], ymax], [coords[0], ymax]]) - conf.append(c) - prediction += pred - cuts.extend(pos) - confidences.extend(conf) - - rec = BBoxOCRRecord(prediction, cuts, confidences, line_bbox) + line.text_direction = self.bounds.text_direction + + if self.tags_ignore is not None: + for tag in line.tags.values(): + if tag in self.tags_ignore: + logger.info(f'Ignoring line segment with tags {line.tags} based on {tag}.') + return BaselineOCRRecord('', [], [], line) + + tag, net = self._resolve_tags_to_model(line.tags, self.nets) + + box, coords = next(extract_polygons(self.im, line)) + self.box = box + + # check if boxes are non-zero in any dimension + if 0 in box.size: + logger.warning(f'bbox {line} with zero dimension. Emitting empty record.') + return BBoxOCRRecord('', (), (), line) + # try conversion into tensor + try: + logger.debug('Preparing run.') + ts_box = self.ts[tag](box) + except Exception: + logger.warning(f'Conversion of line {line} failed. Emitting empty record..') + return BBoxOCRRecord('', (), (), line) + + # check if line is non-zero + if ts_box.max() == ts_box.min(): + logger.warning('Empty run. Emitting empty record.') + return BBoxOCRRecord('', (), (), line) + + _, net = self._resolve_tags_to_model({'type': tag}, self.nets) + + logger.debug(f'Forward pass with model {tag}.') + preds = net.predict(ts_box.unsqueeze(0))[0] + + # calculate recognized LSTM locations of characters + logger.debug('Convert to absolute coordinates') + # calculate recognized LSTM locations of characters + # scale between network output and network input + self.net_scale = ts_box.shape[2]/net.outputs.shape[2] + # scale between network input and original line + self.in_scale = box.size[0]/(ts_box.shape[2]-2*self.pad) + + pred = ''.join(x[0] for x in preds) + pos = [] + conf = [] + + for _, start, end, c in preds: + if self.bounds.text_direction.startswith('horizontal'): + xmin = coords[0] + self._scale_val(start, 0, self.box.size[0]) + xmax = coords[0] + self._scale_val(end, 0, self.box.size[0]) + pos.append([[xmin, coords[1]], [xmin, coords[3]], [xmax, coords[3]], [xmax, coords[1]]]) + else: + ymin = coords[1] + self._scale_val(start, 0, self.box.size[1]) + ymax = coords[1] + self._scale_val(end, 0, self.box.size[1]) + pos.append([[coords[0], ymin], [coords[2], ymin], [coords[2], ymax], [coords[0], ymax]]) + conf.append(c) + prediction += pred + cuts.extend(pos) + confidences.extend(conf) + + rec = BBoxOCRRecord(prediction, cuts, confidences, line) if self.bidi_reordering: logger.debug('BiDi reordering record.') return rec.logical_order(base_dir=self.bidi_reordering if self.bidi_reordering in ('L', 'R') else None) @@ -222,41 +226,41 @@ def _recognize_box_line(self, line): def _recognize_baseline_line(self, line): if self.tags_ignore is not None: - for tag in line['lines'][0]['tags'].values(): + for tag in line.tags.values(): if tag in self.tags_ignore: - logger.info(f'Ignoring line segment with tags {line["lines"][0]["tags"]} based on {tag}.') - return BaselineOCRRecord('', [], [], line['lines'][0]) + logger.info(f'Ignoring line segment with tags {line.tags} based on {tag}.') + return BaselineOCRRecord('', [], [], line) try: box, coords = next(extract_polygons(self.im, line)) except KrakenInputException as e: logger.warning(f'Extracting line failed: {e}') - return BaselineOCRRecord('', [], [], line['lines'][0]) + return BaselineOCRRecord('', [], [], line) self.box = box - tag, net = self._resolve_tags_to_model(coords['tags'], self.nets) + tag, net = self._resolve_tags_to_model(line.tags, self.nets) # check if boxes are non-zero in any dimension if 0 in box.size: - logger.warning(f'bbox {coords} with zero dimension. Emitting empty record.') - return BaselineOCRRecord('', [], [], coords) + logger.warning(f'{line} with zero dimension. Emitting empty record.') + return BaselineOCRRecord('', [], [], line) # try conversion into tensor try: - line = self.ts[tag](box) + ts_box = self.ts[tag](box) except Exception as e: logger.warning(f'Tensor conversion failed with {e}. Emitting empty record.') - return BaselineOCRRecord('', [], [], coords) + return BaselineOCRRecord('', [], [], line) # check if line is non-zero - if line.max() == line.min(): + if ts_box.max() == ts_box.min(): logger.warning('Empty line after tensor conversion. Emitting empty record.') - return BaselineOCRRecord('', [], [], coords) + return BaselineOCRRecord('', [], [], line) - preds = net.predict(line.unsqueeze(0))[0] + preds = net.predict(ts_box.unsqueeze(0))[0] # calculate recognized LSTM locations of characters # scale between network output and network input - self.net_scale = line.shape[2]/net.outputs.shape[2] + self.net_scale = ts_box.shape[2]/net.outputs.shape[2] # scale between network input and original line - self.in_scale = box.size[0]/(line.shape[2]-2*self.pad) + self.in_scale = box.size[0]/(ts_box.shape[2]-2*self.pad) # XXX: fix bounding box calculation ocr_record for multi-codepoint labels. pred = ''.join(x[0] for x in preds) @@ -266,7 +270,7 @@ def _recognize_baseline_line(self, line): pos.append((self._scale_val(start, 0, self.box.size[0]), self._scale_val(end, 0, self.box.size[0]))) conf.append(c) - rec = BaselineOCRRecord(pred, pos, conf, coords) + rec = BaselineOCRRecord(pred, pos, conf, line) if self.bidi_reordering: logger.debug('BiDi reordering record.') return rec.logical_order(base_dir=self.bidi_reordering if self.bidi_reordering in ('L', 'R') else None) @@ -276,8 +280,7 @@ def _recognize_baseline_line(self, line): def __next__(self): bound = self.bounds - setattr(bound, self.seg_key, [next(self.line_iter)]) - return self.next_iter(bound) + return self.next_iter(next(self.line_iter)) def __iter__(self): return self @@ -312,14 +315,6 @@ def rpred(network: TorchSeqRecognizer, An ocr_record containing the recognized text, absolute character positions, and confidence values for each character. """ - bounds = copy.deepcopy(bounds) - if bounds.type == 'bbox': - boxes = bounds.lines - rewrite_boxes = [] - for box in boxes: - rewrite_boxes.append([('default', box)]) - bounds.lines = rewrite_boxes - bounds.script_detection = True return mm_rpred(defaultdict(lambda: network), im, bounds, pad, bidi_reordering) From 11a3a199f98471692adbe5c1b9bfe8ada60973e0 Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Tue, 16 May 2023 16:39:21 +0200 Subject: [PATCH 44/69] Add to_container() method to XMLPage --- kraken/containers.py | 19 ++++++++++--------- kraken/lib/xml.py | 14 +++++++++++++- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/kraken/containers.py b/kraken/containers.py index 3c12f2f0b..f09bd40ba 100644 --- a/kraken/containers.py +++ b/kraken/containers.py @@ -25,7 +25,7 @@ class BaselineLine: type: str = 'baselines' image: Optional[PIL.Image.Image] = None tags: Optional[Dict[str, str]] = None - split: Optional[Literal['train', 'validation', 'test'] = None + split: Optional[Literal['train', 'validation', 'test']] = None regions: Optional[List[str]] = None @dataclass @@ -42,9 +42,9 @@ class BBoxLine: type: str = 'bbox' image: Optional[PIL.Image.Image] = None tags: Optional[Dict[str, str]] = None - split: Optional[Literal['train', 'validation', 'test'] = None + split: Optional[Literal['train', 'validation', 'test']] = None regions: Optional[List[str]] = None - + text_direction: Literal['horizontal-lr', 'horizontal-rl', 'vertical-lr', 'vertical-rl'] = 'horizontal-lr' @dataclass class Region: @@ -68,7 +68,7 @@ class Segmentation: script_detection: bool lines: Sequence[Union[BaselineLine, BBoxLine]] regions: Dict[str, List[Region]] - line_orders: List[List[int]] + line_orders: Optional[List[List[int]]] = None class ocr_record(ABC): @@ -225,7 +225,7 @@ def __getitem__(self, key: Union[int, slice]): def cuts(self) -> Sequence[Tuple[int, int]]: return tuple([compute_polygon_section(self.baseline, self.line, cut[0], cut[1]) for cut in self._cuts]) - def logical_order(self, base_dir: Optional[str] = None) -> 'BaselineOCRRecord': + def logical_order(self, base_dir: Optional[Literal['L', 'R']] = None) -> 'BaselineOCRRecord': """ Returns the OCR record in Unicode logical order, i.e. in the order the characters in the line would be read by a human. @@ -241,7 +241,7 @@ def logical_order(self, base_dir: Optional[str] = None) -> 'BaselineOCRRecord': else: return self - def display_order(self, base_dir: Optional[str] = None) -> 'BaselineOCRRecord': + def display_order(self, base_dir: Optional[Literal['L', 'R']] = None) -> 'BaselineOCRRecord': """ Returns the OCR record in Unicode display order, i.e. ordered from left to right inside the line. @@ -257,7 +257,7 @@ def display_order(self, base_dir: Optional[str] = None) -> 'BaselineOCRRecord': else: return self._reorder(base_dir) - def _reorder(self, base_dir: Optional[str] = None) -> 'BaselineOCRRecord': + def _reorder(self, base_dir: Optional[Literal['L', 'R']] = None) -> 'BaselineOCRRecord': """ Reorder the record using the BiDi algorithm. """ @@ -312,14 +312,15 @@ class BBoxOCRRecord(ocr_record, BBoxLine): """ type = 'bbox' - def __init__(self, prediction: str, + def __init__(self, + prediction: str, cuts: Sequence[Tuple[Tuple[int, int], Tuple[int, int], Tuple[int, int], Tuple[int, int]]], confidences: Sequence[float], line: BBoxLine, - base_dir: Optional['L', 'R'], + base_dir: Optional[Literal['L', 'R']], display_order: bool = True) -> None: if line.type != 'bbox': raise TypeError('Invalid argument type (non-bbox line)') diff --git a/kraken/lib/xml.py b/kraken/lib/xml.py index ec55772e6..01870cd5c 100644 --- a/kraken/lib/xml.py +++ b/kraken/lib/xml.py @@ -27,7 +27,7 @@ from typing import Union, Dict, Any, Sequence, Tuple, Literal, Optional, List from collections import defaultdict -from kraken.containers import BaselineLine +from kraken.containers import Segmentation, BaselineLine from kraken.lib.segmentation import calculate_polygonal_environment from kraken.lib.exceptions import KrakenInputException @@ -584,3 +584,15 @@ def __str__(self): def __repr__(self): return f'XMLPage(filename={self.filename}, filetype={self.filetype})' + + def to_container(self) -> Segmentation: + """ + Returns a Segmentation object. + """ + return Segmentation(type='baselines', + imagename=self.imagename, + text_direction='horizontal_lr', + script_detection=True, + lines=self.get_sorted_lines(), + regions=self._regions, + line_orders=None) From 4210874e2643f107123d1b74ab9b7d93f19a1381 Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Mon, 22 May 2023 14:31:27 +0200 Subject: [PATCH 45/69] make compilation work with new container objects --- kraken/lib/arrow_dataset.py | 50 ++++++++++++++++++++++--------------- kraken/lib/segmentation.py | 6 ++--- kraken/lib/xml.py | 20 +++++++-------- 3 files changed, 43 insertions(+), 33 deletions(-) diff --git a/kraken/lib/arrow_dataset.py b/kraken/lib/arrow_dataset.py index 149c58590..4ca12d9bb 100755 --- a/kraken/lib/arrow_dataset.py +++ b/kraken/lib/arrow_dataset.py @@ -29,8 +29,8 @@ from typing import Optional, List, Union, Callable, Tuple, Dict from multiprocessing import Pool from kraken.lib import functional_im_transforms as F_t -from kraken.lib.segmentation import extract_polygons -from kraken.lib.xml import parse_xml, parse_alto, parse_page +from kraken.lib.segmentation import extract_polygons, Segmentation +from kraken.lib.xml import XMLPage from kraken.lib.util import is_bitonal, make_printable from kraken.lib.exceptions import KrakenInputException from os import extsep, PathLike @@ -43,27 +43,33 @@ def _extract_line(xml_record, skip_empty_lines: bool = True): lines = [] try: - im = Image.open(xml_record['image']) + im = Image.open(xml_record.imagename) except (FileNotFoundError, UnidentifiedImageError): return lines, None, None if is_bitonal(im): im = im.convert('1') - seg_key = 'lines' if 'lines' in xml_record else 'boxes' - recs = xml_record.pop(seg_key) + recs = xml_record.lines.values() for idx, rec in enumerate(recs): + seg = Segmentation(text_direction='horizontal-lr', + imagename=xml_record.imagename, + type=xml_record.type, + lines=[rec], + regions=None, + script_detection=False, + line_orders=None) try: - line_im, line = next(extract_polygons(im, {**xml_record, seg_key: [rec]})) + line_im, line = next(extract_polygons(im, seg)) except KrakenInputException: logger.warning(f'Invalid line {idx} in {im.filename}') continue except Exception as e: logger.warning(f'Unexpected exception {e} from line {idx} in {im.filename}') continue - if not line['text'] and skip_empty_lines: + if not line.text and skip_empty_lines: continue fp = io.BytesIO() line_im.save(fp, format='png') - lines.append({'text': line['text'], 'im': fp.getvalue()}) + lines.append({'text': line.text, 'im': fp.getvalue()}) return lines, im.mode @@ -90,6 +96,7 @@ def parse_path(path: Union[str, PathLike], gt = fp.read().strip('\n\r') if not gt and skip_empty_lines: raise KrakenInputException(f'No text for ground truth line {path}.') + return {'image': path, 'lines': [{'text': gt}]} @@ -135,12 +142,8 @@ def build_binary_dataset(files: Optional[List[Union[str, PathLike, Dict]]] = Non logger.info('Parsing XML files') extract_fn = partial(_extract_line, skip_empty_lines=skip_empty_lines) parse_fn = None - if format_type == 'xml': - parse_fn = parse_xml - elif format_type == 'alto': - parse_fn = parse_alto - elif format_type == 'page': - parse_fn = parse_page + if format_type in ['xml', 'alto', 'page']: + parse_fn = XMLPage elif format_type == 'path': if not ignore_splits: logger.warning('ignore_splits is False and format_type is path. Will not serialize splits.') @@ -163,10 +166,13 @@ def build_binary_dataset(files: Optional[List[Union[str, PathLike, Dict]]] = Non logger.warning(f'Invalid input file {doc}') continue try: - name_ext = str(data['image']).split(extsep, 1) - if name_ext[1] == 'gt.txt': - data['image'] = name_ext[0] + '.png' - with open(data['image'], 'rb') as fp: + if format_type in ['xml', 'alto', 'page']: + imagename = data.imagename + else: + name_ext = str(data['image']).split(extsep, 1) + imagename = name_ext[0] + '.png' + data['image'] = imagename + with open(imagename, 'rb') as fp: Image.open(fp) except (FileNotFoundError, UnidentifiedImageError) as e: logger.warning(f'Could not open file {e.filename} in {doc}') @@ -181,9 +187,13 @@ def build_binary_dataset(files: Optional[List[Union[str, PathLike, Dict]]] = Non alphabet = Counter() num_lines = 0 for doc in docs: - for line in doc['lines']: + if format_type in ['xml', 'alto', 'page']: + lines = doc.lines.values() + else: + lines = doc['lines'] + for line in lines: num_lines += 1 - alphabet.update(line['text']) + alphabet.update(line.text) callback(0, num_lines) diff --git a/kraken/lib/segmentation.py b/kraken/lib/segmentation.py index 8f6b9ccff..34e101cdd 100644 --- a/kraken/lib/segmentation.py +++ b/kraken/lib/segmentation.py @@ -1058,10 +1058,10 @@ def extract_polygons(im: Image.Image, bounds: Segmentation) -> Image.Image: im = np.array(im) for line in bounds.lines: - if line['boundary'] is None: + if line.boundary is None: raise KrakenInputException('No boundary given for line') - pl = np.array(line['boundary']) - baseline = np.array(line['baseline']) + pl = np.array(line.boundary) + baseline = np.array(line.baseline) c_min, c_max = int(pl[:, 0].min()), int(pl[:, 0].max()) r_min, r_max = int(pl[:, 1].min()), int(pl[:, 1].max()) diff --git a/kraken/lib/xml.py b/kraken/lib/xml.py index 01870cd5c..3f60fa7ee 100644 --- a/kraken/lib/xml.py +++ b/kraken/lib/xml.py @@ -27,7 +27,7 @@ from typing import Union, Dict, Any, Sequence, Tuple, Literal, Optional, List from collections import defaultdict -from kraken.containers import Segmentation, BaselineLine +from kraken.containers import Segmentation, BaselineLine, Region from kraken.lib.segmentation import calculate_polygonal_environment from kraken.lib.exceptions import KrakenInputException @@ -167,7 +167,7 @@ def _parse_alto(self): if rtype is None: rtype = alto_regions[region.tag.split('}')[-1]] region_id = region.get('ID') - region_data[rtype].append({'id': region_id, 'boundary': boundary}) + region_data[rtype].append(Region(id=region_id, boundary=coords, tags={'type': rtype})) # register implicit reading order self._orders['region_implicit']['order'].append(region_id) @@ -216,7 +216,7 @@ def _parse_alto(self): text=text, tags=tags, split=split_type, - region=region_id) + regions=[region_id]) # register implicit reading order self._orders['line_implicit']['order'].append(line.get('ID')) @@ -328,7 +328,7 @@ def _parse_page(self): # fall back to default region type if nothing is given if not rtype: rtype = page_regions[region.tag.split('}')[-1]] - region_data[rtype].append({'id': region.get('id'), 'boundary': coords}) + region_data[rtype].append(Region(id=region.get('id'), boundary=coords, tags={'type': rtype})) # register implicit reading order self._orders['region_implicit']['order'].append(region.get('id')) @@ -392,7 +392,7 @@ def _parse_page(self): text=text, tags=tags, split=split_type, - region=region.get('id')) + regions=[region.get('id')]) # register implicit reading order self._orders['line_implicit']['order'].append(line.get('id')) @@ -491,7 +491,7 @@ def get_sorted_regions(self, ro='region_implicit'): if ro not in self.reading_orders: raise ValueError(f'Unknown reading order {ro}') - regions = {reg['id']: key for key, regs in self.regions.items() for reg in regs} + regions = {reg.id: key for key, regs in self.regions.items() for reg in regs} def _traverse_ro(el): _ro = [] @@ -500,7 +500,7 @@ def _traverse_ro(el): else: # if region directly append to ro if el in regions.keys(): - return [reg for reg in self.regions[regions[el]] if reg['id'] == el][0] + return [reg for reg in self.regions[regions[el]] if reg.id == el][0] else: raise ValueError(f'Invalid reading order {ro}') return _ro @@ -516,17 +516,17 @@ def get_sorted_lines_by_region(self, region, ro='line_implicit'): raise ValueError(f'Unknown reading order {ro}') if self.reading_orders[ro]['is_total'] is False: raise ValueError('Fetching lines by region of a non-total order is not supported') - lines = [(id, line) for id, line in self._lines.items() if line['region'] == region] + lines = [(id, line) for id, line in self._lines.items() if line.regions[0] == region] for line in lines: if line[0] not in self.reading_orders[ro]['order']: raise ValueError('Fetching lines by region is only possible for flat orders') return sorted(lines, key=lambda k: self.reading_orders[ro]['order'].index(k[0])) def get_lines_by_tag(self, key, value): - return {k: v for k, v in self._lines.items() if v['tags'].get(key) == value} + return {k: v for k, v in self._lines.items() if v.tags.get(key) == value} def get_lines_by_split(self, split: Literal['train', 'validation', 'test']): - return {k: v for k, v in self._lines.items() if v['tags'].get('split') == split} + return {k: v for k, v in self._lines.items() if v.tags.get('split') == split} @property def tags(self): From 09ad27eee7a0d476d6b941b4461d6018b5199046 Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Mon, 22 May 2023 14:31:50 +0200 Subject: [PATCH 46/69] Fix imports in blla --- kraken/blla.py | 1 + 1 file changed, 1 insertion(+) diff --git a/kraken/blla.py b/kraken/blla.py index 84ce62086..d27ac071b 100644 --- a/kraken/blla.py +++ b/kraken/blla.py @@ -36,6 +36,7 @@ from skimage.filters import sobel from kraken.lib import vgsl, dataset +from kraken.containers import Region, Segmentation from kraken.lib.util import is_bitonal, get_im_str from kraken.lib.exceptions import KrakenInputException, KrakenInvalidModelException from kraken.lib.segmentation import (polygonal_reading_order, From b491122e0cb755a22a7b978d9ee0fad60f50010a Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Mon, 22 May 2023 14:32:09 +0200 Subject: [PATCH 47/69] typo in ketos utils --- kraken/ketos/util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kraken/ketos/util.py b/kraken/ketos/util.py index 009190027..b37b298b2 100644 --- a/kraken/ketos/util.py +++ b/kraken/ketos/util.py @@ -54,7 +54,7 @@ def message(msg, **styles): def to_ptl_device(device: str) -> Tuple[str, Optional[List[int]]]: - if device in ['cpu', 'mps']]): + if device in ['cpu', 'mps']: return device, 'auto' elif any([device.startswith(x) for x in ['tpu', 'cuda', 'hpu', 'ipu']]): dev, idx = device.split(':') From a9cfaf095d6a31020412574f703fce5befaba5d6 Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Tue, 23 May 2023 14:53:42 +0200 Subject: [PATCH 48/69] make path compilation work again --- kraken/lib/arrow_dataset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kraken/lib/arrow_dataset.py b/kraken/lib/arrow_dataset.py index 4ca12d9bb..694fd4279 100755 --- a/kraken/lib/arrow_dataset.py +++ b/kraken/lib/arrow_dataset.py @@ -193,7 +193,7 @@ def build_binary_dataset(files: Optional[List[Union[str, PathLike, Dict]]] = Non lines = doc['lines'] for line in lines: num_lines += 1 - alphabet.update(line.text) + alphabet.update(line.text if format_type in ['xml', 'alto', 'page'] else line['text']) callback(0, num_lines) From 7e8fcf794b4e50040c266f12694b3aeb4228be23 Mon Sep 17 00:00:00 2001 From: Colin Brisson Date: Sat, 17 Jun 2023 14:17:27 +0000 Subject: [PATCH 49/69] add __getitem__ to the Baseline class --- kraken/containers.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kraken/containers.py b/kraken/containers.py index f09bd40ba..1d48698a3 100644 --- a/kraken/containers.py +++ b/kraken/containers.py @@ -28,6 +28,9 @@ class BaselineLine: split: Optional[Literal['train', 'validation', 'test']] = None regions: Optional[List[str]] = None + def __getitem__(self, item): + return getattr(self, item) + @dataclass class BBoxLine: """ From 8f34a9ae6086c2a465591f86cfe648b42c41f4bb Mon Sep 17 00:00:00 2001 From: Colin Brisson Date: Sat, 17 Jun 2023 14:18:09 +0000 Subject: [PATCH 50/69] remove preparse_xml_data import statement --- kraken/lib/train.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kraken/lib/train.py b/kraken/lib/train.py index 253075253..81296eb27 100644 --- a/kraken/lib/train.py +++ b/kraken/lib/train.py @@ -33,7 +33,7 @@ from pytorch_lightning.callbacks import Callback, EarlyStopping, BaseFinetuning, LearningRateMonitor from kraken.lib import models, vgsl, default_specs, progress -from kraken.lib.xml import preparse_xml_data +# from kraken.lib.xml import preparse_xml_data from kraken.lib.util import make_printable from kraken.lib.codec import PytorchCodec from kraken.lib.dataset import (ArrowIPCRecognitionDataset, BaselineSet, From fe897c7da36142b1d9ddb9be976a03f43e2efb3c Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Sun, 2 Jul 2023 11:12:20 +0200 Subject: [PATCH 51/69] Container classes in segmentation --- kraken/blla.py | 46 +++++++++++++++++++------------------- kraken/kraken.py | 7 ++---- kraken/lib/segmentation.py | 18 +++++++-------- kraken/pageseg.py | 4 +++- 4 files changed, 36 insertions(+), 39 deletions(-) diff --git a/kraken/blla.py b/kraken/blla.py index d27ac071b..6d65de794 100644 --- a/kraken/blla.py +++ b/kraken/blla.py @@ -36,7 +36,7 @@ from skimage.filters import sobel from kraken.lib import vgsl, dataset -from kraken.containers import Region, Segmentation +from kraken.containers import Region, Segmentation, BaselineLine from kraken.lib.util import is_bitonal, get_im_str from kraken.lib.exceptions import KrakenInputException, KrakenInvalidModelException from kraken.lib.segmentation import (polygonal_reading_order, @@ -44,6 +44,7 @@ vectorize_lines, vectorize_regions, scale_polygonal_lines, calculate_polygonal_environment, + is_in_region, scale_regions) __all__ = ['segment'] @@ -156,7 +157,7 @@ def vec_regions(heatmap: torch.Tensor, cls_map: Dict, scale: float, **kwargs) -> logger.debug(f'Vectorizing regions of type {region_type}') regions[region_type] = vectorize_regions(heatmap[idx]) for reg_type, regs in regions.items(): - regions[reg_type] = [Region(id=uuid.uuid4(), boundary=x, tags={'type': reg_type}) for x in scale_regions(regs, scale)] + regions[reg_type] = [Region(id=str(uuid.uuid4()), boundary=x, tags={'type': reg_type}) for x in scale_regions(regs, scale)] return regions @@ -205,6 +206,7 @@ def vec_lines(heatmap: torch.Tensor, ... ] """ + st_sep = cls_map['aux']['_start_separator'] end_sep = cls_map['aux']['_end_separator'] @@ -219,28 +221,25 @@ def vec_lines(heatmap: torch.Tensor, lines = [] reg_pols = [geom.Polygon(x) for x in regions] - line_regs = [] for bl_idx in range(len(baselines)): bl = baselines[bl_idx] - mid_point = geom.LineString(bl[1]).interpolate(0.5, normalized=True) - + bl_ls = geom.LineString(bl[1]) suppl_obj = [x[1] for x in baselines[:bl_idx] + baselines[bl_idx+1:]] for reg_idx, reg_pol in enumerate(reg_pols): - if reg_pol.contains(mid_point): + if is_in_region(bl_ls, reg_pol): suppl_obj.append(regions[reg_idx]) - pol = calculate_polygonal_environment( - baselines=[bl[1]], - im_feats=im_feats, - suppl_obj=suppl_obj, - topline=topline, - raise_on_error=raise_on_error - ) + pol = calculate_polygonal_environment(baselines=[bl[1]], + im_feats=im_feats, + suppl_obj=suppl_obj, + topline=topline, + raise_on_error=raise_on_error) if pol[0] is not None: lines.append((bl[0], bl[1], pol[0])) logger.debug('Scaling vectorized lines') sc = scale_polygonal_lines([x[1:] for x in lines], scale) - lines = list(zip([x[0] for x in lines], [x[0] for x in sc], [x[1] for x in sc], line_regs)) + + lines = list(zip([x[0] for x in lines], [x[0] for x in sc], [x[1] for x in sc])) return [{'tags': {'type': bl_type}, 'baseline': bl, 'boundary': pl} for bl_type, bl, pl in lines] @@ -383,11 +382,6 @@ def segment(im: PIL.Image.Image, lines.extend(_lines) - # reorder lines - logger.debug(f'Reordering baselines with main RO function {reading_order_fn}.') - basic_lo = reading_order_fn(lines=lines, regions=regions, text_direction=text_direction[-2:]) - lines = [lines[idx] for idx in basic_lo] - if len(rets['cls_map']['baselines']) > 1: script_detection = True else: @@ -401,17 +395,23 @@ def segment(im: PIL.Image.Image, for reg in rgs: _shp_regs[reg.id] = geom.Polygon(reg.boundary) + # reorder lines + logger.debug(f'Reordering baselines with main RO function {reading_order_fn}.') + basic_lo = reading_order_fn(lines=lines, regions=_shp_regs.values(), text_direction=text_direction[-2:]) + lines = [lines[idx] for idx in basic_lo] + for line in lines: line_regs = [] for reg_id, reg in _shp_regs.items(): - mid_point = geom.LineString(line[1]).interpolate(0.5, normalized=True) - if reg.contains(mid_point): + line_ls = geom.LineString(line['baseline']) + if is_in_region(line_ls, reg): line_regs.append(reg_id) - blls.append(BaselineLine(id=uuid.uuid4(), baseline=line[1], boundary=line[2], tags={'type': line[0]}, regions=line_regs)) + blls.append(BaselineLine(id=str(uuid.uuid4()), baseline=line['baseline'], boundary=line['boundary'], tags=line['tags'], regions=line_regs)) return Segmentation(text_direction=text_direction, + imagename=getattr(im, 'filename', None), type='baselines', lines=blls, - regions=_regs, + regions=regions, script_detection=script_detection, line_orders=[order]) diff --git a/kraken/kraken.py b/kraken/kraken.py index a45e47f75..24a7a62da 100644 --- a/kraken/kraken.py +++ b/kraken/kraken.py @@ -46,7 +46,6 @@ APP_NAME = 'kraken' SEGMENTATION_DEFAULT_MODEL = pkg_resources.resource_filename(__name__, 'blla.mlmodel') DEFAULT_MODEL = ['en_best.mlmodel'] -LEGACY_MODEL_DIR = '/usr/local/share/ocropus' # raise default max image size to 20k * 20k pixels Image.MAX_IMAGE_PIXELS = 20000 ** 2 @@ -581,14 +580,12 @@ def ocr(ctx, model, pad, reorder, base_dir, no_segmentation, text_direction, thr if reorder and base_dir != 'auto': reorder = base_dir - # first we try to find the model in the absolue path, then ~/.kraken, then - # LEGACY_MODEL_DIR + # first we try to find the model in the absolue path, then ~/.kraken nm = {} # type: Dict[str, models.TorchSeqRecognizer] ign_tags = model.pop('ignore') for k, v in model.items(): search = [v, - os.path.join(click.get_app_dir(APP_NAME), v), - os.path.join(LEGACY_MODEL_DIR, v)] + os.path.join(click.get_app_dir(APP_NAME), v)] location = None for loc in search: if os.path.isfile(loc): diff --git a/kraken/lib/segmentation.py b/kraken/lib/segmentation.py index 34e101cdd..ef07142d3 100644 --- a/kraken/lib/segmentation.py +++ b/kraken/lib/segmentation.py @@ -740,7 +740,7 @@ def calculate_polygonal_environment(im: PIL.Image.Image = None, def polygonal_reading_order(lines: Sequence[Dict], text_direction: Literal['lr', 'rl'] = 'lr', - regions: Optional[Sequence[List[Tuple[int, int]]]] = None) -> Sequence[int]: + regions: Optional[Sequence[geom.Polygon]] = None) -> Sequence[int]: """ Given a list of baselines and regions, calculates the correct reading order and applies it to the input. @@ -758,16 +758,14 @@ def polygonal_reading_order(lines: Sequence[Dict], lines = [(line['tags']['type'], line['baseline'], line['boundary']) for line in lines] bounds = [] - if regions is not None: - r = [geom.Polygon(reg) for reg in regions] - else: - r = [] - region_lines = [[] for _ in range(len(r))] + if regions is None: + regions = [] + region_lines = [[] for _ in range(len(regions))] indizes = {} for line_idx, line in enumerate(lines): s_line = geom.LineString(line[1]) in_region = False - for idx, reg in enumerate(r): + for idx, reg in enumerate(regions): if is_in_region(s_line, reg): region_lines[idx].append((line_idx, (slice(s_line.bounds[1], s_line.bounds[3]), slice(s_line.bounds[0], s_line.bounds[2])))) @@ -778,8 +776,8 @@ def polygonal_reading_order(lines: Sequence[Dict], slice(s_line.bounds[0], s_line.bounds[2]))) indizes[line_idx] = ('line', line) # order everything in regions - intra_region_order = [[] for _ in range(len(r))] - for idx, reg in enumerate(r): + intra_region_order = [[] for _ in range(len(regions))] + for idx, reg in enumerate(regions): if len(region_lines[idx]) > 0: order = reading_order([x[1] for x in region_lines[idx]], text_direction) lsort = topsort(order) @@ -819,7 +817,7 @@ def is_in_region(line, region) -> bool: def neural_reading_order(lines: Sequence[Dict], text_direction: str = 'lr', - regions: Optional[Sequence[List[Tuple[int, int]]]] = None, + regions: Optional[Sequence[geom.Polygon]] = None, im_size: Tuple[int, int] = None, model: 'TorchVGSLModel' = None, class_mapping: Dict[str, int] = None) -> Sequence[int]: diff --git a/kraken/pageseg.py b/kraken/pageseg.py index 8bf96b64c..2fcd8f823 100644 --- a/kraken/pageseg.py +++ b/kraken/pageseg.py @@ -19,6 +19,7 @@ Layout analysis methods. """ +import uuid import logging import numpy as np @@ -423,9 +424,10 @@ def segment(im, if isinstance(pad, int): pad = (pad, pad) lines = [(max(x[0]-pad[0], 0), x[1], min(x[2]+pad[1], im.size[0]), x[3]) for x in lines] - lines = [BBoxLine(id=uuid.uuid4(), bbox=line) for line in rotate_lines(lines, 360-angle, offset).tolist()] + lines = [BBoxLine(id=str(uuid.uuid4()), bbox=line) for line in rotate_lines(lines, 360-angle, offset).tolist()] return Segmentation(text_direction=text_direction, + imagename=getattr(im, 'filename', None), type='bbox', regions=None, line_orders=None, From 2cffebd183f6ce72f4f7b00073c3a957d882a5cf Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Tue, 4 Jul 2023 01:11:09 +0200 Subject: [PATCH 52/69] autoinstantiate baselineline/bboxline when loading segmentation from json --- kraken/containers.py | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/kraken/containers.py b/kraken/containers.py index f09bd40ba..ee378d1e9 100644 --- a/kraken/containers.py +++ b/kraken/containers.py @@ -1,7 +1,7 @@ import PIL.Image -from typing import Literal, List, Dict, Sequence, Union, Optional, Tuple +from typing import Literal, List, Dict, Union, Optional, Tuple from dataclasses import dataclass, asdict from abc import ABC, abstractmethod @@ -66,10 +66,15 @@ class Segmentation: imagename: str text_direction: Literal['horizontal-lr', 'horizontal-rl', 'vertical-lr', 'vertical-rl'] script_detection: bool - lines: Sequence[Union[BaselineLine, BBoxLine]] + lines: List[Union[BaselineLine, BBoxLine]] regions: Dict[str, List[Region]] line_orders: Optional[List[List[int]]] = None + def __post_init__(self): + if len(self.lines) and not isinstance(self.lines[0], BBoxLine) and not isinstance(self.lines[0], BaselineLine): + line_cls = BBoxLine if self.type == 'bbox' else BaselineLine + self.lines = [line_cls(**line) for line in self.lines] + class ocr_record(ABC): """ @@ -79,11 +84,11 @@ class ocr_record(ABC): def __init__(self, prediction: str, - cuts: Sequence[Union[Tuple[int, int], Tuple[Tuple[int, int], + cuts: List[Union[Tuple[int, int], Tuple[Tuple[int, int], Tuple[int, int], Tuple[int, int], Tuple[int, int]]]], - confidences: Sequence[float], + confidences: List[float], display_order: bool = True) -> None: self._prediction = prediction self._cuts = cuts @@ -106,7 +111,7 @@ def prediction(self) -> str: return self._prediction @property - def cuts(self) -> Sequence: + def cuts(self) -> List: return self._cuts @property @@ -119,7 +124,7 @@ def __iter__(self): @abstractmethod def __next__(self) -> Tuple[str, - Union[Sequence[Tuple[int, int]], + Union[List[Tuple[int, int]], Tuple[Tuple[int, int], Tuple[int, int], Tuple[int, int], @@ -166,8 +171,8 @@ class BaselineOCRRecord(ocr_record, BaselineLine): type = 'baselines' def __init__(self, prediction: str, - cuts: Sequence[Tuple[int, int]], - confidences: Sequence[float], + cuts: List[Tuple[int, int]], + confidences: List[float], line: BaselineLine, base_dir: Optional[Literal['L', 'R']] = None, display_order: bool = True) -> None: @@ -222,7 +227,7 @@ def __getitem__(self, key: Union[int, slice]): raise TypeError('Invalid argument type') @property - def cuts(self) -> Sequence[Tuple[int, int]]: + def cuts(self) -> List[Tuple[int, int]]: return tuple([compute_polygon_section(self.baseline, self.line, cut[0], cut[1]) for cut in self._cuts]) def logical_order(self, base_dir: Optional[Literal['L', 'R']] = None) -> 'BaselineOCRRecord': @@ -314,11 +319,11 @@ class BBoxOCRRecord(ocr_record, BBoxLine): def __init__(self, prediction: str, - cuts: Sequence[Tuple[Tuple[int, int], + cuts: List[Tuple[Tuple[int, int], Tuple[int, int], Tuple[int, int], Tuple[int, int]]], - confidences: Sequence[float], + confidences: List[float], line: BBoxLine, base_dir: Optional[Literal['L', 'R']], display_order: bool = True) -> None: From 20a869ae89b6f73eab8e9072ede87b63078c4a8b Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Tue, 4 Jul 2023 13:48:25 +0200 Subject: [PATCH 53/69] Use new containers in rpred --- kraken/containers.py | 3 ++- kraken/kraken.py | 31 ++++++++++++++----------------- kraken/rpred.py | 23 +++++++++++++---------- 3 files changed, 29 insertions(+), 28 deletions(-) diff --git a/kraken/containers.py b/kraken/containers.py index ee378d1e9..0a2e13af0 100644 --- a/kraken/containers.py +++ b/kraken/containers.py @@ -1,5 +1,6 @@ import PIL.Image +import bidi.algorithm as bd from typing import Literal, List, Dict, Union, Optional, Tuple from dataclasses import dataclass, asdict @@ -300,7 +301,7 @@ def _reorder(self, base_dir: Optional[Literal['L', 'R']] = None) -> 'BaselineOCR image=self.image, tags=self.tags, split=self.split, - region=self.region) + regions=self.regions) rec = BaselineOCRRecord(prediction=prediction, cuts=cuts, confidences=confidences, diff --git a/kraken/kraken.py b/kraken/kraken.py index 24a7a62da..cb76614d5 100644 --- a/kraken/kraken.py +++ b/kraken/kraken.py @@ -180,7 +180,9 @@ def segmenter(legacy, model, text_direction, scale, maxcolseps, black_colseps, def recognizer(model, pad, no_segmentation, bidi_reordering, tags_ignore, input, output) -> None: import json + import uuid + from kraken.containers import Segmentation, BBoxLine from kraken import rpred ctx = click.get_current_context() @@ -192,17 +194,11 @@ def recognizer(model, pad, no_segmentation, bidi_reordering, tags_ignore, input, if ctx.meta['first_process']: if ctx.meta['input_format_type'] != 'image': doc = get_input_parser(ctx.meta['input_format_type'])(input) - ctx.meta['base_image'] = doc['image'] - doc['text_direction'] = 'horizontal-lr' - if doc['base_dir'] and bidi_reordering is True: - message(f'Setting base text direction for BiDi reordering to {doc["base_dir"]} (from XML input file)') - bidi_reordering = doc['base_dir'] - bounds = {'text_direction': 'horizontal-lr', - 'tags': True, - 'lines': doc.get_sorted_lines(), - 'regions': doc.get_sorted_regions(), - 'type': 'baselines', - 'image': doc.imagename} + ctx.meta['base_image'] = doc.imagename + if doc.base_dir and bidi_reordering is True: + message(f'Setting base text direction for BiDi reordering to {doc.base_dir} (from XML input file)') + bidi_reordering = doc.base_dir + bounds = doc.to_container() try: im = Image.open(ctx.meta['base_image']) except IOError as e: @@ -212,14 +208,15 @@ def recognizer(model, pad, no_segmentation, bidi_reordering, tags_ignore, input, with click.open_file(input, 'r') as fp: try: fp = cast(IO[Any], fp) - bounds = json.load(fp) + bounds = Segmentation(**json.load(fp)) except ValueError as e: raise click.UsageError(f'{input} invalid segmentation: {str(e)}') elif not bounds: if no_segmentation: - bounds = {'script_detection': False, - 'text_direction': 'horizontal-lr', - 'boxes': [(0, 0) + im.size]} + bounds = Segmentation(type='bbox', + text_direction='horizontal-lr', + lines=[BBoxLine(id=uuid.uuid4(), + bbox=((0, 0), (0, im.size[1]), im.size, (im.size[0], 0)))]) else: raise click.UsageError('No line segmentation given. Add one with the input or run `segment` first.') elif no_segmentation: @@ -227,7 +224,7 @@ def recognizer(model, pad, no_segmentation, bidi_reordering, tags_ignore, input, tags = set() # script detection - if 'script_detection' in bounds and bounds['script_detection']: + if bounds.script_detection: it = rpred.mm_rpred(model, im, bounds, pad, bidi_reordering=bidi_reordering, tags_ignore=tags_ignore) @@ -255,7 +252,7 @@ def recognizer(model, pad, no_segmentation, bidi_reordering, tags_ignore, input, image_size=Image.open(ctx.meta['base_image']).size, writing_mode=ctx.meta['text_direction'], scripts=tags, - regions=bounds['regions'] if 'regions' in bounds else None, + regions=bounds.regions, template=ctx.meta['output_template'], template_source='custom' if ctx.meta['output_mode'] == 'template' else 'native', processing_steps=ctx.meta['steps'])) diff --git a/kraken/rpred.py b/kraken/rpred.py index 3354b56b6..412b84c6e 100644 --- a/kraken/rpred.py +++ b/kraken/rpred.py @@ -19,6 +19,7 @@ Generators for recognition on lines images. """ import logging +import dataclasses import numpy as np import bidi.algorithm as bd @@ -47,12 +48,12 @@ class mm_rpred(object): Multi-model version of kraken.rpred.rpred """ def __init__(self, - nets: Dict[str, TorchSeqRecognizer], + nets: Dict[Tuple[str, str], TorchSeqRecognizer], im: Image.Image, bounds: Segmentation, pad: int = 16, bidi_reordering: Union[bool, str] = True, - tags_ignore: Optional[List[str]] = None) -> Generator[ocr_record, None, None]: + tags_ignore: Optional[List[Tuple[str, str]]] = None) -> Generator[ocr_record, None, None]: """ Multi-model version of kraken.rpred.rpred. @@ -61,8 +62,8 @@ def __init__(self, these lines. Args: - nets: A dict mapping tag values to TorchSegRecognizer objects. - Recommended to be an defaultdict. + nets: A dict mapping tag key-value pairs to TorchSegRecognizer + objects. Recommended to be an defaultdict. im: Image to extract text from bounds: A Segmentation data class containing either bounding box or baseline type segmentation. @@ -71,7 +72,8 @@ def __init__(self, Unicode bidirectional algorithm for correct display. Set to L|R to override default text direction. - tags_ignore: List of tag values to ignore during recognition + tags_ignore: List of tag key-value pairs to ignore during + recognition Yields: An ocr_record containing the recognized text, absolute character @@ -115,7 +117,7 @@ def __init__(self, tags = set() for x in bounds.lines: - tags.update(x.tags.values()) + tags.update(x.tags.items()) im_str = get_im_str(im) logger.info(f'Running {len(nets)} multi-script recognizers on {im_str} with {self.len} lines') @@ -231,8 +233,10 @@ def _recognize_baseline_line(self, line): logger.info(f'Ignoring line segment with tags {line.tags} based on {tag}.') return BaselineOCRRecord('', [], [], line) + seg = dataclasses.replace(self.bounds, lines=[line]) + try: - box, coords = next(extract_polygons(self.im, line)) + box, coords = next(extract_polygons(self.im, seg)) except KrakenInputException as e: logger.warning(f'Extracting line failed: {e}') return BaselineOCRRecord('', [], [], line) @@ -279,7 +283,6 @@ def _recognize_baseline_line(self, line): return rec.display_order(None) def __next__(self): - bound = self.bounds return self.next_iter(next(self.line_iter)) def __iter__(self): @@ -319,12 +322,12 @@ def rpred(network: TorchSeqRecognizer, def _resolve_tags_to_model(tags: Sequence[Dict[str, str]], - model_map: Dict[str, TorchSeqRecognizer], + model_map: Dict[Tuple[str, str], TorchSeqRecognizer], default: Optional[TorchSeqRecognizer] = None) -> TorchSeqRecognizer: """ Resolves a sequence of tags """ - for tag in tags.values(): + for tag in tags.items(): if tag in model_map: return tag, model_map[tag] if default: From 3aa34a893dba202325349fab2960fdacf018cad6 Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Tue, 18 Jul 2023 13:02:09 +0200 Subject: [PATCH 54/69] serialization with new container classes --- kraken/align.py | 25 +++--- kraken/containers.py | 40 +++++++-- kraken/kraken.py | 6 +- kraken/lib/segmentation.py | 3 +- kraken/pageseg.py | 5 +- kraken/rpred.py | 6 +- kraken/serialization.py | 167 ++++++++++++++----------------------- kraken/templates/alto | 9 +- 8 files changed, 121 insertions(+), 140 deletions(-) diff --git a/kraken/align.py b/kraken/align.py index 961585221..2c20cc790 100644 --- a/kraken/align.py +++ b/kraken/align.py @@ -23,6 +23,7 @@ """ import torch import logging +import dataclasses import numpy as np from PIL import Image @@ -31,8 +32,9 @@ from dataclasses import dataclass from typing import List, Dict, Any, Optional, Literal -from kraken import rpred +from kraken import rpred, containers from kraken.lib.codec import PytorchCodec +from kraken.lib.xml import XMLPage from kraken.lib.models import TorchSeqRecognizer from kraken.lib.exceptions import KrakenInputException, KrakenEncodeException from kraken.lib.segmentation import compute_polygon_section @@ -40,7 +42,7 @@ logger = logging.getLogger('kraken') -def forced_align(doc: Dict[str, Any], model: TorchSeqRecognizer, base_dir: Optional[Literal['L', 'R']] = None) -> List[rpred.ocr_record]: +def forced_align(doc: Segmentation, model: TorchSeqRecognizer, base_dir: Optional[Literal['L', 'R']] = None) -> containers.Segmentation: """ Performs a forced character alignment of text with recognition model output activations. @@ -50,28 +52,26 @@ def forced_align(doc: Dict[str, Any], model: TorchSeqRecognizer, base_dir: Optio model: Recognition model to use for alignment. Returns: - A list of kraken.rpred.ocr_record. + A Segmentation object where the record's contain the aligned text. """ - im = Image.open(doc['image']) + im = Image.open(doc.imagename) predictor = rpred.rpred(model, im, doc) - if 'type' in predictor.bounds and predictor.bounds['type'] == 'baselines': - rec_class = rpred.BaselineOCRRecord records = [] # enable training mode in last layer to get log_softmax output model.nn.nn[-1].training = True - for idx, line in enumerate(doc['lines']): + for idx, line in enumerate(doc.lines): # convert text to display order - do_text = get_display(line['text'], base_dir=base_dir) + do_text = get_display(line.text, base_dir=base_dir) # encode into labels, ignoring unencodable sequences labels = model.codec.encode(do_text).long() next(predictor) if model.outputs.shape[2] < 2*len(labels): logger.warning(f'Could not align line {idx}. Output sequence length {model.outputs.shape[2]} < ' - f'{2*len(labels)} (length of "{line["text"]}" after encoding).') - records.append(rpred.BaselineOCRRecord('', [], [], line)) + f'{2*len(labels)} (length of "{line.text}" after encoding).') + records.append(containers.BaselineOCRRecord('', [], [], line)) continue emission = torch.tensor(model.outputs).squeeze().T trellis = get_trellis(emission, labels) @@ -85,8 +85,9 @@ def forced_align(doc: Dict[str, Any], model: TorchSeqRecognizer, base_dir: Optio pos.append((predictor._scale_val(seg.start, 0, predictor.box.size[0]), predictor._scale_val(seg.end, 0, predictor.box.size[0]))) conf.append(seg.score) - records.append(rpred.BaselineOCRRecord(pred, pos, conf, line, display_order=True)) - return records + records.append(containers.BaselineOCRRecord(pred, pos, conf, line, display_order=True)) + return dataclasses.replace(doc, lines=records) + """ Copied from the forced alignment with Wav2Vec2 tutorial of pytorch available diff --git a/kraken/containers.py b/kraken/containers.py index 5218c39c3..29a75c522 100644 --- a/kraken/containers.py +++ b/kraken/containers.py @@ -1,17 +1,33 @@ import PIL.Image +import numpy as np import bidi.algorithm as bd +from os import PathLike from typing import Literal, List, Dict, Union, Optional, Tuple from dataclasses import dataclass, asdict from abc import ABC, abstractmethod +from kraken.lib.segmentation import compute_polygon_section + __all__ = ['BaselineLine', 'BBoxLine', 'Segmentation', 'ocr_record', 'BaselineOCRRecord', - 'BBoxOCRRecord'] + 'BBoxOCRRecord', + 'ProcessingStep'] + + +@dataclass +class ProcessingStep: + """ + A processing step in the recognition pipeline. + """ + id: str + category: Literal['preprocessing', 'processing', 'postprocessing'] + description: str + settings: Dict[str, Union[Dict, str, float, int, bool]] @dataclass @@ -29,8 +45,6 @@ class BaselineLine: split: Optional[Literal['train', 'validation', 'test']] = None regions: Optional[List[str]] = None - def __getitem__(self, item): - return getattr(self, item) @dataclass class BBoxLine: @@ -50,6 +64,7 @@ class BBoxLine: regions: Optional[List[str]] = None text_direction: Literal['horizontal-lr', 'horizontal-rl', 'vertical-lr', 'vertical-rl'] = 'horizontal-lr' + @dataclass class Region: """ @@ -64,10 +79,14 @@ class Region: @dataclass class Segmentation: """ + A container class for segmentation or recognition results. + In order to allow easy JSON de-/serialization, nested classes for lines + (BaselineLine/BBoxLine) and regions (Region) are reinstantiated from their + dictionaries. """ type: Literal['baselines', 'bbox'] - imagename: str + imagename: Union[str, PathLike] text_direction: Literal['horizontal-lr', 'horizontal-rl', 'vertical-lr', 'vertical-rl'] script_detection: bool lines: List[Union[BaselineLine, BBoxLine]] @@ -78,6 +97,11 @@ def __post_init__(self): if len(self.lines) and not isinstance(self.lines[0], BBoxLine) and not isinstance(self.lines[0], BaselineLine): line_cls = BBoxLine if self.type == 'bbox' else BaselineLine self.lines = [line_cls(**line) for line in self.lines] + if len(self.regions) and not isinstance(next(iter(self.regions.values()))[0], Region): + regs = {} + for k, v in self.regions.items(): + regs[k] = [Region(**reg) for reg in v] + self.regions = regs class ocr_record(ABC): @@ -195,7 +219,7 @@ def __next__(self) -> Tuple[str, int, float]: self.idx += 1 return (self.prediction[self.idx], compute_polygon_section(self.baseline, - self.line, + self.boundary, self.cuts[self.idx][0], self.cuts[self.idx][1]), self.confidences[self.idx]) @@ -217,7 +241,7 @@ def __getitem__(self, key: Union[int, slice]): prediction = ''.join([x[0] for x in recs]) flat_offsets = sum((tuple(x[1]) for x in recs), ()) cut = compute_polygon_section(self.baseline, - self.line, + self.boundary, min(flat_offsets), max(flat_offsets)) confidence = np.mean([x[2] for x in recs]) @@ -225,14 +249,14 @@ def __getitem__(self, key: Union[int, slice]): elif isinstance(key, int): pred, cut, confidence = self._get_raw_item(key) return (pred, - compute_polygon_section(self.baseline, self.line, cut[0], cut[1]), + compute_polygon_section(self.baseline, self.boundary, cut[0], cut[1]), confidence) else: raise TypeError('Invalid argument type') @property def cuts(self) -> List[Tuple[int, int]]: - return tuple([compute_polygon_section(self.baseline, self.line, cut[0], cut[1]) for cut in self._cuts]) + return tuple([compute_polygon_section(self.baseline, self.boundary, cut[0], cut[1]) for cut in self._cuts]) def logical_order(self, base_dir: Optional[Literal['L', 'R']] = None) -> 'BaselineOCRRecord': """ diff --git a/kraken/kraken.py b/kraken/kraken.py index cb76614d5..0f8fea361 100644 --- a/kraken/kraken.py +++ b/kraken/kraken.py @@ -181,6 +181,7 @@ def recognizer(model, pad, no_segmentation, bidi_reordering, tags_ignore, input, import json import uuid + import dataclasses from kraken.containers import Segmentation, BBoxLine from kraken import rpred @@ -239,6 +240,7 @@ def recognizer(model, pad, no_segmentation, bidi_reordering, tags_ignore, input, for pred in it: preds.append(pred) progress.update(pred_task, advance=1) + results = dataclasses.replace(it.bounds, lines=preds, imagename=ctx.meta['base_image']) ctx = click.get_current_context() with click.open_file(output, 'w', encoding='utf-8') as fp: @@ -247,12 +249,10 @@ def recognizer(model, pad, no_segmentation, bidi_reordering, tags_ignore, input, logger.info('Serializing as {} into {}'.format(ctx.meta['output_mode'], output)) if ctx.meta['output_mode'] != 'native': from kraken import serialization - fp.write(serialization.serialize(records=preds, - image_name=ctx.meta['base_image'], + fp.write(serialization.serialize(results=results, image_size=Image.open(ctx.meta['base_image']).size, writing_mode=ctx.meta['text_direction'], scripts=tags, - regions=bounds.regions, template=ctx.meta['output_template'], template_source='custom' if ctx.meta['output_mode'] == 'template' else 'native', processing_steps=ctx.meta['steps'])) diff --git a/kraken/lib/segmentation.py b/kraken/lib/segmentation.py index ef07142d3..215c4e5d4 100644 --- a/kraken/lib/segmentation.py +++ b/kraken/lib/segmentation.py @@ -42,7 +42,6 @@ from typing import List, Tuple, Union, Dict, Any, Sequence, Optional, Literal -from kraken.containers import Segmentation, BaselineLine, BBoxLine from kraken.lib import default_specs from kraken.lib.exceptions import KrakenInputException @@ -1033,7 +1032,7 @@ def compute_polygon_section(baseline: Sequence[Tuple[int, int]], return tuple(o) -def extract_polygons(im: Image.Image, bounds: Segmentation) -> Image.Image: +def extract_polygons(im: Image.Image, bounds: 'kraken.containers.Segmentation') -> Image.Image: """ Yields the subimages of image im defined in the list of bounding polygons with baselines preserving order. diff --git a/kraken/pageseg.py b/kraken/pageseg.py index 2fcd8f823..88c6a793c 100644 --- a/kraken/pageseg.py +++ b/kraken/pageseg.py @@ -27,11 +27,12 @@ from scipy.ndimage.filters import (gaussian_filter, uniform_filter, maximum_filter) +from kraken.containers import Segmentation, BBoxLine + from kraken.lib import morph, sl from kraken.lib.util import pil2array, is_bitonal, get_im_str from kraken.lib.exceptions import KrakenInputException -from kraken.lib.segmentation import reading_order, topsort, Segmentation, BBoxLine - +from kraken.lib.segmentation import reading_order, topsort __all__ = ['segment'] diff --git a/kraken/rpred.py b/kraken/rpred.py index 412b84c6e..960f4a370 100644 --- a/kraken/rpred.py +++ b/kraken/rpred.py @@ -21,18 +21,16 @@ import logging import dataclasses import numpy as np -import bidi.algorithm as bd -from abc import ABC, abstractmethod from PIL import Image from functools import partial from collections import defaultdict from typing import List, Tuple, Optional, Generator, Union, Dict, Sequence -from kraken.containers import BaselineOCRRecord, BBoxOCRRecord, ocr_record +from kraken.containers import BaselineOCRRecord, BBoxOCRRecord, ocr_record, Segmentation from kraken.lib.util import get_im_str, is_bitonal from kraken.lib.models import TorchSeqRecognizer -from kraken.lib.segmentation import extract_polygons, compute_polygon_section, Segmentation +from kraken.lib.segmentation import extract_polygons from kraken.lib.exceptions import KrakenInputException from kraken.lib.dataset import ImageInputTransforms diff --git a/kraken/serialization.py b/kraken/serialization.py index da644c948..79a25084b 100644 --- a/kraken/serialization.py +++ b/kraken/serialization.py @@ -23,9 +23,9 @@ from pkg_resources import get_distribution from collections import Counter -from kraken.rpred import BaselineOCRRecord, BBoxOCRRecord, ocr_record +from kraken.containers import Segmentation, ProcessingStep from kraken.lib.util import make_printable -from kraken.lib.segmentation import is_in_region, Segmentation +from kraken.lib.segmentation import is_in_region from typing import Union, List, Tuple, Iterable, Optional, Sequence, Dict, Any, Literal @@ -70,95 +70,87 @@ def max_bbox(boxes: Iterable[Sequence[int]]) -> Tuple[int, int, int, int]: return o -def serialize(records: Sequence[ocr_record], - image_name: Union[PathLike, str] = None, +def serialize(results: Segmentation, image_size: Tuple[int, int] = (0, 0), writing_mode: Literal['horizontal-tb', 'vertical-lr', 'vertical-rl'] = 'horizontal-tb', scripts: Optional[Iterable[str]] = None, - regions: Optional[Dict[str, List[List[Tuple[int, int]]]]] = None, template: [PathLike, str] = 'alto', template_source: Literal['native', 'custom'] = 'native', - processing_steps: Optional[List[Dict[str, Union[Dict, str, float, int, bool]]]] = None) -> str: + processing_steps: Optional[List[ProcessingStep]] = None) -> str: """ - Serializes a list of ocr_records into an output document. + Serializes recognition and segmentation results into an output document. - Serializes a list of predictions and their corresponding positions by doing - some hOCR-specific preprocessing and then renders them through one of - several jinja2 templates. + Serializes a Segmentation container object containing either segmentation + or recognition results into an output document. The rendering is performed + with jinja2 templates that can either be shipped with kraken + (`template_source` == 'native') or custom (`template_source` == 'custom'). Note: Empty records are ignored for serialization purposes. Args: - records: List of kraken.rpred.ocr_record - image_name: Name of the source image + segmentation: Segmentation container object image_size: Dimensions of the source image writing_mode: Sets the principal layout of lines and the direction in which blocks progress. Valid values are horizontal-tb, vertical-rl, and vertical-lr. scripts: List of scripts contained in the OCR records - regions: Dictionary mapping region types to a list of region polygons. template: Selector for the serialization format. May be 'hocr', 'alto', 'page' or any template found in the template directory. If template_source is set to `custom` a path to a template is expected. template_source: Switch to enable loading of custom templates from outside the kraken package. - processing_steps: A list of dictionaries describing the processing kraken performed on the inputs:: - - {'category': 'preprocessing', - 'description': 'natural language description of process', - 'settings': {'arg0': 'foo', 'argX': 'bar'} - } + processing_steps: A list of ProcessingStep container classes describing + the processing kraken performed on the inputs. Returns: The rendered template """ - logger.info(f'Serialize {len(records)} records from {image_name} with template {template}.') + logger.info(f'Serialize {len(results.lines)} records from {results.imagename} with template {template}.') page = {'entities': [], 'size': image_size, - 'name': image_name, + 'name': results.imagename, 'writing_mode': writing_mode, 'scripts': scripts, 'date': datetime.datetime.now(datetime.timezone.utc).isoformat(), - 'base_dir': [rec.base_dir for rec in records][0] if len(records) else None} # type: dict + 'base_dir': [rec.base_dir for rec in results.lines][0] if len(results.lines) else None, + 'line_orders': results.line_orders, + 'seg_type': results.type} # type: dict metadata = {'processing_steps': processing_steps, 'version': get_distribution('kraken').version} seg_idx = 0 char_idx = 0 - region_map = {} - idx = 0 - if regions is not None: - for id, regs in regions.items(): - for reg in regs: - region_map[idx] = (id, geom.Polygon(reg), reg) - idx += 1 # build region and line type dict types = [] - for line in records: - if hasattr(line, 'tags') and line.tags is not None: - types.extend(line.tags.values()) - page['types'] = list(set(types)) - if regions is not None: - page['types'].extend(list(regions.keys())) - - is_in_reg = -1 - for idx, record in enumerate(records): - if record.type == 'baselines': - l_obj = geom.LineString(record.baseline) - else: - l_obj = geom.LineString(record.line) - reg = list(filter(lambda x: is_in_region(l_obj, x[1][1]), region_map.items())) - if len(reg) == 0: - cur_ent = page['entities'] - elif reg[0][0] != is_in_reg: - reg = reg[0] - is_in_reg = reg[0] - region = {'index': reg[0], - 'bbox': [int(x) for x in reg[1][1].bounds], - 'boundary': [list(x) for x in reg[1][2]], - 'region_type': reg[1][0], + for line in results.lines: + if line.tags is not None: + types.extend((k, v) for k, v in line.tags.items()) + page['line_types'] = list(set(types)) + page['region_types'] =[list(results.regions.keys())] + + # build region ID to region dict + reg_dict = {} + for key, regs in results.regions.items(): + for reg in regs: + reg_dict[reg.id] = reg + + regs_with_lines = set() + prev_reg = None + for idx, record in enumerate(results.lines): + # line not in region + if len(record.regions) == 0: + cur_ent = page['entitites'] + # line not in same region as previous line + elif prev_reg != record.regions[0]: + prev_reg = record.regions[0] + reg = reg_dict[record.regions[0]] + regs_with_lines.add(reg.id) + region = {'id': reg.id, + 'bbox': max_bbox([reg.boundary]), + 'boundary': [list(x) for x in reg.boundary], + 'tags': reg.tags, 'lines': [], 'type': 'region' } @@ -167,20 +159,19 @@ def serialize(records: Sequence[ocr_record], # set field to indicate the availability of baseline segmentation in # addition to bounding boxes - if record.type == 'baselines': - page['seg_type'] = 'baselines' line = {'index': idx, - 'bbox': max_bbox([record.line]), + 'bbox': max_bbox([record.boundary] if record.type == 'baselines' else record.bbox), 'cuts': record.cuts, 'confidences': record.confidences, 'recognition': [], - 'boundary': [list(x) for x in record.line], + 'boundary': [list(x) for x in record.boundary], 'type': 'line' } - if hasattr(record, 'tags') and record.tags is not None: + if record.tags is not None: line['tags'] = record.tags if record.type == 'baselines': line['baseline'] = [list(x) for x in record.baseline] + splits = regex.split(r'(\s+)', record.prediction) line_offset = 0 logger.debug(f'Record contains {len(splits)} segments') @@ -213,18 +204,19 @@ def serialize(records: Sequence[ocr_record], line_offset += len(segment) cur_ent.append(line) - # No records but there are regions -> serialize all regions - if not records and regions: - logger.debug(f'No lines given but {len(region_map)}. Serialize all regions.') - for reg in region_map.items(): - region = {'index': reg[0], - 'bbox': [int(x) for x in reg[1][1].bounds], - 'boundary': [list(x) for x in reg[1][2]], - 'region_type': reg[1][0], - 'lines': [], - 'type': 'region' - } - page['entities'].append(region) + # serialize all remaining (line-less) regions + for reg_id in regs_with_lines: + reg_dict.pop(reg_id) + logger.debug(f'No lines given but {len(results.regions)}. Serialize all regions.') + for reg in reg_dict.values(): + region = {'id': reg.id, + 'bbox': max_bbox([reg.boundary]), + 'boundary': [list(x) for x in reg.boundary], + 'tags': reg.tags, + 'lines': [], + 'type': 'region' + } + page['entities'].append(region) if template_source == 'native': logger.debug('Initializing native jinja environment.') @@ -246,43 +238,6 @@ def _load_template(name): return tmpl.render(page=page, metadata=metadata) -def serialize_segmentation(segresult: Segmentation, - image_name: Union[PathLike, str] = None, - image_size: Tuple[int, int] = (0, 0), - template: Union[PathLike, str] = 'alto', - template_source: Literal['native', 'custom'] = 'native', - processing_steps: Optional[List[Dict[str, Union[Dict, str, float, int, bool]]]] = None) -> str: - """ - Serializes a segmentation result into an output document. - - Args: - segresult: Result of blla.segment - image_name: Name of the source image - image_size: Dimensions of the source image - template: Selector for the serialization format. Any value accepted by - `serialize` is valid. - template_source: Enables/disables loading of external templates. - - Returns: - (str) rendered template. - """ - if segresult.type == 'baselines': - records = [BaselineOCRRecord('', (), (), bl) for bl in segresult.lines] - else: - records = [] - for line in segresult.lines: - xmin, xmax = min(line[::2]), max(line[::2]) - ymin, ymax = min(line[1::2]), max(line[1::2]) - records.append(BBoxOCRRecord('', (), (), ((xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin)))) - return serialize(records, - image_name=image_name, - image_size=image_size, - regions=segresult.regions, - template=template, - template_source=template_source, - processing_steps=processing_steps) - - def render_report(model: str, chars: int, errors: int, diff --git a/kraken/templates/alto b/kraken/templates/alto index 05d7ab192..3c5f34e99 100644 --- a/kraken/templates/alto +++ b/kraken/templates/alto @@ -49,7 +49,7 @@ {% if metadata.processing_steps %} {% for step in metadata.processing_steps %} - + {{ proc_type_table[step.category] }} {{ step.description }} {% for k, v in step.settings.items() %}{{k}}: {{v}}; {% endfor %} @@ -71,8 +71,11 @@ {% endif %} - {% for reg_type in page.types %} - + {% for type, label in page.line_types %} + + {% endfor %} + {% for label in page.region_types %} + {% endfor %} From 4eef3c57b864cb2ab83c33760a7db6c6cc7b7d19 Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Wed, 19 Jul 2023 01:20:05 +0200 Subject: [PATCH 55/69] Add alternative reading orders to ALTO output --- kraken/serialization.py | 7 ++++++- kraken/templates/alto | 21 +++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/kraken/serialization.py b/kraken/serialization.py index 79a25084b..ae6679762 100644 --- a/kraken/serialization.py +++ b/kraken/serialization.py @@ -114,7 +114,6 @@ def serialize(results: Segmentation, 'scripts': scripts, 'date': datetime.datetime.now(datetime.timezone.utc).isoformat(), 'base_dir': [rec.base_dir for rec in results.lines][0] if len(results.lines) else None, - 'line_orders': results.line_orders, 'seg_type': results.type} # type: dict metadata = {'processing_steps': processing_steps, 'version': get_distribution('kraken').version} @@ -130,6 +129,12 @@ def serialize(results: Segmentation, page['line_types'] = list(set(types)) page['region_types'] =[list(results.regions.keys())] + # map reading orders indices to line IDs + ros = [] + for ro in results.line_orders: + ros.append([results.lines[idx].id for idx in ro]) + page['line_orders'] = ros + # build region ID to region dict reg_dict = {} for key, regs in results.regions.items(): diff --git a/kraken/templates/alto b/kraken/templates/alto index 3c5f34e99..ccddf4182 100644 --- a/kraken/templates/alto +++ b/kraken/templates/alto @@ -78,6 +78,27 @@ {% endfor %} + {% if len(page.line_orders) > 0 %} + + {% if len(page.line_orders) == 1 %} + + {% for id in page.line_orders[0] %} + + {% endfor %} + + {% else %} + + {% for ro in page.line_orders %} + + {% for id in ro %} + + {% endfor %} + + {% endfor %} + + {% endif %} + + {% endif %} From c08edcb76effefb4d96002e5ada6092321bc7b62 Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Wed, 19 Jul 2023 12:31:55 +0200 Subject: [PATCH 56/69] docstrings --- kraken/align.py | 9 ++-- kraken/blla.py | 7 +-- kraken/containers.py | 120 ++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 123 insertions(+), 13 deletions(-) diff --git a/kraken/align.py b/kraken/align.py index 2c20cc790..ff76a1d43 100644 --- a/kraken/align.py +++ b/kraken/align.py @@ -32,7 +32,8 @@ from dataclasses import dataclass from typing import List, Dict, Any, Optional, Literal -from kraken import rpred, containers +from kraken import rpred +from kraken.containers import Segmentation, BaselineOCRRecord from kraken.lib.codec import PytorchCodec from kraken.lib.xml import XMLPage from kraken.lib.models import TorchSeqRecognizer @@ -42,7 +43,7 @@ logger = logging.getLogger('kraken') -def forced_align(doc: Segmentation, model: TorchSeqRecognizer, base_dir: Optional[Literal['L', 'R']] = None) -> containers.Segmentation: +def forced_align(doc: Segmentation, model: TorchSeqRecognizer, base_dir: Optional[Literal['L', 'R']] = None) -> Segmentation: """ Performs a forced character alignment of text with recognition model output activations. @@ -71,7 +72,7 @@ def forced_align(doc: Segmentation, model: TorchSeqRecognizer, base_dir: Optiona if model.outputs.shape[2] < 2*len(labels): logger.warning(f'Could not align line {idx}. Output sequence length {model.outputs.shape[2]} < ' f'{2*len(labels)} (length of "{line.text}" after encoding).') - records.append(containers.BaselineOCRRecord('', [], [], line)) + records.append(BaselineOCRRecord('', [], [], line)) continue emission = torch.tensor(model.outputs).squeeze().T trellis = get_trellis(emission, labels) @@ -85,7 +86,7 @@ def forced_align(doc: Segmentation, model: TorchSeqRecognizer, base_dir: Optiona pos.append((predictor._scale_val(seg.start, 0, predictor.box.size[0]), predictor._scale_val(seg.end, 0, predictor.box.size[0]))) conf.append(seg.score) - records.append(containers.BaselineOCRRecord(pred, pos, conf, line, display_order=True)) + records.append(BaselineOCRRecord(pred, pos, conf, line, display_order=True)) return dataclasses.replace(doc, lines=records) diff --git a/kraken/blla.py b/kraken/blla.py index 6d65de794..050a2abb7 100644 --- a/kraken/blla.py +++ b/kraken/blla.py @@ -276,9 +276,10 @@ def segment(im: PIL.Image.Image, autocast: Runs the model with automatic mixed precision Returns: - A :class:`kraken.lib.blla.Segmentation` class containing reading order - sorted baselines (polylines) and their respective polygonal boundaries. - The format of the line and region records is shown below. The last and + A :class:`kraken.containers.Segmentation` class containing reading order + sorted baselines (polylines) and their respective polygonal boundaries + as :class:`kraken.containers.BaselineLine` records. + The format of the line and region records is shown below. The last and first point of each boundary polygon are connected. .. code-block:: diff --git a/kraken/containers.py b/kraken/containers.py index 29a75c522..892d00109 100644 --- a/kraken/containers.py +++ b/kraken/containers.py @@ -23,6 +23,12 @@ class ProcessingStep: """ A processing step in the recognition pipeline. + + Attributes: + id: Unique identifier + category: Category of processing step that has been performat. + description: Natural-language description of the process. + settings: Dict describing the parameters of the processing step. """ id: str category: Literal['preprocessing', 'processing', 'postprocessing'] @@ -33,6 +39,26 @@ class ProcessingStep: @dataclass class BaselineLine: """ + Baseline-type line record. + + A container class for a single line in baseline + bounding polygon format, + optionally containing a transcription, tags, or associated regions. + + Attributes: + id: Unique identifier + baseline: List of tuples `(x_n, y_n)` defining the baseline. + boundary: List of tuples `(x_n, y_n)` defining the bounding polygon of + the line. The first and last points should be identical. + text: Transcription of this line. + base_dir: An optional string defining the base direction (also called + paragraph direction) for the BiDi algorithm. Valid values are + 'L' or 'R'. If None is given the default auto-resolution will + be used. + image: Image object of this line. + tags: A dict mapping types to values. + split: Defines whether this line is in the `train`, `validation`, or + `test` set during training. + regions: A list of identifiers of regions the line is associated with. """ id: str baseline: List[Tuple[int, int]] @@ -49,6 +75,27 @@ class BaselineLine: @dataclass class BBoxLine: """ + Bounding box-type line record. + + A container class for a single line in axis-aligned bounding box format, + optionally containing a transcription, tags, or associated regions. + + Attributes: + id: Unique identifier + bbox: Tuple in form `((x0, y0), (x1, y0), (x1, y1), (x0, y1))` defining + the bounding box. + text: Transcription of this line. + base_dir: An optional string defining the base direction (also called + paragraph direction) for the BiDi algorithm. Valid values are + 'L' or 'R'. If None is given the default auto-resolution will + be used. + image: Image object of this line. + tags: A dict mapping types to values. + split: Defines whether this line is in the `train`, `validation`, or + `test` set during training. + regions: A list of identifiers of regions the line is associated with. + text_direction: Sets the principal orientation (of the line) and + reading direction (of the document). """ id: str bbox: Tuple[Tuple[int, int], @@ -68,7 +115,14 @@ class BBoxLine: @dataclass class Region: """ + Container class of a single polygonal region. + Attributes: + id: Unique identifier + boundary: List of tuples `(x_n, y_n)` defining the bounding polygon of + the region. The first and last points should be identical. + image: Image object containing the region. + tags: A dict mapping types to values. """ id: str boundary: List[Tuple[int, int]] @@ -84,6 +138,22 @@ class Segmentation: In order to allow easy JSON de-/serialization, nested classes for lines (BaselineLine/BBoxLine) and regions (Region) are reinstantiated from their dictionaries. + + Attributes: + type: Field indicating if baselines + (:class:`kraken.containers.BaselineLine`) or bbox + (:class:`kraken.containers.BBoxLine`) line records are in the + segmentation. + imagename: Path to the image associated with the segmentation. + text_direction: Sets the principal orientation (of the line), i.e. + horizontal/vertical, and reading direction (of the + document), i.e. lr/rl. + script_detection: Flag indicating if the line records have tags. + lines: List of line records. Records are expected to be in a valid + reading order. + regions: Dict mapping types to lists of regions. + line_orders: List of alternative reading orders for the segmentation. + Each reading order is a list of line indices. """ type: Literal['baselines', 'bbox'] imagename: Union[str, PathLike] @@ -185,6 +255,16 @@ class BaselineOCRRecord(ocr_record, BaselineLine): as a list of tuples [(x0, y0), (x1, y2), ...]. confidences: A list of floats indicating the confidence value of each code point. + base_dir: An optional string defining the base direction (also called + paragraph direction) for the BiDi algorithm. Valid values are + 'L' or 'R'. If None is given the default auto-resolution will + be used. + display_order: Flag indicating the order of the code points in the + prediction. In display order (`True`) the n-th code + point in the string corresponds to the n-th leftmost + code point, in logical order (`False`) the n-th code + point corresponds to the n-th read code point. See [UAX + #9](https://unicode.org/reports/tr9) for more details. Notes: When slicing the record the behavior of the cuts is changed from @@ -342,6 +422,34 @@ class BBoxOCRRecord(ocr_record, BBoxLine): """ A record object containing the recognition result of a single line in bbox format. + + Attributes: + type: 'bbox' to indicate a bounding box record + prediction: The text predicted by the network as one continuous string. + cuts: The absolute bounding polygons for each code point in prediction + as a list of 4-tuples `((x0, y0), (x1, y0), (x1, y1), (x0, y1))`. + confidences: A list of floats indicating the confidence value of each + code point. + base_dir: An optional string defining the base direction (also called + paragraph direction) for the BiDi algorithm. Valid values are + 'L' or 'R'. If None is given the default auto-resolution will + be used. + display_order: Flag indicating the order of the code points in the + prediction. In display order (`True`) the n-th code + point in the string corresponds to the n-th leftmost + code point, in logical order (`False`) the n-th code + point corresponds to the n-th read code point. See [UAX + #9](https://unicode.org/reports/tr9) for more details. + + Notes: + When slicing the record the behavior of the cuts is changed from + earlier versions of kraken. Instead of returning per-character bounding + polygons a single polygons section of the line bounding polygon + starting at the first and extending to the last code point emitted by + the network is returned. This aids numerical stability when computing + aggregated bounding polygons such as for words. Individual code point + bounding polygons are still accessible through the `cuts` attribute or + by iterating over the record code point by code point. """ type = 'bbox' @@ -468,13 +576,13 @@ def _reorder(self, base_dir: Optional[Literal['L', 'R']] = None) -> 'BBoxOCRReco image=self.image, tags=self.tags, split=self.split, - region=self.region) + regions=self.regions) rec = BBoxOCRRecord(prediction=prediction, - cuts=cuts, - confidences=confidences, - line=line, - base_dir=base_dir, - display_order=not self._display_order) + cuts=cuts, + confidences=confidences, + line=line, + base_dir=base_dir, + display_order=not self._display_order) return rec From 6dcc004ed4551fece62c40f43916696f07efca19 Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Wed, 19 Jul 2023 13:01:58 +0200 Subject: [PATCH 57/69] more docstrings --- kraken/linegen.py | 4 +--- kraken/pageseg.py | 14 ++++++-------- kraken/rpred.py | 3 ++- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/kraken/linegen.py b/kraken/linegen.py index d53daf117..6ea15aa8d 100644 --- a/kraken/linegen.py +++ b/kraken/linegen.py @@ -38,8 +38,6 @@ from scipy.ndimage.interpolation import affine_transform, geometric_transform from PIL import Image, ImageOps -from typing import AnyStr - import logging import ctypes import ctypes.util @@ -104,7 +102,7 @@ class ensureBytes(object): bytes. """ @classmethod - def from_param(cls, value: AnyStr) -> bytes: + def from_param(cls, value: str) -> bytes: if isinstance(value, bytes): return value else: diff --git a/kraken/pageseg.py b/kraken/pageseg.py index 88c6a793c..b98b175b7 100644 --- a/kraken/pageseg.py +++ b/kraken/pageseg.py @@ -19,6 +19,7 @@ Layout analysis methods. """ +import PIL import uuid import logging import numpy as np @@ -303,7 +304,7 @@ def rotate_lines(lines: np.ndarray, angle: float, offset: int) -> np.ndarray: return np.column_stack((x.flatten(), y.flatten())).reshape(-1, 4) -def segment(im, +def segment(im: PIL.Image.Image, text_direction: str = 'horizontal-lr', scale: Optional[float] = None, maxcolseps: float = 2, @@ -311,7 +312,7 @@ def segment(im, no_hlines: bool = True, pad: Union[int, Tuple[int, int]] = 0, mask: Optional[np.ndarray] = None, - reading_order_fn: Callable = reading_order) -> Dict[str, Any]: + reading_order_fn: Callable = reading_order) -> Segmentation: """ Segments a page into text lines. @@ -338,12 +339,9 @@ def segment(im, direction in (`rl`, `lr`). Returns: - A dictionary containing the text direction and a list of reading order - sorted bounding boxes under the key 'boxes': - - .. code-block:: - - {'text_direction': '$dir', 'boxes': [(x1, y1, x2, y2),...]} + A :class:`kraken.containers.Segmentation` class containing reading + order sorted bounding box-type lines as + :class:`kraken.containers.BBoxLine` records. Raises: KrakenInputException: if the input image is not binarized or the text diff --git a/kraken/rpred.py b/kraken/rpred.py index 960f4a370..823cad5ea 100644 --- a/kraken/rpred.py +++ b/kraken/rpred.py @@ -304,7 +304,8 @@ def rpred(network: TorchSeqRecognizer, Args: network: A TorchSegRecognizer object im: Image to extract text from - bounds: A Segmentation class instance containing either a baseline or bbox segmentation. + bounds: A Segmentation class instance containing either a baseline or + bbox segmentation. pad: Extra blank padding to the left and right of text line. Auto-disabled when expected network inputs are incompatible with padding. From dd1b9ed7637ef79b272649227d135d4d1d8f9d0a Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Thu, 20 Jul 2023 15:09:14 +0200 Subject: [PATCH 58/69] better default output name in ketos compile --- kraken/ketos/dataset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kraken/ketos/dataset.py b/kraken/ketos/dataset.py index 8d83938c3..3996a2a52 100644 --- a/kraken/ketos/dataset.py +++ b/kraken/ketos/dataset.py @@ -23,7 +23,7 @@ @click.command('compile') @click.pass_context -@click.option('-o', '--output', show_default=True, type=click.Path(), default='model', help='Output model file') +@click.option('-o', '--output', show_default=True, type=click.Path(), default='dataset.arrow', help='Output dataset file') @click.option('--workers', show_default=True, default=1, help='Number of parallel workers for text line extraction.') @click.option('-f', '--format-type', type=click.Choice(['path', 'xml', 'alto', 'page']), default='xml', show_default=True, help='Sets the training data format. In ALTO and PageXML mode all ' From 10c83d33804063852eb745ed5b320bed2d111282 Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Thu, 20 Jul 2023 15:09:33 +0200 Subject: [PATCH 59/69] fix import in arrow_dataset --- kraken/lib/arrow_dataset.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kraken/lib/arrow_dataset.py b/kraken/lib/arrow_dataset.py index 694fd4279..bac1c1a0a 100755 --- a/kraken/lib/arrow_dataset.py +++ b/kraken/lib/arrow_dataset.py @@ -28,8 +28,9 @@ from collections import Counter from typing import Optional, List, Union, Callable, Tuple, Dict from multiprocessing import Pool +from kraken.containers import Segmentation from kraken.lib import functional_im_transforms as F_t -from kraken.lib.segmentation import extract_polygons, Segmentation +from kraken.lib.segmentation import extract_polygons from kraken.lib.xml import XMLPage from kraken.lib.util import is_bitonal, make_printable from kraken.lib.exceptions import KrakenInputException From e72f5ae7c7124458ac83cc96945e6cc96bddc588 Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Fri, 21 Jul 2023 15:29:26 +0200 Subject: [PATCH 60/69] arrow dataset builder test skeleton --- tests/test_arrow_dataset.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 tests/test_arrow_dataset.py diff --git a/tests/test_arrow_dataset.py b/tests/test_arrow_dataset.py new file mode 100644 index 000000000..fff086c12 --- /dev/null +++ b/tests/test_arrow_dataset.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- + +import unittest +import json + +import kraken + +from pytest import raises +from pathlib import Path + +from kraken.lib import xml +from kraken.lib.arrow_dataset import build_binary_dataset + +thisfile = Path(__file__).resolve().parent +resources = thisfile / 'resources' + +class TestKrakenArrowCompilation(unittest.TestCase): + """ + Tests for binary datasets + """ + def setUp(self): + self.xml = resources / '170025120000003,0074.xml' + self.bls = xml.XMLPage(self.xml) + self.box_lines = [resources / '000236.png'] + + def test_build_path_dataset(self): + pass + + def test_build_xml_dataset(self): + pass + + def test_build_obj_dataset(self): + pass + + def test_build_empty_dataset(self): + pass From 260d04766e59e446b548beac510cb4c33ef6ea5a Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Mon, 24 Jul 2023 12:55:16 +0200 Subject: [PATCH 61/69] Small fixes to RO dataset class --- kraken/lib/dataset/ro.py | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/kraken/lib/dataset/ro.py b/kraken/lib/dataset/ro.py index e6c6bf678..3edb82b88 100644 --- a/kraken/lib/dataset/ro.py +++ b/kraken/lib/dataset/ro.py @@ -108,12 +108,12 @@ def __init__(self, files: Sequence[Union[PathLike, str]] = None, w, h = Image.open(doc.imagename).size sorted_lines = [] for line in order: - line_coords = np.array(line['baseline']) / (w, h) + line_coords = np.array(line.baseline) / (w, h) line_center = np.mean(line_coords, axis=0) cl = torch.zeros(self.num_classes, dtype=torch.float) # if class is not in class mapping default to None class (idx 0) - cl[self.class_mapping.get(line['tags']['type'], 0)] = 1 - line_data = {'type': line['tags']['type'], + cl[self.class_mapping.get(line.tags['type'], 0)] = 1 + line_data = {'type': line.tags['type'], 'features': torch.cat((cl, # one hot encoded line type torch.tensor(line_center, dtype=torch.float), # line center torch.tensor(line_coords[0, :], dtype=torch.float), # start_point coord @@ -121,9 +121,11 @@ def __init__(self, files: Sequence[Union[PathLike, str]] = None, )) } sorted_lines.append(line_data) - self.data.append(sorted_lines) - self._num_pairs += int(factorial(len(sorted_lines))/factorial(len(sorted_lines)-2)) - + if len(sorted_lines) > 1: + self.data.append(sorted_lines) + self._num_pairs += int(factorial(len(sorted_lines))/factorial(len(sorted_lines)-2)) + else: + logger.info(f'Page {doc} has less than 2 lines. Skipping') except KrakenInputException as e: logger.warning(e) continue @@ -212,12 +214,12 @@ def __init__(self, files: Sequence[Union[PathLike, str]] = None, w, h = Image.open(doc.imagename).size sorted_lines = [] for line in order: - line_coords = np.array(line['baseline']) / (w, h) + line_coords = np.array(line.baseline) / (w, h) line_center = np.mean(line_coords, axis=0) cl = torch.zeros(self.num_classes, dtype=torch.float) # if class is not in class mapping default to None class (idx 0) - cl[self.class_mapping.get(line['tags']['type'], 0)] = 1 - line_data = {'type': line['tags']['type'], + cl[self.class_mapping.get(line.tags['type'], 0)] = 1 + line_data = {'type': line.tags['type'], 'features': torch.cat((cl, # one hot encoded line type torch.tensor(line_center, dtype=torch.float), # line center torch.tensor(line_coords[0, :], dtype=torch.float), # start_point coord @@ -225,7 +227,10 @@ def __init__(self, files: Sequence[Union[PathLike, str]] = None, )) } sorted_lines.append(line_data) - self.data.append(sorted_lines) + if len(sorted_lines) > 1: + self.data.append(sorted_lines) + else: + logger.info(f'Page {doc} has less than 2 lines. Skipping') except KrakenInputException as e: logger.warning(e) continue From 7118f1eea34356a0be01bb654c04798774d5552e Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Mon, 24 Jul 2023 13:09:07 +0200 Subject: [PATCH 62/69] forced alignment contrib script with container classes --- kraken/contrib/forced_alignment_overlay.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/kraken/contrib/forced_alignment_overlay.py b/kraken/contrib/forced_alignment_overlay.py index 5e9f19eff..1dd700c1b 100755 --- a/kraken/contrib/forced_alignment_overlay.py +++ b/kraken/contrib/forced_alignment_overlay.py @@ -35,7 +35,7 @@ def _repl_alto(fname, cuts): doc = etree.parse(fp) lines = doc.findall('.//{*}TextLine') char_idx = 0 - for line, line_cuts in zip(lines, cuts): + for line, line_cuts in zip(lines, cuts.lines): idx = 0 for el in line: if el.tag.endswith('Shape'): @@ -65,7 +65,7 @@ def _repl_page(fname, cuts): with open(fname, 'rb') as fp: doc = etree.parse(fp) lines = doc.findall('.//{*}TextLine') - for line, line_cuts in zip(lines, cuts): + for line, line_cuts in zip(lines, cuts.lines): glyphs = line.findall('../{*}Glyph/{*}Coords') for glyph, cut in zip(glyphs, line_cuts): glyph.attrib['points'] = ' '.join([','.join([str(x) for x in pt]) for pt in cut]) @@ -96,34 +96,33 @@ def cli(format_type, model, output, files): from PIL import Image, ImageDraw - from kraken.lib import models, xml + from kraken.lib.xml import XMLPage + from kraken.lib import models from kraken import align if format_type == 'alto': - fn = xml.parse_alto repl_fn = _repl_alto else: - fn = xml.parse_page repl_fn = _repl_page click.echo(f'Loading model {model}') net = models.load_any(model) for doc in files: click.echo(f'Processing {doc} ', nl=False) - data = fn(doc) - im = Image.open(data['image']).convert('RGBA') - records = align.forced_align(data, net) + data = XMLPage(doc) + im = Image.open(data.imagename).convert('RGBA') + result = align.forced_align(data.to_container, net) if output == 'overlay': tmp = Image.new('RGBA', im.size, (0, 0, 0, 0)) draw = ImageDraw.Draw(tmp) - for record in records: + for record in result.lines: for pol in record.cuts: c = next(cmap) draw.polygon([tuple(x) for x in pol], fill=c, outline=c[:3]) base_image = Image.alpha_composite(im, tmp) base_image.save(f'high_{os.path.basename(doc)}_algn.png') else: - repl_fn(doc, records) + repl_fn(doc, result) click.secho('\u2713', fg='green') From da6c4f6f5c5b49d82c991bed9fb63cdfe5b236ac Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Mon, 24 Jul 2023 13:13:15 +0200 Subject: [PATCH 63/69] extract_lines.py with container classes --- kraken/contrib/extract_lines.py | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/kraken/contrib/extract_lines.py b/kraken/contrib/extract_lines.py index b00db3a43..e2a41b8ba 100755 --- a/kraken/contrib/extract_lines.py +++ b/kraken/contrib/extract_lines.py @@ -1,8 +1,6 @@ #! /usr/bin/env python - import click - @click.command() @click.option('-f', '--format-type', type=click.Choice(['xml', 'alto', 'page', 'binary']), default='xml', help='Sets the input document format. In ALTO and PageXML mode all ' @@ -10,17 +8,8 @@ 'link to source images.') @click.option('-i', '--model', default=None, show_default=True, type=click.Path(exists=True), help='Baseline detection model to use. Overrides format type and expects image files as input.') -@click.option('--repolygonize/--no-repolygonize', show_default=True, - default=False, help='Repolygonizes line data in ALTO/PageXML ' - 'files. This ensures that the trained model is compatible with the ' - 'segmenter in kraken even if the original image files either do ' - 'not contain anything but transcriptions and baseline information ' - 'or the polygon data was created using a different method. Will ' - 'be ignored in `path` mode. Note, that this option will be slow ' - 'and will not scale input images to the same size as the segmenter ' - 'does.') @click.argument('files', nargs=-1) -def cli(format_type, model, repolygonize, files): +def cli(format_type, model, files): """ A small script extracting rectified line polygons as defined in either ALTO or PageXML files or run a model to do the same. @@ -42,14 +31,14 @@ def cli(format_type, model, repolygonize, files): for doc in files: click.echo(f'Processing {doc} ', nl=False) if format_type != 'binary': - data = xml.preparse_xml_data([doc], format_type, repolygonize=repolygonize) - if len(data) > 0: - bounds = {'type': 'baselines', 'lines': [{'boundary': t['boundary'], 'baseline': t['baseline'], 'text': t['text']} for t in data]} - for idx, (im, box) in enumerate(segmentation.extract_polygons(Image.open(data[0]['image']), bounds)): + data = xml.XMLPage(doc, format_type) + if len(data.lines) > 0: + bounds = data.to_container() + for idx, (im, box) in enumerate(segmentation.extract_polygons(Image.open(bounds.imagename), bounds)): click.echo('.', nl=False) - im.save('{}.{}.jpg'.format(splitext(data[0]['image'])[0], idx)) - with open('{}.{}.gt.txt'.format(splitext(data[0]['image'])[0], idx), 'w') as fp: - fp.write(box['text']) + im.save('{}.{}.jpg'.format(splitext(bounds.imagename)[0], idx)) + with open('{}.{}.gt.txt'.format(splitext(bounds.imagename)[0], idx), 'w') as fp: + fp.write(box.text) else: with pa.memory_map(doc, 'rb') as source: ds_table = pa.ipc.open_file(source).read_all() From 7cf2dc404be2b8bebf915b7bab30ac193000732b Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Mon, 24 Jul 2023 13:53:04 +0200 Subject: [PATCH 64/69] More contrib scripts with containers --- kraken/contrib/heatmap_overlay.py | 1 - kraken/contrib/repolygonize.py | 10 ++--- kraken/contrib/segmentation_overlay.py | 60 +++++++++++--------------- 3 files changed, 28 insertions(+), 43 deletions(-) diff --git a/kraken/contrib/heatmap_overlay.py b/kraken/contrib/heatmap_overlay.py index 8e2c7236b..0b1c22197 100755 --- a/kraken/contrib/heatmap_overlay.py +++ b/kraken/contrib/heatmap_overlay.py @@ -4,7 +4,6 @@ """ import click - @click.command() @click.option('-i', '--model', default=None, show_default=True, type=click.Path(exists=True), help='Baseline detection model to use.') diff --git a/kraken/contrib/repolygonize.py b/kraken/contrib/repolygonize.py index db40b8987..d3f498c57 100755 --- a/kraken/contrib/repolygonize.py +++ b/kraken/contrib/repolygonize.py @@ -85,10 +85,8 @@ def _repl_page(fname, polygons): doc.write(fp, encoding='UTF-8', xml_declaration=True) if format_type == 'page': - parse_fn = xml.parse_page repl_fn = _repl_page else: - parse_fn = xml.parse_alto repl_fn = _repl_alto topline = {'topline': True, @@ -97,11 +95,11 @@ def _repl_page(fname, polygons): for doc in files: click.echo(f'Processing {doc} ') - seg = parse_fn(doc) - im = Image.open(seg['image']).convert('L') + seg = xml.XMLPage(doc).to_container() + im = Image.open(seg.imagename).convert('L') baselines = [] - for x in seg['lines']: - bl = x['baseline'] if x['baseline'] is not None else [0, 0] + for x in seg.lines: + bl = x.baseline if x.baseline is not None else [0, 0] baselines.append(bl) o = calculate_polygonal_environment(im, baselines, scale=(1800, 0), topline=topline) repl_fn(doc, o) diff --git a/kraken/contrib/segmentation_overlay.py b/kraken/contrib/segmentation_overlay.py index 229862fae..a9ff235ff 100755 --- a/kraken/contrib/segmentation_overlay.py +++ b/kraken/contrib/segmentation_overlay.py @@ -7,6 +7,7 @@ import os import click import unicodedata +import dataclasses from itertools import cycle from collections import defaultdict @@ -27,10 +28,6 @@ def slugify(value): return value @click.command() -@click.option('-f', '--format-type', type=click.Choice(['xml', 'alto', 'page']), default='xml', - help='Sets the input document format. In ALTO and PageXML mode all ' - 'data is extracted from xml files containing both baselines, polygons, and a ' - 'link to source images.') @click.option('-i', '--model', default=None, show_default=True, type=click.Path(exists=True), help='Baseline detection model to use. Overrides format type and expects image files as input.') @click.option('-d', '--text-direction', default='horizontal-lr', @@ -48,7 +45,7 @@ def slugify(value): 'and will not scale input images to the same size as the segmenter ' 'does.') @click.argument('files', nargs=-1) -def cli(format_type, model, text_direction, repolygonize, files): +def cli(model, text_direction, repolygonize, files): """ A script producing overlays of lines and regions from either ALTO or PageXML files or run a model to do the same. @@ -64,47 +61,38 @@ def cli(format_type, model, text_direction, repolygonize, files): from kraken import blla if model is None: - if format_type == 'xml': - fn = xml.parse_xml - elif format_type == 'alto': - fn = xml.parse_alto - else: - fn = xml.parse_page for doc in files: click.echo(f'Processing {doc} ', nl=False) - data = fn(doc) + data = xml.XMLPage(doc) if repolygonize: - im = Image.open(data['image']).convert('L') - lines = data['lines'] - polygons = segmentation.calculate_polygonal_environment(im, [x['baseline'] for x in lines], scale=(1200, 0)) - data['lines'] = [{'boundary': polygon, - 'baseline': orig['baseline'], - 'text': orig['text'], - 'tags': orig['tags']} for orig, polygon in zip(lines, polygons)] + im = Image.open(data.imagename).convert('L') + lines = data.lines + polygons = segmentation.calculate_polygonal_environment(im, [x.baseline for x in lines], scale=(1200, 0)) + data.lines = [dataclasses.replace(orig, boundary=polygon) for orig, polygon in zip(lines, polygons)] # reorder lines by type lines = defaultdict(list) - for line in data['lines']: - lines[line['tags']['type']].append(line) - im = Image.open(data['image']).convert('RGBA') + for line in data.lines: + lines[line.tags['type']].append(line) + im = Image.open(data.imagename).convert('RGBA') for t, ls in lines.items(): tmp = Image.new('RGBA', im.size, (0, 0, 0, 0)) draw = ImageDraw.Draw(tmp) for idx, line in enumerate(ls): c = next(cmap) - if line['boundary']: - draw.polygon([tuple(x) for x in line['boundary']], fill=c, outline=c[:3]) - if line['baseline']: - draw.line([tuple(x) for x in line['baseline']], fill=bmap, width=2, joint='curve') - draw.text(line['baseline'][0], str(idx), fill=(0, 0, 0, 255)) + if line.boundary: + draw.polygon([tuple(x) for x in line.boundary], fill=c, outline=c[:3]) + if line.baseline: + draw.line([tuple(x) for x in line.baseline], fill=bmap, width=2, joint='curve') + draw.text(line.baseline[0], str(idx), fill=(0, 0, 0, 255)) base_image = Image.alpha_composite(im, tmp) base_image.save(f'high_{os.path.basename(doc)}_lines_{slugify(t)}.png') - for t, regs in data['regions'].items(): + for t, regs in data.regions.items(): tmp = Image.new('RGBA', im.size, (0, 0, 0, 0)) draw = ImageDraw.Draw(tmp) for reg in regs: c = next(cmap) try: - draw.polygon(reg, fill=c, outline=c[:3]) + draw.polygon(reg.boundary, fill=c, outline=c[:3]) except Exception: pass base_image = Image.alpha_composite(im, tmp) @@ -118,26 +106,26 @@ def cli(format_type, model, text_direction, repolygonize, files): res = blla.segment(im, model=net, text_direction=text_direction) # reorder lines by type lines = defaultdict(list) - for line in res['lines']: - lines[line['tags']['type']].append(line) + for line in res.lines: + lines[line.tags['type']].append(line) im = im.convert('RGBA') for t, ls in lines.items(): tmp = Image.new('RGBA', im.size, (0, 0, 0, 0)) draw = ImageDraw.Draw(tmp) for idx, line in enumerate(ls): c = next(cmap) - draw.polygon([tuple(x) for x in line['boundary']], fill=c, outline=c[:3]) - draw.line([tuple(x) for x in line['baseline']], fill=bmap, width=2, joint='curve') - draw.text(line['baseline'][0], str(idx), fill=(0, 0, 0, 255)) + draw.polygon([tuple(x) for x in line.boundary], fill=c, outline=c[:3]) + draw.line([tuple(x) for x in line.baseline], fill=bmap, width=2, joint='curve') + draw.text(line.baseline[0], str(idx), fill=(0, 0, 0, 255)) base_image = Image.alpha_composite(im, tmp) base_image.save(f'high_{os.path.basename(doc)}_lines_{slugify(t)}.png') - for t, regs in res['regions'].items(): + for t, regs in res.regions.items(): tmp = Image.new('RGBA', im.size, (0, 0, 0, 0)) draw = ImageDraw.Draw(tmp) for reg in regs: c = next(cmap) try: - draw.polygon([tuple(x) for x in reg], fill=c, outline=c[:3]) + draw.polygon([tuple(x) for x in reg.boundary], fill=c, outline=c[:3]) except Exception: pass From 46d6c063b8a4bfa7a667eeedb4aec03d284a6fb3 Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Wed, 26 Jul 2023 11:26:38 +0200 Subject: [PATCH 65/69] Switch recognition datasets to container classes --- kraken/containers.py | 12 +-- kraken/lib/dataset/recognition.py | 159 +++++++++++++++++++----------- 2 files changed, 108 insertions(+), 63 deletions(-) diff --git a/kraken/containers.py b/kraken/containers.py index 892d00109..c58ef9c10 100644 --- a/kraken/containers.py +++ b/kraken/containers.py @@ -54,7 +54,7 @@ class BaselineLine: paragraph direction) for the BiDi algorithm. Valid values are 'L' or 'R'. If None is given the default auto-resolution will be used. - image: Image object of this line. + imagename: Path to the image associated with the line. tags: A dict mapping types to values. split: Defines whether this line is in the `train`, `validation`, or `test` set during training. @@ -66,7 +66,7 @@ class BaselineLine: text: Optional[str] = None base_dir: Optional[Literal['L', 'R']] = None type: str = 'baselines' - image: Optional[PIL.Image.Image] = None + imagename: Optional[Union[str, PathLike]] = None tags: Optional[Dict[str, str]] = None split: Optional[Literal['train', 'validation', 'test']] = None regions: Optional[List[str]] = None @@ -89,7 +89,7 @@ class BBoxLine: paragraph direction) for the BiDi algorithm. Valid values are 'L' or 'R'. If None is given the default auto-resolution will be used. - image: Image object of this line. + imagename: Path to the image associated with the line.. tags: A dict mapping types to values. split: Defines whether this line is in the `train`, `validation`, or `test` set during training. @@ -105,7 +105,7 @@ class BBoxLine: text: Optional[str] = None base_dir: Optional[Literal['L', 'R']] = None type: str = 'bbox' - image: Optional[PIL.Image.Image] = None + imagename: Optional[Union[str, PathLike]] = None tags: Optional[Dict[str, str]] = None split: Optional[Literal['train', 'validation', 'test']] = None regions: Optional[List[str]] = None @@ -121,12 +121,12 @@ class Region: id: Unique identifier boundary: List of tuples `(x_n, y_n)` defining the bounding polygon of the region. The first and last points should be identical. - image: Image object containing the region. + imagename: Path to the image associated with the region. tags: A dict mapping types to values. """ id: str boundary: List[Tuple[int, int]] - image: Optional[PIL.Image.Image] = None + imagename: Optional[Union[str, PathLike]] = None tags: Optional[Dict[str, str]] = None diff --git a/kraken/lib/dataset/recognition.py b/kraken/lib/dataset/recognition.py index d381b9d97..95bdef25e 100644 --- a/kraken/lib/dataset/recognition.py +++ b/kraken/lib/dataset/recognition.py @@ -56,7 +56,7 @@ def __init__(self): ShiftScaleRotate, OpticalDistortion, ElasticTransform, PixelDropout ) - + self._transforms = Compose([ ToFloat(), PixelDropout(p=0.2), @@ -71,7 +71,7 @@ def __init__(self): ElasticTransform(alpha=64, sigma=25, alpha_affine=0.25, p=0.1), ], p=0.2), ], p=0.5) - + def __call__(self, image): return self._transforms(image=image) @@ -319,54 +319,67 @@ def __init__(self, self.im_mode = '1' - def add(self, *args, **kwargs): + def add(self, + line: Optional[BaselineLine] = None, + page: Optional[Segmentation] = None): """ - Adds a line to the dataset. + Adds an indiviual line or all lines on a page to the dataset. Args: - im (path): Path to the whole page image - text (str): Transcription of the line. - baseline (list): A list of coordinates [[x0, y0], ..., [xn, yn]]. - boundary (list): A polygon mask for the line. + line: BaselineLine container object of a line. + page: Segmentation container object for a page. """ - if 'preparse' not in kwargs or not kwargs['preparse']: - kwargs = self.parse(*args, **kwargs) - self._images.append((kwargs['image'], kwargs['baseline'], kwargs['boundary'])) - self._gt.append(kwargs['text']) - self.alphabet.update(kwargs['text']) + if line: + self.add_line(line) + if page: + self.add_page(page) + if not (line and page): + raise ValueError('Neither line nor page data provided in dataset builder') - def parse(self, - image: Union[PathLike, str, Image.Image], - text: str, - baseline: List[Tuple[int, int]], - boundary: List[Tuple[int, int]], - *args, - **kwargs): + def add_page(self, page: Segmentation): """ - Parses a sample for the dataset and returns it. + Adds all lines on a page to the dataset. - This function is mainly uses for parallelized loading of training data. + Invalid lines will be skipped and a warning will be printed. Args: - im (path): Path to the whole page image - text (str): Transcription of the line. - baseline (list): A list of coordinates [[x0, y0], ..., [xn, yn]]. - boundary (list): A polygon mask for the line. + page: Segmentation container object for a page. """ - orig_text = text + if page.type != 'baselines': + raise ValueError(f'Invalid segmentation of type {page.type} (expected "baselines")') + for line in page.lines: + try: + self.add_line(dataclasses.replace(line, imagename=page.imagename)) + except ValueError as e: + logger.warning(e) + + def add_line(self, line: BaselineLine) + """ + Adds a line to the dataset. + + Args: + line: BaselineLine container object for a line. + + Raises: + ValueError if the transcription of the line is empty after + transformation or either baseline or bounding polygon are missing. + """ + if line.type != 'baselines': + raise ValueError(f'Invalid line of type {line.type} (expected "baselines")') + + text = line.text for func in self.text_transforms: text = func(text) if not text and self.skip_empty_lines: - raise KrakenInputException(f'Text line "{orig_text}" is empty after transformations') - if not baseline: - raise KrakenInputException('No baseline given for line') - if not boundary: - raise KrakenInputException('No boundary given for line') - return {'text': text, - 'image': image, - 'baseline': baseline, - 'boundary': boundary, - 'preparse': True} + raise ValueError(f'Text line "{line.text}" is empty after transformations') + if not line.baseline: + raise ValueError('No baseline given for line') + if not line.boundary: + raise ValueError('No boundary given for line') + + self._images.append((line.image, line.baseline, line.boundary)) + self._gt.append(text) + self.alphabet.update(text) def encode(self, codec: Optional[PytorchCodec] = None) -> None: """ @@ -493,35 +506,67 @@ def __init__(self, split: Callable[[Union[PathLike, str]], str] = F_t.default_sp self.im_mode = '1' - def add(self, *args, **kwargs) -> None: + def add(self, + line: Optional[BBoxLine] = None, + page: Optional[Segmentation] = None): """ - Adds a line-image-text pair to the dataset. + Adds an indiviual line or all lines on a page to the dataset. Args: - image (str): Input image path + line: BBoxLine container object of a line. + page: Segmentation container object for a page. """ - if 'preparse' not in kwargs or not kwargs['preparse']: - kwargs = self.parse(*args, **kwargs) - self._images.append(kwargs['image']) - self._gt.append(kwargs['text']) - self.alphabet.update(kwargs['text']) + if line: + self.add_line(line) + if page: + self.add_page(page) + if not (line and page): + raise ValueError('Neither line nor page data provided in dataset builder') - def parse(self, image: Union[PathLike, str, Image.Image], *args, **kwargs) -> Dict: + def add_page(self, page: Segmentation): """ - Parses a sample for this dataset. + Adds all lines on a page to the dataset. - This is mostly used to parallelize populating the dataset. + Invalid lines will be skipped and a warning will be printed. Args: - image (str): Input image path - """ - with open(self.split(image), 'r', encoding='utf-8') as fp: - text = fp.read().strip('\n\r') - for func in self.text_transforms: - text = func(text) - if not text and self.skip_empty_lines: - raise KrakenInputException(f'Text line is empty ({fp.name})') - return {'image': image, 'text': text, 'preparse': True} + page: Segmentation container object for a page. + """ + if page.type != 'bbox': + raise ValueError(f'Invalid segmentation of type {page.type} (expected "bbox")') + for line in page.lines: + try: + self.add_line(dataclasses.replace(line, imagename=page.imagename)) + except ValueError as e: + logger.warning(e) + + def add_line(self, line: BBoxLine) + """ + Adds a line to the dataset. + + Args: + line: BBoxLine container object for a line. + + Raises: + ValueError if the transcription of the line is empty after + transformation or either baseline or bounding polygon are missing. + """ + if line.type != 'bbox': + raise ValueError(f'Invalid line of type {line.type} (expected "bbox")') + + text = line.text + for func in self.text_transforms: + text = func(text) + if not text and self.skip_empty_lines: + raise ValueError(f'Text line "{line.text}" is empty after transformations') + if not line.baseline: + raise ValueError('No baseline given for line') + if not line.boundary: + raise ValueError('No boundary given for line') + + self._images.append(line.image) + self._gt.append(text) + self.alphabet.update(text) def encode(self, codec: Optional[PytorchCodec] = None) -> None: """ From a5a8a20932ea2cc4a1f8777c281d7e193542d682 Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Wed, 30 Aug 2023 23:01:57 +0200 Subject: [PATCH 66/69] s/preparse_xml_data/XMLPage/g --- docs/api_docs.rst | 227 +++++++++++++++++------------ kraken/ketos/recognition.py | 4 +- kraken/lib/dataset/recognition.py | 14 +- kraken/lib/dataset/segmentation.py | 98 +++---------- kraken/lib/pretrain/model.py | 42 ++++-- kraken/lib/train.py | 48 +++--- 6 files changed, 207 insertions(+), 226 deletions(-) diff --git a/docs/api_docs.rst b/docs/api_docs.rst index 46379f2b8..cb85ff91f 100644 --- a/docs/api_docs.rst +++ b/docs/api_docs.rst @@ -2,8 +2,11 @@ API Reference ************* +Segmentation +============ + kraken.blla module -================== +------------------ .. note:: @@ -14,7 +17,7 @@ kraken.blla module .. autoapifunction:: kraken.blla.segment kraken.pageseg module -===================== +--------------------- .. note:: @@ -24,22 +27,22 @@ kraken.pageseg module .. autoapifunction:: kraken.pageseg.segment -kraken.rpred module -=================== +Recognition +=========== -.. autoapifunction:: kraken.rpred.bidi_record +kraken.rpred module +------------------- .. autoapiclass:: kraken.rpred.mm_rpred :members: -.. autoapiclass:: kraken.rpred.ocr_record - :members: - .. autoapifunction:: kraken.rpred.rpred +Serialization +============= kraken.serialization module -=========================== +--------------------------- .. autoapifunction:: kraken.serialization.render_report @@ -47,127 +50,118 @@ kraken.serialization module .. autoapifunction:: kraken.serialization.serialize_segmentation -kraken.lib.models module -======================== +Default templates +----------------- -.. autoapiclass:: kraken.lib.models.TorchSeqRecognizer - :members: +ALTO 4.4 +^^^^^^^^ -.. autoapifunction:: kraken.lib.models.load_any +.. literalinclude:: ../../templates/alto + :language: xml+jinja -kraken.lib.vgsl module -====================== +PageXML +^^^^^^^ -.. autoapiclass:: kraken.lib.vgsl.TorchVGSLModel - :members: +.. literalinclude:: ../../templates/alto + :language: xml+jinja -kraken.lib.xml module -===================== +hOCR +^^^^ -.. autoapifunction:: kraken.lib.xml.parse_xml +.. literalinclude:: ../../templates/alto + :language: xml+jinja -.. autoapifunction:: kraken.lib.xml.parse_page +ABBYY XML +^^^^^^^^^ -.. autoapifunction:: kraken.lib.xml.parse_alto +.. literalinclude:: ../../templates/abbyyxml + :language: xml+jinja + +Containers and Helpers +====================== kraken.lib.codec module -======================= +----------------------- .. autoapiclass:: kraken.lib.codec.PytorchCodec :members: -kraken.lib.train module -======================= +kraken.containers module +------------------------ -Training Schedulers -------------------- +.. autoapiclass:: kraken.containers.Segmentation + :members: -.. autoapiclass:: kraken.lib.train.TrainScheduler - :members: +.. autoapiclass:: kraken.containers.BaselineLine + :members: -.. autoapiclass:: kraken.lib.train.annealing_step - :members: +.. autoapiclass:: kraken.containers.BBoxLine + :members: -.. autoapiclass:: kraken.lib.train.annealing_const - :members: +.. autoapiclass:: kraken.containers.ocr_record + :members: -.. autoapiclass:: kraken.lib.train.annealing_exponential - :members: +.. autoapiclass:: kraken.containers.BaselineOCRRecord + :members: -.. autoapiclass:: kraken.lib.train.annealing_reduceonplateau - :members: +.. autoapiclass:: kraken.containers.BBoxOCRRecord + :members: -.. autoapiclass:: kraken.lib.train.annealing_cosine - :members: +.. autoapiclass:: kraken.containers.ProcessingStep + :members: -.. autoapiclass:: kraken.lib.train.annealing_onecycle - :members: +kraken.lib.ctc_decoder +---------------------- -Training Stoppers ------------------ +.. autoapifunction:: kraken.lib.ctc_decoder.beam_decoder -.. autoapiclass:: kraken.lib.train.TrainStopper - :members: +.. autoapifunction:: kraken.lib.ctc_decoder.greedy_decoder -.. autoapiclass:: kraken.lib.train.EarlyStopping - :members: +.. autoapifunction:: kraken.lib.ctc_decoder.blank_threshold_decoder -.. autoapiclass:: kraken.lib.train.EpochStopping - :members: +kraken.lib.exceptions +--------------------- -.. autoapiclass:: kraken.lib.train.NoStopping +.. autoapiclass:: kraken.lib.exceptions.KrakenCodecException :members: -Loss and Evaluation Functions ------------------------------ - -.. autoapifunction:: kraken.lib.train.recognition_loss_fn - -.. autoapifunction:: kraken.lib.train.baseline_label_loss_fn - -.. autoapifunction:: kraken.lib.train.recognition_evaluator_fn - -.. autoapifunction:: kraken.lib.train.baseline_label_evaluator_fn - -Trainer -------- - -.. autoapiclass:: kraken.lib.train.KrakenTrainer +.. autoapiclass:: kraken.lib.exceptions.KrakenStopTrainingException :members: +.. autoapiclass:: kraken.lib.exceptions.KrakenEncodeException + :members: -kraken.lib.dataset module -========================= - -Datasets --------- +.. autoapiclass:: kraken.lib.exceptions.KrakenRecordException + :members: -.. autoapiclass:: kraken.lib.dataset.BaselineSet +.. autoapiclass:: kraken.lib.exceptions.KrakenInvalidModelException :members: -.. autoapiclass:: kraken.lib.dataset.PolygonGTDataset +.. autoapiclass:: kraken.lib.exceptions.KrakenInputException :members: -.. autoapiclass:: kraken.lib.dataset.GroundTruthDataset +.. autoapiclass:: kraken.lib.exceptions.KrakenRepoException :members: -Helpers -------- +.. autoapiclass:: kraken.lib.exceptions.KrakenCairoSurfaceException + :members: -.. autoapifunction:: kraken.lib.dataset.compute_error +kraken.lib.models module +------------------------ -.. autoapifunction:: kraken.lib.dataset.preparse_xml_data +.. autoapiclass:: kraken.lib.models.TorchSeqRecognizer + :members: -.. autoapifunction:: kraken.lib.dataset.generate_input_transforms +.. autoapifunction:: kraken.lib.models.load_any kraken.lib.segmentation module ------------------------------ .. autoapifunction:: kraken.lib.segmentation.reading_order -.. autoapifunction:: kraken.lib.segmentation.polygonal_reading_order +.. autoapifunction:: kraken.lib.segmentation.neural_reading_order -.. autoapifunction:: kraken.lib.segmentation.denoising_hysteresis_thresh +.. autoapifunction:: kraken.lib.segmentation.polygonal_reading_order .. autoapifunction:: kraken.lib.segmentation.vectorize_lines @@ -181,43 +175,82 @@ kraken.lib.segmentation module .. autoapifunction:: kraken.lib.segmentation.extract_polygons +kraken.lib.vgsl module +---------------------- -kraken.lib.ctc_decoder -====================== +.. autoapiclass:: kraken.lib.vgsl.TorchVGSLModel + :members: -.. autoapifunction:: kraken.lib.ctc_decoder.beam_decoder +kraken.lib.xml module +--------------------- -.. autoapifunction:: kraken.lib.ctc_decoder.greedy_decoder +.. autoapiclass:: kraken.lib.xml.XMLPage -.. autoapifunction:: kraken.lib.ctc_decoder.blank_threshold_decoder +Training +======== -kraken.lib.exceptions -===================== +kraken.lib.train module +----------------------- -.. autoapiclass:: kraken.lib.exceptions.KrakenCodecException - :members: +Loss and Evaluation Functions +----------------------------- -.. autoapiclass:: kraken.lib.exceptions.KrakenStopTrainingException +.. autoapifunction:: kraken.lib.train.recognition_loss_fn + +.. autoapifunction:: kraken.lib.train.baseline_label_loss_fn + +.. autoapifunction:: kraken.lib.train.recognition_evaluator_fn + +.. autoapifunction:: kraken.lib.train.baseline_label_evaluator_fn + +Trainer +------- + +.. autoapiclass:: kraken.lib.train.KrakenTrainer :members: -.. autoapiclass:: kraken.lib.exceptions.KrakenEncodeException + +kraken.lib.dataset module +------------------------- + +Recognition datasets +^^^^^^^^^^^^^^^^^^^^ + +.. autoapiclass:: kraken.lib.dataset.ArrowIPCRecognitionDataset :members: -.. autoapiclass:: kraken.lib.exceptions.KrakenRecordException +.. autoapiclass:: kraken.lib.dataset.BaselineSet :members: -.. autoapiclass:: kraken.lib.exceptions.KrakenInvalidModelException +.. autoapiclass:: kraken.lib.dataset.GroundTruthDataset :members: -.. autoapiclass:: kraken.lib.exceptions.KrakenInputException +Segmentation datasets +^^^^^^^^^^^^^^^^^^^^^ + +.. autoapiclass:: kraken.lib.dataset.PolygonGTDataset :members: -.. autoapiclass:: kraken.lib.exceptions.KrakenRepoException +Reading order datasets +^^^^^^^^^^^^^^^^^^^^^^ + +.. autoapiclass:: kraken.lib.dataset.PairWiseROSet :members: -.. autoapiclass:: kraken.lib.exceptions.KrakenCairoSurfaceException +.. autoapiclass:: kraken.lib.dataset.PageWiseROSet :members: +Helpers +^^^^^^^ + +.. autoapiclass:: kraken.lib.dataset.ImageInputTransforms + :members: + +.. autoapifunction:: kraken.lib.dataset.collate_sequences + +.. autoapifunction:: kraken.lib.dataset.global_align + +.. autoapifunction:: kraken.lib.dataset.compute_confusions Legacy modules ============== diff --git a/kraken/ketos/recognition.py b/kraken/ketos/recognition.py index 2d8eaf86b..ba324e810 100644 --- a/kraken/ketos/recognition.py +++ b/kraken/ketos/recognition.py @@ -384,7 +384,7 @@ def test(ctx, batch_size, model, evaluation_files, device, pad, workers, from kraken.serialization import render_report from kraken.lib import models - from kraken.lib.xml import preparse_xml_data + from kraken.lib.xml import XMLPage from kraken.lib.dataset import (global_align, compute_confusions, PolygonGTDataset, GroundTruthDataset, ImageInputTransforms, @@ -413,7 +413,7 @@ def test(ctx, batch_size, model, evaluation_files, device, pad, workers, if format_type in ['xml', 'page', 'alto']: if repolygonize: message('Repolygonizing data') - test_set = preparse_xml_data(test_set, format_type, repolygonize) + test_set = [{'page': XMLPage(file, filetype=format_type).to_container()} for file in test_set] valid_norm = False DatasetClass = PolygonGTDataset elif format_type == 'binary': diff --git a/kraken/lib/dataset/recognition.py b/kraken/lib/dataset/recognition.py index 95bdef25e..0ddb5585e 100644 --- a/kraken/lib/dataset/recognition.py +++ b/kraken/lib/dataset/recognition.py @@ -30,6 +30,7 @@ from torch.utils.data import Dataset from typing import Dict, List, Tuple, Callable, Optional, Any, Union, Literal +from kraken.containers import BaselineLine, BBoxLine, Segmentation from kraken.lib.util import is_bitonal from kraken.lib.codec import PytorchCodec from kraken.lib.segmentation import extract_polygons @@ -353,7 +354,7 @@ def add_page(self, page: Segmentation): except ValueError as e: logger.warning(e) - def add_line(self, line: BaselineLine) + def add_line(self, line: BaselineLine): """ Adds a line to the dataset. @@ -445,8 +446,7 @@ class GroundTruthDataset(Dataset): All data is cached in memory. """ - def __init__(self, split: Callable[[Union[PathLike, str]], str] = F_t.default_split, - suffix: str = '.gt.txt', + def __init__(self, normalization: Optional[str] = None, whitespace_normalization: bool = True, skip_empty_lines: bool = True, @@ -457,10 +457,6 @@ def __init__(self, split: Callable[[Union[PathLike, str]], str] = F_t.default_sp Reads a list of image-text pairs and creates a ground truth set. Args: - split: Function for generating the base name without - extensions from paths - suffix: Suffix to attach to image base name for text - retrieval mode: Image color space. Either RGB (color) or L (grayscale/bw). Only L is compatible with vertical scaling/dewarping. @@ -479,8 +475,6 @@ def __init__(self, split: Callable[[Union[PathLike, str]], str] = F_t.default_sp tensor suitable for forward passes. augmentation: Enables augmentation. """ - self.suffix = suffix - self.split = partial(F_t.suffix_split, split=split, suffix=suffix) self._images = [] # type: Union[List[Image], List[torch.Tensor]] self._gt = [] # type: List[str] self.alphabet = Counter() # type: Counter @@ -540,7 +534,7 @@ def add_page(self, page: Segmentation): except ValueError as e: logger.warning(e) - def add_line(self, line: BBoxLine) + def add_line(self, line: BBoxLine): """ Adds a line to the dataset. diff --git a/kraken/lib/dataset/segmentation.py b/kraken/lib/dataset/segmentation.py index faa0536ce..0d248800e 100644 --- a/kraken/lib/dataset/segmentation.py +++ b/kraken/lib/dataset/segmentation.py @@ -33,6 +33,7 @@ from skimage.draw import polygon +from kraken.containers import Segmentation from kraken.lib.xml import XMLPage from kraken.lib.exceptions import KrakenInputException @@ -48,24 +49,20 @@ class BaselineSet(Dataset): """ Dataset for training a baseline/region segmentation model. """ - def __init__(self, imgs: Sequence[Union[PathLike, str]] = None, - suffix: str = '.path', + def __init__(self, line_width: int = 4, padding: Tuple[int, int, int, int] = (0, 0, 0, 0), im_transforms: Callable[[Any], torch.Tensor] = transforms.Compose([]), - mode: Optional[Literal['path', 'alto', 'page', 'xml']] = 'path', + mode: Optional[Literal['alto', 'page', 'xml']] = 'xml', augmentation: bool = False, valid_baselines: Sequence[str] = None, merge_baselines: Dict[str, Sequence[str]] = None, valid_regions: Sequence[str] = None, merge_regions: Dict[str, Sequence[str]] = None): """ - Reads a list of image-json pairs and creates a data set. + Creates a dataset for a text-line and region segmentation model. Args: - imgs: - suffix: Suffix to attach to image base name to load JSON files - from. line_width: Height of the baseline in the scaled input. padding: Tuple of ints containing the left/right, top/bottom padding of the input images. @@ -90,7 +87,6 @@ def __init__(self, imgs: Sequence[Union[PathLike, str]] = None, self.mode = mode self.im_mode = '1' self.pad = padding - self.aug = None self.targets = [] # n-th entry contains semantic of n-th class self.class_mapping = {'aux': {'_start_separator': 0, '_end_separator': 1}, 'baselines': {}, 'regions': {}} @@ -102,53 +98,8 @@ def __init__(self, imgs: Sequence[Union[PathLike, str]] = None, self.mreg_dict = merge_regions if merge_regions is not None else {} self.valid_baselines = valid_baselines self.valid_regions = valid_regions - if mode in ['alto', 'page', 'xml']: - im_paths = [] - self.targets = [] - for img in imgs: - try: - data = XMLPage(img) - im_paths.append(data.imagename) - lines = defaultdict(list) - for line in data.get_sorted_lines(): - if valid_baselines is None or set(line['tags'].values()).intersection(valid_baselines): - tags = set(line['tags'].values()).intersection(valid_baselines) if valid_baselines else line['tags'].values() - for tag in tags: - lines[self.mbl_dict.get(tag, tag)].append(line['baseline']) - self.class_stats['baselines'][self.mbl_dict.get(tag, tag)] += 1 - regions = defaultdict(list) - for k, v in data.regions.items(): - if valid_regions is None or k in valid_regions: - regions[self.mreg_dict.get(k, k)].extend(v) - self.class_stats['regions'][self.mreg_dict.get(k, k)] += len(v) - self.targets.append({'baselines': lines, 'regions': regions}) - except KrakenInputException as e: - logger.warning(e) - continue - # get line types - imgs = im_paths - # calculate class mapping - line_types = set() - region_types = set() - for page in self.targets: - for line_type in page['baselines'].keys(): - line_types.add(line_type) - for reg_type in page['regions'].keys(): - region_types.add(reg_type) - idx = -1 - for idx, line_type in enumerate(line_types): - self.class_mapping['baselines'][line_type] = idx + self.num_classes - self.num_classes += idx + 1 - idx = -1 - for idx, reg_type in enumerate(region_types): - self.class_mapping['regions'][reg_type] = idx + self.num_classes - self.num_classes += idx + 1 - elif mode == 'path': - pass - elif mode is None: - imgs = [] - else: - raise Exception('invalid dataset mode') + + self.aug = None if augmentation: import cv2 cv2.setNumThreads(0) @@ -172,37 +123,26 @@ def __init__(self, imgs: Sequence[Union[PathLike, str]] = None, ], p=0.2), HueSaturationValue(hue_shift_limit=20, sat_shift_limit=0.1, val_shift_limit=0.1, p=0.3), ], p=0.5) - self.imgs = imgs self.line_width = line_width self.transforms = im_transforms self.seg_type = None - def add(self, - image: Union[PathLike, str, Image.Image], - baselines: List[List[List[Tuple[int, int]]]] = None, - regions: Dict[str, List[List[Tuple[int, int]]]] = None, - *args, - **kwargs): + def add(self, doc: Union[Segmentation, XMLPage]): """ Adds a page to the dataset. Args: - im: Path to the whole page image - baseline: A list containing dicts with a list of coordinates - and tags [{'baseline': [[x0, y0], ..., - [xn, yn]], 'tags': ('script_type',)}, ...] - regions: A dict containing list of lists of coordinates - {'region_type_0': [[x0, y0], ..., [xn, yn]]], - 'region_type_1': ...}. + doc: Either a Segmentation container class or an XMLPage. """ - if self.mode: - raise Exception(f'The `add` method is incompatible with dataset mode {self.mode}') + if doc.type != 'baselines': + raise ValueError(f'{doc} is of type {doc.type}. Expected "baselines".') + baselines_ = defaultdict(list) - for line in baselines: - if self.valid_baselines is None or set(line['tags'].values()).intersection(self.valid_baselines): - tags = set(line['tags'].values()).intersection(self.valid_baselines) if self.valid_baselines else line['tags'].values() + for line in doc.lines: + if self.valid_baselines is None or set(line.tags.values()).intersection(self.valid_baselines): + tags = set(line.tags.values()).intersection(self.valid_baselines) if self.valid_baselines else line.tags.values() for tag in tags: - baselines_[tag].append(line['baseline']) + baselines_[tag].append(line.baseline) self.class_stats['baselines'][tag] += 1 if tag not in self.class_mapping['baselines']: @@ -210,7 +150,7 @@ def add(self, self.class_mapping['baselines'][tag] = self.num_classes - 1 regions_ = defaultdict(list) - for k, v in regions.items(): + for k, v in doc.regions.items(): reg_type = self.mreg_dict.get(k, k) if self.valid_regions is None or reg_type in self.valid_regions: regions_[reg_type].extend(v) @@ -224,11 +164,7 @@ def add(self, def __getitem__(self, idx): im = self.imgs[idx] - if self.mode != 'path': - target = self.targets[idx] - else: - with open('{}.path'.format(path.splitext(im)[0]), 'r') as fp: - target = json.load(fp) + target = self.targets[idx] if not isinstance(im, Image.Image): try: logger.debug(f'Attempting to load {im}') diff --git a/kraken/lib/pretrain/model.py b/kraken/lib/pretrain/model.py index 3b5087e96..61dc04ab4 100644 --- a/kraken/lib/pretrain/model.py +++ b/kraken/lib/pretrain/model.py @@ -45,7 +45,7 @@ from pytorch_lightning.utilities.memory import is_oom_error, garbage_collection_cuda from kraken.lib import vgsl, default_specs, layers -from kraken.lib.xml import preparse_xml_data +from kraken.lib.xml import XMLPage from kraken.lib.codec import PytorchCodec from kraken.lib.dataset import (ArrowIPCRecognitionDataset, GroundTruthDataset, PolygonGTDataset, @@ -108,10 +108,10 @@ def __init__(self, valid_norm = True if format_type in ['xml', 'page', 'alto']: logger.info(f'Parsing {len(training_data)} XML files for training data') - training_data = preparse_xml_data(training_data, format_type, repolygonize) + training_data = [{'page': XMLPage(file, format_type).to_container()} for file in training_data] if evaluation_data: logger.info(f'Parsing {len(evaluation_data)} XML files for validation data') - evaluation_data = preparse_xml_data(evaluation_data, format_type, repolygonize) + evaluation_data = [{'page': XMLPage(file, format_type).to_container()} for file in evaluation_data] if binary_dataset_split: logger.warning('Internal binary dataset splits are enabled but using non-binary dataset files. Will be ignored.') binary_dataset_split = False @@ -144,7 +144,7 @@ def __init__(self, valid_norm = True # format_type is None. Determine training type from length of training data entry elif not format_type: - if len(training_data[0]) >= 4: + if training_data[0].type == 'baselines': DatasetClass = PolygonGTDataset valid_norm = False else: @@ -156,6 +156,22 @@ def __init__(self, if binary_dataset_split: logger.warning('Internal binary dataset splits are enabled but using non-binary dataset files. Will be ignored.') binary_dataset_split = False + samples = [] + for sample in training_data: + if isinstance(sample, Segmentation): + samples.append({'page': sample}) + else: + samples.append({'line': sample}) + training_data = samples + if evaluation_data: + samples = [] + for sample in evaluation_data: + if isinstance(sample, Segmentation): + samples.append({'page': sample}) + else: + samples.append({'line': sample}) + evaluation_data = samples + else: raise ValueError(f'format_type {format_type} not in [alto, page, xml, path, binary].') @@ -203,18 +219,12 @@ def _build_dataset(self, skip_empty_lines=False, **kwargs) - if (self.hparams.num_workers and self.hparams.num_workers > 1) and self.hparams.format_type != 'binary': - with Pool(processes=self.hparams.num_workers) as pool: - for im in pool.imap_unordered(partial(_star_fun, dataset.parse), training_data, 5): - logger.debug(f'Adding sample {im} to training set') - if im: - dataset.add(**im) - else: - for im in training_data: - try: - dataset.add(**im) - except KrakenInputException as e: - logger.warning(str(e)) + for sample in training_data: + try: + dataset.add(**sample) + except KrakenInputException as e: + logger.warning(str(e)) + return dataset def train_dataloader(self): diff --git a/kraken/lib/train.py b/kraken/lib/train.py index 81296eb27..f94568f78 100644 --- a/kraken/lib/train.py +++ b/kraken/lib/train.py @@ -33,7 +33,6 @@ from pytorch_lightning.callbacks import Callback, EarlyStopping, BaseFinetuning, LearningRateMonitor from kraken.lib import models, vgsl, default_specs, progress -# from kraken.lib.xml import preparse_xml_data from kraken.lib.util import make_printable from kraken.lib.codec import PytorchCodec from kraken.lib.dataset import (ArrowIPCRecognitionDataset, BaselineSet, @@ -271,10 +270,10 @@ def __init__(self, valid_norm = True if format_type in ['xml', 'page', 'alto']: logger.info(f'Parsing {len(training_data)} XML files for training data') - training_data = preparse_xml_data(training_data, format_type, repolygonize) + training_data = [{'page': XMLPage(file, format_type).to_container()} for file in training_data] if evaluation_data: logger.info(f'Parsing {len(evaluation_data)} XML files for validation data') - evaluation_data = preparse_xml_data(evaluation_data, format_type, repolygonize) + evaluation_data = [{'page': XMLPage(file, format_type).to_container()} for file in evaluation_data] if binary_dataset_split: logger.warning('Internal binary dataset splits are enabled but using non-binary dataset files. Will be ignored.') binary_dataset_split = False @@ -305,9 +304,9 @@ def __init__(self, logger.info(f'Got {len(evaluation_data)} line strip images for validation data') evaluation_data = [{'image': im} for im in evaluation_data] valid_norm = True - # format_type is None. Determine training type from length of training data entry + # format_type is None. Determine training type from container class types elif not format_type: - if len(training_data[0]) >= 4: + if training_data[0].type == 'baselines': DatasetClass = PolygonGTDataset valid_norm = False else: @@ -319,6 +318,21 @@ def __init__(self, if binary_dataset_split: logger.warning('Internal binary dataset splits are enabled but using non-binary dataset files. Will be ignored.') binary_dataset_split = False + samples = [] + for sample in training_data: + if isinstance(sample, Segmentation): + samples.append({'page': sample}) + else: + samples.append({'line': sample}) + training_data = samples + if evaluation_data: + samples = [] + for sample in evaluation_data: + if isinstance(sample, Segmentation): + samples.append({'page': sample}) + else: + samples.append({'line': sample}) + evaluation_data = samples else: raise ValueError(f'format_type {format_type} not in [alto, page, xml, path, binary].') @@ -423,21 +437,15 @@ def _build_dataset(self, augmentation=self.hparams.hyper_params['augment'], **kwargs) - if (self.num_workers and self.num_workers > 1) and self.format_type != 'binary': - with Pool(processes=self.num_workers) as pool: - for im in pool.imap_unordered(partial(_star_fun, dataset.parse), training_data, 5): - logger.debug(f'Adding sample {im} to training set') - if im: - dataset.add(**im) - else: - for im in training_data: - try: - dataset.add(**im) - except KrakenInputException as e: - logger.warning(str(e)) - if self.format_type == 'binary' and self.hparams.hyper_params['normalization']: - logger.debug('Rebuilding dataset using unicode normalization') - dataset.rebuild_alphabet() + for sample in training_data: + try: + dataset.add(**sample) + except KrakenInputException as e: + logger.warning(str(e)) + if self.format_type == 'binary' and self.hparams.hyper_params['normalization']: + logger.debug('Rebuilding dataset using unicode normalization') + dataset.rebuild_alphabet() + return dataset def forward(self, x, seq_lens=None): From 0fddaace613bb6ee3e8578fa353b810100931a9b Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Mon, 18 Sep 2023 11:52:48 +0200 Subject: [PATCH 67/69] Move progress bar imports around to prevent torch import Would drastically slow down display of help message in CLI drivers --- kraken/ketos/recognition.py | 2 +- kraken/ketos/repo.py | 3 +-- kraken/ketos/ro.py | 4 ++-- kraken/ketos/segmentation.py | 2 +- kraken/ketos/transcription.py | 3 ++- kraken/kraken.py | 29 ++++++++++++++++------------- 6 files changed, 23 insertions(+), 20 deletions(-) diff --git a/kraken/ketos/recognition.py b/kraken/ketos/recognition.py index ba324e810..04b6a30b0 100644 --- a/kraken/ketos/recognition.py +++ b/kraken/ketos/recognition.py @@ -24,7 +24,6 @@ from typing import List -from kraken.lib.progress import KrakenProgressBar from kraken.lib.exceptions import KrakenInputException from kraken.lib.default_specs import RECOGNITION_HYPER_PARAMS, RECOGNITION_SPEC from .util import _validate_manifests, _expand_gt, message, to_ptl_device @@ -390,6 +389,7 @@ def test(ctx, batch_size, model, evaluation_files, device, pad, workers, ImageInputTransforms, ArrowIPCRecognitionDataset, collate_sequences) + from kraken.lib.progress import KrakenProgressBar logger.info('Building test set from {} line images'.format(len(test_set) + len(evaluation_files))) diff --git a/kraken/ketos/repo.py b/kraken/ketos/repo.py index 32a49ac5e..52c9db8e3 100644 --- a/kraken/ketos/repo.py +++ b/kraken/ketos/repo.py @@ -22,8 +22,6 @@ import click import logging -from kraken.lib.progress import KrakenDownloadProgressBar - from .util import message logging.captureWarnings(True) @@ -52,6 +50,7 @@ def publish(ctx, metadata, access_token, private, model): from kraken import repo from kraken.lib import models + from kraken.lib.progress import KrakenDownloadProgressBar with pkg_resources.resource_stream('kraken', 'metadata.schema.json') as fp: schema = json.load(fp) diff --git a/kraken/ketos/ro.py b/kraken/ketos/ro.py index 1dcc0856f..17fc8cf09 100644 --- a/kraken/ketos/ro.py +++ b/kraken/ketos/ro.py @@ -25,7 +25,6 @@ from PIL import Image from typing import Dict -from kraken.lib.progress import KrakenProgressBar from kraken.lib.exceptions import KrakenInputException from kraken.lib.default_specs import READING_ORDER_HYPER_PARAMS @@ -152,8 +151,9 @@ def rotrain(ctx, batch_size, output, load, freq, quit, epochs, min_epochs, lag, """ import shutil - from kraken.lib.train import KrakenTrainer from kraken.lib.ro import ROModel + from kraken.lib.train import KrakenTrainer + from kraken.lib.progress import KrakenProgressBar if not (0 <= freq <= 1) and freq % 1.0 != 0: raise click.BadOptionUsage('freq', 'freq needs to be either in the interval [0,1.0] or a positive integer.') diff --git a/kraken/ketos/segmentation.py b/kraken/ketos/segmentation.py index dfcb7c739..e356d08df 100644 --- a/kraken/ketos/segmentation.py +++ b/kraken/ketos/segmentation.py @@ -24,7 +24,6 @@ from PIL import Image -from kraken.lib.progress import KrakenProgressBar from kraken.lib.exceptions import KrakenInputException from kraken.lib.default_specs import SEGMENTATION_HYPER_PARAMS, SEGMENTATION_SPEC @@ -230,6 +229,7 @@ def segtrain(ctx, output, spec, line_width, pad, load, freq, quit, epochs, import shutil from kraken.lib.train import SegmentationModel, KrakenTrainer + from kraken.lib.progress import KrakenProgressBar if resize != 'fail' and not load: raise click.BadOptionUsage('resize', 'resize option requires loading an existing model') diff --git a/kraken/ketos/transcription.py b/kraken/ketos/transcription.py index 490c0ac4e..dd4402c66 100644 --- a/kraken/ketos/transcription.py +++ b/kraken/ketos/transcription.py @@ -27,7 +27,6 @@ from typing import IO, Any, cast from bidi.algorithm import get_display -from kraken.lib.progress import KrakenProgressBar from .util import message logging.captureWarnings(True) @@ -68,6 +67,7 @@ def extract(ctx, binarize, normalization, normalize_whitespace, reorder, from lxml import html, etree from kraken import binarization + from kraken.lib.progress import KrakenProgressBar try: os.mkdir(output) @@ -172,6 +172,7 @@ def transcription(ctx, text_direction, scale, bw, maxcolseps, from kraken import binarization from kraken.lib import models + from kraken.lib.progress import KrakenProgressBar ti = transcribe.TranscriptionInterface(font, font_style) diff --git a/kraken/kraken.py b/kraken/kraken.py index 0f8fea361..38647eee8 100644 --- a/kraken/kraken.py +++ b/kraken/kraken.py @@ -24,16 +24,16 @@ import dataclasses import pkg_resources -from typing import Dict, Union, List, cast, Any, IO, Callable +from PIL import Image from pathlib import Path -from rich.traceback import install from functools import partial -from PIL import Image +from rich.traceback import install +from threadpoolctl import threadpool_limits +from typing import Dict, Union, List, cast, Any, IO, Callable import click from kraken.lib import log -from kraken.lib.progress import KrakenProgressBar, KrakenDownloadProgressBar warnings.simplefilter('ignore', UserWarning) @@ -118,8 +118,7 @@ def segmenter(legacy, model, text_direction, scale, maxcolseps, black_colseps, remove_hlines, pad, mask, device, input, output) -> None: import json - from kraken import pageseg - from kraken import blla + from kraken import blla, pageseg ctx = click.get_current_context() @@ -183,8 +182,10 @@ def recognizer(model, pad, no_segmentation, bidi_reordering, tags_ignore, input, import uuid import dataclasses - from kraken.containers import Segmentation, BBoxLine from kraken import rpred + from kraken.containers import Segmentation, BBoxLine + + from kraken.lib.progress import KrakenProgressBar ctx = click.get_current_context() @@ -301,8 +302,10 @@ def recognizer(model, pad, no_segmentation, bidi_reordering, tags_ignore, input, help='Raises the exception that caused processing to fail in the case of an error') @click.option('-2', '--autocast', default=False, show_default=True, flag_value=True, help='On compatible devices, uses autocast for `segment` which lower the memory usage.') +@click.option('--threads', default=1, show_default=True, type=click.IntRange(1), + help='Size of thread pools for intra-op parallelization') def cli(input, batch_input, suffix, verbose, format_type, pdf_format, - serializer, template, device, raise_on_error, autocast): + serializer, template, device, raise_on_error, autocast, threads): """ Base command for recognition functionality. @@ -345,6 +348,8 @@ def process_pipeline(subcommands, input, batch_input, suffix, verbose, format_ty import uuid import tempfile + from kraken.lib.progress import KrakenProgressBar + ctx = click.get_current_context() input = list(input) @@ -563,9 +568,7 @@ def _validate_mm(ctx, param, value): show_default=True, type=click.Choice(['horizontal-tb', 'vertical-lr', 'vertical-rl']), help='Sets principal text direction in serialization output') -@click.option('--threads', default=1, show_default=True, type=click.IntRange(1), - help='Number of threads to use for OpenMP parallelization.') -def ocr(ctx, model, pad, reorder, base_dir, no_segmentation, text_direction, threads): +def ocr(ctx, model, pad, reorder, base_dir, no_segmentation, text_direction): """ Recognizes text in line images. """ @@ -607,8 +610,6 @@ def ocr(ctx, model, pad, reorder, base_dir, no_segmentation, text_direction, thr nn = defaultdict(lambda: nm['default']) # type: Dict[str, models.TorchSeqRecognizer] nn.update(nm) nm = nn - # thread count is global so setting it once is sufficient - nm[k].nn.set_num_threads(threads) ctx.meta['steps'].append({'category': 'processing', 'description': 'Text line recognition', @@ -661,6 +662,7 @@ def list_models(ctx): Lists models in the repository. """ from kraken import repo + from kraken.lib.progress import KrakenProgressBar with KrakenProgressBar() as progress: download_task = progress.add_task('Retrieving model list', total=0, visible=True if not ctx.meta['verbose'] else False) @@ -678,6 +680,7 @@ def get(ctx, model_id): Retrieves a model from the repository. """ from kraken import repo + from kraken.lib.progress import KrakenDownloadProgressBar try: os.makedirs(click.get_app_dir(APP_NAME)) From 003568bf6c5c91095b6efbcec845145f3371fa75 Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Mon, 18 Sep 2023 12:30:55 +0200 Subject: [PATCH 68/69] add threadpool limits to CLI drivers --- conda/meta.yaml | 1 + environment.yml | 1 + environment_cuda.yml | 1 + kraken/ketos/pretrain.py | 11 +-- kraken/ketos/recognition.py | 132 +++++++++++++++++++---------------- kraken/ketos/ro.py | 12 ++-- kraken/ketos/segmentation.py | 99 ++++++++++++++------------ kraken/kraken.py | 6 +- setup.cfg | 1 + 9 files changed, 147 insertions(+), 117 deletions(-) diff --git a/conda/meta.yaml b/conda/meta.yaml index f68acffae..7380314bd 100644 --- a/conda/meta.yaml +++ b/conda/meta.yaml @@ -32,6 +32,7 @@ requirements: - pyarrow - pytorch-lightning~=2.0 - torchmetrics>=0.10.0 + - conda-forge::threadpoolctl~=3.2.0 - albumentations - rich about: diff --git a/environment.yml b/environment.yml index 9344af7a6..05112f876 100644 --- a/environment.yml +++ b/environment.yml @@ -24,6 +24,7 @@ dependencies: - pyarrow - conda-forge::pytorch-lightning~=2.0.0 - conda-forge::torchmetrics>=0.10.0 + - conda-forge::threadpoolctl~=3.2 - pip - albumentations - rich diff --git a/environment_cuda.yml b/environment_cuda.yml index 49c1faa70..8464004b6 100644 --- a/environment_cuda.yml +++ b/environment_cuda.yml @@ -25,6 +25,7 @@ dependencies: - pyarrow - conda-forge::pytorch-lightning~=2.0.0 - conda-forge::torchmetrics>=0.10.0 + - conda-forge::threadpoolctl~=3.2 - pip - albumentations - rich diff --git a/kraken/ketos/pretrain.py b/kraken/ketos/pretrain.py index ea5a14e19..512de415d 100644 --- a/kraken/ketos/pretrain.py +++ b/kraken/ketos/pretrain.py @@ -133,7 +133,8 @@ @click.option('-e', '--evaluation-files', show_default=True, default=None, multiple=True, callback=_validate_manifests, type=click.File(mode='r', lazy=True), help='File(s) with paths to evaluation data. Overrides the `-p` parameter') -@click.option('--workers', show_default=True, default=1, help='Number of OpenMP threads and workers when running on CPU.') +@click.option('--workers', show_default=True, default=1, type=click.IntRange(1), help='Number of worker processes.') +@click.option('--threads', show_default=True, default=1, type=click.IntRange(1), help='Maximum size of OpenMP/BLAS thread pool.') @click.option('--load-hyper-parameters/--no-load-hyper-parameters', show_default=True, default=False, help='When loading an existing model, retrieve hyperparameters from the model') @click.option('--repolygonize/--no-repolygonize', show_default=True, @@ -182,8 +183,8 @@ def pretrain(ctx, batch_size, pad, output, spec, load, freq, quit, epochs, min_epochs, lag, min_delta, device, precision, optimizer, lrate, momentum, weight_decay, warmup, schedule, gamma, step_size, sched_patience, cos_max, partition, fixed_splits, training_files, - evaluation_files, workers, load_hyper_parameters, repolygonize, - force_binarization, format_type, augment, + evaluation_files, workers, threads, load_hyper_parameters, repolygonize, + force_binarization, format_type, augment, mask_probability, mask_width, num_negatives, logit_temp, ground_truth): """ @@ -199,6 +200,7 @@ def pretrain(ctx, batch_size, pad, output, spec, load, freq, quit, epochs, raise click.BadOptionUsage('augment', 'augmentation needs the `albumentations` package installed.') import shutil + from threadpoolctl import threadpool_limits from kraken.lib.train import KrakenTrainer from kraken.lib.pretrain import PretrainDataModule, RecognitionPretrainModel @@ -280,7 +282,8 @@ def pretrain(ctx, batch_size, pad, output, spec, load, freq, quit, epochs, enable_progress_bar=True if not ctx.meta['verbose'] else False, deterministic=ctx.meta['deterministic'], **val_check_interval) - trainer.fit(model, datamodule=data_module) + with threadpool_limits(limits=threads): + trainer.fit(model, datamodule=data_module) if quit == 'early': message('Moving best model {0}_{1}.mlmodel ({2}) to {0}_best.mlmodel'.format( diff --git a/kraken/ketos/recognition.py b/kraken/ketos/recognition.py index 04b6a30b0..050316cc2 100644 --- a/kraken/ketos/recognition.py +++ b/kraken/ketos/recognition.py @@ -23,6 +23,7 @@ import pathlib from typing import List +from threadpoolctl import threadpool_limits from kraken.lib.exceptions import KrakenInputException from kraken.lib.default_specs import RECOGNITION_HYPER_PARAMS, RECOGNITION_SPEC @@ -155,7 +156,8 @@ @click.option('-e', '--evaluation-files', show_default=True, default=None, multiple=True, callback=_validate_manifests, type=click.File(mode='r', lazy=True), help='File(s) with paths to evaluation data. Overrides the `-p` parameter') -@click.option('--workers', show_default=True, default=1, help='Number of OpenMP threads and workers when running on CPU.') +@click.option('--workers', show_default=True, default=1, type=click.IntRange(1), help='Number of worker processes.') +@click.option('--threads', show_default=True, default=1, type=click.IntRange(1), help='Maximum size of OpenMP/BLAS thread pool.') @click.option('--load-hyper-parameters/--no-load-hyper-parameters', show_default=True, default=False, help='When loading an existing model, retrieve hyperparameters from the model') @click.option('--repolygonize/--no-repolygonize', show_default=True, @@ -310,7 +312,8 @@ def train(ctx, batch_size, pad, output, spec, append, load, freq, quit, epochs, log_dir=log_dir, **val_check_interval) try: - trainer.fit(model) + with threadpool_limits(limits=threads): + trainer.fit(model) except KrakenInputException as e: if e.args[0].startswith('Training data and model codec alphabets mismatch') and resize == 'fail': raise click.BadOptionUsage('resize', 'Mismatched training data for loaded model. Set option `--resize` to `new` or `add`') @@ -337,7 +340,12 @@ def train(ctx, batch_size, pad, output, spec, append, load, freq, quit, epochs, @click.option('-d', '--device', show_default=True, default='cpu', help='Select device to use (cpu, cuda:0, cuda:1, ...)') @click.option('--pad', show_default=True, type=click.INT, default=16, help='Left and right ' 'padding around lines') -@click.option('--workers', show_default=True, default=1, help='Number of OpenMP threads when running on CPU.') +@click.option('--workers', show_default=True, default=1, + type=click.IntRange(1), + help='Number of worker processes when running on CPU.') +@click.option('--threads', show_default=True, default=1, + type=click.IntRange(1), + help='Max size of thread pools for OpenMP/BLAS operations.') @click.option('--reorder/--no-reorder', show_default=True, default=True, help='Reordering of code points to display order') @click.option('--base-dir', show_default=True, default='auto', type=click.Choice(['L', 'R', 'auto']), help='Set base text ' @@ -370,8 +378,8 @@ def train(ctx, batch_size, pad, output, spec, append, load, freq, quit, epochs, 'collections of pre-extracted text line images.') @click.argument('test_set', nargs=-1, callback=_expand_gt, type=click.Path(exists=False, dir_okay=False)) def test(ctx, batch_size, model, evaluation_files, device, pad, workers, - reorder, base_dir, normalization, normalize_whitespace, repolygonize, - force_binarization, format_type, test_set): + threads, reorder, base_dir, normalization, normalize_whitespace, + repolygonize, force_binarization, format_type, test_set): """ Evaluate on a test set. """ @@ -401,8 +409,6 @@ def test(ctx, batch_size, model, evaluation_files, device, pad, workers, test_set = list(test_set) - # set number of OpenMP threads - next(iter(nn.values())).nn.set_num_threads(1) if evaluation_files: test_set.extend(evaluation_files) @@ -439,62 +445,64 @@ def test(ctx, batch_size, model, evaluation_files, device, pad, workers, reorder = base_dir acc_list = [] - for p, net in nn.items(): - algn_gt: List[str] = [] - algn_pred: List[str] = [] - chars = 0 - error = 0 - message('Evaluating {}'.format(p)) - logger.info('Evaluating {}'.format(p)) - batch, channels, height, width = net.nn.input - ts = ImageInputTransforms(batch, height, width, channels, (pad, 0), valid_norm, force_binarization) - ds = DatasetClass(normalization=normalization, - whitespace_normalization=normalize_whitespace, - reorder=reorder, - im_transforms=ts) - for line in test_set: - try: - ds.add(**line) - except KrakenInputException as e: - logger.info(e) - # don't encode validation set as the alphabets may not match causing encoding failures - ds.no_encode() - ds_loader = DataLoader(ds, - batch_size=batch_size, - num_workers=workers, - pin_memory=True, - collate_fn=collate_sequences) - - with KrakenProgressBar() as progress: - batches = len(ds_loader) - pred_task = progress.add_task('Evaluating', total=batches, visible=True if not ctx.meta['verbose'] else False) - - for batch in ds_loader: - im = batch['image'] - text = batch['target'] - lens = batch['seq_lens'] + + with threadpool_limits(limits=threads): + for p, net in nn.items(): + algn_gt: List[str] = [] + algn_pred: List[str] = [] + chars = 0 + error = 0 + message('Evaluating {}'.format(p)) + logger.info('Evaluating {}'.format(p)) + batch, channels, height, width = net.nn.input + ts = ImageInputTransforms(batch, height, width, channels, (pad, 0), valid_norm, force_binarization) + ds = DatasetClass(normalization=normalization, + whitespace_normalization=normalize_whitespace, + reorder=reorder, + im_transforms=ts) + for line in test_set: try: - pred = net.predict_string(im, lens) - for x, y in zip(pred, text): - chars += len(y) - c, algn1, algn2 = global_align(y, x) - algn_gt.extend(algn1) - algn_pred.extend(algn2) - error += c - except FileNotFoundError as e: - batches -= 1 - progress.update(pred_task, total=batches) - logger.warning('{} {}. Skipping.'.format(e.strerror, e.filename)) + ds.add(**line) except KrakenInputException as e: - batches -= 1 - progress.update(pred_task, total=batches) - logger.warning(str(e)) - progress.update(pred_task, advance=1) - - acc_list.append((chars - error) / chars) - confusions, scripts, ins, dels, subs = compute_confusions(algn_gt, algn_pred) - rep = render_report(p, chars, error, confusions, scripts, ins, dels, subs) - logger.info(rep) - message(rep) + logger.info(e) + # don't encode validation set as the alphabets may not match causing encoding failures + ds.no_encode() + ds_loader = DataLoader(ds, + batch_size=batch_size, + num_workers=workers, + pin_memory=True, + collate_fn=collate_sequences) + + with KrakenProgressBar() as progress: + batches = len(ds_loader) + pred_task = progress.add_task('Evaluating', total=batches, visible=True if not ctx.meta['verbose'] else False) + + for batch in ds_loader: + im = batch['image'] + text = batch['target'] + lens = batch['seq_lens'] + try: + pred = net.predict_string(im, lens) + for x, y in zip(pred, text): + chars += len(y) + c, algn1, algn2 = global_align(y, x) + algn_gt.extend(algn1) + algn_pred.extend(algn2) + error += c + except FileNotFoundError as e: + batches -= 1 + progress.update(pred_task, total=batches) + logger.warning('{} {}. Skipping.'.format(e.strerror, e.filename)) + except KrakenInputException as e: + batches -= 1 + progress.update(pred_task, total=batches) + logger.warning(str(e)) + progress.update(pred_task, advance=1) + + acc_list.append((chars - error) / chars) + confusions, scripts, ins, dels, subs = compute_confusions(algn_gt, algn_pred) + rep = render_report(p, chars, error, confusions, scripts, ins, dels, subs) + logger.info(rep) + message(rep) logger.info('Average accuracy: {:0.2f}%, (stddev: {:0.2f})'.format(np.mean(acc_list) * 100, np.std(acc_list) * 100)) message('Average accuracy: {:0.2f}%, (stddev: {:0.2f})'.format(np.mean(acc_list) * 100, np.std(acc_list) * 100)) diff --git a/kraken/ketos/ro.py b/kraken/ketos/ro.py index 17fc8cf09..006c1b8bd 100644 --- a/kraken/ketos/ro.py +++ b/kraken/ketos/ro.py @@ -124,7 +124,8 @@ @click.option('-e', '--evaluation-files', show_default=True, default=None, multiple=True, callback=_validate_manifests, type=click.File(mode='r', lazy=True), help='File(s) with paths to evaluation data. Overrides the `-p` parameter') -@click.option('--workers', show_default=True, default=1, help='Number of OpenMP threads and workers when running on CPU.') +@click.option('--workers', show_default=True, default=1, type=click.IntRange(1), help='Number of worker proesses.') +@click.option('--threads', show_default=True, default=1, type=click.IntRange(1), help='Maximum size of OpenMP/BLAS thread pool.') @click.option('--load-hyper-parameters/--no-load-hyper-parameters', show_default=True, default=False, help='When loading an existing model, retrieve hyper-parameters from the model') @click.option('-f', '--format-type', type=click.Choice(['xml', 'alto', 'page']), default='xml', @@ -144,13 +145,15 @@ def rotrain(ctx, batch_size, output, load, freq, quit, epochs, min_epochs, lag, min_delta, device, precision, optimizer, lrate, momentum, weight_decay, warmup, schedule, gamma, step_size, sched_patience, cos_max, partition, training_files, evaluation_files, workers, - load_hyper_parameters, format_type, pl_logger, log_dir, level, - reading_order, ground_truth): + threads, load_hyper_parameters, format_type, pl_logger, log_dir, + level, reading_order, ground_truth): """ Trains a baseline labeling model for layout analysis """ import shutil + from threadpoolctl import threadpool_limits + from kraken.lib.ro import ROModel from kraken.lib.train import KrakenTrainer from kraken.lib.progress import KrakenProgressBar @@ -250,7 +253,8 @@ def rotrain(ctx, batch_size, output, load, freq, quit, epochs, min_epochs, lag, log_dir=log_dir, **val_check_interval) - trainer.fit(model) + with threadpool_limits(limits=threads): + trainer.fit(model) if quit == 'early': message('Moving best model {0}_{1}.mlmodel ({2}) to {0}_best.mlmodel'.format( diff --git a/kraken/ketos/segmentation.py b/kraken/ketos/segmentation.py index e356d08df..9db1060e6 100644 --- a/kraken/ketos/segmentation.py +++ b/kraken/ketos/segmentation.py @@ -151,7 +151,8 @@ def _validate_merging(ctx, param, value): @click.option('-e', '--evaluation-files', show_default=True, default=None, multiple=True, callback=_validate_manifests, type=click.File(mode='r', lazy=True), help='File(s) with paths to evaluation data. Overrides the `-p` parameter') -@click.option('--workers', show_default=True, default=1, help='Number of OpenMP threads and workers when running on CPU.') +@click.option('--workers', show_default=True, default=1, type=click.IntRange(1), help='Number of worker proesses.') +@click.option('--threads', show_default=True, default=1, type=click.IntRange(1), help='Maximum size of OpenMP/BLAS thread pool.') @click.option('--load-hyper-parameters/--no-load-hyper-parameters', show_default=True, default=False, help='When loading an existing model, retrieve hyper-parameters from the model') @click.option('--force-binarization/--no-binarization', show_default=True, @@ -218,7 +219,7 @@ def segtrain(ctx, output, spec, line_width, pad, load, freq, quit, epochs, min_epochs, lag, min_delta, device, precision, optimizer, lrate, momentum, weight_decay, warmup, schedule, gamma, step_size, sched_patience, cos_max, partition, training_files, - evaluation_files, workers, load_hyper_parameters, + evaluation_files, workers, threads, load_hyper_parameters, force_binarization, format_type, suppress_regions, suppress_baselines, valid_regions, valid_baselines, merge_regions, merge_baselines, bounding_regions, @@ -228,6 +229,8 @@ def segtrain(ctx, output, spec, line_width, pad, load, freq, quit, epochs, """ import shutil + from threadpoolctl import threadpool_limits + from kraken.lib.train import SegmentationModel, KrakenTrainer from kraken.lib.progress import KrakenProgressBar @@ -347,7 +350,8 @@ def segtrain(ctx, output, spec, line_width, pad, load, freq, quit, epochs, log_dir=log_dir, **val_check_interval) - trainer.fit(model) + with threadpool_limits(limits=threads): + trainer.fit(model) if quit == 'early': message('Moving best model {0}_{1}.mlmodel ({2}) to {0}_best.mlmodel'.format( @@ -365,7 +369,10 @@ def segtrain(ctx, output, spec, line_width, pad, load, freq, quit, epochs, callback=_validate_manifests, type=click.File(mode='r', lazy=True), help='File(s) with paths to evaluation data.') @click.option('-d', '--device', show_default=True, default='cpu', help='Select device to use (cpu, cuda:0, cuda:1, ...)') -@click.option('--workers', show_default=True, default=1, help='Number of OpenMP threads when running on CPU.') +@click.option('--workers', default=1, show_default=True, type=click.IntRange(1), + help='Number of worker processes for data loading.') +@click.option('--threads', default=1, show_default=True, type=click.IntRange(1), + help='Size of thread pools for intra-op parallelization') @click.option('--force-binarization/--no-binarization', show_default=True, default=False, help='Forces input images to be binary, otherwise ' 'the appropriate color format will be auto-determined through the ' @@ -403,7 +410,7 @@ def segtrain(ctx, output, spec, line_width, pad, load, freq, quit, epochs, @click.option("--threshold", type=click.FloatRange(.01, .99), default=.3, show_default=True, help="Threshold for heatmap binarization. Training threshold is .3, prediction is .5") @click.argument('test_set', nargs=-1, callback=_expand_gt, type=click.Path(exists=False, dir_okay=False)) -def segtest(ctx, model, evaluation_files, device, workers, threshold, +def segtest(ctx, model, evaluation_files, device, workers, threads, threshold, force_binarization, format_type, test_set, suppress_regions, suppress_baselines, valid_regions, valid_baselines, merge_regions, merge_baselines, bounding_regions): @@ -413,6 +420,7 @@ def segtest(ctx, model, evaluation_files, device, workers, threshold, if not model: raise click.UsageError('No model to evaluate given.') + from threadpoolctl import threadpool_limits from torch.utils.data import DataLoader import torch import torch.nn.functional as F @@ -502,46 +510,47 @@ def segtest(ctx, model, evaluation_files, device, workers, threshold, with KrakenProgressBar() as progress: batches = len(ds_loader) pred_task = progress.add_task('Evaluating', total=batches, visible=True if not ctx.meta['verbose'] else False) - for batch in ds_loader: - x, y = batch['image'], batch['target'] - try: - pred, _ = nn.nn(x) - # scale target to output size - y = F.interpolate(y, size=(pred.size(2), pred.size(3))).squeeze(0).bool() - pred = pred.squeeze() > threshold - pred = pred.view(pred.size(0), -1) - y = y.view(y.size(0), -1) - pages.append({ - 'intersections': (y & pred).sum(dim=1, dtype=torch.double), - 'unions': (y | pred).sum(dim=1, dtype=torch.double), - 'corrects': torch.eq(y, pred).sum(dim=1, dtype=torch.double), - 'cls_cnt': y.sum(dim=1, dtype=torch.double), - 'all_n': torch.tensor(y.size(1), dtype=torch.double, device=device) - }) - if lines_idx: - y_baselines = y[lines_idx].sum(dim=0, dtype=torch.bool) - pred_baselines = pred[lines_idx].sum(dim=0, dtype=torch.bool) - pages[-1]["baselines"] = { - 'intersections': (y_baselines & pred_baselines).sum(dim=0, dtype=torch.double), - 'unions': (y_baselines | pred_baselines).sum(dim=0, dtype=torch.double), - } - if regions_idx: - y_regions_idx = y[regions_idx].sum(dim=0, dtype=torch.bool) - pred_regions_idx = pred[regions_idx].sum(dim=0, dtype=torch.bool) - pages[-1]["regions"] = { - 'intersections': (y_regions_idx & pred_regions_idx).sum(dim=0, dtype=torch.double), - 'unions': (y_regions_idx | pred_regions_idx).sum(dim=0, dtype=torch.double), - } - - except FileNotFoundError as e: - batches -= 1 - progress.update(pred_task, total=batches) - logger.warning('{} {}. Skipping.'.format(e.strerror, e.filename)) - except KrakenInputException as e: - batches -= 1 - progress.update(pred_task, total=batches) - logger.warning(str(e)) - progress.update(pred_task, advance=1) + with threadpool_limits(limits=threads): + for batch in ds_loader: + x, y = batch['image'], batch['target'] + try: + pred, _ = nn.nn(x) + # scale target to output size + y = F.interpolate(y, size=(pred.size(2), pred.size(3))).squeeze(0).bool() + pred = pred.squeeze() > threshold + pred = pred.view(pred.size(0), -1) + y = y.view(y.size(0), -1) + pages.append({ + 'intersections': (y & pred).sum(dim=1, dtype=torch.double), + 'unions': (y | pred).sum(dim=1, dtype=torch.double), + 'corrects': torch.eq(y, pred).sum(dim=1, dtype=torch.double), + 'cls_cnt': y.sum(dim=1, dtype=torch.double), + 'all_n': torch.tensor(y.size(1), dtype=torch.double, device=device) + }) + if lines_idx: + y_baselines = y[lines_idx].sum(dim=0, dtype=torch.bool) + pred_baselines = pred[lines_idx].sum(dim=0, dtype=torch.bool) + pages[-1]["baselines"] = { + 'intersections': (y_baselines & pred_baselines).sum(dim=0, dtype=torch.double), + 'unions': (y_baselines | pred_baselines).sum(dim=0, dtype=torch.double), + } + if regions_idx: + y_regions_idx = y[regions_idx].sum(dim=0, dtype=torch.bool) + pred_regions_idx = pred[regions_idx].sum(dim=0, dtype=torch.bool) + pages[-1]["regions"] = { + 'intersections': (y_regions_idx & pred_regions_idx).sum(dim=0, dtype=torch.double), + 'unions': (y_regions_idx | pred_regions_idx).sum(dim=0, dtype=torch.double), + } + + except FileNotFoundError as e: + batches -= 1 + progress.update(pred_task, total=batches) + logger.warning('{} {}. Skipping.'.format(e.strerror, e.filename)) + except KrakenInputException as e: + batches -= 1 + progress.update(pred_task, total=batches) + logger.warning(str(e)) + progress.update(pred_task, advance=1) # Accuracy / pixel corrects = torch.stack([x['corrects'] for x in pages], -1).sum(dim=-1) diff --git a/kraken/kraken.py b/kraken/kraken.py index 38647eee8..27a0f5cef 100644 --- a/kraken/kraken.py +++ b/kraken/kraken.py @@ -28,7 +28,6 @@ from pathlib import Path from functools import partial from rich.traceback import install -from threadpoolctl import threadpool_limits from typing import Dict, Union, List, cast, Any, IO, Callable import click @@ -335,6 +334,7 @@ def cli(input, batch_input, suffix, verbose, format_type, pdf_format, ctx.meta['verbose'] = verbose ctx.meta['steps'] = [] ctx.meta["autocast"] = autocast + ctx.meta['threads'] = threads log.set_logger(logger, level=30 - min(10 * verbose, 20)) @@ -348,6 +348,7 @@ def process_pipeline(subcommands, input, batch_input, suffix, verbose, format_ty import uuid import tempfile + from threadpoolctl import threadpool_limits from kraken.lib.progress import KrakenProgressBar ctx = click.get_current_context() @@ -414,7 +415,8 @@ def process_pipeline(subcommands, input, batch_input, suffix, verbose, format_ty for idx, (task, input, output) in enumerate(zip(subcommands, fc, fc[1:])): if len(fc) - 2 == idx: ctx.meta['last_process'] = True - task(input=input, output=output) + with threadpool_limits(limits=ctx.meta['threads']): + task(input=input, output=output) except Exception as e: logger.error(f'Failed processing {io_pair[0]}: {str(e)}') if ctx.meta['raise_failed']: diff --git a/setup.cfg b/setup.cfg index 60c3994d6..f26ab7957 100644 --- a/setup.cfg +++ b/setup.cfg @@ -60,6 +60,7 @@ install_requires = pyarrow pytorch-lightning~=2.0.0 torchmetrics>=0.10.0 + threadpoolctl~=3.2.0 rich [options.extras_require] From 8a39a9a1f3d7efa7f5c466c034319d8f927ceab0 Mon Sep 17 00:00:00 2001 From: Benjamin Kiessling Date: Tue, 26 Sep 2023 00:19:33 +0200 Subject: [PATCH 69/69] wip --- kraken/containers.py | 20 ++++++---- kraken/lib/segmentation.py | 2 +- tests/resources/bl_records.json | 2 +- tests/resources/records.json | 2 +- tests/test_align.py | 29 +++++++++++++++ tests/test_serialization.py | 65 +++++++++++++++++++++++---------- tests/test_xml.py | 2 +- 7 files changed, 91 insertions(+), 31 deletions(-) create mode 100644 tests/test_align.py diff --git a/kraken/containers.py b/kraken/containers.py index c58ef9c10..445e66bc5 100644 --- a/kraken/containers.py +++ b/kraken/containers.py @@ -4,7 +4,7 @@ import bidi.algorithm as bd from os import PathLike -from typing import Literal, List, Dict, Union, Optional, Tuple +from typing import Literal, List, Dict, Union, Optional, Tuple, Any from dataclasses import dataclass, asdict from abc import ABC, abstractmethod @@ -281,12 +281,14 @@ class BaselineOCRRecord(ocr_record, BaselineLine): def __init__(self, prediction: str, cuts: List[Tuple[int, int]], confidences: List[float], - line: BaselineLine, + line: Union[BaselineLine, Dict[str, Any]], base_dir: Optional[Literal['L', 'R']] = None, display_order: bool = True) -> None: - if line.type != 'baselines': + if not isinstance(line, dict): + line = asdict(line) + if line['type'] != 'baselines': raise TypeError('Invalid argument type (non-baseline line)') - BaselineLine.__init__(self, **asdict(line)) + BaselineLine.__init__(self, **line) self._line_base_dir = self.base_dir self.base_dir = base_dir ocr_record.__init__(self, prediction, cuts, confidences, display_order) @@ -460,12 +462,14 @@ def __init__(self, Tuple[int, int], Tuple[int, int]]], confidences: List[float], - line: BBoxLine, - base_dir: Optional[Literal['L', 'R']], + line: Union[BBoxLine, Dict[str, Any]], + base_dir: Optional[Literal['L', 'R']] = None, display_order: bool = True) -> None: - if line.type != 'bbox': + if not isinstance(line, dict): + line = asdict(line) + if line['type'] != 'bbox': raise TypeError('Invalid argument type (non-bbox line)') - BBoxLine.__init__(self, **asdict(line)) + BBoxLine.__init__(self, **line) self._line_base_dir = self.base_dir self.base_dir = base_dir ocr_record.__init__(self, prediction, cuts, confidences, display_order) diff --git a/kraken/lib/segmentation.py b/kraken/lib/segmentation.py index 215c4e5d4..58984679b 100644 --- a/kraken/lib/segmentation.py +++ b/kraken/lib/segmentation.py @@ -27,7 +27,7 @@ from PIL import Image from scipy.ndimage import maximum_filter, binary_erosion -from scipy.ndimage.morphology import distance_transform_cdt +from scipy.ndimage import distance_transform_cdt from scipy.spatial.distance import pdist, squareform from shapely.ops import nearest_points, unary_union diff --git a/tests/resources/bl_records.json b/tests/resources/bl_records.json index df6c8005e..4f2e3565b 100644 --- a/tests/resources/bl_records.json +++ b/tests/resources/bl_records.json @@ -1 +1 @@ -{"lines": [{"prediction": "238", "cuts": [[39, 39], [65, 65], [96, 96]], "confidences": [0.9999942779541016, 0.9999815225601196, 0.9999997615814209], "line": {"boundary": [[1364, 228], [1366, 191], [1382, 177], [1444, 177], [1473, 202], [1479, 226], [1452, 241], [1380, 239], [1368, 231]], "baseline": [[1366, 230], [1440, 230], [1480, 227]]}}, {"prediction": "\u0648\u0644\u0627 \u064a\u0639\u062a\u0646\u064a \u0628\u0642\u0636\u0627\u0621 \u062d\u0627\u062c\u0629 \u0645\u0636\u0637\u0631 \u0645\u0644\u0647\u0648\u0641\u060c \u0641\u0627\u0655\u0646\u0647 \u0645\u0646 \u0627\u0644\u0642\u0627\u0633\u064a\u0629 \u0642\u0644\u0648\u0628\u0647\u0645\u060c \u0627\u0644\u0645\u0639\u0631\u0636\u064a\u0646 \u0639\u0646", "cuts": [[1291, 1291], [1266, 1273], [1254, 1260], [1235, 1241], [1223, 1223], [1204, 1210], [1192, 1192], [1179, 1179], [1167, 1167], [1136, 1136], [1124, 1124], [1111, 1111], [1086, 1086], [1055, 1061], [1043, 1043], [1018, 1024], [999, 999], [981, 981], [956, 962], [937, 937], [913, 919], [900, 900], [875, 881], [838, 838], [819, 819], [795, 801], [782, 782], [770, 770], [751, 751], [732, 732], [714, 714], [670, 670], [646, 658], [639, 639], [627, 633], [621, 621], [615, 615], [602, 602], [577, 584], [565, 565], [540, 540], [515, 521], [503, 509], [497, 497], [484, 484], [459, 466], [447, 447], [428, 428], [410, 410], [385, 391], [379, 379], [360, 360], [348, 348], [329, 329], [310, 310], [286, 292], [273, 273], [248, 255], [242, 242], [230, 230], [211, 211], [192, 192], [174, 174], [149, 155], [118, 118], [99, 106], [68, 75], [56, 56], [31, 31]], "confidences": [0.9918296933174133, 0.9999946355819702, 0.9999945163726807, 0.9999985694885254, 0.9999771118164062, 0.9999879598617554, 0.9987093210220337, 0.9987457990646362, 0.9997931122779846, 0.999688982963562, 0.9999251365661621, 0.587072491645813, 0.9999754428863525, 0.99989914894104, 0.9920154809951782, 0.9999525547027588, 0.9999878406524658, 0.9997779726982117, 0.9999771118164062, 0.9966135621070862, 0.9999631643295288, 0.9998565912246704, 0.9999505281448364, 0.9981879591941833, 0.8379210829734802, 0.9999366998672485, 0.9966161847114563, 0.9895930290222168, 0.9999837875366211, 0.9999969005584717, 0.8669806122779846, 0.974852979183197, 0.9999998807907104, 0.9999897480010986, 0.9999994039535522, 0.9228501319885254, 0.9999959468841553, 0.9999970197677612, 0.9999334812164307, 0.9997324347496033, 0.9999984502792358, 0.9917014241218567, 0.9999793767929077, 1.0, 0.9999970197677612, 0.9935858845710754, 0.9999581575393677, 0.8294568061828613, 0.9999990463256836, 0.9999179840087891, 0.9999973773956299, 0.9964264035224915, 0.9883396625518799, 0.7585844993591309, 0.962102472782135, 0.8603035807609558, 0.9998493194580078, 0.9999865293502808, 0.9999082088470459, 0.9998868703842163, 0.999321699142456, 0.9835970997810364, 0.8893908262252808, 0.9999939203262329, 0.9998082518577576, 0.9999799728393555, 0.9999867677688599, 0.5397316217422485, 0.8847019076347351], "line": {"boundary": [[167, 337], [177, 290], [216, 288], [233, 302], [255, 288], [278, 300], [309, 282], [329, 300], [356, 296], [387, 276], [415, 294], [452, 300], [465, 292], [487, 298], [508, 278], [534, 278], [547, 288], [567, 278], [590, 298], [614, 278], [651, 276], [680, 298], [693, 292], [736, 302], [769, 280], [797, 282], [814, 298], [840, 296], [865, 280], [890, 298], [914, 278], [951, 292], [984, 265], [996, 265], [1021, 288], [1056, 280], [1072, 292], [1097, 282], [1113, 296], [1132, 280], [1179, 306], [1204, 282], [1228, 294], [1265, 284], [1298, 311], [1331, 290], [1362, 296], [1428, 280], [1491, 335], [1463, 335], [1446, 350], [1401, 335], [1380, 350], [1347, 341], [1313, 362], [1288, 345], [1270, 350], [1259, 341], [1212, 339], [1189, 346], [1171, 331], [1093, 348], [1066, 335], [1003, 339], [978, 362], [941, 337], [910, 348], [853, 352], [808, 327], [783, 346], [732, 327], [701, 350], [682, 350], [664, 335], [602, 335], [569, 346], [547, 327], [499, 348], [458, 343], [440, 358], [405, 333], [345, 335], [327, 348], [292, 337], [245, 350], [226, 335], [171, 348]], "baseline": [[169, 339], [269, 335], [304, 329], [1288, 329], [1368, 331], [1389, 335], [1492, 337]]}}, {"prediction": "\u0631\u0628\u0647\u0645\u060c \u0642\u0644\u0628\u0647 \u0628\u0639\u064a\u062f \u0645\u0646 \u0627\u0644\u0627\u0653\u062e\u0631\u0629\u060c \u0645\u062a\u0639\u0644\u0642 \u0628\u0627\u0644\u062f\u0646\u064a\u0627\u060c\u0639\u0644\u0645\u0647 \u062f\u0643\u0627\u0646\u0647\u060c \u0648\u064a\u062a\u0627\u0654\u0643\u0644 \u0648\u064a\u0631\u062a\u0632\u0642\u060c \u0648\u0644\u0627 \u064a\u0639\u0627\u0645\u0644", "cuts": [[1302, 1302], [1280, 1280], [1269, 1269], [1241, 1241], [1224, 1224], [1201, 1207], [1190, 1190], [1179, 1179], [1168, 1168], [1151, 1151], [1134, 1140], [1123, 1123], [1106, 1106], [1089, 1095], [1078, 1078], [1050, 1055], [1039, 1039], [1016, 1016], [988, 994], [982, 982], [966, 971], [954, 960], [949, 949], [926, 926], [909, 909], [893, 893], [876, 876], [853, 859], [842, 842], [831, 831], [808, 808], [797, 797], [780, 780], [747, 758], [741, 741], [730, 730], [719, 719], [702, 702], [685, 685], [674, 674], [662, 662], [646, 646], [629, 629], [612, 612], [589, 589], [573, 573], [550, 556], [539, 539], [522, 522], [500, 505], [494, 494], [477, 477], [460, 466], [443, 449], [432, 432], [415, 415], [404, 404], [393, 393], [382, 387], [376, 376], [354, 359], [320, 331], [314, 314], [292, 292], [281, 281], [264, 264], [253, 253], [230, 230], [196, 202], [180, 185], [168, 174], [146, 152], [135, 140], [112, 118], [107, 107], [84, 90], [73, 73], [56, 56], [39, 45]], "confidences": [0.999483585357666, 0.9989271759986877, 0.9999568462371826, 0.9999847412109375, 0.9999631643295288, 0.9999524354934692, 0.9999338388442993, 0.9999792575836182, 0.9999816417694092, 0.9999970197677612, 0.9998041987419128, 0.9913369417190552, 0.9999105930328369, 0.9999866485595703, 0.9997265934944153, 0.9998457431793213, 0.9999898672103882, 0.9658181667327881, 0.9998805522918701, 0.9999202489852905, 0.9999966621398926, 0.9999680519104004, 0.6977024078369141, 0.681415855884552, 0.9998468160629272, 0.999790370464325, 0.998950719833374, 0.9989398121833801, 0.9998928308486938, 0.9993798732757568, 0.9988120794296265, 0.9999618530273438, 0.978205144405365, 0.9999402761459351, 0.9968582391738892, 0.9899797439575195, 0.9999998807907104, 0.9918898940086365, 1.0, 0.6370328664779663, 0.9123554229736328, 0.9965428709983826, 0.9990838766098022, 0.6425076127052307, 0.999977707862854, 0.9999984502792358, 0.9999998807907104, 0.9999978542327881, 0.5949130058288574, 0.9999992847442627, 0.998330295085907, 0.863884687423706, 0.9997449517250061, 0.9999986886978149, 0.9999614953994751, 0.9998691082000732, 0.9999910593032837, 0.7401690483093262, 0.9997996687889099, 0.999140739440918, 0.9999452829360962, 0.9999958276748657, 0.9999629259109497, 0.999981164932251, 0.9999955892562866, 0.9999098777770996, 0.9996079802513123, 0.8690406680107117, 0.9999758005142212, 0.9999970197677612, 0.9999972581863403, 0.999855637550354, 0.9997760653495789, 0.9999991655349731, 0.9998064637184143, 0.9998438358306885, 0.8385894894599915, 0.9975091218948364, 0.9999769926071167], "line": {"boundary": [[161, 405], [165, 380], [189, 358], [206, 364], [224, 356], [257, 372], [276, 354], [298, 352], [335, 378], [384, 356], [393, 366], [413, 360], [432, 376], [467, 372], [477, 380], [516, 345], [528, 345], [571, 374], [600, 380], [639, 345], [670, 345], [713, 374], [754, 356], [787, 372], [805, 358], [844, 368], [875, 356], [904, 382], [941, 356], [961, 372], [982, 368], [1007, 382], [1029, 364], [1060, 378], [1087, 354], [1113, 350], [1150, 378], [1165, 372], [1204, 383], [1222, 368], [1284, 383], [1325, 358], [1384, 382], [1452, 380], [1465, 391], [1471, 415], [1440, 428], [1403, 424], [1385, 440], [1352, 413], [1317, 426], [1294, 413], [1274, 426], [1257, 419], [1231, 426], [1204, 409], [1169, 430], [1091, 409], [1044, 428], [1009, 407], [941, 415], [923, 426], [902, 419], [885, 424], [871, 413], [814, 424], [799, 411], [771, 422], [705, 405], [686, 415], [600, 407], [580, 422], [528, 411], [504, 428], [489, 428], [471, 415], [458, 422], [384, 424], [337, 405], [315, 422], [274, 405], [255, 421], [218, 409], [191, 426], [165, 419]], "baseline": [[163, 407], [1251, 407], [1325, 409], [1378, 417], [1473, 417]]}}, {"prediction": "\u0627\u0644\u0644\u0647 \u0628\u0639\u0644\u0645\u0647 \u0627\u0655\u0644\u0627 \u0642\u0644\u064a\u0644\u0627\u060c \u064a\u0633\u0643\u062a \u0639\u0646 \u0627\u0644\u062d\u0642 \u062e\u0634\u064a\u0629 \u0633\u0642\u0648\u0637 \u0645\u0646\u0632\u0644\u062a\u0647\u060c \u0648\u064a\u0645\u0627\u0644\u064a\u0654 \u0639\u0644\u0649 \u0627\u0644\u0628\u0627\u0637\u0644 \u0637\u0644\u0628\u0627", "cuts": [[1285, 1285], [1274, 1280], [1263, 1263], [1258, 1258], [1236, 1241], [1230, 1230], [1214, 1214], [1198, 1198], [1181, 1181], [1159, 1165], [1143, 1148], [1137, 1137], [1126, 1132], [1116, 1121], [1105, 1110], [1088, 1094], [1077, 1077], [1066, 1066], [1050, 1050], [1034, 1039], [1023, 1028], [1006, 1006], [990, 995], [979, 979], [957, 962], [935, 935], [897, 897], [869, 875], [853, 853], [831, 831], [804, 809], [798, 798], [787, 787], [760, 760], [744, 744], [711, 716], [694, 694], [667, 667], [645, 645], [629, 629], [612, 618], [596, 602], [574, 574], [558, 558], [525, 525], [498, 503], [487, 492], [476, 476], [459, 465], [443, 448], [432, 432], [421, 421], [399, 405], [383, 388], [377, 377], [355, 355], [339, 339], [323, 323], [312, 312], [295, 295], [279, 284], [262, 268], [252, 252], [230, 230], [213, 213], [180, 186], [175, 175], [164, 170], [153, 153], [137, 142], [120, 120], [104, 104], [66, 71], [49, 55], [38, 38], [22, 22], [11, 16]], "confidences": [0.6223369240760803, 0.999977707862854, 0.9996325969696045, 0.9976205229759216, 0.9999915361404419, 0.9999442100524902, 0.9999935626983643, 0.9986664056777954, 0.9931642413139343, 0.999994158744812, 0.99997878074646, 0.9995927214622498, 0.9999953508377075, 0.9999521970748901, 0.9999961853027344, 0.9999990463256836, 0.9987659454345703, 0.9996764659881592, 0.9999899864196777, 0.9999946355819702, 0.9998693466186523, 0.9999508857727051, 0.9999494552612305, 0.9999960660934448, 0.9999898672103882, 0.9999620914459229, 0.9999936819076538, 0.9995738863945007, 0.9997065663337708, 0.9967997074127197, 0.9996978044509888, 0.9999291896820068, 0.9937999844551086, 0.7420834898948669, 0.9566183090209961, 0.9999990463256836, 0.9998831748962402, 0.9999995231628418, 0.8633337020874023, 0.9998383522033691, 1.0, 0.9999995231628418, 0.9999997615814209, 1.0, 0.9999963045120239, 0.9999927282333374, 0.9999871253967285, 0.9999827146530151, 0.9999991655349731, 0.9999961853027344, 0.9995644688606262, 0.9999992847442627, 0.9997718930244446, 0.999894380569458, 1.0, 0.9999994039535522, 0.9999990463256836, 0.9999983310699463, 1.0, 0.9998666048049927, 1.0, 0.9999922513961792, 0.9998834133148193, 0.9998831748962402, 0.9995386600494385, 0.9999641180038452, 0.9991496801376343, 0.9994356036186218, 0.9781442284584045, 0.9928722381591797, 0.9813116192817688, 0.9999994039535522, 0.9999991655349731, 0.999995231628418, 0.9999226331710815, 0.9999967813491821, 0.9934727549552917], "line": {"boundary": [[175, 477], [181, 422], [191, 432], [216, 430], [241, 454], [265, 432], [315, 438], [335, 430], [364, 456], [395, 432], [432, 458], [471, 432], [508, 456], [551, 458], [586, 448], [604, 432], [649, 450], [688, 432], [711, 454], [731, 438], [768, 454], [793, 434], [816, 450], [836, 440], [846, 450], [863, 442], [877, 454], [902, 430], [931, 450], [957, 436], [980, 454], [1000, 456], [1019, 446], [1037, 456], [1111, 434], [1134, 456], [1187, 426], [1212, 444], [1220, 436], [1251, 444], [1286, 436], [1315, 456], [1339, 456], [1356, 438], [1399, 458], [1424, 438], [1450, 440], [1467, 456], [1473, 481], [1440, 497], [1413, 495], [1393, 508], [1376, 497], [1319, 493], [1288, 506], [1257, 485], [1218, 504], [1165, 491], [1134, 502], [1122, 493], [1050, 495], [1031, 485], [996, 508], [978, 508], [961, 493], [931, 493], [906, 512], [867, 487], [807, 504], [783, 485], [732, 493], [721, 504], [666, 485], [621, 504], [555, 487], [538, 502], [481, 491], [450, 508], [421, 489], [378, 504], [356, 502], [341, 489], [313, 500], [298, 487], [261, 506], [245, 504], [226, 487], [179, 497]], "baseline": [[177, 479], [298, 477], [360, 485], [403, 487], [508, 487], [573, 483], [795, 483], [818, 487], [1475, 483]]}}, {"prediction": "\u0644\u0644\u0631\u0641\u0639\u0629\u060c \u0641\u0645\u0627 \u0627\u0654\u0628\u0639\u062f \u0647\u0630\u0627 \u0639\u0646 \u0627\u0644\u0644\u0647 \u0648\u0639\u0646 \u0637\u0631\u064a\u0642\u0647\u060c \u0639\u0644\u0645\u0647 \u062d\u062c\u0629 \u0639\u0644\u064a\u0647.", "cuts": [[917, 917], [901, 901], [890, 890], [868, 868], [852, 852], [836, 836], [820, 820], [799, 804], [793, 793], [772, 772], [756, 756], [740, 745], [734, 734], [724, 729], [718, 718], [708, 708], [686, 691], [665, 670], [649, 654], [633, 633], [611, 611], [595, 600], [584, 584], [557, 557], [536, 536], [525, 531], [520, 520], [504, 504], [493, 499], [477, 482], [466, 472], [445, 445], [418, 423], [391, 397], [375, 375], [359, 359], [343, 343], [327, 327], [311, 311], [295, 295], [273, 279], [263, 263], [247, 247], [225, 225], [209, 209], [188, 193], [166, 166], [139, 139], [118, 118], [97, 102], [86, 86], [64, 70], [54, 54], [43, 43], [21, 27]], "confidences": [0.9995940327644348, 0.9446550607681274, 0.9981995820999146, 0.9999864101409912, 0.6075229644775391, 0.99992835521698, 0.9997560381889343, 0.9999634027481079, 0.9998078942298889, 0.9999945163726807, 0.9999508857727051, 0.9999924898147583, 0.9999949932098389, 0.9999926090240479, 0.9998032450675964, 0.9999107122421265, 0.9999817609786987, 0.9998050332069397, 0.9999833106994629, 0.9495548605918884, 0.9993699193000793, 0.9999070167541504, 0.9915083646774292, 0.999798595905304, 0.9999779462814331, 0.9999381303787231, 0.9971952438354492, 0.9972888231277466, 0.9999881982803345, 0.9999971389770508, 0.9995827078819275, 0.9999994039535522, 0.999994158744812, 0.9999692440032959, 0.9996156692504883, 0.5591697096824646, 0.9999910593032837, 0.9997571110725403, 0.8518497347831726, 0.9999833106994629, 0.9999939203262329, 0.9998816251754761, 0.9999935626983643, 0.9927089214324951, 0.9999922513961792, 0.996820330619812, 0.9999880790710449, 0.9995105266571045, 0.9999866485595703, 0.9999912977218628, 0.9999923706054688, 0.9999964237213135, 0.9993194341659546, 0.999998927116394, 0.9977816939353943], "line": {"boundary": [[547, 549], [551, 536], [600, 508], [629, 526], [649, 510], [680, 530], [703, 526], [723, 534], [773, 512], [814, 534], [853, 514], [883, 530], [908, 510], [933, 536], [945, 524], [1013, 532], [1035, 510], [1097, 534], [1165, 514], [1200, 537], [1214, 524], [1231, 532], [1265, 506], [1302, 530], [1321, 518], [1339, 534], [1358, 518], [1382, 530], [1391, 520], [1415, 528], [1424, 518], [1448, 516], [1465, 534], [1471, 559], [1417, 596], [1389, 575], [1337, 567], [1319, 575], [1274, 567], [1245, 582], [1233, 573], [1146, 573], [1128, 563], [1093, 586], [1076, 586], [1060, 571], [1021, 567], [998, 580], [974, 569], [957, 584], [939, 584], [918, 567], [888, 584], [814, 561], [803, 569], [742, 569], [725, 559], [666, 575], [635, 559], [592, 578], [549, 563]], "baseline": [[549, 551], [582, 557], [807, 555], [966, 565], [1473, 561]]}}, {"prediction": "\u0648\u0645\u062a\u0649 \u0631\u0627\u0654\u064a\u062a\u0645 \u0627\u0644\u0639\u0627\u0644\u0645 \u0642\u0644\u064a\u0644 \u0627\u0644\u0648\u0631\u0639 \u0641\u064a \u0643\u0644\u0627\u0645\u0647 \u060c \u064a\u062a\u0643\u0644\u0645 \u0645\u062c\u0627\u0632\u0641\u0629\u060c \u0648\u064a\u0643\u0630\u0628 \u0627\u0654\u062d\u064a\u0627\u0646\u0627\u060c", "cuts": [[1223, 1223], [1201, 1201], [1190, 1190], [1173, 1173], [1129, 1140], [1118, 1118], [1101, 1107], [1096, 1096], [1090, 1090], [1079, 1079], [1062, 1062], [1029, 1040], [1024, 1024], [1013, 1018], [996, 996], [979, 979], [968, 968], [946, 952], [919, 924], [908, 908], [891, 891], [880, 880], [863, 869], [830, 836], [819, 825], [808, 813], [797, 802], [775, 775], [747, 747], [714, 719], [703, 703], [692, 692], [647, 659], [636, 636], [614, 614], [603, 609], [587, 587], [570, 570], [559, 559], [553, 553], [526, 531], [515, 515], [504, 504], [481, 481], [465, 465], [443, 448], [415, 421], [404, 404], [376, 376], [354, 360], [343, 343], [326, 326], [310, 310], [293, 293], [266, 271], [255, 255], [238, 238], [216, 216], [199, 199], [160, 166], [122, 127], [116, 116], [105, 111], [89, 89], [72, 72], [61, 61], [50, 50], [39, 39], [28, 28]], "confidences": [0.9989548921585083, 0.9999619722366333, 0.9992412328720093, 0.9541407823562622, 0.9999735355377197, 0.9940637946128845, 0.999988317489624, 0.9999865293502808, 0.9999904632568359, 0.550645649433136, 0.9999978542327881, 0.9999892711639404, 0.9996963739395142, 0.9999991655349731, 0.5165660977363586, 0.999988317489624, 0.9995940327644348, 0.9999626874923706, 0.9998881816864014, 0.9940359592437744, 0.9996926784515381, 0.9999793767929077, 0.9998898506164551, 0.9998297691345215, 0.9897773861885071, 0.9994788765907288, 0.9995355606079102, 0.9969993829727173, 0.9985607266426086, 0.9999980926513672, 0.9996448755264282, 0.9874195456504822, 0.9999774694442749, 0.9867774844169617, 0.9744981527328491, 0.9999960660934448, 0.9999808073043823, 0.9993176460266113, 0.9999256134033203, 0.9999924898147583, 0.9999164342880249, 0.9999569654464722, 0.9999525547027588, 0.9997047781944275, 0.9999884366989136, 0.9999561309814453, 0.9999027252197266, 0.999967098236084, 0.9966025352478027, 0.9999691247940063, 0.9973816275596619, 0.9003818035125732, 0.9999942779541016, 0.9996612071990967, 0.9999988079071045, 0.9940271377563477, 0.9999754428863525, 0.9999979734420776, 0.9999998807907104, 0.9994766116142273, 0.9999978542327881, 0.9999990463256836, 0.9999949932098389, 0.9999971389770508, 0.8546275496482849, 0.9999696016311646, 0.9999052286148071, 0.9918268918991089, 0.5608442425727844], "line": {"boundary": [[161, 631], [163, 592], [185, 573], [230, 604], [257, 580], [309, 610], [341, 586], [380, 584], [423, 615], [456, 588], [508, 588], [524, 604], [559, 604], [575, 619], [612, 586], [649, 586], [682, 619], [697, 606], [729, 606], [742, 594], [793, 592], [822, 619], [853, 592], [877, 612], [896, 600], [927, 610], [949, 590], [962, 590], [990, 613], [1013, 592], [1023, 598], [1039, 588], [1060, 592], [1083, 613], [1113, 588], [1140, 602], [1163, 588], [1196, 615], [1251, 582], [1294, 621], [1309, 619], [1327, 602], [1360, 612], [1378, 608], [1389, 619], [1395, 647], [1366, 660], [1341, 649], [1321, 662], [1302, 662], [1282, 647], [1270, 658], [1226, 652], [1200, 670], [1175, 647], [1120, 645], [1087, 668], [1062, 645], [1009, 666], [970, 645], [894, 672], [867, 649], [832, 670], [799, 643], [748, 651], [688, 641], [670, 656], [647, 645], [615, 645], [586, 666], [561, 641], [483, 654], [426, 633], [397, 654], [331, 643], [311, 656], [280, 631], [228, 654], [165, 637]], "baseline": [[163, 633], [651, 633], [674, 637], [729, 637], [818, 643], [857, 641], [890, 647], [912, 647], [931, 652], [968, 643], [1011, 641], [1245, 641], [1300, 647], [1345, 645], [1397, 649]]}}, {"prediction": "\u0648\u064a\u0633\u062a\u0639\u0645\u0644 \u0627\u0644\u0647\u0632\u0644 \u0648\u0627\u0644\u0644\u0639\u0628\u060c \u0648\u064a\u0630\u0643\u0631 \u0627\u0644\u0645\u0631\u062f\u0627\u0646\u060c \u0648\u064a\u0645\u064a\u0644 \u0627\u0655\u0644\u064a\u0647\u0645\u060c \u0627\u0654\u0648 \u0631\u0627\u0654\u064a\u062a\u0645\u0648\u0647 \u0642\u0644\u064a\u0644 \u0627\u0644\u0648\u0631\u0639 \u0641\u064a", "cuts": [[1298, 1298], [1283, 1283], [1267, 1267], [1246, 1246], [1229, 1229], [1208, 1208], [1187, 1192], [1155, 1160], [1149, 1149], [1139, 1139], [1117, 1123], [1107, 1107], [1085, 1091], [1053, 1058], [1042, 1042], [1026, 1026], [1016, 1016], [1000, 1000], [984, 984], [952, 957], [925, 925], [903, 909], [893, 893], [877, 877], [861, 861], [839, 839], [818, 818], [797, 802], [786, 791], [775, 780], [759, 759], [738, 743], [722, 722], [706, 711], [695, 695], [668, 668], [647, 658], [641, 641], [620, 620], [604, 604], [588, 588], [572, 577], [545, 545], [535, 540], [524, 529], [519, 519], [508, 508], [492, 492], [470, 470], [449, 454], [433, 438], [428, 428], [417, 422], [412, 412], [385, 390], [369, 374], [353, 358], [347, 347], [342, 342], [331, 331], [315, 315], [299, 299], [278, 278], [257, 262], [246, 246], [235, 235], [219, 219], [203, 208], [176, 182], [166, 171], [155, 160], [144, 144], [123, 123], [96, 96], [70, 75], [59, 59], [43, 48]], "confidences": [0.9960930943489075, 0.9970471262931824, 0.9999514818191528, 0.9999713897705078, 0.9975488781929016, 0.9999634027481079, 0.9999896287918091, 0.9999924898147583, 0.9999784231185913, 0.9999960660934448, 0.9997754693031311, 0.9999520778656006, 0.9999797344207764, 0.9999464750289917, 0.999251663684845, 0.9999938011169434, 0.9999744892120361, 0.9904490113258362, 0.6805958151817322, 0.9993933439254761, 0.8523104190826416, 0.9999516010284424, 0.9954115748405457, 0.9966720342636108, 0.9986212253570557, 0.9991599321365356, 0.9994064569473267, 0.9998964071273804, 0.999936580657959, 0.9998494386672974, 0.9968574047088623, 0.9996702671051025, 0.999824583530426, 0.9998010993003845, 0.9999998807907104, 0.9999876022338867, 0.9999998807907104, 0.9999998807907104, 0.9999731779098511, 0.9999935626983643, 0.9999995231628418, 0.9998869895935059, 0.9999819993972778, 0.9999923706054688, 0.9999994039535522, 0.9997376799583435, 0.9999179840087891, 0.999994158744812, 0.9999123811721802, 0.9999874830245972, 1.0, 0.9014513492584229, 0.9999948740005493, 0.9999997615814209, 0.9999998807907104, 0.9998629093170166, 0.9677855372428894, 0.9999980926513672, 0.9946457147598267, 0.9999970197677612, 0.972520649433136, 0.999996542930603, 0.9999895095825195, 0.9999986886978149, 0.9621902108192444, 0.9999395608901978, 0.9999432563781738, 0.9999014139175415, 0.9999983310699463, 0.999998927116394, 0.999935507774353, 0.9999979734420776, 0.9999970197677612, 0.9999983310699463, 0.9999921321868896, 0.9999868869781494, 0.9999967813491821], "line": {"boundary": [[155, 727], [159, 701], [198, 666], [222, 680], [239, 672], [270, 682], [300, 660], [327, 682], [346, 664], [391, 664], [413, 682], [434, 686], [471, 676], [493, 656], [530, 684], [567, 656], [592, 680], [615, 688], [656, 664], [674, 666], [693, 684], [717, 668], [736, 686], [785, 682], [795, 690], [822, 670], [851, 668], [886, 690], [916, 666], [935, 670], [951, 686], [968, 672], [1005, 666], [1027, 686], [1052, 691], [1060, 684], [1103, 691], [1136, 666], [1169, 670], [1194, 690], [1220, 668], [1255, 682], [1280, 666], [1306, 686], [1325, 670], [1352, 690], [1376, 680], [1403, 691], [1446, 686], [1456, 719], [1442, 736], [1366, 725], [1345, 727], [1325, 742], [1311, 742], [1290, 725], [1233, 738], [1198, 723], [1177, 736], [1155, 725], [1120, 723], [1087, 736], [1056, 719], [1029, 734], [980, 723], [962, 736], [947, 736], [931, 723], [914, 723], [869, 736], [853, 723], [824, 727], [797, 719], [779, 732], [742, 725], [703, 740], [684, 728], [623, 728], [608, 742], [578, 719], [547, 732], [528, 725], [481, 732], [469, 723], [436, 732], [407, 717], [348, 736], [304, 721], [245, 744], [216, 727], [179, 736], [161, 727]], "baseline": [[157, 728], [187, 727], [270, 730], [313, 721], [403, 719], [460, 713], [526, 721], [900, 719], [1457, 721]]}}, {"prediction": "\u0627\u0644\u0645\u0627\u0654\u0643\u0644 \u0648\u0627\u0644\u0645\u0634\u0631\u0628\u060c \u0648\u0627\u0644\u0645\u062f\u062e\u0644 \u0648\u0627\u0644\u0645\u062e\u0631\u062c\u060c \u0644\u0627 \u064a\u0628\u0627\u0644\u064a \u0645\u0627 \u0627\u0654\u0643\u0644 \u062d\u0644\u0627\u0644\u0627 \u0643\u0627\u0646 \u0627\u0654\u0648 \u062d\u0631\u0627\u0645\u0627\u060c", "cuts": [[1298, 1304], [1291, 1291], [1273, 1273], [1254, 1260], [1248, 1248], [1242, 1242], [1217, 1223], [1180, 1192], [1173, 1173], [1155, 1155], [1142, 1142], [1124, 1124], [1105, 1105], [1080, 1080], [1049, 1049], [1018, 1018], [993, 1000], [987, 987], [969, 969], [956, 956], [937, 937], [919, 919], [888, 888], [869, 869], [826, 838], [820, 820], [795, 801], [788, 788], [770, 770], [739, 739], [720, 720], [695, 695], [671, 671], [646, 658], [633, 639], [621, 627], [596, 608], [590, 590], [577, 577], [565, 565], [553, 553], [540, 540], [503, 509], [490, 490], [472, 478], [453, 459], [441, 447], [435, 435], [428, 428], [404, 410], [366, 379], [348, 348], [329, 329], [310, 323], [298, 298], [286, 292], [255, 267], [248, 248], [224, 230], [211, 211], [180, 186], [174, 174], [168, 168], [155, 155], [124, 130], [106, 106], [87, 87], [68, 68], [56, 56], [37, 43], [25, 25]], "confidences": [0.9999150037765503, 0.9998385906219482, 0.999996542930603, 0.9999994039535522, 0.9999864101409912, 0.9999818801879883, 0.9999929666519165, 0.999998927116394, 0.9999581575393677, 0.9999958276748657, 0.9999880790710449, 0.9997920393943787, 0.9999971389770508, 0.9999898672103882, 0.9999960660934448, 0.9710601568222046, 0.996277391910553, 0.9998601675033569, 0.922956645488739, 0.9978814721107483, 0.999930739402771, 0.9997755885124207, 0.9413175582885742, 0.9998894929885864, 0.9993183612823486, 0.999767005443573, 0.9998904466629028, 0.6034071445465088, 0.9915944933891296, 0.999853253364563, 0.9953317046165466, 0.9986577033996582, 0.9640184640884399, 0.9999998807907104, 0.9999990463256836, 0.9999948740005493, 0.9999983310699463, 0.9997009038925171, 0.9999697208404541, 0.9999998807907104, 0.9997711777687073, 0.9742012023925781, 0.9999996423721313, 0.9999728202819824, 0.9999784231185913, 0.9999963045120239, 0.9999946355819702, 0.9767754077911377, 0.9991446733474731, 0.9999916553497314, 0.9999847412109375, 0.9995253086090088, 0.9999663829803467, 0.9999977350234985, 0.9770731925964355, 0.9999951124191284, 0.9999915361404419, 0.9999960660934448, 0.9701045751571655, 0.9999648332595825, 0.9999688863754272, 0.9998500347137451, 0.9999626874923706, 0.9992308616638184, 0.9999960660934448, 0.9513243436813354, 0.9999858140945435, 0.9998613595962524, 0.9998170733451843, 0.9996731281280518, 0.9003341794013977], "line": {"boundary": [[159, 783], [163, 748], [191, 732], [218, 740], [237, 758], [274, 766], [313, 730], [341, 750], [401, 744], [417, 730], [432, 730], [458, 746], [475, 742], [522, 769], [553, 742], [567, 746], [588, 732], [606, 748], [621, 742], [660, 775], [697, 742], [738, 762], [758, 744], [781, 740], [808, 760], [859, 766], [879, 748], [906, 764], [929, 742], [941, 742], [980, 771], [1013, 746], [1025, 756], [1068, 754], [1076, 762], [1103, 744], [1146, 773], [1161, 762], [1191, 771], [1212, 762], [1224, 769], [1243, 752], [1265, 764], [1292, 746], [1335, 775], [1362, 750], [1374, 754], [1401, 738], [1419, 754], [1430, 746], [1446, 750], [1465, 767], [1471, 793], [1448, 805], [1389, 803], [1360, 822], [1345, 820], [1329, 805], [1311, 816], [1288, 805], [1189, 816], [1152, 795], [1124, 814], [1101, 803], [1035, 799], [1007, 820], [994, 820], [974, 803], [947, 810], [935, 801], [885, 799], [857, 822], [834, 822], [797, 791], [779, 803], [758, 791], [738, 808], [721, 810], [707, 801], [676, 822], [643, 795], [617, 799], [606, 789], [569, 799], [551, 814], [534, 814], [510, 793], [460, 803], [403, 789], [389, 799], [350, 803], [329, 789], [294, 808], [265, 791], [222, 810], [204, 797], [171, 797], [163, 789]], "baseline": [[161, 785], [216, 791], [935, 789], [959, 795], [1472, 795]]}}, {"prediction": "\u0641\u0627\u062a\u0647\u0645\u0648\u0647 \u0639\u0644\u0649 \u0639\u0644\u0645\u0647 \u0648\u0639\u0644\u0649 \u062f\u064a\u0646\u0647\u060c \u0648\u0644\u0627 \u062a\u0642\u0644\u062f\u0648\u0647 \u0627\u0654\u0645\u0648\u0631\u0643\u0645\u060c \u0648\u0627\u062d\u0630\u0631\u0648\u0647 \u0627\u0654\u0646 \u064a\u0633\u0644\u0628\u0643\u0645 \u062f\u064a\u0646\u0643\u0645\u060c", "cuts": [[1304, 1304], [1293, 1293], [1283, 1283], [1266, 1266], [1245, 1245], [1228, 1228], [1207, 1207], [1185, 1191], [1169, 1169], [1147, 1147], [1131, 1131], [1099, 1104], [1082, 1082], [1066, 1066], [1044, 1044], [1028, 1028], [1001, 1012], [996, 996], [969, 969], [947, 947], [936, 936], [898, 904], [887, 887], [871, 871], [860, 860], [850, 850], [828, 828], [806, 812], [795, 801], [774, 779], [763, 768], [741, 747], [731, 731], [720, 720], [703, 703], [687, 687], [666, 666], [644, 644], [622, 628], [617, 617], [606, 612], [601, 601], [584, 584], [563, 563], [541, 541], [514, 514], [498, 498], [476, 482], [465, 465], [449, 449], [422, 422], [406, 406], [384, 384], [363, 363], [341, 341], [325, 330], [314, 319], [308, 308], [298, 298], [265, 276], [260, 260], [244, 244], [216, 216], [206, 206], [184, 184], [157, 162], [135, 141], [124, 124], [108, 108], [97, 97], [76, 76], [54, 54], [32, 32]], "confidences": [0.9997382760047913, 0.9998076558113098, 0.9998169541358948, 0.9999723434448242, 0.9999963045120239, 0.9998815059661865, 0.9978761672973633, 0.9999692440032959, 0.9999940395355225, 0.9997928738594055, 0.9998121857643127, 0.9998834133148193, 0.9999972581863403, 0.9986358284950256, 0.9999672174453735, 0.9999685287475586, 0.9999872446060181, 0.9996137022972107, 0.9999386072158813, 0.9998291730880737, 0.9996570348739624, 0.9997431635856628, 0.9989522695541382, 0.9978705644607544, 0.6706600189208984, 0.727662205696106, 0.9994146823883057, 0.9998109936714172, 0.9988961219787598, 0.9999992847442627, 0.9999957084655762, 0.9998549222946167, 0.7824696898460388, 0.9987309575080872, 0.9999728202819824, 0.9999747276306152, 0.9984544515609741, 0.9999949932098389, 0.9999949932098389, 0.9999567270278931, 0.9999990463256836, 0.9999997615814209, 1.0, 0.999970555305481, 0.9995998740196228, 0.9863488674163818, 0.9999908208847046, 0.9998534917831421, 0.989995002746582, 0.999998927116394, 0.9999957084655762, 0.9997010827064514, 0.9999995231628418, 0.9993435740470886, 0.9999997615814209, 0.9204366207122803, 0.9999145269393921, 0.9971621036529541, 0.9998090863227844, 0.9999971389770508, 0.9999977350234985, 0.999993085861206, 0.999996542930603, 0.9999635219573975, 0.9999977350234985, 0.9999561309814453, 0.9999858140945435, 0.9998942613601685, 0.9999914169311523, 0.9876537919044495, 0.9992417097091675, 0.9999997615814209, 0.9282093048095703], "line": {"boundary": [[152, 853], [171, 834], [189, 840], [228, 812], [284, 845], [329, 816], [413, 834], [446, 810], [489, 838], [516, 840], [545, 818], [563, 834], [588, 822], [619, 843], [633, 834], [649, 842], [666, 826], [688, 822], [713, 842], [754, 812], [789, 840], [832, 822], [881, 830], [920, 820], [951, 845], [962, 836], [1001, 832], [1027, 834], [1046, 851], [1081, 822], [1146, 845], [1163, 834], [1179, 840], [1204, 822], [1247, 853], [1282, 822], [1323, 847], [1335, 840], [1391, 845], [1409, 832], [1440, 828], [1462, 845], [1467, 869], [1401, 894], [1382, 881], [1348, 894], [1321, 871], [1269, 894], [1253, 894], [1226, 875], [1183, 881], [1152, 873], [1128, 890], [1095, 879], [1056, 894], [1033, 877], [1003, 888], [955, 871], [935, 888], [922, 888], [888, 867], [869, 879], [812, 879], [795, 888], [764, 869], [715, 890], [674, 877], [645, 898], [619, 875], [600, 888], [571, 871], [493, 886], [465, 869], [434, 881], [415, 875], [401, 884], [372, 877], [356, 886], [323, 875], [296, 896], [267, 875], [251, 884], [214, 875], [187, 896], [153, 867]], "baseline": [[153, 855], [228, 869], [1237, 867], [1269, 871], [1290, 867], [1309, 871], [1469, 871]]}}, {"prediction": "\u0628\u062a\u0647\u0648\u064a\u0646\u0647 \u0644\u0644\u0627\u0654\u0633\u064a\u0627\u0621 \u0627\u0644\u0635\u063a\u064a\u0631\u0629 \u0645\u0646 \u0627\u0644\u062d\u0631\u0627\u0645 \u0648\u0627\u0644\u0634\u0628\u0647\u0627\u062a\u060c \u064a\u0633\u0631\u0642 \u0628\u0630\u0644\u0643 \u0639\u0642\u0648\u0644\u0643\u0645\u060c \u0641\u064a\u0633\u062a\u062f\u0631\u062c\u0643\u0645", "cuts": [[1297, 1297], [1286, 1286], [1275, 1275], [1253, 1253], [1237, 1237], [1220, 1226], [1209, 1209], [1188, 1193], [1182, 1182], [1160, 1166], [1149, 1155], [1138, 1144], [1133, 1133], [1111, 1111], [1095, 1095], [1084, 1084], [1062, 1067], [1056, 1056], [1045, 1045], [1023, 1023], [991, 991], [974, 974], [958, 963], [941, 941], [919, 925], [914, 914], [887, 887], [859, 870], [854, 854], [843, 843], [815, 815], [799, 799], [783, 783], [766, 766], [744, 750], [739, 739], [717, 722], [706, 711], [690, 690], [668, 668], [651, 651], [629, 635], [607, 607], [580, 580], [558, 569], [553, 553], [536, 536], [509, 514], [493, 493], [454, 465], [449, 449], [438, 438], [416, 416], [405, 405], [356, 361], [345, 345], [328, 328], [306, 306], [290, 290], [268, 268], [246, 246], [224, 224], [203, 213], [197, 197], [186, 186], [164, 164], [148, 148], [131, 131], [109, 109], [82, 82], [55, 55], [27, 33]], "confidences": [0.9998189806938171, 0.9999316930770874, 0.9999616146087646, 0.9999963045120239, 0.9904739260673523, 0.9999723434448242, 0.9971580505371094, 0.9988645315170288, 0.9999966621398926, 0.9999974966049194, 0.9999973773956299, 0.9999791383743286, 0.9999858140945435, 0.9999887943267822, 0.5477295517921448, 0.9999678134918213, 0.9999643564224243, 0.8171812295913696, 0.9827367067337036, 0.9935495257377625, 0.9999936819076538, 0.999975323677063, 0.9999910593032837, 0.998406708240509, 0.9999222755432129, 0.9998999834060669, 0.9343510866165161, 0.9997228980064392, 0.7628433108329773, 0.6818570494651794, 0.950019121170044, 0.6418173313140869, 0.9943951368331909, 0.9706724882125854, 0.9999768733978271, 0.9994531273841858, 0.9995731711387634, 0.9997699856758118, 0.9988842606544495, 0.9997255206108093, 0.9999997615814209, 0.9999980926513672, 0.999735414981842, 0.9235820770263672, 0.9999998807907104, 0.9999927282333374, 0.9976077079772949, 0.9999990463256836, 0.8485846519470215, 0.9999995231628418, 0.9999078512191772, 0.9999904632568359, 0.999980092048645, 0.9999996423721313, 0.9956209063529968, 0.9999990463256836, 0.9995309114456177, 0.9999940395355225, 0.9999079704284668, 1.0, 0.9180242419242859, 0.9995797276496887, 0.9999831914901733, 0.9998835325241089, 0.9994398951530457, 0.988334059715271, 0.9999781847000122, 0.9999990463256836, 0.8295435309410095, 0.9995015859603882, 0.9999979734420776, 0.9999895095825195], "line": {"boundary": [[159, 947], [163, 923], [210, 894], [247, 920], [276, 906], [317, 920], [337, 902], [362, 916], [389, 918], [430, 892], [456, 908], [479, 900], [510, 920], [549, 896], [586, 896], [610, 918], [643, 898], [670, 921], [762, 912], [775, 900], [812, 921], [853, 898], [898, 925], [923, 902], [947, 918], [990, 898], [1019, 920], [1066, 920], [1087, 906], [1111, 921], [1136, 902], [1155, 920], [1183, 900], [1200, 902], [1218, 918], [1235, 902], [1259, 916], [1282, 896], [1298, 896], [1327, 900], [1347, 918], [1372, 912], [1391, 923], [1434, 912], [1473, 953], [1446, 970], [1428, 964], [1395, 972], [1343, 953], [1302, 962], [1276, 957], [1261, 968], [1216, 953], [1138, 957], [1099, 974], [1072, 953], [1037, 974], [1003, 957], [962, 957], [949, 970], [906, 980], [888, 964], [867, 968], [853, 957], [793, 968], [777, 957], [721, 953], [699, 966], [674, 957], [645, 968], [615, 957], [600, 966], [580, 955], [528, 957], [506, 945], [450, 968], [419, 953], [382, 976], [356, 953], [335, 966], [313, 957], [276, 955], [251, 966], [200, 955], [177, 976], [163, 974]], "baseline": [[161, 949], [241, 945], [664, 945], [705, 949], [729, 945], [822, 955], [923, 951], [1087, 951], [1107, 955], [1214, 945], [1362, 955], [1475, 954]]}}, {"prediction": "\u0645\u0646 \u062d\u064a\u062b \u0644\u0627 \u062a\u0639\u0644\u0645\u0648\u0646.", "cuts": [[343, 343], [316, 321], [294, 298], [276, 276], [258, 258], [230, 230], [199, 203], [185, 190], [172, 181], [158, 163], [149, 149], [136, 136], [118, 118], [95, 99], [77, 81], [54, 59], [32, 32]], "confidences": [0.9013516902923584, 0.9993886947631836, 0.9999945163726807, 0.9106606841087341, 0.9922044277191162, 0.9999985694885254, 0.999998927116394, 0.9999998807907104, 0.9999948740005493, 0.9999319314956665, 0.9999568462371826, 0.9999945163726807, 0.5355215072631836, 0.9999971389770508, 0.9999983310699463, 0.9999912977218628, 0.9786094427108765], "line": {"boundary": [[1109, 1025], [1146, 980], [1173, 997], [1189, 997], [1210, 976], [1231, 988], [1294, 976], [1309, 992], [1333, 984], [1348, 999], [1364, 992], [1395, 1007], [1409, 994], [1424, 1001], [1444, 997], [1473, 1027], [1454, 1029], [1411, 1056], [1380, 1031], [1347, 1046], [1300, 1029], [1274, 1036], [1263, 1025], [1241, 1036], [1191, 1036], [1171, 1050], [1118, 1025]], "baseline": [[1111, 1027], [1274, 1023], [1366, 1033], [1475, 1029]]}}, {"prediction": "\u0648\u0645\u062a\u0649 \u0631\u0627\u0654\u064a\u062a\u0645 \u0627\u0644\u0639\u0627\u0644\u0645 \u064a\u0642\u0628\u0644 \u0627\u0644\u0647\u062f\u064a\u0629 \u0645\u0646 \u0627\u0644\u0645\u0633\u062a\u0641\u062a\u064a \u0648\u064a\u0641\u062a\u064a\u0647 \u0639\u0644\u0649 \u063a\u0631\u0636\u0647\u060c \u0648\u064a\u062f\u062e\u0644 \u0641\u064a", "cuts": [[1225, 1225], [1201, 1207], [1189, 1189], [1172, 1172], [1136, 1148], [1130, 1130], [1112, 1118], [1106, 1106], [1100, 1100], [1088, 1088], [1064, 1070], [1041, 1047], [1029, 1035], [1023, 1023], [1011, 1011], [993, 993], [981, 981], [957, 963], [934, 940], [928, 928], [916, 916], [898, 898], [880, 886], [850, 856], [838, 844], [827, 833], [815, 815], [797, 797], [779, 779], [761, 761], [737, 743], [726, 731], [702, 702], [678, 684], [666, 672], [654, 660], [636, 642], [613, 618], [595, 595], [583, 583], [565, 565], [553, 553], [517, 523], [505, 505], [488, 488], [476, 476], [458, 458], [446, 446], [434, 434], [410, 416], [393, 393], [375, 375], [363, 363], [327, 333], [309, 309], [291, 291], [268, 268], [238, 238], [220, 220], [196, 202], [184, 184], [167, 167], [155, 155], [125, 131], [101, 107], [59, 71], [54, 54], [36, 42]], "confidences": [0.9998977184295654, 0.9992066025733948, 0.9999544620513916, 0.9999667406082153, 0.9999969005584717, 0.9999951124191284, 0.9999479055404663, 0.8893346190452576, 0.9999771118164062, 0.9999749660491943, 0.9999734163284302, 0.9999407529830933, 0.9999978542327881, 0.888961911201477, 0.999985933303833, 0.9997301697731018, 0.9997461438179016, 0.9999942779541016, 0.9999889135360718, 0.9999947547912598, 0.7681607007980347, 0.9985297918319702, 0.9992446899414062, 0.9999771118164062, 0.9999252557754517, 0.9995318651199341, 0.9955322742462158, 0.9999263286590576, 0.9855678081512451, 0.8572127223014832, 0.9958655834197998, 0.9997792840003967, 0.8732478022575378, 0.9998902082443237, 0.9997214674949646, 0.9999871253967285, 0.999970555305481, 0.9999971389770508, 0.9999786615371704, 0.9999481439590454, 0.9997740387916565, 0.8626158833503723, 0.9999806880950928, 0.9998955726623535, 0.9999974966049194, 0.9999513626098633, 0.9972359538078308, 0.9995180368423462, 0.9999905824661255, 1.0, 0.9999990463256836, 0.9999991655349731, 0.9999984502792358, 0.9999990463256836, 0.9999250173568726, 0.9994828701019287, 0.9996166229248047, 0.995541512966156, 0.9999992847442627, 0.999984860420227, 0.9904344081878662, 0.9992062449455261, 0.9999986886978149, 0.9761358499526978, 0.999893069267273, 0.9999963045120239, 0.9997863173484802, 0.9999911785125732], "line": {"boundary": [[159, 1103], [163, 1081], [196, 1052], [224, 1072], [249, 1050], [348, 1073], [382, 1062], [393, 1070], [409, 1056], [432, 1068], [458, 1052], [487, 1079], [518, 1050], [561, 1075], [573, 1064], [623, 1052], [641, 1070], [684, 1081], [727, 1052], [760, 1073], [773, 1073], [799, 1050], [834, 1072], [849, 1066], [859, 1073], [883, 1070], [908, 1052], [927, 1066], [955, 1066], [980, 1050], [1007, 1072], [1027, 1052], [1040, 1060], [1056, 1056], [1089, 1083], [1120, 1054], [1150, 1066], [1173, 1054], [1204, 1079], [1245, 1048], [1257, 1048], [1296, 1085], [1321, 1068], [1370, 1072], [1395, 1105], [1364, 1126], [1339, 1114], [1317, 1128], [1298, 1128], [1282, 1114], [1269, 1124], [1228, 1118], [1210, 1134], [1179, 1111], [1126, 1111], [1093, 1134], [1079, 1120], [1052, 1114], [1019, 1128], [990, 1109], [953, 1122], [939, 1111], [912, 1120], [892, 1101], [853, 1124], [838, 1124], [818, 1107], [721, 1109], [692, 1134], [662, 1111], [643, 1120], [612, 1109], [584, 1120], [565, 1101], [502, 1124], [485, 1122], [465, 1103], [428, 1122], [411, 1109], [360, 1107], [350, 1099], [325, 1118], [276, 1103], [239, 1126], [214, 1109], [177, 1130], [163, 1116]], "baseline": [[161, 1105], [214, 1111], [276, 1105], [296, 1099], [421, 1103], [906, 1101], [1005, 1111], [1093, 1111], [1113, 1107], [1165, 1105], [1206, 1111], [1397, 1107]]}}, {"prediction": "\u0627\u0644\u062a\u0627\u0654\u0648\u064a\u0644\u0627\u062a \u0648\u0627\u0644\u0634\u0628\u0647\u0627\u062a \u060c \u0643\u0645\u0633\u0627\u0654\u0644\u0629 \u0627\u0644\u0627\u0633\u062a\u062d\u0644\u0627\u0644\u060c \u0648\u0645\u0633\u0627\u0654\u0644\u0629 \u0627\u0644\u0631\u0628\u0627 \u0648\u0627\u0644\u0645\u0639\u0627\u0645\u0644\u0629\u060c \u0648\u0644\u0627 \u062a\u062c\u062f\u0648\u0646\u0647", "cuts": [[1293, 1298], [1284, 1289], [1274, 1274], [1260, 1265], [1255, 1255], [1245, 1245], [1231, 1231], [1212, 1216], [1202, 1207], [1168, 1168], [1130, 1140], [1120, 1120], [1101, 1106], [1091, 1096], [1077, 1077], [1053, 1053], [1039, 1039], [1019, 1024], [995, 995], [976, 976], [971, 971], [942, 947], [928, 928], [904, 904], [880, 885], [861, 865], [856, 856], [851, 851], [837, 837], [808, 813], [798, 803], [784, 789], [774, 779], [755, 755], [731, 731], [702, 702], [683, 688], [673, 678], [659, 659], [630, 630], [601, 611], [591, 591], [572, 572], [548, 553], [529, 529], [519, 524], [514, 514], [505, 505], [476, 481], [466, 466], [457, 457], [442, 442], [428, 428], [409, 414], [389, 394], [380, 380], [361, 361], [351, 351], [332, 332], [308, 308], [293, 298], [279, 284], [264, 264], [250, 250], [231, 236], [207, 212], [197, 197], [173, 178], [159, 168], [130, 139], [125, 125], [101, 101], [82, 82], [58, 58], [38, 38], [24, 24]], "confidences": [0.9996144771575928, 0.9999929666519165, 0.9999830722808838, 0.9999926090240479, 0.999995231628418, 0.9467358589172363, 0.999972939491272, 0.9995086193084717, 0.9998672008514404, 0.9942428469657898, 0.9999955892562866, 0.9999592304229736, 0.9999781847000122, 0.999994158744812, 0.9999866485595703, 0.999920129776001, 0.9999884366989136, 0.9986990690231323, 0.9997000694274902, 0.9911819696426392, 0.978091299533844, 0.999427855014801, 0.9999485015869141, 0.7997534275054932, 0.9999580383300781, 0.999704897403717, 0.520932137966156, 0.9891839027404785, 0.9960983991622925, 0.999637246131897, 0.9975180625915527, 0.9990247488021851, 0.9999979734420776, 0.7142682075500488, 0.9256128668785095, 1.0, 0.9999996423721313, 0.9998838901519775, 0.9999998807907104, 0.9960355162620544, 0.9999982118606567, 0.999958872795105, 0.9999998807907104, 1.0, 0.9999998807907104, 0.999992847442627, 0.9999973773956299, 0.9999991655349731, 0.9975195527076721, 0.9820659756660461, 0.9999998807907104, 0.9999421834945679, 0.9999996423721313, 0.9999991655349731, 0.9999985694885254, 0.6958007216453552, 0.9832131266593933, 0.9999997615814209, 0.9999964237213135, 0.9855285286903381, 0.9999996423721313, 0.9993840456008911, 0.9999988079071045, 0.9999823570251465, 0.9999992847442627, 0.9999185800552368, 0.9999986886978149, 0.9999874830245972, 0.9999970197677612, 0.9999932050704956, 0.9994638562202454, 0.9987408518791199, 0.999816358089447, 0.9999561309814453, 0.9999673366546631, 0.9998775720596313], "line": {"boundary": [[159, 1165], [163, 1146], [185, 1132], [210, 1146], [224, 1136], [253, 1146], [267, 1134], [286, 1138], [315, 1126], [327, 1126], [360, 1155], [389, 1130], [405, 1128], [426, 1138], [442, 1128], [467, 1148], [502, 1128], [536, 1150], [559, 1130], [578, 1146], [598, 1128], [629, 1142], [676, 1122], [707, 1151], [746, 1148], [758, 1157], [799, 1130], [836, 1132], [861, 1150], [875, 1140], [892, 1148], [912, 1130], [949, 1132], [962, 1144], [1001, 1122], [1042, 1153], [1081, 1132], [1105, 1151], [1150, 1144], [1163, 1132], [1200, 1151], [1241, 1130], [1286, 1161], [1309, 1144], [1360, 1134], [1378, 1148], [1405, 1124], [1450, 1142], [1467, 1179], [1444, 1190], [1405, 1190], [1387, 1202], [1290, 1183], [1269, 1200], [1239, 1188], [1193, 1200], [1163, 1187], [1111, 1188], [1099, 1179], [1056, 1190], [984, 1188], [961, 1179], [931, 1190], [910, 1185], [799, 1192], [762, 1179], [736, 1198], [713, 1187], [656, 1188], [623, 1179], [588, 1200], [567, 1196], [547, 1179], [526, 1196], [499, 1187], [401, 1187], [366, 1177], [345, 1194], [329, 1194], [296, 1175], [253, 1192], [220, 1185], [191, 1194], [163, 1179]], "baseline": [[161, 1167], [191, 1173], [306, 1177], [469, 1177], [493, 1181], [1469, 1181]]}}, {"prediction": "\u0645\u062a\u0639\u0641\u0641\u0627 \u0641\u064a \u0645\u0639\u064a\u0634\u062a\u0647\u060c \u062a\u0631\u0648\u0646\u0647 \u0637\u0627\u0645\u0639\u0627 \u0641\u064a \u0627\u0654\u0645\u0648\u0627\u0644 \u0627\u0644\u0646\u0627\u0633\u060c \u064a\u062f\u0627\u062e\u0644 \u0627\u0644\u0642\u0636\u0627\u0629 \u0644\u064a\u0648\u0644\u0648\u0647 \u0627\u0644\u0648\u0644\u0627\u064a\u0627\u062a\u060c", "cuts": [[1294, 1294], [1276, 1276], [1264, 1264], [1246, 1246], [1228, 1228], [1210, 1216], [1192, 1198], [1186, 1186], [1168, 1168], [1131, 1137], [1125, 1125], [1101, 1101], [1089, 1089], [1071, 1071], [1047, 1047], [1035, 1035], [1017, 1017], [999, 1005], [987, 987], [975, 975], [957, 957], [939, 939], [921, 921], [897, 903], [879, 879], [861, 867], [849, 849], [831, 831], [812, 818], [794, 800], [788, 788], [770, 770], [740, 740], [728, 734], [722, 722], [710, 710], [698, 698], [680, 680], [668, 668], [638, 638], [626, 632], [614, 620], [602, 602], [590, 596], [572, 572], [536, 536], [512, 518], [506, 506], [487, 494], [469, 475], [451, 451], [433, 433], [397, 403], [391, 391], [379, 379], [367, 367], [343, 343], [313, 313], [301, 301], [277, 289], [271, 271], [259, 259], [241, 247], [229, 229], [211, 211], [193, 193], [175, 181], [163, 169], [150, 157], [138, 138], [114, 120], [102, 108], [90, 90], [72, 78], [48, 48], [24, 24]], "confidences": [0.9999475479125977, 0.9999927282333374, 0.999996542930603, 0.9999957084655762, 0.999872088432312, 0.9999810457229614, 0.9999687671661377, 0.9999939203262329, 0.9999991655349731, 0.9942613840103149, 0.9999487400054932, 0.9999334812164307, 0.9999005794525146, 0.9999839067459106, 0.9999810457229614, 0.989007830619812, 0.9996200799942017, 0.9999685287475586, 0.6469784379005432, 0.6665834188461304, 0.7252474427223206, 0.9857919216156006, 0.9997385144233704, 0.9999765157699585, 0.9963619112968445, 0.9994021654129028, 0.9998681545257568, 0.9810250401496887, 0.9838308095932007, 0.9998787641525269, 0.9988665580749512, 0.9999058246612549, 0.6534377336502075, 0.9997734427452087, 0.631395697593689, 0.9998962879180908, 0.9999980926513672, 0.9978647828102112, 0.9999958276748657, 0.9721218943595886, 0.9996869564056396, 0.9999868869781494, 0.9235882759094238, 0.9999985694885254, 0.9997712969779968, 0.826903223991394, 0.999829888343811, 0.9994933605194092, 0.9999945163726807, 0.9999911785125732, 0.9996364116668701, 0.9999428987503052, 0.9976553916931152, 0.9999971389770508, 0.71103435754776, 0.9999946355819702, 0.9999889135360718, 0.9995948672294617, 0.9999077320098877, 0.9999817609786987, 0.9998291730880737, 0.999971866607666, 0.9999986886978149, 0.9999973773956299, 0.9999977350234985, 0.9933046698570251, 0.9999994039535522, 0.9999998807907104, 0.9999985694885254, 0.9998321533203125, 0.9999990463256836, 0.9999988079071045, 0.9999668598175049, 0.9999896287918091, 0.9999967813491821, 0.9679267406463623], "line": {"boundary": [[159, 1249], [167, 1220], [245, 1204], [267, 1204], [282, 1216], [300, 1204], [331, 1224], [348, 1224], [370, 1204], [391, 1220], [411, 1204], [432, 1216], [460, 1208], [473, 1220], [530, 1206], [555, 1226], [577, 1208], [590, 1216], [656, 1210], [697, 1235], [732, 1210], [762, 1206], [789, 1224], [816, 1208], [840, 1224], [867, 1200], [906, 1227], [961, 1194], [992, 1222], [1023, 1210], [1050, 1233], [1076, 1216], [1109, 1231], [1128, 1216], [1154, 1229], [1214, 1216], [1233, 1231], [1272, 1229], [1286, 1243], [1350, 1198], [1362, 1198], [1409, 1227], [1430, 1224], [1477, 1257], [1458, 1257], [1430, 1272], [1356, 1270], [1345, 1263], [1309, 1292], [1272, 1266], [1222, 1280], [1212, 1270], [1146, 1261], [1124, 1280], [1109, 1282], [1081, 1278], [1048, 1257], [1029, 1268], [959, 1268], [947, 1259], [916, 1288], [904, 1288], [877, 1265], [824, 1278], [783, 1261], [729, 1263], [707, 1280], [688, 1280], [668, 1263], [643, 1274], [598, 1261], [563, 1282], [538, 1263], [477, 1266], [430, 1257], [409, 1274], [345, 1276], [319, 1257], [270, 1276], [257, 1265], [230, 1272], [214, 1261], [163, 1259]], "baseline": [[161, 1251], [272, 1255], [292, 1259], [1424, 1257], [1478, 1258]]}}, {"prediction": "\u0645\u0639 \u0634\u0631\u0647\u0647 \u0639\u0644\u0649 \u0627\u0644\u062f\u0646\u064a\u0627\u060c \u0648\u0642\u0644\u0629 \u0648\u0631\u0639\u0647 \u0648\u0645\u0628\u0627\u0644\u0627\u062a\u0647 \u0628\u0627\u0644\u062d\u0644\u0627\u0644 \u0648\u0627\u0644\u062d\u0631\u0627\u0645\u060c \u0641\u0627\u062a\u0647\u0645\u0648\u0647 \u0639\u0644\u0649 \u0639\u0644\u0645\u0647", "cuts": [[1287, 1287], [1265, 1265], [1232, 1237], [1221, 1221], [1193, 1199], [1172, 1172], [1155, 1155], [1128, 1133], [1111, 1111], [1095, 1095], [1078, 1078], [1046, 1051], [1035, 1040], [1024, 1024], [1007, 1013], [991, 991], [980, 980], [964, 969], [953, 953], [931, 936], [920, 920], [898, 898], [881, 887], [870, 870], [843, 849], [832, 832], [810, 816], [788, 788], [772, 772], [745, 750], [734, 734], [712, 712], [695, 695], [684, 684], [668, 673], [657, 662], [646, 646], [630, 630], [602, 613], [597, 597], [586, 586], [575, 575], [547, 547], [526, 531], [515, 520], [498, 498], [460, 471], [449, 454], [432, 438], [422, 427], [400, 400], [378, 383], [361, 361], [345, 345], [328, 328], [307, 312], [296, 296], [285, 285], [274, 274], [257, 257], [235, 235], [219, 219], [197, 197], [170, 175], [159, 159], [137, 137], [126, 126], [82, 93], [71, 71], [55, 55], [33, 38], [22, 22]], "confidences": [0.9999666213989258, 0.9999809265136719, 0.9999946355819702, 0.9999746084213257, 0.9999938011169434, 0.9999914169311523, 0.9090831279754639, 0.9998230338096619, 0.9976154565811157, 0.9993928670883179, 0.9999699592590332, 0.9991900324821472, 0.9999030828475952, 0.999919056892395, 0.9999741315841675, 0.983220100402832, 0.9999629259109497, 0.9999812841415405, 0.6795229315757751, 0.9999853372573853, 0.9986742734909058, 0.9999790191650391, 0.9995899796485901, 0.9987137317657471, 0.9991518259048462, 0.9978784322738647, 0.9999507665634155, 0.9689961075782776, 0.9987661838531494, 0.9999948740005493, 0.581307590007782, 0.9999948740005493, 0.9670306444168091, 0.9999184608459473, 0.9983538389205933, 0.9998610019683838, 0.9507318735122681, 0.9808312058448792, 0.9999997615814209, 0.9999678134918213, 0.9999990463256836, 0.9445680975914001, 0.9999923706054688, 0.9999840259552002, 0.9999967813491821, 0.9999991655349731, 0.9999967813491821, 0.9999933242797852, 0.9997896552085876, 0.9999817609786987, 0.9999405145645142, 0.9997538924217224, 0.9999948740005493, 0.9972209930419922, 0.9999998807907104, 0.9999997615814209, 0.9762755036354065, 0.9999904632568359, 0.9999934434890747, 1.0, 0.9999963045120239, 0.9913928508758545, 0.9357872009277344, 0.9999316930770874, 1.0, 0.9693567752838135, 0.9998525381088257, 0.9999986886978149, 0.9999988079071045, 0.9999994039535522, 0.9999954700469971, 0.9990108013153076], "line": {"boundary": [[167, 1327], [171, 1296], [189, 1300], [208, 1282], [255, 1313], [292, 1282], [335, 1307], [348, 1300], [403, 1305], [436, 1284], [473, 1303], [497, 1300], [512, 1286], [538, 1303], [555, 1302], [582, 1284], [621, 1309], [651, 1284], [686, 1288], [705, 1303], [731, 1286], [766, 1313], [803, 1286], [840, 1288], [859, 1305], [896, 1305], [904, 1313], [941, 1300], [998, 1307], [1017, 1290], [1033, 1288], [1058, 1290], [1091, 1313], [1116, 1290], [1161, 1298], [1177, 1288], [1218, 1313], [1251, 1288], [1290, 1311], [1302, 1302], [1339, 1303], [1376, 1280], [1407, 1307], [1446, 1307], [1463, 1335], [1430, 1370], [1405, 1368], [1380, 1344], [1347, 1360], [1323, 1360], [1288, 1335], [1233, 1362], [1216, 1362], [1196, 1346], [1148, 1348], [1126, 1358], [1095, 1339], [1068, 1358], [1007, 1339], [986, 1356], [953, 1356], [908, 1339], [886, 1356], [844, 1356], [832, 1344], [775, 1341], [744, 1356], [732, 1344], [637, 1346], [625, 1339], [598, 1354], [555, 1341], [536, 1356], [516, 1352], [504, 1362], [465, 1337], [454, 1344], [428, 1342], [419, 1352], [387, 1342], [366, 1356], [331, 1331], [276, 1356], [261, 1354], [241, 1337], [202, 1344], [169, 1339]], "baseline": [[169, 1329], [200, 1327], [249, 1333], [461, 1333], [500, 1337], [1335, 1335], [1378, 1337], [1397, 1341], [1465, 1337]]}}, {"prediction": "\u0648\u062f\u064a\u0646\u0647.", "cuts": [[103, 106], [84, 84], [68, 68], [53, 53], [42, 42], [27, 27]], "confidences": [0.9999996423721313, 0.9999984502792358, 0.9997429251670837, 0.9999673366546631, 0.99901282787323, 0.9717188477516174], "line": {"boundary": [[1343, 1411], [1350, 1391], [1387, 1374], [1444, 1383], [1483, 1415], [1462, 1415], [1432, 1438], [1345, 1418]], "baseline": [[1345, 1413], [1419, 1413], [1484, 1417]]}}, {"prediction": "\u0648\u0645\u062a\u0649 \u0631\u0627\u0654\u064a\u062a\u0645 \u0639\u0627\u0644\u0645\u0627 \u0641\u064a \u0639\u0642\u0644\u0647 \u0633\u062e\u0627\u0641\u0629\u060c \u0648\u0641\u064a \u0646\u0638\u0631\u0647 \u0642\u0635\u0648\u0631\u060c \u064a\u0636\u0639 \u0627\u0644\u0627\u0654\u0634\u064a\u0627\u0621 - \u063a\u0627\u0644\u0628\u0627- \u0641\u064a", "cuts": [[1207, 1207], [1188, 1188], [1169, 1176], [1157, 1157], [1126, 1132], [1120, 1120], [1101, 1101], [1095, 1095], [1089, 1089], [1076, 1076], [1057, 1057], [1033, 1045], [1026, 1026], [1008, 1008], [995, 995], [977, 977], [958, 964], [939, 945], [933, 933], [914, 921], [890, 896], [871, 871], [852, 852], [840, 840], [821, 821], [802, 809], [790, 790], [753, 753], [734, 740], [722, 722], [709, 709], [690, 690], [672, 678], [666, 666], [647, 647], [628, 628], [597, 603], [591, 591], [566, 566], [547, 554], [535, 535], [510, 516], [504, 504], [485, 485], [454, 454], [435, 435], [417, 417], [398, 404], [392, 392], [367, 373], [330, 330], [311, 317], [305, 305], [286, 292], [280, 280], [268, 274], [261, 261], [236, 236], [218, 224], [205, 212], [187, 193], [174, 174], [156, 162], [143, 143], [124, 124], [112, 112], [100, 100], [87, 93], [68, 68], [50, 56], [37, 37], [25, 25]], "confidences": [0.9859687089920044, 0.9289069175720215, 0.9999728202819824, 0.9997435212135315, 0.9999949932098389, 0.9999815225601196, 0.999997615814209, 0.9999803304672241, 0.9999959468841553, 0.7412486672401428, 0.9978557229042053, 0.9999803304672241, 0.9999420642852783, 0.9999202489852905, 0.999981164932251, 0.9999773502349854, 0.9999395608901978, 0.9999784231185913, 0.9947001934051514, 0.9998576641082764, 0.9994139671325684, 0.9998432397842407, 0.9999716281890869, 0.9998818635940552, 0.6902597546577454, 0.999955415725708, 0.9993599057197571, 0.9993149042129517, 0.9999468326568604, 0.9852787256240845, 0.9907258749008179, 0.9999005794525146, 0.997058629989624, 0.9781426191329956, 0.8489178419113159, 0.8708791732788086, 0.9999767541885376, 0.9941668510437012, 0.966301441192627, 0.9999921321868896, 0.9917047023773193, 0.9999977350234985, 0.999850869178772, 0.589336633682251, 0.9973306655883789, 0.9999990463256836, 0.9893065094947815, 0.999998927116394, 0.9999986886978149, 0.9999996423721313, 0.999987006187439, 0.9929701685905457, 0.9999936819076538, 0.9999984502792358, 0.9898472428321838, 0.9999828338623047, 0.9971639513969421, 0.965297281742096, 0.9999982118606567, 0.9999784231185913, 0.9949177503585815, 0.9999829530715942, 0.9999810457229614, 0.9888534545898438, 0.9037330150604248, 0.9999834299087524, 0.9911187291145325, 0.9999808073043823, 0.9999874830245972, 0.9999430179595947, 0.9984645843505859, 0.9996715784072876], "line": {"boundary": [[173, 1489], [177, 1456], [192, 1440], [214, 1452], [253, 1422], [269, 1436], [300, 1438], [321, 1456], [352, 1457], [376, 1440], [399, 1452], [430, 1430], [467, 1442], [485, 1459], [508, 1459], [524, 1444], [557, 1463], [567, 1456], [631, 1463], [658, 1442], [695, 1459], [727, 1438], [750, 1444], [771, 1463], [795, 1442], [834, 1461], [861, 1442], [896, 1442], [914, 1444], [933, 1463], [964, 1463], [988, 1442], [1000, 1442], [1039, 1456], [1054, 1471], [1113, 1428], [1140, 1448], [1155, 1442], [1187, 1456], [1200, 1469], [1257, 1434], [1296, 1471], [1321, 1454], [1370, 1459], [1393, 1496], [1360, 1512], [1337, 1500], [1313, 1516], [1284, 1506], [1231, 1506], [1216, 1524], [1183, 1500], [1130, 1502], [1107, 1495], [1077, 1522], [1060, 1520], [1039, 1498], [978, 1500], [964, 1493], [927, 1500], [838, 1493], [787, 1520], [775, 1520], [752, 1496], [697, 1512], [676, 1495], [625, 1502], [614, 1512], [578, 1508], [567, 1496], [547, 1508], [532, 1502], [508, 1522], [487, 1522], [461, 1498], [423, 1495], [387, 1506], [374, 1493], [319, 1483], [263, 1506], [235, 1487], [214, 1489], [187, 1516], [175, 1516]], "baseline": [[175, 1491], [370, 1483], [403, 1487], [483, 1489], [502, 1493], [1216, 1491], [1276, 1496], [1339, 1495], [1395, 1498]]}}, {"prediction": "\u063a\u064a\u0631 \u0645\u0648\u0627\u0636\u0639\u0647\u0627\u060c \u0641\u0627\u062a\u0647\u0645\u0648\u0647 \u0639\u0644\u0649 \u0627\u0633\u062a\u0646\u0628\u0627\u0637\u0647 \u0648\u0639\u0644\u0645\u0647 \u0648\u0631\u0627\u0654\u064a\u0647\u060c \u0648\u0644\u0627 \u062a\u0642\u0644\u062f\u0648\u0647", "cuts": [[967, 967], [946, 946], [929, 935], [908, 913], [902, 902], [886, 886], [865, 870], [848, 848], [816, 816], [794, 794], [778, 778], [767, 767], [746, 751], [740, 740], [724, 729], [713, 713], [697, 697], [675, 675], [659, 659], [638, 638], [621, 627], [605, 605], [589, 589], [573, 573], [540, 546], [535, 535], [519, 519], [497, 497], [481, 481], [470, 475], [459, 465], [438, 438], [421, 421], [400, 405], [389, 394], [367, 367], [351, 351], [330, 330], [313, 313], [292, 297], [281, 281], [265, 265], [243, 249], [238, 238], [232, 232], [222, 222], [200, 205], [184, 189], [178, 178], [151, 157], [140, 146], [124, 130], [119, 119], [103, 103], [86, 86], [70, 70], [49, 49], [27, 27]], "confidences": [0.9998652935028076, 0.5277007222175598, 0.9999955892562866, 0.9999880790710449, 0.9999693632125854, 0.9982054233551025, 0.9999960660934448, 0.8161394596099854, 0.9999929666519165, 0.9999971389770508, 0.9999768733978271, 0.9989445805549622, 0.9983943104743958, 0.835041344165802, 0.9998206496238708, 0.9999831914901733, 0.9942564368247986, 0.999908447265625, 0.9996181726455688, 0.5019206404685974, 0.9998630285263062, 0.9985489249229431, 0.9094343781471252, 0.9999619722366333, 0.999971866607666, 0.9999991655349731, 0.9999998807907104, 0.9989573955535889, 0.9999986886978149, 0.9999964237213135, 0.9999872446060181, 0.916301965713501, 0.9883456230163574, 0.9999982118606567, 0.9999991655349731, 0.9999978542327881, 0.6548713445663452, 0.936079204082489, 0.9999892711639404, 0.9997774958610535, 0.9999997615814209, 0.9538446664810181, 0.9998297691345215, 0.9999701976776123, 0.9999457597732544, 0.9999912977218628, 0.9999996423721313, 0.999927282333374, 0.9924691319465637, 0.9999955892562866, 0.9999499320983887, 0.9999344348907471, 0.993513286113739, 0.999993085861206, 0.9991191029548645, 0.9999837875366211, 0.9999768733978271, 0.9998847246170044], "line": {"boundary": [[481, 1563], [489, 1533], [522, 1532], [547, 1506], [561, 1506], [571, 1516], [580, 1508], [600, 1518], [631, 1516], [658, 1539], [684, 1533], [707, 1510], [744, 1537], [766, 1539], [785, 1530], [795, 1537], [814, 1518], [875, 1541], [898, 1518], [933, 1520], [980, 1539], [1005, 1520], [1027, 1541], [1052, 1518], [1093, 1541], [1157, 1541], [1189, 1522], [1208, 1522], [1226, 1537], [1249, 1522], [1263, 1535], [1298, 1541], [1335, 1524], [1354, 1541], [1374, 1539], [1391, 1553], [1432, 1526], [1465, 1565], [1417, 1592], [1387, 1594], [1370, 1578], [1352, 1596], [1321, 1578], [1280, 1578], [1257, 1588], [1222, 1571], [1187, 1576], [1175, 1588], [1142, 1578], [1115, 1590], [1089, 1569], [1039, 1592], [1021, 1590], [1005, 1574], [957, 1576], [939, 1588], [922, 1572], [879, 1571], [865, 1584], [847, 1586], [834, 1574], [768, 1571], [754, 1584], [731, 1588], [662, 1571], [641, 1588], [600, 1565], [557, 1584], [534, 1574], [506, 1584], [483, 1572]], "baseline": [[483, 1565], [651, 1565], [779, 1571], [1362, 1569], [1417, 1576], [1466, 1567]]}}, {"prediction": "\u0648\u0645\u062a\u0649 \u0631\u0627\u0654\u064a\u062a\u0645 \u0627\u0644\u0639\u0627\u0644\u0645 \u0644\u0627 \u064a\u062a\u0645\u0645 \u0635\u0644\u0627\u062a\u0647 \u0627\u0644\u0645\u0641\u0631\u0648\u0636\u0629 \u060c \u0648\u0644\u0627 \u064a\u0637\u0645\u064a\u0654\u0646 \u0641\u064a \u0631\u0643\u0648\u0639\u0647\u0627", "cuts": [[1209, 1209], [1187, 1187], [1176, 1176], [1159, 1159], [1109, 1115], [1098, 1098], [1081, 1087], [1076, 1076], [1070, 1070], [1059, 1059], [1042, 1042], [1004, 1015], [998, 998], [987, 993], [970, 970], [954, 954], [943, 943], [920, 920], [887, 893], [871, 876], [859, 865], [826, 837], [821, 821], [804, 804], [787, 793], [765, 765], [726, 737], [710, 710], [676, 682], [665, 671], [649, 649], [638, 638], [604, 610], [593, 599], [582, 582], [566, 566], [549, 549], [532, 532], [510, 510], [482, 488], [455, 455], [444, 444], [438, 438], [399, 410], [394, 394], [366, 372], [355, 360], [322, 333], [316, 316], [288, 294], [272, 272], [255, 255], [250, 250], [233, 238], [194, 205], [183, 183], [172, 172], [122, 133], [111, 111], [94, 94], [72, 72], [50, 50], [28, 28], [11, 11]], "confidences": [0.999711811542511, 0.9999942779541016, 0.9999964237213135, 0.9999276399612427, 0.9999908208847046, 0.9999871253967285, 0.9998900890350342, 0.9831660985946655, 0.9998063445091248, 0.999962329864502, 0.9997965693473816, 0.9999898672103882, 0.99998939037323, 0.9999788999557495, 0.9994357228279114, 0.9999730587005615, 0.7908631563186646, 0.9956527948379517, 0.9997575879096985, 0.9996732473373413, 0.9956046342849731, 0.999534010887146, 0.9465213418006897, 0.9551798105239868, 0.9997909665107727, 0.974587619304657, 0.9998075366020203, 0.999998927116394, 0.9999898672103882, 0.9993416666984558, 0.9999842643737793, 0.999870777130127, 0.9998094439506531, 0.9999909400939941, 0.9999817609786987, 0.9999905824661255, 0.7308676242828369, 0.9999252557754517, 0.9999912977218628, 0.9999830722808838, 0.9993329644203186, 0.9937452077865601, 0.9980292916297913, 0.9999990463256836, 0.9999905824661255, 0.9998887777328491, 0.9999932050704956, 0.9999985694885254, 0.9999996423721313, 0.9999998807907104, 0.9999985694885254, 0.8817663788795471, 0.9893107414245605, 0.9999843835830688, 0.9999984502792358, 0.9999974966049194, 0.9999927282333374, 0.9999198913574219, 0.9998809099197388, 0.9999985694885254, 0.9999926090240479, 0.9989744424819946, 0.9999724626541138, 0.7024262547492981], "line": {"boundary": [[169, 1647], [173, 1594], [185, 1606], [210, 1604], [222, 1613], [259, 1594], [296, 1629], [335, 1596], [370, 1621], [403, 1602], [424, 1608], [442, 1592], [481, 1615], [508, 1592], [532, 1592], [571, 1625], [602, 1596], [617, 1596], [629, 1608], [647, 1602], [678, 1617], [701, 1598], [723, 1610], [744, 1596], [777, 1621], [799, 1604], [838, 1600], [857, 1617], [877, 1611], [900, 1633], [959, 1598], [992, 1613], [1009, 1598], [1037, 1598], [1066, 1625], [1093, 1596], [1122, 1610], [1146, 1596], [1185, 1623], [1241, 1590], [1284, 1631], [1329, 1606], [1354, 1617], [1368, 1613], [1407, 1648], [1382, 1650], [1360, 1666], [1333, 1654], [1309, 1670], [1272, 1652], [1253, 1666], [1212, 1660], [1193, 1676], [1163, 1650], [1101, 1652], [1070, 1676], [1040, 1648], [1021, 1658], [998, 1647], [978, 1664], [933, 1664], [920, 1678], [885, 1650], [822, 1658], [779, 1647], [705, 1654], [686, 1668], [582, 1647], [543, 1662], [506, 1647], [491, 1647], [475, 1660], [415, 1650], [397, 1666], [380, 1666], [354, 1647], [327, 1672], [315, 1672], [288, 1647], [265, 1660], [247, 1650], [230, 1662], [200, 1650], [185, 1660], [173, 1654]], "baseline": [[171, 1648], [1204, 1647], [1249, 1648], [1270, 1654], [1408, 1650]]}}, {"prediction": "\u0648\u0633\u062c\u0648\u062f\u0647\u0627\u060c \u0648\u0644\u0627 \u064a\u062d\u0636\u0631 \u0645\u0639 \u0642\u0631\u0627\u0621\u062a\u0647 \u0641\u064a\u0647\u0627 \u0628\u0627\u0644\u062e\u0634\u0648\u0639 \u0648\u0627\u0644\u062d\u0636\u0648\u0631\u060c \u0648\u0627\u0644\u062a\u062f\u0628\u0631 \u0648\u0627\u0644\u062a\u0631\u062a\u064a\u0644", "cuts": [[1292, 1292], [1267, 1267], [1231, 1231], [1213, 1213], [1194, 1194], [1170, 1170], [1152, 1158], [1139, 1139], [1109, 1121], [1097, 1103], [1072, 1079], [1060, 1066], [1030, 1042], [1024, 1024], [999, 999], [975, 975], [944, 944], [908, 914], [896, 896], [877, 877], [841, 847], [829, 829], [817, 817], [798, 798], [786, 786], [768, 768], [756, 756], [725, 731], [713, 713], [701, 701], [689, 689], [670, 670], [640, 652], [634, 634], [622, 622], [609, 609], [585, 585], [561, 561], [536, 536], [512, 512], [475, 481], [469, 469], [451, 451], [439, 439], [414, 414], [390, 390], [360, 360], [335, 335], [317, 317], [286, 293], [274, 274], [256, 262], [250, 250], [238, 238], [225, 225], [201, 201], [189, 189], [152, 165], [146, 146], [128, 128], [116, 122], [104, 104], [91, 91], [79, 79], [61, 61], [49, 49]], "confidences": [0.9998243451118469, 0.9999920129776001, 0.9999985694885254, 0.9999624490737915, 0.9999974966049194, 0.9999874830245972, 0.9999926090240479, 0.9999836683273315, 0.999990701675415, 0.9999855756759644, 0.99998939037323, 0.9999979734420776, 0.9996892213821411, 0.9999874830245972, 0.9997640252113342, 0.9999427795410156, 0.9993060827255249, 0.9446314573287964, 0.7036338448524475, 0.9967960715293884, 0.9986769556999207, 0.9841193556785583, 0.9994528889656067, 0.9409279823303223, 0.9999997615814209, 0.9999942779541016, 0.9934862852096558, 0.9999960660934448, 0.9999674558639526, 0.9067964553833008, 0.9972527623176575, 0.999915599822998, 0.9999898672103882, 0.9999929666519165, 0.9999849796295166, 0.9999780654907227, 0.9999867677688599, 0.999346911907196, 0.9999964237213135, 0.9825494289398193, 0.9995606541633606, 0.9960000514984131, 0.9999972581863403, 0.9731088280677795, 0.9996663331985474, 0.5042705535888672, 1.0, 0.9998852014541626, 0.9999935626983643, 0.9747305512428284, 0.7328495383262634, 0.9999992847442627, 0.9982652068138123, 0.9998162388801575, 1.0, 0.9999856948852539, 0.8613400459289551, 0.9995511174201965, 0.9720507264137268, 0.9999809265136719, 0.9999556541442871, 0.9989655017852783, 0.9860791563987732, 0.9992793202400208, 0.9793171882629395, 0.9999933242797852], "line": {"boundary": [[161, 1732], [165, 1686], [194, 1670], [237, 1684], [270, 1670], [319, 1707], [387, 1670], [409, 1674], [442, 1699], [461, 1686], [514, 1693], [532, 1678], [561, 1687], [588, 1670], [633, 1701], [656, 1684], [678, 1695], [701, 1680], [740, 1684], [752, 1674], [764, 1674], [787, 1695], [810, 1676], [830, 1689], [861, 1678], [885, 1699], [908, 1682], [945, 1678], [955, 1687], [980, 1678], [1003, 1699], [1046, 1693], [1070, 1713], [1115, 1682], [1175, 1695], [1200, 1674], [1226, 1674], [1261, 1703], [1296, 1676], [1348, 1695], [1440, 1693], [1469, 1726], [1456, 1726], [1432, 1746], [1389, 1734], [1347, 1748], [1321, 1732], [1280, 1732], [1270, 1725], [1233, 1744], [1193, 1725], [1171, 1742], [1142, 1730], [1074, 1748], [1060, 1736], [1033, 1758], [1007, 1754], [982, 1730], [949, 1748], [927, 1730], [904, 1734], [886, 1725], [851, 1744], [822, 1742], [803, 1725], [779, 1744], [760, 1730], [703, 1732], [668, 1756], [653, 1754], [627, 1730], [612, 1740], [584, 1728], [518, 1730], [489, 1742], [438, 1725], [411, 1740], [395, 1728], [364, 1728], [352, 1740], [325, 1742], [306, 1726], [286, 1740], [253, 1728], [228, 1744], [216, 1736], [179, 1744], [165, 1732]], "baseline": [[163, 1734], [214, 1732], [255, 1725], [364, 1726], [397, 1723], [419, 1723], [438, 1726], [1419, 1725], [1471, 1728]]}}, {"prediction": "\u0641\u0627\u062a\u0647\u0645\u0648\u0647 \u0628\u0642\u0633\u0627\u0648\u0629 \u0627\u0644\u0642\u0644\u0628\u060c \u0648\u0628\u0639\u062f\u0647 \u0639\u0646 \u0627\u0644\u0631\u0628 \u0639\u0632 \u0648\u062c\u0644.", "cuts": [[683, 683], [668, 673], [659, 659], [644, 644], [620, 620], [601, 606], [582, 582], [563, 567], [558, 558], [543, 543], [524, 524], [500, 500], [490, 490], [466, 466], [452, 452], [442, 447], [433, 433], [418, 418], [399, 404], [375, 375], [346, 346], [327, 332], [317, 322], [298, 303], [284, 284], [269, 269], [245, 245], [226, 231], [212, 212], [188, 192], [163, 168], [159, 159], [149, 149], [135, 135], [106, 106], [72, 77], [63, 63], [53, 58], [48, 48], [43, 43], [38, 38], [29, 34], [19, 19]], "confidences": [0.9739862084388733, 0.9999884366989136, 0.9997276663780212, 0.9999849796295166, 0.9990725517272949, 0.9999985694885254, 0.9983306527137756, 0.9980822801589966, 0.9998204112052917, 0.997825026512146, 0.9833168387413025, 0.9999716281890869, 0.999618411064148, 0.9978547692298889, 0.6419629454612732, 0.9951518774032593, 0.9999998807907104, 1.0, 0.9999918937683105, 0.9991405010223389, 0.9999971389770508, 1.0, 1.0, 0.9999991655349731, 0.9999963045120239, 0.9999998807907104, 0.9999983310699463, 0.999997615814209, 0.9999990463256836, 0.9999960660934448, 0.9999496936798096, 0.9999961853027344, 0.9999966621398926, 0.9999440908432007, 0.9999513626098633, 0.9999853372573853, 0.9999990463256836, 0.9999951124191284, 0.9946951270103455, 0.9999998807907104, 0.9999974966049194, 0.9999954700469971, 0.9960901141166687], "line": {"boundary": [[764, 1799], [766, 1769], [783, 1752], [812, 1746], [849, 1775], [879, 1767], [898, 1750], [929, 1769], [974, 1765], [986, 1773], [1025, 1762], [1048, 1771], [1103, 1767], [1122, 1777], [1150, 1752], [1189, 1752], [1233, 1767], [1253, 1754], [1272, 1773], [1288, 1758], [1304, 1758], [1321, 1775], [1384, 1775], [1426, 1756], [1463, 1783], [1469, 1802], [1415, 1810], [1401, 1822], [1368, 1812], [1354, 1824], [1327, 1812], [1306, 1822], [1292, 1810], [1253, 1810], [1231, 1822], [1206, 1806], [1146, 1810], [1130, 1824], [1093, 1806], [1072, 1820], [1048, 1820], [1035, 1808], [994, 1810], [980, 1802], [935, 1826], [912, 1808], [886, 1822], [851, 1822], [830, 1804], [785, 1812], [766, 1806]], "baseline": [[766, 1801], [846, 1801], [867, 1804], [1470, 1804]]}}, {"prediction": "\u0648\u0645\u062a\u0649 \u0648\u062c\u062f\u062a\u0645 \u0627\u0644\u0639\u0627\u0644\u0645 \u0644\u0627 \u0645\u0639\u0627\u0645\u0644\u0629 \u0644\u0647 \u0645\u0639 \u0631\u0628\u0647 \u0639\u0632 \u0648\u062c\u0644 \u060c \u062a\u0638\u0647\u0631 \u0639\u0644\u064a\u0647 \u0628\u0647\u062c\u062a\u0647\u0627 \u0648\u0627\u0654\u0646\u0648\u0627\u0631\u0647\u0627", "cuts": [[1236, 1236], [1214, 1214], [1197, 1197], [1186, 1186], [1142, 1153], [1131, 1136], [1103, 1103], [1086, 1086], [1064, 1064], [1042, 1042], [1014, 1020], [1009, 1009], [998, 998], [981, 981], [964, 964], [953, 953], [931, 937], [898, 909], [887, 892], [876, 881], [853, 859], [842, 842], [820, 820], [804, 809], [792, 792], [770, 770], [759, 759], [732, 743], [726, 726], [709, 709], [682, 687], [671, 671], [648, 648], [621, 626], [610, 610], [593, 593], [576, 576], [554, 560], [543, 543], [532, 538], [526, 526], [521, 521], [515, 515], [504, 510], [499, 499], [493, 493], [471, 477], [460, 460], [438, 443], [421, 421], [399, 405], [371, 377], [355, 355], [338, 338], [327, 327], [310, 310], [283, 288], [277, 277], [260, 260], [233, 233], [211, 211], [200, 200], [177, 183], [155, 166], [150, 150], [133, 133], [122, 127], [116, 116], [105, 105], [83, 89], [72, 72], [50, 50], [33, 33]], "confidences": [0.9999624490737915, 0.9999873638153076, 0.9999958276748657, 0.9999871253967285, 0.9999827146530151, 0.9999901056289673, 0.999958872795105, 0.519729733467102, 0.9999949932098389, 0.990899920463562, 0.9998747110366821, 0.9999719858169556, 0.9997586607933044, 0.9992938041687012, 0.9987748265266418, 0.9998395442962646, 0.9999606609344482, 0.9999958276748657, 0.999937891960144, 0.9987975358963013, 0.9998902082443237, 0.9642751216888428, 0.9995637536048889, 0.9999383687973022, 0.9973805546760559, 0.9987190961837769, 0.524466335773468, 0.9997130036354065, 0.9221267104148865, 0.9419159293174744, 0.9999997615814209, 0.9999914169311523, 0.9996623992919922, 0.999992847442627, 0.9999985694885254, 0.999937891960144, 0.9998610019683838, 0.9999767541885376, 0.9999998807907104, 0.8760433197021484, 0.9968647360801697, 0.9999954700469971, 0.999546229839325, 0.9998810291290283, 0.656394362449646, 0.9994986057281494, 0.9998539686203003, 0.9695175290107727, 0.9999977350234985, 0.9999958276748657, 0.9999986886978149, 0.9999974966049194, 0.9999914169311523, 0.9999973773956299, 0.9956705570220947, 0.9975166320800781, 0.9996503591537476, 0.9999966621398926, 0.9999432563781738, 0.9999326467514038, 0.960350751876831, 0.9999790191650391, 0.9999817609786987, 0.9995361566543579, 0.9995700716972351, 0.9970190525054932, 0.9999990463256836, 0.9999368190765381, 0.7645747065544128, 0.9985374212265015, 0.9979060888290405, 0.9999816417694092, 0.990431010723114], "line": {"boundary": [[144, 1867], [146, 1841], [161, 1828], [196, 1843], [214, 1828], [233, 1838], [263, 1816], [290, 1841], [315, 1826], [329, 1840], [399, 1840], [423, 1853], [473, 1828], [516, 1861], [559, 1828], [598, 1832], [614, 1845], [658, 1822], [693, 1845], [713, 1840], [746, 1847], [758, 1859], [775, 1845], [808, 1845], [820, 1855], [849, 1828], [871, 1840], [900, 1828], [920, 1838], [935, 1828], [957, 1847], [978, 1847], [1000, 1828], [1021, 1826], [1052, 1851], [1079, 1828], [1107, 1841], [1132, 1830], [1161, 1855], [1193, 1836], [1249, 1851], [1265, 1845], [1286, 1863], [1327, 1840], [1368, 1847], [1407, 1882], [1376, 1886], [1364, 1898], [1347, 1898], [1337, 1888], [1298, 1904], [1276, 1886], [1261, 1900], [1189, 1890], [1165, 1910], [1142, 1888], [1087, 1886], [1052, 1908], [1031, 1886], [1007, 1888], [994, 1877], [961, 1890], [898, 1890], [869, 1875], [853, 1888], [826, 1880], [781, 1910], [752, 1888], [717, 1896], [690, 1875], [668, 1888], [635, 1888], [606, 1873], [584, 1888], [524, 1900], [499, 1880], [460, 1896], [432, 1880], [409, 1894], [376, 1884], [329, 1894], [302, 1875], [276, 1894], [253, 1884], [237, 1896], [196, 1894], [148, 1869]], "baseline": [[146, 1869], [194, 1875], [323, 1873], [345, 1878], [499, 1882], [555, 1880], [590, 1873], [859, 1875], [900, 1871], [923, 1877], [1037, 1878], [1056, 1882], [1276, 1880], [1354, 1888], [1408, 1884]]}}, {"prediction": "\u0648\u0633\u0643\u064a\u0646\u062a\u0647\u0627\u060c \u0645\u0646 \u062a\u0644\u0627\u0648\u0629 \u0648\u0635\u064a\u0627\u0645 \u0648\u0642\u064a\u0627\u0645\u060c \u0641\u0627\u0639\u0644\u0645\u0648\u0627 \u0627\u0654\u0646\u0647 \u0642\u0644\u064a\u0644 \u0627\u0644\u0646\u0635\u064a\u0628 \u0645\u0646 \u062b\u0645\u0631\u0629 \u0627\u0644\u0639\u0644\u0645 \u0627\u0655\u0630 \u062b\u0645\u0631\u0629", "cuts": [[1298, 1298], [1273, 1278], [1243, 1243], [1228, 1228], [1213, 1213], [1203, 1203], [1188, 1188], [1173, 1173], [1158, 1158], [1138, 1143], [1128, 1133], [1108, 1108], [1082, 1087], [1072, 1072], [1057, 1062], [1047, 1052], [1027, 1027], [1007, 1007], [987, 992], [977, 977], [947, 947], [922, 922], [907, 907], [892, 892], [872, 877], [862, 862], [842, 842], [827, 827], [817, 822], [797, 802], [782, 782], [762, 767], [757, 757], [742, 742], [727, 727], [707, 707], [687, 687], [667, 672], [651, 651], [636, 641], [626, 631], [621, 621], [616, 616], [601, 601], [581, 586], [571, 571], [556, 561], [546, 546], [531, 536], [501, 506], [491, 496], [481, 486], [471, 471], [451, 451], [426, 426], [396, 401], [366, 371], [356, 356], [331, 331], [306, 311], [296, 301], [281, 281], [261, 261], [246, 246], [226, 231], [216, 221], [205, 210], [190, 190], [175, 175], [150, 155], [135, 135], [125, 130], [115, 120], [110, 110], [90, 95], [80, 80], [65, 65], [45, 45], [30, 30]], "confidences": [0.9999417066574097, 0.9999945163726807, 0.9999974966049194, 0.999985933303833, 0.9999626874923706, 0.99998939037323, 0.9999910593032837, 0.9999959468841553, 0.756621778011322, 0.9993665814399719, 0.9999896287918091, 0.9500626921653748, 0.9996504783630371, 0.9999996423721313, 0.9999978542327881, 0.9999961853027344, 0.9998416900634766, 0.9999421834945679, 0.9999947547912598, 0.9550725221633911, 0.999901294708252, 0.999994158744812, 0.99993896484375, 0.9998705387115479, 0.9994296431541443, 0.9963602423667908, 0.9768261909484863, 0.9845020174980164, 0.9991129040718079, 0.9998034834861755, 0.9999189376831055, 0.9999998807907104, 0.9999986886978149, 0.9999935626983643, 0.9999996423721313, 0.9999974966049194, 0.999970555305481, 0.9999955892562866, 0.9999997615814209, 1.0, 0.9998965263366699, 0.9999996423721313, 0.9999357461929321, 0.7053399085998535, 0.9999959468841553, 0.9999908208847046, 0.9990590214729309, 0.9999946355819702, 0.9999847412109375, 0.9993951320648193, 0.9999172687530518, 0.9937519431114197, 0.9999966621398926, 0.9998257756233215, 0.514997661113739, 0.9999998807907104, 0.9999972581863403, 0.9961175918579102, 0.996932864189148, 0.9999979734420776, 0.9999998807907104, 0.9999791383743286, 0.9999998807907104, 0.9914675951004028, 0.9999983310699463, 0.9999983310699463, 0.9999997615814209, 0.9999996423721313, 0.9999963045120239, 0.9999997615814209, 0.9999983310699463, 0.9999831914901733, 1.0, 0.9999969005584717, 0.999922513961792, 0.6258425116539001, 0.9992828965187073, 0.9999939203262329, 0.9998599290847778], "line": {"boundary": [[152, 1951], [155, 1912], [163, 1906], [192, 1923], [214, 1904], [255, 1904], [288, 1923], [311, 1902], [329, 1912], [354, 1902], [368, 1912], [387, 1908], [409, 1925], [440, 1904], [460, 1921], [520, 1919], [543, 1929], [575, 1927], [617, 1904], [635, 1906], [653, 1921], [670, 1906], [684, 1912], [695, 1904], [734, 1919], [762, 1898], [795, 1908], [814, 1925], [840, 1906], [859, 1917], [873, 1908], [896, 1908], [914, 1923], [931, 1923], [955, 1906], [984, 1908], [1015, 1929], [1040, 1908], [1070, 1929], [1085, 1921], [1122, 1925], [1140, 1912], [1159, 1921], [1194, 1908], [1230, 1925], [1278, 1929], [1306, 1908], [1327, 1921], [1393, 1906], [1417, 1927], [1438, 1925], [1471, 1953], [1434, 1976], [1387, 1964], [1368, 1976], [1343, 1966], [1319, 1976], [1278, 1958], [1253, 1978], [1230, 1980], [1212, 1964], [1150, 1974], [1128, 1960], [1115, 1974], [1085, 1964], [1056, 1976], [1042, 1970], [1029, 1984], [1005, 1970], [951, 1972], [933, 1984], [902, 1958], [826, 1962], [795, 1976], [775, 1958], [742, 1962], [727, 1955], [658, 1980], [631, 1960], [534, 1974], [506, 1955], [463, 1976], [440, 1958], [387, 1972], [368, 1955], [313, 1960], [284, 1980], [233, 1955], [185, 1972], [155, 1955]], "baseline": [[153, 1953], [830, 1951], [916, 1953], [941, 1962], [961, 1958], [1419, 1960], [1473, 1955]]}}, {"prediction": "\u0627\u0644\u0639\u0644\u0645 \u0627\u0644\u0645\u0639\u0627\u0645\u0644\u0629 \u060c \u0648\u0642\u0644\u064a\u0644 \u0627\u0644\u0646\u0635\u064a\u0628 \u0645\u0646 \u0627\u0644\u0645\u062d\u0628\u0629 \u0648\u0627\u0644\u062e\u0634\u064a\u0629\u060c \u0648( \u0627\u0655\u0646\u0645\u0627 \u064a\u062e\u0634\u0649 \u0627\u0644\u0644\u0647 \u0645\u0646 \u0639\u0628\u0627\u062f\u0648", "cuts": [[1303, 1303], [1290, 1290], [1272, 1272], [1260, 1260], [1235, 1235], [1211, 1217], [1198, 1204], [1186, 1192], [1168, 1168], [1149, 1149], [1131, 1137], [1118, 1118], [1106, 1106], [1088, 1088], [1081, 1081], [1075, 1075], [1051, 1057], [1038, 1038], [1014, 1014], [1002, 1002], [989, 989], [977, 977], [940, 946], [928, 934], [916, 922], [903, 903], [885, 885], [860, 860], [836, 836], [793, 799], [780, 780], [756, 762], [725, 731], [719, 719], [707, 713], [688, 688], [664, 664], [645, 645], [633, 633], [602, 608], [590, 596], [571, 578], [559, 565], [535, 535], [516, 516], [492, 492], [479, 479], [461, 461], [436, 442], [424, 424], [399, 399], [381, 381], [369, 375], [363, 363], [356, 356], [338, 338], [326, 326], [313, 320], [301, 301], [289, 289], [270, 270], [246, 252], [221, 227], [215, 215], [203, 209], [191, 191], [178, 184], [160, 166], [154, 154], [135, 135], [104, 111], [92, 92], [80, 80], [61, 61], [49, 49], [37, 37]], "confidences": [0.9999939203262329, 0.9962407350540161, 0.6148149967193604, 0.9995049238204956, 0.9999679327011108, 0.9998955726623535, 0.9999966621398926, 0.9999812841415405, 0.9999535083770752, 0.9999920129776001, 0.9999990463256836, 0.9946582913398743, 0.7702367901802063, 0.9436891078948975, 0.9403108358383179, 0.874081015586853, 0.9999908208847046, 0.9999887943267822, 0.999915599822998, 0.9999622106552124, 0.9999816417694092, 0.9999862909317017, 0.9991334080696106, 0.9935688972473145, 0.9999759197235107, 0.9654875993728638, 0.9996823072433472, 0.9990022778511047, 0.564159095287323, 0.9998770952224731, 0.9986807703971863, 0.9983581900596619, 0.9994006156921387, 0.9421390295028687, 0.9977754950523376, 0.9938074946403503, 0.9999985694885254, 0.9493253231048584, 0.9998681545257568, 0.9999979734420776, 0.9999966621398926, 0.9997482895851135, 0.9999991655349731, 0.9999998807907104, 0.9024093747138977, 0.8440074920654297, 0.9990659356117249, 0.9999818801879883, 0.98737633228302, 0.9999896287918091, 0.9998379945755005, 0.9999996423721313, 0.9999951124191284, 0.9999514818191528, 0.9996600151062012, 0.971716046333313, 0.9997962117195129, 0.999996542930603, 0.9999830722808838, 0.9999959468841553, 0.9999998807907104, 0.999964714050293, 0.9999979734420776, 0.5567030906677246, 0.999757707118988, 0.8596031069755554, 0.998282790184021, 0.7934605479240417, 0.9993875026702881, 0.9990580677986145, 0.9998717308044434, 0.9960359930992126, 0.998126208782196, 0.9995434880256653, 0.5599195957183838, 0.99636310338974], "line": {"boundary": [[152, 2046], [165, 2009], [218, 1982], [247, 2005], [269, 1986], [300, 2005], [331, 1984], [358, 1982], [378, 1999], [401, 1982], [440, 1980], [458, 1993], [483, 1982], [512, 1995], [530, 1986], [555, 2009], [582, 2019], [610, 1993], [637, 2003], [647, 1995], [686, 1999], [701, 1990], [740, 2013], [771, 1995], [789, 2011], [816, 2015], [851, 1993], [879, 2015], [894, 2007], [927, 2009], [939, 2019], [947, 2011], [966, 2019], [1011, 2017], [1050, 1992], [1089, 2017], [1109, 1999], [1124, 2003], [1138, 1995], [1157, 1997], [1191, 2025], [1222, 1997], [1237, 1995], [1255, 2005], [1270, 1997], [1300, 2015], [1329, 1993], [1362, 2019], [1391, 1995], [1409, 2005], [1430, 1995], [1463, 2021], [1469, 2040], [1438, 2056], [1395, 2056], [1378, 2071], [1347, 2054], [1230, 2058], [1196, 2048], [1171, 2068], [1144, 2058], [1093, 2071], [1072, 2052], [966, 2066], [939, 2044], [898, 2070], [865, 2052], [799, 2052], [777, 2066], [750, 2044], [725, 2064], [680, 2050], [623, 2062], [584, 2044], [567, 2060], [541, 2060], [528, 2071], [497, 2066], [483, 2054], [436, 2058], [424, 2050], [389, 2062], [368, 2060], [356, 2048], [309, 2048], [294, 2060], [259, 2058], [251, 2050], [228, 2060], [202, 2050], [187, 2058], [155, 2054]], "baseline": [[153, 2048], [491, 2048], [604, 2042], [816, 2042], [883, 2046], [1470, 2042]]}}, {"prediction": "\u0627\u0644\u0639\u0644\u0645\u0648\u0627).", "cuts": [[358, 365], [352, 352], [338, 338], [325, 325], [311, 311], [291, 291], [270, 277], [250, 250], [34, 41]], "confidences": [0.9943757057189941, 0.7291178107261658, 0.9799628257751465, 0.7520625591278076, 0.6908395886421204, 0.9932884573936462, 0.9999109506607056, 0.48956286907196045, 0.997548520565033], "line": {"boundary": [[1099, 2132], [1101, 2107], [1132, 2075], [1212, 2099], [1228, 2081], [1245, 2089], [1280, 2077], [1309, 2097], [1352, 2064], [1378, 2079], [1446, 2077], [1465, 2097], [1471, 2126], [1442, 2144], [1384, 2146], [1343, 2165], [1327, 2165], [1304, 2144], [1284, 2153], [1247, 2140], [1208, 2157], [1193, 2144], [1134, 2149], [1107, 2132]], "baseline": [[1101, 2134], [1473, 2128]]}}], "regions": {"text": [[[0, 0], [1655, 0], [1655, 2339], [0, 2339]]]}} \ No newline at end of file +["lines": [{"prediction": "238", "cuts": [[39, 39], [65, 65], [96, 96]], "confidences": [0.9999942779541016, 0.9999815225601196, 0.9999997615814209], "line": {"boundary": [[1364, 228], [1366, 191], [1382, 177], [1444, 177], [1473, 202], [1479, 226], [1452, 241], [1380, 239], [1368, 231]], "baseline": [[1366, 230], [1440, 230], [1480, 227]], "id": "214dff1d-3871-4604-8438-18c0ae332b0d", "type": "baselines"}}, {"prediction": "\u0648\u0644\u0627 \u064a\u0639\u062a\u0646\u064a \u0628\u0642\u0636\u0627\u0621 \u062d\u0627\u062c\u0629 \u0645\u0636\u0637\u0631 \u0645\u0644\u0647\u0648\u0641\u060c \u0641\u0627\u0655\u0646\u0647 \u0645\u0646 \u0627\u0644\u0642\u0627\u0633\u064a\u0629 \u0642\u0644\u0648\u0628\u0647\u0645\u060c \u0627\u0644\u0645\u0639\u0631\u0636\u064a\u0646 \u0639\u0646", "cuts": [[1291, 1291], [1266, 1273], [1254, 1260], [1235, 1241], [1223, 1223], [1204, 1210], [1192, 1192], [1179, 1179], [1167, 1167], [1136, 1136], [1124, 1124], [1111, 1111], [1086, 1086], [1055, 1061], [1043, 1043], [1018, 1024], [999, 999], [981, 981], [956, 962], [937, 937], [913, 919], [900, 900], [875, 881], [838, 838], [819, 819], [795, 801], [782, 782], [770, 770], [751, 751], [732, 732], [714, 714], [670, 670], [646, 658], [639, 639], [627, 633], [621, 621], [615, 615], [602, 602], [577, 584], [565, 565], [540, 540], [515, 521], [503, 509], [497, 497], [484, 484], [459, 466], [447, 447], [428, 428], [410, 410], [385, 391], [379, 379], [360, 360], [348, 348], [329, 329], [310, 310], [286, 292], [273, 273], [248, 255], [242, 242], [230, 230], [211, 211], [192, 192], [174, 174], [149, 155], [118, 118], [99, 106], [68, 75], [56, 56], [31, 31]], "confidences": [0.9918296933174133, 0.9999946355819702, 0.9999945163726807, 0.9999985694885254, 0.9999771118164062, 0.9999879598617554, 0.9987093210220337, 0.9987457990646362, 0.9997931122779846, 0.999688982963562, 0.9999251365661621, 0.587072491645813, 0.9999754428863525, 0.99989914894104, 0.9920154809951782, 0.9999525547027588, 0.9999878406524658, 0.9997779726982117, 0.9999771118164062, 0.9966135621070862, 0.9999631643295288, 0.9998565912246704, 0.9999505281448364, 0.9981879591941833, 0.8379210829734802, 0.9999366998672485, 0.9966161847114563, 0.9895930290222168, 0.9999837875366211, 0.9999969005584717, 0.8669806122779846, 0.974852979183197, 0.9999998807907104, 0.9999897480010986, 0.9999994039535522, 0.9228501319885254, 0.9999959468841553, 0.9999970197677612, 0.9999334812164307, 0.9997324347496033, 0.9999984502792358, 0.9917014241218567, 0.9999793767929077, 1.0, 0.9999970197677612, 0.9935858845710754, 0.9999581575393677, 0.8294568061828613, 0.9999990463256836, 0.9999179840087891, 0.9999973773956299, 0.9964264035224915, 0.9883396625518799, 0.7585844993591309, 0.962102472782135, 0.8603035807609558, 0.9998493194580078, 0.9999865293502808, 0.9999082088470459, 0.9998868703842163, 0.999321699142456, 0.9835970997810364, 0.8893908262252808, 0.9999939203262329, 0.9998082518577576, 0.9999799728393555, 0.9999867677688599, 0.5397316217422485, 0.8847019076347351], "line": {"boundary": [[167, 337], [177, 290], [216, 288], [233, 302], [255, 288], [278, 300], [309, 282], [329, 300], [356, 296], [387, 276], [415, 294], [452, 300], [465, 292], [487, 298], [508, 278], [534, 278], [547, 288], [567, 278], [590, 298], [614, 278], [651, 276], [680, 298], [693, 292], [736, 302], [769, 280], [797, 282], [814, 298], [840, 296], [865, 280], [890, 298], [914, 278], [951, 292], [984, 265], [996, 265], [1021, 288], [1056, 280], [1072, 292], [1097, 282], [1113, 296], [1132, 280], [1179, 306], [1204, 282], [1228, 294], [1265, 284], [1298, 311], [1331, 290], [1362, 296], [1428, 280], [1491, 335], [1463, 335], [1446, 350], [1401, 335], [1380, 350], [1347, 341], [1313, 362], [1288, 345], [1270, 350], [1259, 341], [1212, 339], [1189, 346], [1171, 331], [1093, 348], [1066, 335], [1003, 339], [978, 362], [941, 337], [910, 348], [853, 352], [808, 327], [783, 346], [732, 327], [701, 350], [682, 350], [664, 335], [602, 335], [569, 346], [547, 327], [499, 348], [458, 343], [440, 358], [405, 333], [345, 335], [327, 348], [292, 337], [245, 350], [226, 335], [171, 348]], "baseline": [[169, 339], [269, 335], [304, 329], [1288, 329], [1368, 331], [1389, 335], [1492, 337]], "id": "f8a7011f-22a0-4aa6-b9e8-dbf130eec847", "type": "baselines"}}, {"prediction": "\u0631\u0628\u0647\u0645\u060c \u0642\u0644\u0628\u0647 \u0628\u0639\u064a\u062f \u0645\u0646 \u0627\u0644\u0627\u0653\u062e\u0631\u0629\u060c \u0645\u062a\u0639\u0644\u0642 \u0628\u0627\u0644\u062f\u0646\u064a\u0627\u060c\u0639\u0644\u0645\u0647 \u062f\u0643\u0627\u0646\u0647\u060c \u0648\u064a\u062a\u0627\u0654\u0643\u0644 \u0648\u064a\u0631\u062a\u0632\u0642\u060c \u0648\u0644\u0627 \u064a\u0639\u0627\u0645\u0644", "cuts": [[1302, 1302], [1280, 1280], [1269, 1269], [1241, 1241], [1224, 1224], [1201, 1207], [1190, 1190], [1179, 1179], [1168, 1168], [1151, 1151], [1134, 1140], [1123, 1123], [1106, 1106], [1089, 1095], [1078, 1078], [1050, 1055], [1039, 1039], [1016, 1016], [988, 994], [982, 982], [966, 971], [954, 960], [949, 949], [926, 926], [909, 909], [893, 893], [876, 876], [853, 859], [842, 842], [831, 831], [808, 808], [797, 797], [780, 780], [747, 758], [741, 741], [730, 730], [719, 719], [702, 702], [685, 685], [674, 674], [662, 662], [646, 646], [629, 629], [612, 612], [589, 589], [573, 573], [550, 556], [539, 539], [522, 522], [500, 505], [494, 494], [477, 477], [460, 466], [443, 449], [432, 432], [415, 415], [404, 404], [393, 393], [382, 387], [376, 376], [354, 359], [320, 331], [314, 314], [292, 292], [281, 281], [264, 264], [253, 253], [230, 230], [196, 202], [180, 185], [168, 174], [146, 152], [135, 140], [112, 118], [107, 107], [84, 90], [73, 73], [56, 56], [39, 45]], "confidences": [0.999483585357666, 0.9989271759986877, 0.9999568462371826, 0.9999847412109375, 0.9999631643295288, 0.9999524354934692, 0.9999338388442993, 0.9999792575836182, 0.9999816417694092, 0.9999970197677612, 0.9998041987419128, 0.9913369417190552, 0.9999105930328369, 0.9999866485595703, 0.9997265934944153, 0.9998457431793213, 0.9999898672103882, 0.9658181667327881, 0.9998805522918701, 0.9999202489852905, 0.9999966621398926, 0.9999680519104004, 0.6977024078369141, 0.681415855884552, 0.9998468160629272, 0.999790370464325, 0.998950719833374, 0.9989398121833801, 0.9998928308486938, 0.9993798732757568, 0.9988120794296265, 0.9999618530273438, 0.978205144405365, 0.9999402761459351, 0.9968582391738892, 0.9899797439575195, 0.9999998807907104, 0.9918898940086365, 1.0, 0.6370328664779663, 0.9123554229736328, 0.9965428709983826, 0.9990838766098022, 0.6425076127052307, 0.999977707862854, 0.9999984502792358, 0.9999998807907104, 0.9999978542327881, 0.5949130058288574, 0.9999992847442627, 0.998330295085907, 0.863884687423706, 0.9997449517250061, 0.9999986886978149, 0.9999614953994751, 0.9998691082000732, 0.9999910593032837, 0.7401690483093262, 0.9997996687889099, 0.999140739440918, 0.9999452829360962, 0.9999958276748657, 0.9999629259109497, 0.999981164932251, 0.9999955892562866, 0.9999098777770996, 0.9996079802513123, 0.8690406680107117, 0.9999758005142212, 0.9999970197677612, 0.9999972581863403, 0.999855637550354, 0.9997760653495789, 0.9999991655349731, 0.9998064637184143, 0.9998438358306885, 0.8385894894599915, 0.9975091218948364, 0.9999769926071167], "line": {"boundary": [[161, 405], [165, 380], [189, 358], [206, 364], [224, 356], [257, 372], [276, 354], [298, 352], [335, 378], [384, 356], [393, 366], [413, 360], [432, 376], [467, 372], [477, 380], [516, 345], [528, 345], [571, 374], [600, 380], [639, 345], [670, 345], [713, 374], [754, 356], [787, 372], [805, 358], [844, 368], [875, 356], [904, 382], [941, 356], [961, 372], [982, 368], [1007, 382], [1029, 364], [1060, 378], [1087, 354], [1113, 350], [1150, 378], [1165, 372], [1204, 383], [1222, 368], [1284, 383], [1325, 358], [1384, 382], [1452, 380], [1465, 391], [1471, 415], [1440, 428], [1403, 424], [1385, 440], [1352, 413], [1317, 426], [1294, 413], [1274, 426], [1257, 419], [1231, 426], [1204, 409], [1169, 430], [1091, 409], [1044, 428], [1009, 407], [941, 415], [923, 426], [902, 419], [885, 424], [871, 413], [814, 424], [799, 411], [771, 422], [705, 405], [686, 415], [600, 407], [580, 422], [528, 411], [504, 428], [489, 428], [471, 415], [458, 422], [384, 424], [337, 405], [315, 422], [274, 405], [255, 421], [218, 409], [191, 426], [165, 419]], "baseline": [[163, 407], [1251, 407], [1325, 409], [1378, 417], [1473, 417]], "id": "625a33bb-09c9-4b52-a700-b196e8fd21dc", "type": "baselines"}}, {"prediction": "\u0627\u0644\u0644\u0647 \u0628\u0639\u0644\u0645\u0647 \u0627\u0655\u0644\u0627 \u0642\u0644\u064a\u0644\u0627\u060c \u064a\u0633\u0643\u062a \u0639\u0646 \u0627\u0644\u062d\u0642 \u062e\u0634\u064a\u0629 \u0633\u0642\u0648\u0637 \u0645\u0646\u0632\u0644\u062a\u0647\u060c \u0648\u064a\u0645\u0627\u0644\u064a\u0654 \u0639\u0644\u0649 \u0627\u0644\u0628\u0627\u0637\u0644 \u0637\u0644\u0628\u0627", "cuts": [[1285, 1285], [1274, 1280], [1263, 1263], [1258, 1258], [1236, 1241], [1230, 1230], [1214, 1214], [1198, 1198], [1181, 1181], [1159, 1165], [1143, 1148], [1137, 1137], [1126, 1132], [1116, 1121], [1105, 1110], [1088, 1094], [1077, 1077], [1066, 1066], [1050, 1050], [1034, 1039], [1023, 1028], [1006, 1006], [990, 995], [979, 979], [957, 962], [935, 935], [897, 897], [869, 875], [853, 853], [831, 831], [804, 809], [798, 798], [787, 787], [760, 760], [744, 744], [711, 716], [694, 694], [667, 667], [645, 645], [629, 629], [612, 618], [596, 602], [574, 574], [558, 558], [525, 525], [498, 503], [487, 492], [476, 476], [459, 465], [443, 448], [432, 432], [421, 421], [399, 405], [383, 388], [377, 377], [355, 355], [339, 339], [323, 323], [312, 312], [295, 295], [279, 284], [262, 268], [252, 252], [230, 230], [213, 213], [180, 186], [175, 175], [164, 170], [153, 153], [137, 142], [120, 120], [104, 104], [66, 71], [49, 55], [38, 38], [22, 22], [11, 16]], "confidences": [0.6223369240760803, 0.999977707862854, 0.9996325969696045, 0.9976205229759216, 0.9999915361404419, 0.9999442100524902, 0.9999935626983643, 0.9986664056777954, 0.9931642413139343, 0.999994158744812, 0.99997878074646, 0.9995927214622498, 0.9999953508377075, 0.9999521970748901, 0.9999961853027344, 0.9999990463256836, 0.9987659454345703, 0.9996764659881592, 0.9999899864196777, 0.9999946355819702, 0.9998693466186523, 0.9999508857727051, 0.9999494552612305, 0.9999960660934448, 0.9999898672103882, 0.9999620914459229, 0.9999936819076538, 0.9995738863945007, 0.9997065663337708, 0.9967997074127197, 0.9996978044509888, 0.9999291896820068, 0.9937999844551086, 0.7420834898948669, 0.9566183090209961, 0.9999990463256836, 0.9998831748962402, 0.9999995231628418, 0.8633337020874023, 0.9998383522033691, 1.0, 0.9999995231628418, 0.9999997615814209, 1.0, 0.9999963045120239, 0.9999927282333374, 0.9999871253967285, 0.9999827146530151, 0.9999991655349731, 0.9999961853027344, 0.9995644688606262, 0.9999992847442627, 0.9997718930244446, 0.999894380569458, 1.0, 0.9999994039535522, 0.9999990463256836, 0.9999983310699463, 1.0, 0.9998666048049927, 1.0, 0.9999922513961792, 0.9998834133148193, 0.9998831748962402, 0.9995386600494385, 0.9999641180038452, 0.9991496801376343, 0.9994356036186218, 0.9781442284584045, 0.9928722381591797, 0.9813116192817688, 0.9999994039535522, 0.9999991655349731, 0.999995231628418, 0.9999226331710815, 0.9999967813491821, 0.9934727549552917], "line": {"boundary": [[175, 477], [181, 422], [191, 432], [216, 430], [241, 454], [265, 432], [315, 438], [335, 430], [364, 456], [395, 432], [432, 458], [471, 432], [508, 456], [551, 458], [586, 448], [604, 432], [649, 450], [688, 432], [711, 454], [731, 438], [768, 454], [793, 434], [816, 450], [836, 440], [846, 450], [863, 442], [877, 454], [902, 430], [931, 450], [957, 436], [980, 454], [1000, 456], [1019, 446], [1037, 456], [1111, 434], [1134, 456], [1187, 426], [1212, 444], [1220, 436], [1251, 444], [1286, 436], [1315, 456], [1339, 456], [1356, 438], [1399, 458], [1424, 438], [1450, 440], [1467, 456], [1473, 481], [1440, 497], [1413, 495], [1393, 508], [1376, 497], [1319, 493], [1288, 506], [1257, 485], [1218, 504], [1165, 491], [1134, 502], [1122, 493], [1050, 495], [1031, 485], [996, 508], [978, 508], [961, 493], [931, 493], [906, 512], [867, 487], [807, 504], [783, 485], [732, 493], [721, 504], [666, 485], [621, 504], [555, 487], [538, 502], [481, 491], [450, 508], [421, 489], [378, 504], [356, 502], [341, 489], [313, 500], [298, 487], [261, 506], [245, 504], [226, 487], [179, 497]], "baseline": [[177, 479], [298, 477], [360, 485], [403, 487], [508, 487], [573, 483], [795, 483], [818, 487], [1475, 483]], "id": "3182e755-7ae8-4ba6-843e-b9c6556619d2", "type": "baselines"}}, {"prediction": "\u0644\u0644\u0631\u0641\u0639\u0629\u060c \u0641\u0645\u0627 \u0627\u0654\u0628\u0639\u062f \u0647\u0630\u0627 \u0639\u0646 \u0627\u0644\u0644\u0647 \u0648\u0639\u0646 \u0637\u0631\u064a\u0642\u0647\u060c \u0639\u0644\u0645\u0647 \u062d\u062c\u0629 \u0639\u0644\u064a\u0647.", "cuts": [[917, 917], [901, 901], [890, 890], [868, 868], [852, 852], [836, 836], [820, 820], [799, 804], [793, 793], [772, 772], [756, 756], [740, 745], [734, 734], [724, 729], [718, 718], [708, 708], [686, 691], [665, 670], [649, 654], [633, 633], [611, 611], [595, 600], [584, 584], [557, 557], [536, 536], [525, 531], [520, 520], [504, 504], [493, 499], [477, 482], [466, 472], [445, 445], [418, 423], [391, 397], [375, 375], [359, 359], [343, 343], [327, 327], [311, 311], [295, 295], [273, 279], [263, 263], [247, 247], [225, 225], [209, 209], [188, 193], [166, 166], [139, 139], [118, 118], [97, 102], [86, 86], [64, 70], [54, 54], [43, 43], [21, 27]], "confidences": [0.9995940327644348, 0.9446550607681274, 0.9981995820999146, 0.9999864101409912, 0.6075229644775391, 0.99992835521698, 0.9997560381889343, 0.9999634027481079, 0.9998078942298889, 0.9999945163726807, 0.9999508857727051, 0.9999924898147583, 0.9999949932098389, 0.9999926090240479, 0.9998032450675964, 0.9999107122421265, 0.9999817609786987, 0.9998050332069397, 0.9999833106994629, 0.9495548605918884, 0.9993699193000793, 0.9999070167541504, 0.9915083646774292, 0.999798595905304, 0.9999779462814331, 0.9999381303787231, 0.9971952438354492, 0.9972888231277466, 0.9999881982803345, 0.9999971389770508, 0.9995827078819275, 0.9999994039535522, 0.999994158744812, 0.9999692440032959, 0.9996156692504883, 0.5591697096824646, 0.9999910593032837, 0.9997571110725403, 0.8518497347831726, 0.9999833106994629, 0.9999939203262329, 0.9998816251754761, 0.9999935626983643, 0.9927089214324951, 0.9999922513961792, 0.996820330619812, 0.9999880790710449, 0.9995105266571045, 0.9999866485595703, 0.9999912977218628, 0.9999923706054688, 0.9999964237213135, 0.9993194341659546, 0.999998927116394, 0.9977816939353943], "line": {"boundary": [[547, 549], [551, 536], [600, 508], [629, 526], [649, 510], [680, 530], [703, 526], [723, 534], [773, 512], [814, 534], [853, 514], [883, 530], [908, 510], [933, 536], [945, 524], [1013, 532], [1035, 510], [1097, 534], [1165, 514], [1200, 537], [1214, 524], [1231, 532], [1265, 506], [1302, 530], [1321, 518], [1339, 534], [1358, 518], [1382, 530], [1391, 520], [1415, 528], [1424, 518], [1448, 516], [1465, 534], [1471, 559], [1417, 596], [1389, 575], [1337, 567], [1319, 575], [1274, 567], [1245, 582], [1233, 573], [1146, 573], [1128, 563], [1093, 586], [1076, 586], [1060, 571], [1021, 567], [998, 580], [974, 569], [957, 584], [939, 584], [918, 567], [888, 584], [814, 561], [803, 569], [742, 569], [725, 559], [666, 575], [635, 559], [592, 578], [549, 563]], "baseline": [[549, 551], [582, 557], [807, 555], [966, 565], [1473, 561]], "id": "8b76a51e-29ae-48ee-9ab9-b0761c9061d2", "type": "baselines"}}, {"prediction": "\u0648\u0645\u062a\u0649 \u0631\u0627\u0654\u064a\u062a\u0645 \u0627\u0644\u0639\u0627\u0644\u0645 \u0642\u0644\u064a\u0644 \u0627\u0644\u0648\u0631\u0639 \u0641\u064a \u0643\u0644\u0627\u0645\u0647 \u060c \u064a\u062a\u0643\u0644\u0645 \u0645\u062c\u0627\u0632\u0641\u0629\u060c \u0648\u064a\u0643\u0630\u0628 \u0627\u0654\u062d\u064a\u0627\u0646\u0627\u060c", "cuts": [[1223, 1223], [1201, 1201], [1190, 1190], [1173, 1173], [1129, 1140], [1118, 1118], [1101, 1107], [1096, 1096], [1090, 1090], [1079, 1079], [1062, 1062], [1029, 1040], [1024, 1024], [1013, 1018], [996, 996], [979, 979], [968, 968], [946, 952], [919, 924], [908, 908], [891, 891], [880, 880], [863, 869], [830, 836], [819, 825], [808, 813], [797, 802], [775, 775], [747, 747], [714, 719], [703, 703], [692, 692], [647, 659], [636, 636], [614, 614], [603, 609], [587, 587], [570, 570], [559, 559], [553, 553], [526, 531], [515, 515], [504, 504], [481, 481], [465, 465], [443, 448], [415, 421], [404, 404], [376, 376], [354, 360], [343, 343], [326, 326], [310, 310], [293, 293], [266, 271], [255, 255], [238, 238], [216, 216], [199, 199], [160, 166], [122, 127], [116, 116], [105, 111], [89, 89], [72, 72], [61, 61], [50, 50], [39, 39], [28, 28]], "confidences": [0.9989548921585083, 0.9999619722366333, 0.9992412328720093, 0.9541407823562622, 0.9999735355377197, 0.9940637946128845, 0.999988317489624, 0.9999865293502808, 0.9999904632568359, 0.550645649433136, 0.9999978542327881, 0.9999892711639404, 0.9996963739395142, 0.9999991655349731, 0.5165660977363586, 0.999988317489624, 0.9995940327644348, 0.9999626874923706, 0.9998881816864014, 0.9940359592437744, 0.9996926784515381, 0.9999793767929077, 0.9998898506164551, 0.9998297691345215, 0.9897773861885071, 0.9994788765907288, 0.9995355606079102, 0.9969993829727173, 0.9985607266426086, 0.9999980926513672, 0.9996448755264282, 0.9874195456504822, 0.9999774694442749, 0.9867774844169617, 0.9744981527328491, 0.9999960660934448, 0.9999808073043823, 0.9993176460266113, 0.9999256134033203, 0.9999924898147583, 0.9999164342880249, 0.9999569654464722, 0.9999525547027588, 0.9997047781944275, 0.9999884366989136, 0.9999561309814453, 0.9999027252197266, 0.999967098236084, 0.9966025352478027, 0.9999691247940063, 0.9973816275596619, 0.9003818035125732, 0.9999942779541016, 0.9996612071990967, 0.9999988079071045, 0.9940271377563477, 0.9999754428863525, 0.9999979734420776, 0.9999998807907104, 0.9994766116142273, 0.9999978542327881, 0.9999990463256836, 0.9999949932098389, 0.9999971389770508, 0.8546275496482849, 0.9999696016311646, 0.9999052286148071, 0.9918268918991089, 0.5608442425727844], "line": {"boundary": [[161, 631], [163, 592], [185, 573], [230, 604], [257, 580], [309, 610], [341, 586], [380, 584], [423, 615], [456, 588], [508, 588], [524, 604], [559, 604], [575, 619], [612, 586], [649, 586], [682, 619], [697, 606], [729, 606], [742, 594], [793, 592], [822, 619], [853, 592], [877, 612], [896, 600], [927, 610], [949, 590], [962, 590], [990, 613], [1013, 592], [1023, 598], [1039, 588], [1060, 592], [1083, 613], [1113, 588], [1140, 602], [1163, 588], [1196, 615], [1251, 582], [1294, 621], [1309, 619], [1327, 602], [1360, 612], [1378, 608], [1389, 619], [1395, 647], [1366, 660], [1341, 649], [1321, 662], [1302, 662], [1282, 647], [1270, 658], [1226, 652], [1200, 670], [1175, 647], [1120, 645], [1087, 668], [1062, 645], [1009, 666], [970, 645], [894, 672], [867, 649], [832, 670], [799, 643], [748, 651], [688, 641], [670, 656], [647, 645], [615, 645], [586, 666], [561, 641], [483, 654], [426, 633], [397, 654], [331, 643], [311, 656], [280, 631], [228, 654], [165, 637]], "baseline": [[163, 633], [651, 633], [674, 637], [729, 637], [818, 643], [857, 641], [890, 647], [912, 647], [931, 652], [968, 643], [1011, 641], [1245, 641], [1300, 647], [1345, 645], [1397, 649]], "id": "686e1fad-8441-4e7f-87de-30b56113fbae", "type": "baselines"}}, {"prediction": "\u0648\u064a\u0633\u062a\u0639\u0645\u0644 \u0627\u0644\u0647\u0632\u0644 \u0648\u0627\u0644\u0644\u0639\u0628\u060c \u0648\u064a\u0630\u0643\u0631 \u0627\u0644\u0645\u0631\u062f\u0627\u0646\u060c \u0648\u064a\u0645\u064a\u0644 \u0627\u0655\u0644\u064a\u0647\u0645\u060c \u0627\u0654\u0648 \u0631\u0627\u0654\u064a\u062a\u0645\u0648\u0647 \u0642\u0644\u064a\u0644 \u0627\u0644\u0648\u0631\u0639 \u0641\u064a", "cuts": [[1298, 1298], [1283, 1283], [1267, 1267], [1246, 1246], [1229, 1229], [1208, 1208], [1187, 1192], [1155, 1160], [1149, 1149], [1139, 1139], [1117, 1123], [1107, 1107], [1085, 1091], [1053, 1058], [1042, 1042], [1026, 1026], [1016, 1016], [1000, 1000], [984, 984], [952, 957], [925, 925], [903, 909], [893, 893], [877, 877], [861, 861], [839, 839], [818, 818], [797, 802], [786, 791], [775, 780], [759, 759], [738, 743], [722, 722], [706, 711], [695, 695], [668, 668], [647, 658], [641, 641], [620, 620], [604, 604], [588, 588], [572, 577], [545, 545], [535, 540], [524, 529], [519, 519], [508, 508], [492, 492], [470, 470], [449, 454], [433, 438], [428, 428], [417, 422], [412, 412], [385, 390], [369, 374], [353, 358], [347, 347], [342, 342], [331, 331], [315, 315], [299, 299], [278, 278], [257, 262], [246, 246], [235, 235], [219, 219], [203, 208], [176, 182], [166, 171], [155, 160], [144, 144], [123, 123], [96, 96], [70, 75], [59, 59], [43, 48]], "confidences": [0.9960930943489075, 0.9970471262931824, 0.9999514818191528, 0.9999713897705078, 0.9975488781929016, 0.9999634027481079, 0.9999896287918091, 0.9999924898147583, 0.9999784231185913, 0.9999960660934448, 0.9997754693031311, 0.9999520778656006, 0.9999797344207764, 0.9999464750289917, 0.999251663684845, 0.9999938011169434, 0.9999744892120361, 0.9904490113258362, 0.6805958151817322, 0.9993933439254761, 0.8523104190826416, 0.9999516010284424, 0.9954115748405457, 0.9966720342636108, 0.9986212253570557, 0.9991599321365356, 0.9994064569473267, 0.9998964071273804, 0.999936580657959, 0.9998494386672974, 0.9968574047088623, 0.9996702671051025, 0.999824583530426, 0.9998010993003845, 0.9999998807907104, 0.9999876022338867, 0.9999998807907104, 0.9999998807907104, 0.9999731779098511, 0.9999935626983643, 0.9999995231628418, 0.9998869895935059, 0.9999819993972778, 0.9999923706054688, 0.9999994039535522, 0.9997376799583435, 0.9999179840087891, 0.999994158744812, 0.9999123811721802, 0.9999874830245972, 1.0, 0.9014513492584229, 0.9999948740005493, 0.9999997615814209, 0.9999998807907104, 0.9998629093170166, 0.9677855372428894, 0.9999980926513672, 0.9946457147598267, 0.9999970197677612, 0.972520649433136, 0.999996542930603, 0.9999895095825195, 0.9999986886978149, 0.9621902108192444, 0.9999395608901978, 0.9999432563781738, 0.9999014139175415, 0.9999983310699463, 0.999998927116394, 0.999935507774353, 0.9999979734420776, 0.9999970197677612, 0.9999983310699463, 0.9999921321868896, 0.9999868869781494, 0.9999967813491821], "line": {"boundary": [[155, 727], [159, 701], [198, 666], [222, 680], [239, 672], [270, 682], [300, 660], [327, 682], [346, 664], [391, 664], [413, 682], [434, 686], [471, 676], [493, 656], [530, 684], [567, 656], [592, 680], [615, 688], [656, 664], [674, 666], [693, 684], [717, 668], [736, 686], [785, 682], [795, 690], [822, 670], [851, 668], [886, 690], [916, 666], [935, 670], [951, 686], [968, 672], [1005, 666], [1027, 686], [1052, 691], [1060, 684], [1103, 691], [1136, 666], [1169, 670], [1194, 690], [1220, 668], [1255, 682], [1280, 666], [1306, 686], [1325, 670], [1352, 690], [1376, 680], [1403, 691], [1446, 686], [1456, 719], [1442, 736], [1366, 725], [1345, 727], [1325, 742], [1311, 742], [1290, 725], [1233, 738], [1198, 723], [1177, 736], [1155, 725], [1120, 723], [1087, 736], [1056, 719], [1029, 734], [980, 723], [962, 736], [947, 736], [931, 723], [914, 723], [869, 736], [853, 723], [824, 727], [797, 719], [779, 732], [742, 725], [703, 740], [684, 728], [623, 728], [608, 742], [578, 719], [547, 732], [528, 725], [481, 732], [469, 723], [436, 732], [407, 717], [348, 736], [304, 721], [245, 744], [216, 727], [179, 736], [161, 727]], "baseline": [[157, 728], [187, 727], [270, 730], [313, 721], [403, 719], [460, 713], [526, 721], [900, 719], [1457, 721]], "id": "9328cfe4-3094-4550-8d35-93480796fc10", "type": "baselines"}}, {"prediction": "\u0627\u0644\u0645\u0627\u0654\u0643\u0644 \u0648\u0627\u0644\u0645\u0634\u0631\u0628\u060c \u0648\u0627\u0644\u0645\u062f\u062e\u0644 \u0648\u0627\u0644\u0645\u062e\u0631\u062c\u060c \u0644\u0627 \u064a\u0628\u0627\u0644\u064a \u0645\u0627 \u0627\u0654\u0643\u0644 \u062d\u0644\u0627\u0644\u0627 \u0643\u0627\u0646 \u0627\u0654\u0648 \u062d\u0631\u0627\u0645\u0627\u060c", "cuts": [[1298, 1304], [1291, 1291], [1273, 1273], [1254, 1260], [1248, 1248], [1242, 1242], [1217, 1223], [1180, 1192], [1173, 1173], [1155, 1155], [1142, 1142], [1124, 1124], [1105, 1105], [1080, 1080], [1049, 1049], [1018, 1018], [993, 1000], [987, 987], [969, 969], [956, 956], [937, 937], [919, 919], [888, 888], [869, 869], [826, 838], [820, 820], [795, 801], [788, 788], [770, 770], [739, 739], [720, 720], [695, 695], [671, 671], [646, 658], [633, 639], [621, 627], [596, 608], [590, 590], [577, 577], [565, 565], [553, 553], [540, 540], [503, 509], [490, 490], [472, 478], [453, 459], [441, 447], [435, 435], [428, 428], [404, 410], [366, 379], [348, 348], [329, 329], [310, 323], [298, 298], [286, 292], [255, 267], [248, 248], [224, 230], [211, 211], [180, 186], [174, 174], [168, 168], [155, 155], [124, 130], [106, 106], [87, 87], [68, 68], [56, 56], [37, 43], [25, 25]], "confidences": [0.9999150037765503, 0.9998385906219482, 0.999996542930603, 0.9999994039535522, 0.9999864101409912, 0.9999818801879883, 0.9999929666519165, 0.999998927116394, 0.9999581575393677, 0.9999958276748657, 0.9999880790710449, 0.9997920393943787, 0.9999971389770508, 0.9999898672103882, 0.9999960660934448, 0.9710601568222046, 0.996277391910553, 0.9998601675033569, 0.922956645488739, 0.9978814721107483, 0.999930739402771, 0.9997755885124207, 0.9413175582885742, 0.9998894929885864, 0.9993183612823486, 0.999767005443573, 0.9998904466629028, 0.6034071445465088, 0.9915944933891296, 0.999853253364563, 0.9953317046165466, 0.9986577033996582, 0.9640184640884399, 0.9999998807907104, 0.9999990463256836, 0.9999948740005493, 0.9999983310699463, 0.9997009038925171, 0.9999697208404541, 0.9999998807907104, 0.9997711777687073, 0.9742012023925781, 0.9999996423721313, 0.9999728202819824, 0.9999784231185913, 0.9999963045120239, 0.9999946355819702, 0.9767754077911377, 0.9991446733474731, 0.9999916553497314, 0.9999847412109375, 0.9995253086090088, 0.9999663829803467, 0.9999977350234985, 0.9770731925964355, 0.9999951124191284, 0.9999915361404419, 0.9999960660934448, 0.9701045751571655, 0.9999648332595825, 0.9999688863754272, 0.9998500347137451, 0.9999626874923706, 0.9992308616638184, 0.9999960660934448, 0.9513243436813354, 0.9999858140945435, 0.9998613595962524, 0.9998170733451843, 0.9996731281280518, 0.9003341794013977], "line": {"boundary": [[159, 783], [163, 748], [191, 732], [218, 740], [237, 758], [274, 766], [313, 730], [341, 750], [401, 744], [417, 730], [432, 730], [458, 746], [475, 742], [522, 769], [553, 742], [567, 746], [588, 732], [606, 748], [621, 742], [660, 775], [697, 742], [738, 762], [758, 744], [781, 740], [808, 760], [859, 766], [879, 748], [906, 764], [929, 742], [941, 742], [980, 771], [1013, 746], [1025, 756], [1068, 754], [1076, 762], [1103, 744], [1146, 773], [1161, 762], [1191, 771], [1212, 762], [1224, 769], [1243, 752], [1265, 764], [1292, 746], [1335, 775], [1362, 750], [1374, 754], [1401, 738], [1419, 754], [1430, 746], [1446, 750], [1465, 767], [1471, 793], [1448, 805], [1389, 803], [1360, 822], [1345, 820], [1329, 805], [1311, 816], [1288, 805], [1189, 816], [1152, 795], [1124, 814], [1101, 803], [1035, 799], [1007, 820], [994, 820], [974, 803], [947, 810], [935, 801], [885, 799], [857, 822], [834, 822], [797, 791], [779, 803], [758, 791], [738, 808], [721, 810], [707, 801], [676, 822], [643, 795], [617, 799], [606, 789], [569, 799], [551, 814], [534, 814], [510, 793], [460, 803], [403, 789], [389, 799], [350, 803], [329, 789], [294, 808], [265, 791], [222, 810], [204, 797], [171, 797], [163, 789]], "baseline": [[161, 785], [216, 791], [935, 789], [959, 795], [1472, 795]], "id": "e04b91bb-5bd9-4ec4-af42-6ae24b8437f2", "type": "baselines"}}, {"prediction": "\u0641\u0627\u062a\u0647\u0645\u0648\u0647 \u0639\u0644\u0649 \u0639\u0644\u0645\u0647 \u0648\u0639\u0644\u0649 \u062f\u064a\u0646\u0647\u060c \u0648\u0644\u0627 \u062a\u0642\u0644\u062f\u0648\u0647 \u0627\u0654\u0645\u0648\u0631\u0643\u0645\u060c \u0648\u0627\u062d\u0630\u0631\u0648\u0647 \u0627\u0654\u0646 \u064a\u0633\u0644\u0628\u0643\u0645 \u062f\u064a\u0646\u0643\u0645\u060c", "cuts": [[1304, 1304], [1293, 1293], [1283, 1283], [1266, 1266], [1245, 1245], [1228, 1228], [1207, 1207], [1185, 1191], [1169, 1169], [1147, 1147], [1131, 1131], [1099, 1104], [1082, 1082], [1066, 1066], [1044, 1044], [1028, 1028], [1001, 1012], [996, 996], [969, 969], [947, 947], [936, 936], [898, 904], [887, 887], [871, 871], [860, 860], [850, 850], [828, 828], [806, 812], [795, 801], [774, 779], [763, 768], [741, 747], [731, 731], [720, 720], [703, 703], [687, 687], [666, 666], [644, 644], [622, 628], [617, 617], [606, 612], [601, 601], [584, 584], [563, 563], [541, 541], [514, 514], [498, 498], [476, 482], [465, 465], [449, 449], [422, 422], [406, 406], [384, 384], [363, 363], [341, 341], [325, 330], [314, 319], [308, 308], [298, 298], [265, 276], [260, 260], [244, 244], [216, 216], [206, 206], [184, 184], [157, 162], [135, 141], [124, 124], [108, 108], [97, 97], [76, 76], [54, 54], [32, 32]], "confidences": [0.9997382760047913, 0.9998076558113098, 0.9998169541358948, 0.9999723434448242, 0.9999963045120239, 0.9998815059661865, 0.9978761672973633, 0.9999692440032959, 0.9999940395355225, 0.9997928738594055, 0.9998121857643127, 0.9998834133148193, 0.9999972581863403, 0.9986358284950256, 0.9999672174453735, 0.9999685287475586, 0.9999872446060181, 0.9996137022972107, 0.9999386072158813, 0.9998291730880737, 0.9996570348739624, 0.9997431635856628, 0.9989522695541382, 0.9978705644607544, 0.6706600189208984, 0.727662205696106, 0.9994146823883057, 0.9998109936714172, 0.9988961219787598, 0.9999992847442627, 0.9999957084655762, 0.9998549222946167, 0.7824696898460388, 0.9987309575080872, 0.9999728202819824, 0.9999747276306152, 0.9984544515609741, 0.9999949932098389, 0.9999949932098389, 0.9999567270278931, 0.9999990463256836, 0.9999997615814209, 1.0, 0.999970555305481, 0.9995998740196228, 0.9863488674163818, 0.9999908208847046, 0.9998534917831421, 0.989995002746582, 0.999998927116394, 0.9999957084655762, 0.9997010827064514, 0.9999995231628418, 0.9993435740470886, 0.9999997615814209, 0.9204366207122803, 0.9999145269393921, 0.9971621036529541, 0.9998090863227844, 0.9999971389770508, 0.9999977350234985, 0.999993085861206, 0.999996542930603, 0.9999635219573975, 0.9999977350234985, 0.9999561309814453, 0.9999858140945435, 0.9998942613601685, 0.9999914169311523, 0.9876537919044495, 0.9992417097091675, 0.9999997615814209, 0.9282093048095703], "line": {"boundary": [[152, 853], [171, 834], [189, 840], [228, 812], [284, 845], [329, 816], [413, 834], [446, 810], [489, 838], [516, 840], [545, 818], [563, 834], [588, 822], [619, 843], [633, 834], [649, 842], [666, 826], [688, 822], [713, 842], [754, 812], [789, 840], [832, 822], [881, 830], [920, 820], [951, 845], [962, 836], [1001, 832], [1027, 834], [1046, 851], [1081, 822], [1146, 845], [1163, 834], [1179, 840], [1204, 822], [1247, 853], [1282, 822], [1323, 847], [1335, 840], [1391, 845], [1409, 832], [1440, 828], [1462, 845], [1467, 869], [1401, 894], [1382, 881], [1348, 894], [1321, 871], [1269, 894], [1253, 894], [1226, 875], [1183, 881], [1152, 873], [1128, 890], [1095, 879], [1056, 894], [1033, 877], [1003, 888], [955, 871], [935, 888], [922, 888], [888, 867], [869, 879], [812, 879], [795, 888], [764, 869], [715, 890], [674, 877], [645, 898], [619, 875], [600, 888], [571, 871], [493, 886], [465, 869], [434, 881], [415, 875], [401, 884], [372, 877], [356, 886], [323, 875], [296, 896], [267, 875], [251, 884], [214, 875], [187, 896], [153, 867]], "baseline": [[153, 855], [228, 869], [1237, 867], [1269, 871], [1290, 867], [1309, 871], [1469, 871]], "id": "769b1e46-2664-49b4-b5ce-3849db9d0eb2", "type": "baselines"}}, {"prediction": "\u0628\u062a\u0647\u0648\u064a\u0646\u0647 \u0644\u0644\u0627\u0654\u0633\u064a\u0627\u0621 \u0627\u0644\u0635\u063a\u064a\u0631\u0629 \u0645\u0646 \u0627\u0644\u062d\u0631\u0627\u0645 \u0648\u0627\u0644\u0634\u0628\u0647\u0627\u062a\u060c \u064a\u0633\u0631\u0642 \u0628\u0630\u0644\u0643 \u0639\u0642\u0648\u0644\u0643\u0645\u060c \u0641\u064a\u0633\u062a\u062f\u0631\u062c\u0643\u0645", "cuts": [[1297, 1297], [1286, 1286], [1275, 1275], [1253, 1253], [1237, 1237], [1220, 1226], [1209, 1209], [1188, 1193], [1182, 1182], [1160, 1166], [1149, 1155], [1138, 1144], [1133, 1133], [1111, 1111], [1095, 1095], [1084, 1084], [1062, 1067], [1056, 1056], [1045, 1045], [1023, 1023], [991, 991], [974, 974], [958, 963], [941, 941], [919, 925], [914, 914], [887, 887], [859, 870], [854, 854], [843, 843], [815, 815], [799, 799], [783, 783], [766, 766], [744, 750], [739, 739], [717, 722], [706, 711], [690, 690], [668, 668], [651, 651], [629, 635], [607, 607], [580, 580], [558, 569], [553, 553], [536, 536], [509, 514], [493, 493], [454, 465], [449, 449], [438, 438], [416, 416], [405, 405], [356, 361], [345, 345], [328, 328], [306, 306], [290, 290], [268, 268], [246, 246], [224, 224], [203, 213], [197, 197], [186, 186], [164, 164], [148, 148], [131, 131], [109, 109], [82, 82], [55, 55], [27, 33]], "confidences": [0.9998189806938171, 0.9999316930770874, 0.9999616146087646, 0.9999963045120239, 0.9904739260673523, 0.9999723434448242, 0.9971580505371094, 0.9988645315170288, 0.9999966621398926, 0.9999974966049194, 0.9999973773956299, 0.9999791383743286, 0.9999858140945435, 0.9999887943267822, 0.5477295517921448, 0.9999678134918213, 0.9999643564224243, 0.8171812295913696, 0.9827367067337036, 0.9935495257377625, 0.9999936819076538, 0.999975323677063, 0.9999910593032837, 0.998406708240509, 0.9999222755432129, 0.9998999834060669, 0.9343510866165161, 0.9997228980064392, 0.7628433108329773, 0.6818570494651794, 0.950019121170044, 0.6418173313140869, 0.9943951368331909, 0.9706724882125854, 0.9999768733978271, 0.9994531273841858, 0.9995731711387634, 0.9997699856758118, 0.9988842606544495, 0.9997255206108093, 0.9999997615814209, 0.9999980926513672, 0.999735414981842, 0.9235820770263672, 0.9999998807907104, 0.9999927282333374, 0.9976077079772949, 0.9999990463256836, 0.8485846519470215, 0.9999995231628418, 0.9999078512191772, 0.9999904632568359, 0.999980092048645, 0.9999996423721313, 0.9956209063529968, 0.9999990463256836, 0.9995309114456177, 0.9999940395355225, 0.9999079704284668, 1.0, 0.9180242419242859, 0.9995797276496887, 0.9999831914901733, 0.9998835325241089, 0.9994398951530457, 0.988334059715271, 0.9999781847000122, 0.9999990463256836, 0.8295435309410095, 0.9995015859603882, 0.9999979734420776, 0.9999895095825195], "line": {"boundary": [[159, 947], [163, 923], [210, 894], [247, 920], [276, 906], [317, 920], [337, 902], [362, 916], [389, 918], [430, 892], [456, 908], [479, 900], [510, 920], [549, 896], [586, 896], [610, 918], [643, 898], [670, 921], [762, 912], [775, 900], [812, 921], [853, 898], [898, 925], [923, 902], [947, 918], [990, 898], [1019, 920], [1066, 920], [1087, 906], [1111, 921], [1136, 902], [1155, 920], [1183, 900], [1200, 902], [1218, 918], [1235, 902], [1259, 916], [1282, 896], [1298, 896], [1327, 900], [1347, 918], [1372, 912], [1391, 923], [1434, 912], [1473, 953], [1446, 970], [1428, 964], [1395, 972], [1343, 953], [1302, 962], [1276, 957], [1261, 968], [1216, 953], [1138, 957], [1099, 974], [1072, 953], [1037, 974], [1003, 957], [962, 957], [949, 970], [906, 980], [888, 964], [867, 968], [853, 957], [793, 968], [777, 957], [721, 953], [699, 966], [674, 957], [645, 968], [615, 957], [600, 966], [580, 955], [528, 957], [506, 945], [450, 968], [419, 953], [382, 976], [356, 953], [335, 966], [313, 957], [276, 955], [251, 966], [200, 955], [177, 976], [163, 974]], "baseline": [[161, 949], [241, 945], [664, 945], [705, 949], [729, 945], [822, 955], [923, 951], [1087, 951], [1107, 955], [1214, 945], [1362, 955], [1475, 954]], "id": "771803aa-5b21-4917-91a9-866b9583ac64", "type": "baselines"}}, {"prediction": "\u0645\u0646 \u062d\u064a\u062b \u0644\u0627 \u062a\u0639\u0644\u0645\u0648\u0646.", "cuts": [[343, 343], [316, 321], [294, 298], [276, 276], [258, 258], [230, 230], [199, 203], [185, 190], [172, 181], [158, 163], [149, 149], [136, 136], [118, 118], [95, 99], [77, 81], [54, 59], [32, 32]], "confidences": [0.9013516902923584, 0.9993886947631836, 0.9999945163726807, 0.9106606841087341, 0.9922044277191162, 0.9999985694885254, 0.999998927116394, 0.9999998807907104, 0.9999948740005493, 0.9999319314956665, 0.9999568462371826, 0.9999945163726807, 0.5355215072631836, 0.9999971389770508, 0.9999983310699463, 0.9999912977218628, 0.9786094427108765], "line": {"boundary": [[1109, 1025], [1146, 980], [1173, 997], [1189, 997], [1210, 976], [1231, 988], [1294, 976], [1309, 992], [1333, 984], [1348, 999], [1364, 992], [1395, 1007], [1409, 994], [1424, 1001], [1444, 997], [1473, 1027], [1454, 1029], [1411, 1056], [1380, 1031], [1347, 1046], [1300, 1029], [1274, 1036], [1263, 1025], [1241, 1036], [1191, 1036], [1171, 1050], [1118, 1025]], "baseline": [[1111, 1027], [1274, 1023], [1366, 1033], [1475, 1029]], "id": "fee112e6-da9c-4f6a-8aac-52b284d831d2", "type": "baselines"}}, {"prediction": "\u0648\u0645\u062a\u0649 \u0631\u0627\u0654\u064a\u062a\u0645 \u0627\u0644\u0639\u0627\u0644\u0645 \u064a\u0642\u0628\u0644 \u0627\u0644\u0647\u062f\u064a\u0629 \u0645\u0646 \u0627\u0644\u0645\u0633\u062a\u0641\u062a\u064a \u0648\u064a\u0641\u062a\u064a\u0647 \u0639\u0644\u0649 \u063a\u0631\u0636\u0647\u060c \u0648\u064a\u062f\u062e\u0644 \u0641\u064a", "cuts": [[1225, 1225], [1201, 1207], [1189, 1189], [1172, 1172], [1136, 1148], [1130, 1130], [1112, 1118], [1106, 1106], [1100, 1100], [1088, 1088], [1064, 1070], [1041, 1047], [1029, 1035], [1023, 1023], [1011, 1011], [993, 993], [981, 981], [957, 963], [934, 940], [928, 928], [916, 916], [898, 898], [880, 886], [850, 856], [838, 844], [827, 833], [815, 815], [797, 797], [779, 779], [761, 761], [737, 743], [726, 731], [702, 702], [678, 684], [666, 672], [654, 660], [636, 642], [613, 618], [595, 595], [583, 583], [565, 565], [553, 553], [517, 523], [505, 505], [488, 488], [476, 476], [458, 458], [446, 446], [434, 434], [410, 416], [393, 393], [375, 375], [363, 363], [327, 333], [309, 309], [291, 291], [268, 268], [238, 238], [220, 220], [196, 202], [184, 184], [167, 167], [155, 155], [125, 131], [101, 107], [59, 71], [54, 54], [36, 42]], "confidences": [0.9998977184295654, 0.9992066025733948, 0.9999544620513916, 0.9999667406082153, 0.9999969005584717, 0.9999951124191284, 0.9999479055404663, 0.8893346190452576, 0.9999771118164062, 0.9999749660491943, 0.9999734163284302, 0.9999407529830933, 0.9999978542327881, 0.888961911201477, 0.999985933303833, 0.9997301697731018, 0.9997461438179016, 0.9999942779541016, 0.9999889135360718, 0.9999947547912598, 0.7681607007980347, 0.9985297918319702, 0.9992446899414062, 0.9999771118164062, 0.9999252557754517, 0.9995318651199341, 0.9955322742462158, 0.9999263286590576, 0.9855678081512451, 0.8572127223014832, 0.9958655834197998, 0.9997792840003967, 0.8732478022575378, 0.9998902082443237, 0.9997214674949646, 0.9999871253967285, 0.999970555305481, 0.9999971389770508, 0.9999786615371704, 0.9999481439590454, 0.9997740387916565, 0.8626158833503723, 0.9999806880950928, 0.9998955726623535, 0.9999974966049194, 0.9999513626098633, 0.9972359538078308, 0.9995180368423462, 0.9999905824661255, 1.0, 0.9999990463256836, 0.9999991655349731, 0.9999984502792358, 0.9999990463256836, 0.9999250173568726, 0.9994828701019287, 0.9996166229248047, 0.995541512966156, 0.9999992847442627, 0.999984860420227, 0.9904344081878662, 0.9992062449455261, 0.9999986886978149, 0.9761358499526978, 0.999893069267273, 0.9999963045120239, 0.9997863173484802, 0.9999911785125732], "line": {"boundary": [[159, 1103], [163, 1081], [196, 1052], [224, 1072], [249, 1050], [348, 1073], [382, 1062], [393, 1070], [409, 1056], [432, 1068], [458, 1052], [487, 1079], [518, 1050], [561, 1075], [573, 1064], [623, 1052], [641, 1070], [684, 1081], [727, 1052], [760, 1073], [773, 1073], [799, 1050], [834, 1072], [849, 1066], [859, 1073], [883, 1070], [908, 1052], [927, 1066], [955, 1066], [980, 1050], [1007, 1072], [1027, 1052], [1040, 1060], [1056, 1056], [1089, 1083], [1120, 1054], [1150, 1066], [1173, 1054], [1204, 1079], [1245, 1048], [1257, 1048], [1296, 1085], [1321, 1068], [1370, 1072], [1395, 1105], [1364, 1126], [1339, 1114], [1317, 1128], [1298, 1128], [1282, 1114], [1269, 1124], [1228, 1118], [1210, 1134], [1179, 1111], [1126, 1111], [1093, 1134], [1079, 1120], [1052, 1114], [1019, 1128], [990, 1109], [953, 1122], [939, 1111], [912, 1120], [892, 1101], [853, 1124], [838, 1124], [818, 1107], [721, 1109], [692, 1134], [662, 1111], [643, 1120], [612, 1109], [584, 1120], [565, 1101], [502, 1124], [485, 1122], [465, 1103], [428, 1122], [411, 1109], [360, 1107], [350, 1099], [325, 1118], [276, 1103], [239, 1126], [214, 1109], [177, 1130], [163, 1116]], "baseline": [[161, 1105], [214, 1111], [276, 1105], [296, 1099], [421, 1103], [906, 1101], [1005, 1111], [1093, 1111], [1113, 1107], [1165, 1105], [1206, 1111], [1397, 1107]], "id": "dcda2e78-1bb5-46b5-8d9c-e59695916a0b", "type": "baselines"}}, {"prediction": "\u0627\u0644\u062a\u0627\u0654\u0648\u064a\u0644\u0627\u062a \u0648\u0627\u0644\u0634\u0628\u0647\u0627\u062a \u060c \u0643\u0645\u0633\u0627\u0654\u0644\u0629 \u0627\u0644\u0627\u0633\u062a\u062d\u0644\u0627\u0644\u060c \u0648\u0645\u0633\u0627\u0654\u0644\u0629 \u0627\u0644\u0631\u0628\u0627 \u0648\u0627\u0644\u0645\u0639\u0627\u0645\u0644\u0629\u060c \u0648\u0644\u0627 \u062a\u062c\u062f\u0648\u0646\u0647", "cuts": [[1293, 1298], [1284, 1289], [1274, 1274], [1260, 1265], [1255, 1255], [1245, 1245], [1231, 1231], [1212, 1216], [1202, 1207], [1168, 1168], [1130, 1140], [1120, 1120], [1101, 1106], [1091, 1096], [1077, 1077], [1053, 1053], [1039, 1039], [1019, 1024], [995, 995], [976, 976], [971, 971], [942, 947], [928, 928], [904, 904], [880, 885], [861, 865], [856, 856], [851, 851], [837, 837], [808, 813], [798, 803], [784, 789], [774, 779], [755, 755], [731, 731], [702, 702], [683, 688], [673, 678], [659, 659], [630, 630], [601, 611], [591, 591], [572, 572], [548, 553], [529, 529], [519, 524], [514, 514], [505, 505], [476, 481], [466, 466], [457, 457], [442, 442], [428, 428], [409, 414], [389, 394], [380, 380], [361, 361], [351, 351], [332, 332], [308, 308], [293, 298], [279, 284], [264, 264], [250, 250], [231, 236], [207, 212], [197, 197], [173, 178], [159, 168], [130, 139], [125, 125], [101, 101], [82, 82], [58, 58], [38, 38], [24, 24]], "confidences": [0.9996144771575928, 0.9999929666519165, 0.9999830722808838, 0.9999926090240479, 0.999995231628418, 0.9467358589172363, 0.999972939491272, 0.9995086193084717, 0.9998672008514404, 0.9942428469657898, 0.9999955892562866, 0.9999592304229736, 0.9999781847000122, 0.999994158744812, 0.9999866485595703, 0.999920129776001, 0.9999884366989136, 0.9986990690231323, 0.9997000694274902, 0.9911819696426392, 0.978091299533844, 0.999427855014801, 0.9999485015869141, 0.7997534275054932, 0.9999580383300781, 0.999704897403717, 0.520932137966156, 0.9891839027404785, 0.9960983991622925, 0.999637246131897, 0.9975180625915527, 0.9990247488021851, 0.9999979734420776, 0.7142682075500488, 0.9256128668785095, 1.0, 0.9999996423721313, 0.9998838901519775, 0.9999998807907104, 0.9960355162620544, 0.9999982118606567, 0.999958872795105, 0.9999998807907104, 1.0, 0.9999998807907104, 0.999992847442627, 0.9999973773956299, 0.9999991655349731, 0.9975195527076721, 0.9820659756660461, 0.9999998807907104, 0.9999421834945679, 0.9999996423721313, 0.9999991655349731, 0.9999985694885254, 0.6958007216453552, 0.9832131266593933, 0.9999997615814209, 0.9999964237213135, 0.9855285286903381, 0.9999996423721313, 0.9993840456008911, 0.9999988079071045, 0.9999823570251465, 0.9999992847442627, 0.9999185800552368, 0.9999986886978149, 0.9999874830245972, 0.9999970197677612, 0.9999932050704956, 0.9994638562202454, 0.9987408518791199, 0.999816358089447, 0.9999561309814453, 0.9999673366546631, 0.9998775720596313], "line": {"boundary": [[159, 1165], [163, 1146], [185, 1132], [210, 1146], [224, 1136], [253, 1146], [267, 1134], [286, 1138], [315, 1126], [327, 1126], [360, 1155], [389, 1130], [405, 1128], [426, 1138], [442, 1128], [467, 1148], [502, 1128], [536, 1150], [559, 1130], [578, 1146], [598, 1128], [629, 1142], [676, 1122], [707, 1151], [746, 1148], [758, 1157], [799, 1130], [836, 1132], [861, 1150], [875, 1140], [892, 1148], [912, 1130], [949, 1132], [962, 1144], [1001, 1122], [1042, 1153], [1081, 1132], [1105, 1151], [1150, 1144], [1163, 1132], [1200, 1151], [1241, 1130], [1286, 1161], [1309, 1144], [1360, 1134], [1378, 1148], [1405, 1124], [1450, 1142], [1467, 1179], [1444, 1190], [1405, 1190], [1387, 1202], [1290, 1183], [1269, 1200], [1239, 1188], [1193, 1200], [1163, 1187], [1111, 1188], [1099, 1179], [1056, 1190], [984, 1188], [961, 1179], [931, 1190], [910, 1185], [799, 1192], [762, 1179], [736, 1198], [713, 1187], [656, 1188], [623, 1179], [588, 1200], [567, 1196], [547, 1179], [526, 1196], [499, 1187], [401, 1187], [366, 1177], [345, 1194], [329, 1194], [296, 1175], [253, 1192], [220, 1185], [191, 1194], [163, 1179]], "baseline": [[161, 1167], [191, 1173], [306, 1177], [469, 1177], [493, 1181], [1469, 1181]], "id": "530f6c70-c51e-4d3c-b027-f256d70e5e3f", "type": "baselines"}}, {"prediction": "\u0645\u062a\u0639\u0641\u0641\u0627 \u0641\u064a \u0645\u0639\u064a\u0634\u062a\u0647\u060c \u062a\u0631\u0648\u0646\u0647 \u0637\u0627\u0645\u0639\u0627 \u0641\u064a \u0627\u0654\u0645\u0648\u0627\u0644 \u0627\u0644\u0646\u0627\u0633\u060c \u064a\u062f\u0627\u062e\u0644 \u0627\u0644\u0642\u0636\u0627\u0629 \u0644\u064a\u0648\u0644\u0648\u0647 \u0627\u0644\u0648\u0644\u0627\u064a\u0627\u062a\u060c", "cuts": [[1294, 1294], [1276, 1276], [1264, 1264], [1246, 1246], [1228, 1228], [1210, 1216], [1192, 1198], [1186, 1186], [1168, 1168], [1131, 1137], [1125, 1125], [1101, 1101], [1089, 1089], [1071, 1071], [1047, 1047], [1035, 1035], [1017, 1017], [999, 1005], [987, 987], [975, 975], [957, 957], [939, 939], [921, 921], [897, 903], [879, 879], [861, 867], [849, 849], [831, 831], [812, 818], [794, 800], [788, 788], [770, 770], [740, 740], [728, 734], [722, 722], [710, 710], [698, 698], [680, 680], [668, 668], [638, 638], [626, 632], [614, 620], [602, 602], [590, 596], [572, 572], [536, 536], [512, 518], [506, 506], [487, 494], [469, 475], [451, 451], [433, 433], [397, 403], [391, 391], [379, 379], [367, 367], [343, 343], [313, 313], [301, 301], [277, 289], [271, 271], [259, 259], [241, 247], [229, 229], [211, 211], [193, 193], [175, 181], [163, 169], [150, 157], [138, 138], [114, 120], [102, 108], [90, 90], [72, 78], [48, 48], [24, 24]], "confidences": [0.9999475479125977, 0.9999927282333374, 0.999996542930603, 0.9999957084655762, 0.999872088432312, 0.9999810457229614, 0.9999687671661377, 0.9999939203262329, 0.9999991655349731, 0.9942613840103149, 0.9999487400054932, 0.9999334812164307, 0.9999005794525146, 0.9999839067459106, 0.9999810457229614, 0.989007830619812, 0.9996200799942017, 0.9999685287475586, 0.6469784379005432, 0.6665834188461304, 0.7252474427223206, 0.9857919216156006, 0.9997385144233704, 0.9999765157699585, 0.9963619112968445, 0.9994021654129028, 0.9998681545257568, 0.9810250401496887, 0.9838308095932007, 0.9998787641525269, 0.9988665580749512, 0.9999058246612549, 0.6534377336502075, 0.9997734427452087, 0.631395697593689, 0.9998962879180908, 0.9999980926513672, 0.9978647828102112, 0.9999958276748657, 0.9721218943595886, 0.9996869564056396, 0.9999868869781494, 0.9235882759094238, 0.9999985694885254, 0.9997712969779968, 0.826903223991394, 0.999829888343811, 0.9994933605194092, 0.9999945163726807, 0.9999911785125732, 0.9996364116668701, 0.9999428987503052, 0.9976553916931152, 0.9999971389770508, 0.71103435754776, 0.9999946355819702, 0.9999889135360718, 0.9995948672294617, 0.9999077320098877, 0.9999817609786987, 0.9998291730880737, 0.999971866607666, 0.9999986886978149, 0.9999973773956299, 0.9999977350234985, 0.9933046698570251, 0.9999994039535522, 0.9999998807907104, 0.9999985694885254, 0.9998321533203125, 0.9999990463256836, 0.9999988079071045, 0.9999668598175049, 0.9999896287918091, 0.9999967813491821, 0.9679267406463623], "line": {"boundary": [[159, 1249], [167, 1220], [245, 1204], [267, 1204], [282, 1216], [300, 1204], [331, 1224], [348, 1224], [370, 1204], [391, 1220], [411, 1204], [432, 1216], [460, 1208], [473, 1220], [530, 1206], [555, 1226], [577, 1208], [590, 1216], [656, 1210], [697, 1235], [732, 1210], [762, 1206], [789, 1224], [816, 1208], [840, 1224], [867, 1200], [906, 1227], [961, 1194], [992, 1222], [1023, 1210], [1050, 1233], [1076, 1216], [1109, 1231], [1128, 1216], [1154, 1229], [1214, 1216], [1233, 1231], [1272, 1229], [1286, 1243], [1350, 1198], [1362, 1198], [1409, 1227], [1430, 1224], [1477, 1257], [1458, 1257], [1430, 1272], [1356, 1270], [1345, 1263], [1309, 1292], [1272, 1266], [1222, 1280], [1212, 1270], [1146, 1261], [1124, 1280], [1109, 1282], [1081, 1278], [1048, 1257], [1029, 1268], [959, 1268], [947, 1259], [916, 1288], [904, 1288], [877, 1265], [824, 1278], [783, 1261], [729, 1263], [707, 1280], [688, 1280], [668, 1263], [643, 1274], [598, 1261], [563, 1282], [538, 1263], [477, 1266], [430, 1257], [409, 1274], [345, 1276], [319, 1257], [270, 1276], [257, 1265], [230, 1272], [214, 1261], [163, 1259]], "baseline": [[161, 1251], [272, 1255], [292, 1259], [1424, 1257], [1478, 1258]], "id": "78d4ba39-b9a2-42e6-b87c-1914573b9df8", "type": "baselines"}}, {"prediction": "\u0645\u0639 \u0634\u0631\u0647\u0647 \u0639\u0644\u0649 \u0627\u0644\u062f\u0646\u064a\u0627\u060c \u0648\u0642\u0644\u0629 \u0648\u0631\u0639\u0647 \u0648\u0645\u0628\u0627\u0644\u0627\u062a\u0647 \u0628\u0627\u0644\u062d\u0644\u0627\u0644 \u0648\u0627\u0644\u062d\u0631\u0627\u0645\u060c \u0641\u0627\u062a\u0647\u0645\u0648\u0647 \u0639\u0644\u0649 \u0639\u0644\u0645\u0647", "cuts": [[1287, 1287], [1265, 1265], [1232, 1237], [1221, 1221], [1193, 1199], [1172, 1172], [1155, 1155], [1128, 1133], [1111, 1111], [1095, 1095], [1078, 1078], [1046, 1051], [1035, 1040], [1024, 1024], [1007, 1013], [991, 991], [980, 980], [964, 969], [953, 953], [931, 936], [920, 920], [898, 898], [881, 887], [870, 870], [843, 849], [832, 832], [810, 816], [788, 788], [772, 772], [745, 750], [734, 734], [712, 712], [695, 695], [684, 684], [668, 673], [657, 662], [646, 646], [630, 630], [602, 613], [597, 597], [586, 586], [575, 575], [547, 547], [526, 531], [515, 520], [498, 498], [460, 471], [449, 454], [432, 438], [422, 427], [400, 400], [378, 383], [361, 361], [345, 345], [328, 328], [307, 312], [296, 296], [285, 285], [274, 274], [257, 257], [235, 235], [219, 219], [197, 197], [170, 175], [159, 159], [137, 137], [126, 126], [82, 93], [71, 71], [55, 55], [33, 38], [22, 22]], "confidences": [0.9999666213989258, 0.9999809265136719, 0.9999946355819702, 0.9999746084213257, 0.9999938011169434, 0.9999914169311523, 0.9090831279754639, 0.9998230338096619, 0.9976154565811157, 0.9993928670883179, 0.9999699592590332, 0.9991900324821472, 0.9999030828475952, 0.999919056892395, 0.9999741315841675, 0.983220100402832, 0.9999629259109497, 0.9999812841415405, 0.6795229315757751, 0.9999853372573853, 0.9986742734909058, 0.9999790191650391, 0.9995899796485901, 0.9987137317657471, 0.9991518259048462, 0.9978784322738647, 0.9999507665634155, 0.9689961075782776, 0.9987661838531494, 0.9999948740005493, 0.581307590007782, 0.9999948740005493, 0.9670306444168091, 0.9999184608459473, 0.9983538389205933, 0.9998610019683838, 0.9507318735122681, 0.9808312058448792, 0.9999997615814209, 0.9999678134918213, 0.9999990463256836, 0.9445680975914001, 0.9999923706054688, 0.9999840259552002, 0.9999967813491821, 0.9999991655349731, 0.9999967813491821, 0.9999933242797852, 0.9997896552085876, 0.9999817609786987, 0.9999405145645142, 0.9997538924217224, 0.9999948740005493, 0.9972209930419922, 0.9999998807907104, 0.9999997615814209, 0.9762755036354065, 0.9999904632568359, 0.9999934434890747, 1.0, 0.9999963045120239, 0.9913928508758545, 0.9357872009277344, 0.9999316930770874, 1.0, 0.9693567752838135, 0.9998525381088257, 0.9999986886978149, 0.9999988079071045, 0.9999994039535522, 0.9999954700469971, 0.9990108013153076], "line": {"boundary": [[167, 1327], [171, 1296], [189, 1300], [208, 1282], [255, 1313], [292, 1282], [335, 1307], [348, 1300], [403, 1305], [436, 1284], [473, 1303], [497, 1300], [512, 1286], [538, 1303], [555, 1302], [582, 1284], [621, 1309], [651, 1284], [686, 1288], [705, 1303], [731, 1286], [766, 1313], [803, 1286], [840, 1288], [859, 1305], [896, 1305], [904, 1313], [941, 1300], [998, 1307], [1017, 1290], [1033, 1288], [1058, 1290], [1091, 1313], [1116, 1290], [1161, 1298], [1177, 1288], [1218, 1313], [1251, 1288], [1290, 1311], [1302, 1302], [1339, 1303], [1376, 1280], [1407, 1307], [1446, 1307], [1463, 1335], [1430, 1370], [1405, 1368], [1380, 1344], [1347, 1360], [1323, 1360], [1288, 1335], [1233, 1362], [1216, 1362], [1196, 1346], [1148, 1348], [1126, 1358], [1095, 1339], [1068, 1358], [1007, 1339], [986, 1356], [953, 1356], [908, 1339], [886, 1356], [844, 1356], [832, 1344], [775, 1341], [744, 1356], [732, 1344], [637, 1346], [625, 1339], [598, 1354], [555, 1341], [536, 1356], [516, 1352], [504, 1362], [465, 1337], [454, 1344], [428, 1342], [419, 1352], [387, 1342], [366, 1356], [331, 1331], [276, 1356], [261, 1354], [241, 1337], [202, 1344], [169, 1339]], "baseline": [[169, 1329], [200, 1327], [249, 1333], [461, 1333], [500, 1337], [1335, 1335], [1378, 1337], [1397, 1341], [1465, 1337]], "id": "6089b007-2e7f-4f07-bfee-d5c9445abfa6", "type": "baselines"}}, {"prediction": "\u0648\u062f\u064a\u0646\u0647.", "cuts": [[103, 106], [84, 84], [68, 68], [53, 53], [42, 42], [27, 27]], "confidences": [0.9999996423721313, 0.9999984502792358, 0.9997429251670837, 0.9999673366546631, 0.99901282787323, 0.9717188477516174], "line": {"boundary": [[1343, 1411], [1350, 1391], [1387, 1374], [1444, 1383], [1483, 1415], [1462, 1415], [1432, 1438], [1345, 1418]], "baseline": [[1345, 1413], [1419, 1413], [1484, 1417]], "id": "0bfd6754-4a7c-4b17-b17a-e51aae0c8044", "type": "baselines"}}, {"prediction": "\u0648\u0645\u062a\u0649 \u0631\u0627\u0654\u064a\u062a\u0645 \u0639\u0627\u0644\u0645\u0627 \u0641\u064a \u0639\u0642\u0644\u0647 \u0633\u062e\u0627\u0641\u0629\u060c \u0648\u0641\u064a \u0646\u0638\u0631\u0647 \u0642\u0635\u0648\u0631\u060c \u064a\u0636\u0639 \u0627\u0644\u0627\u0654\u0634\u064a\u0627\u0621 - \u063a\u0627\u0644\u0628\u0627- \u0641\u064a", "cuts": [[1207, 1207], [1188, 1188], [1169, 1176], [1157, 1157], [1126, 1132], [1120, 1120], [1101, 1101], [1095, 1095], [1089, 1089], [1076, 1076], [1057, 1057], [1033, 1045], [1026, 1026], [1008, 1008], [995, 995], [977, 977], [958, 964], [939, 945], [933, 933], [914, 921], [890, 896], [871, 871], [852, 852], [840, 840], [821, 821], [802, 809], [790, 790], [753, 753], [734, 740], [722, 722], [709, 709], [690, 690], [672, 678], [666, 666], [647, 647], [628, 628], [597, 603], [591, 591], [566, 566], [547, 554], [535, 535], [510, 516], [504, 504], [485, 485], [454, 454], [435, 435], [417, 417], [398, 404], [392, 392], [367, 373], [330, 330], [311, 317], [305, 305], [286, 292], [280, 280], [268, 274], [261, 261], [236, 236], [218, 224], [205, 212], [187, 193], [174, 174], [156, 162], [143, 143], [124, 124], [112, 112], [100, 100], [87, 93], [68, 68], [50, 56], [37, 37], [25, 25]], "confidences": [0.9859687089920044, 0.9289069175720215, 0.9999728202819824, 0.9997435212135315, 0.9999949932098389, 0.9999815225601196, 0.999997615814209, 0.9999803304672241, 0.9999959468841553, 0.7412486672401428, 0.9978557229042053, 0.9999803304672241, 0.9999420642852783, 0.9999202489852905, 0.999981164932251, 0.9999773502349854, 0.9999395608901978, 0.9999784231185913, 0.9947001934051514, 0.9998576641082764, 0.9994139671325684, 0.9998432397842407, 0.9999716281890869, 0.9998818635940552, 0.6902597546577454, 0.999955415725708, 0.9993599057197571, 0.9993149042129517, 0.9999468326568604, 0.9852787256240845, 0.9907258749008179, 0.9999005794525146, 0.997058629989624, 0.9781426191329956, 0.8489178419113159, 0.8708791732788086, 0.9999767541885376, 0.9941668510437012, 0.966301441192627, 0.9999921321868896, 0.9917047023773193, 0.9999977350234985, 0.999850869178772, 0.589336633682251, 0.9973306655883789, 0.9999990463256836, 0.9893065094947815, 0.999998927116394, 0.9999986886978149, 0.9999996423721313, 0.999987006187439, 0.9929701685905457, 0.9999936819076538, 0.9999984502792358, 0.9898472428321838, 0.9999828338623047, 0.9971639513969421, 0.965297281742096, 0.9999982118606567, 0.9999784231185913, 0.9949177503585815, 0.9999829530715942, 0.9999810457229614, 0.9888534545898438, 0.9037330150604248, 0.9999834299087524, 0.9911187291145325, 0.9999808073043823, 0.9999874830245972, 0.9999430179595947, 0.9984645843505859, 0.9996715784072876], "line": {"boundary": [[173, 1489], [177, 1456], [192, 1440], [214, 1452], [253, 1422], [269, 1436], [300, 1438], [321, 1456], [352, 1457], [376, 1440], [399, 1452], [430, 1430], [467, 1442], [485, 1459], [508, 1459], [524, 1444], [557, 1463], [567, 1456], [631, 1463], [658, 1442], [695, 1459], [727, 1438], [750, 1444], [771, 1463], [795, 1442], [834, 1461], [861, 1442], [896, 1442], [914, 1444], [933, 1463], [964, 1463], [988, 1442], [1000, 1442], [1039, 1456], [1054, 1471], [1113, 1428], [1140, 1448], [1155, 1442], [1187, 1456], [1200, 1469], [1257, 1434], [1296, 1471], [1321, 1454], [1370, 1459], [1393, 1496], [1360, 1512], [1337, 1500], [1313, 1516], [1284, 1506], [1231, 1506], [1216, 1524], [1183, 1500], [1130, 1502], [1107, 1495], [1077, 1522], [1060, 1520], [1039, 1498], [978, 1500], [964, 1493], [927, 1500], [838, 1493], [787, 1520], [775, 1520], [752, 1496], [697, 1512], [676, 1495], [625, 1502], [614, 1512], [578, 1508], [567, 1496], [547, 1508], [532, 1502], [508, 1522], [487, 1522], [461, 1498], [423, 1495], [387, 1506], [374, 1493], [319, 1483], [263, 1506], [235, 1487], [214, 1489], [187, 1516], [175, 1516]], "baseline": [[175, 1491], [370, 1483], [403, 1487], [483, 1489], [502, 1493], [1216, 1491], [1276, 1496], [1339, 1495], [1395, 1498]], "id": "61bdb5af-8737-49d8-b20f-e600b41759f7", "type": "baselines"}}, {"prediction": "\u063a\u064a\u0631 \u0645\u0648\u0627\u0636\u0639\u0647\u0627\u060c \u0641\u0627\u062a\u0647\u0645\u0648\u0647 \u0639\u0644\u0649 \u0627\u0633\u062a\u0646\u0628\u0627\u0637\u0647 \u0648\u0639\u0644\u0645\u0647 \u0648\u0631\u0627\u0654\u064a\u0647\u060c \u0648\u0644\u0627 \u062a\u0642\u0644\u062f\u0648\u0647", "cuts": [[967, 967], [946, 946], [929, 935], [908, 913], [902, 902], [886, 886], [865, 870], [848, 848], [816, 816], [794, 794], [778, 778], [767, 767], [746, 751], [740, 740], [724, 729], [713, 713], [697, 697], [675, 675], [659, 659], [638, 638], [621, 627], [605, 605], [589, 589], [573, 573], [540, 546], [535, 535], [519, 519], [497, 497], [481, 481], [470, 475], [459, 465], [438, 438], [421, 421], [400, 405], [389, 394], [367, 367], [351, 351], [330, 330], [313, 313], [292, 297], [281, 281], [265, 265], [243, 249], [238, 238], [232, 232], [222, 222], [200, 205], [184, 189], [178, 178], [151, 157], [140, 146], [124, 130], [119, 119], [103, 103], [86, 86], [70, 70], [49, 49], [27, 27]], "confidences": [0.9998652935028076, 0.5277007222175598, 0.9999955892562866, 0.9999880790710449, 0.9999693632125854, 0.9982054233551025, 0.9999960660934448, 0.8161394596099854, 0.9999929666519165, 0.9999971389770508, 0.9999768733978271, 0.9989445805549622, 0.9983943104743958, 0.835041344165802, 0.9998206496238708, 0.9999831914901733, 0.9942564368247986, 0.999908447265625, 0.9996181726455688, 0.5019206404685974, 0.9998630285263062, 0.9985489249229431, 0.9094343781471252, 0.9999619722366333, 0.999971866607666, 0.9999991655349731, 0.9999998807907104, 0.9989573955535889, 0.9999986886978149, 0.9999964237213135, 0.9999872446060181, 0.916301965713501, 0.9883456230163574, 0.9999982118606567, 0.9999991655349731, 0.9999978542327881, 0.6548713445663452, 0.936079204082489, 0.9999892711639404, 0.9997774958610535, 0.9999997615814209, 0.9538446664810181, 0.9998297691345215, 0.9999701976776123, 0.9999457597732544, 0.9999912977218628, 0.9999996423721313, 0.999927282333374, 0.9924691319465637, 0.9999955892562866, 0.9999499320983887, 0.9999344348907471, 0.993513286113739, 0.999993085861206, 0.9991191029548645, 0.9999837875366211, 0.9999768733978271, 0.9998847246170044], "line": {"boundary": [[481, 1563], [489, 1533], [522, 1532], [547, 1506], [561, 1506], [571, 1516], [580, 1508], [600, 1518], [631, 1516], [658, 1539], [684, 1533], [707, 1510], [744, 1537], [766, 1539], [785, 1530], [795, 1537], [814, 1518], [875, 1541], [898, 1518], [933, 1520], [980, 1539], [1005, 1520], [1027, 1541], [1052, 1518], [1093, 1541], [1157, 1541], [1189, 1522], [1208, 1522], [1226, 1537], [1249, 1522], [1263, 1535], [1298, 1541], [1335, 1524], [1354, 1541], [1374, 1539], [1391, 1553], [1432, 1526], [1465, 1565], [1417, 1592], [1387, 1594], [1370, 1578], [1352, 1596], [1321, 1578], [1280, 1578], [1257, 1588], [1222, 1571], [1187, 1576], [1175, 1588], [1142, 1578], [1115, 1590], [1089, 1569], [1039, 1592], [1021, 1590], [1005, 1574], [957, 1576], [939, 1588], [922, 1572], [879, 1571], [865, 1584], [847, 1586], [834, 1574], [768, 1571], [754, 1584], [731, 1588], [662, 1571], [641, 1588], [600, 1565], [557, 1584], [534, 1574], [506, 1584], [483, 1572]], "baseline": [[483, 1565], [651, 1565], [779, 1571], [1362, 1569], [1417, 1576], [1466, 1567]], "id": "69b7c31f-5408-4dd0-85d6-75e1e8d434fb", "type": "baselines"}}, {"prediction": "\u0648\u0645\u062a\u0649 \u0631\u0627\u0654\u064a\u062a\u0645 \u0627\u0644\u0639\u0627\u0644\u0645 \u0644\u0627 \u064a\u062a\u0645\u0645 \u0635\u0644\u0627\u062a\u0647 \u0627\u0644\u0645\u0641\u0631\u0648\u0636\u0629 \u060c \u0648\u0644\u0627 \u064a\u0637\u0645\u064a\u0654\u0646 \u0641\u064a \u0631\u0643\u0648\u0639\u0647\u0627", "cuts": [[1209, 1209], [1187, 1187], [1176, 1176], [1159, 1159], [1109, 1115], [1098, 1098], [1081, 1087], [1076, 1076], [1070, 1070], [1059, 1059], [1042, 1042], [1004, 1015], [998, 998], [987, 993], [970, 970], [954, 954], [943, 943], [920, 920], [887, 893], [871, 876], [859, 865], [826, 837], [821, 821], [804, 804], [787, 793], [765, 765], [726, 737], [710, 710], [676, 682], [665, 671], [649, 649], [638, 638], [604, 610], [593, 599], [582, 582], [566, 566], [549, 549], [532, 532], [510, 510], [482, 488], [455, 455], [444, 444], [438, 438], [399, 410], [394, 394], [366, 372], [355, 360], [322, 333], [316, 316], [288, 294], [272, 272], [255, 255], [250, 250], [233, 238], [194, 205], [183, 183], [172, 172], [122, 133], [111, 111], [94, 94], [72, 72], [50, 50], [28, 28], [11, 11]], "confidences": [0.999711811542511, 0.9999942779541016, 0.9999964237213135, 0.9999276399612427, 0.9999908208847046, 0.9999871253967285, 0.9998900890350342, 0.9831660985946655, 0.9998063445091248, 0.999962329864502, 0.9997965693473816, 0.9999898672103882, 0.99998939037323, 0.9999788999557495, 0.9994357228279114, 0.9999730587005615, 0.7908631563186646, 0.9956527948379517, 0.9997575879096985, 0.9996732473373413, 0.9956046342849731, 0.999534010887146, 0.9465213418006897, 0.9551798105239868, 0.9997909665107727, 0.974587619304657, 0.9998075366020203, 0.999998927116394, 0.9999898672103882, 0.9993416666984558, 0.9999842643737793, 0.999870777130127, 0.9998094439506531, 0.9999909400939941, 0.9999817609786987, 0.9999905824661255, 0.7308676242828369, 0.9999252557754517, 0.9999912977218628, 0.9999830722808838, 0.9993329644203186, 0.9937452077865601, 0.9980292916297913, 0.9999990463256836, 0.9999905824661255, 0.9998887777328491, 0.9999932050704956, 0.9999985694885254, 0.9999996423721313, 0.9999998807907104, 0.9999985694885254, 0.8817663788795471, 0.9893107414245605, 0.9999843835830688, 0.9999984502792358, 0.9999974966049194, 0.9999927282333374, 0.9999198913574219, 0.9998809099197388, 0.9999985694885254, 0.9999926090240479, 0.9989744424819946, 0.9999724626541138, 0.7024262547492981], "line": {"boundary": [[169, 1647], [173, 1594], [185, 1606], [210, 1604], [222, 1613], [259, 1594], [296, 1629], [335, 1596], [370, 1621], [403, 1602], [424, 1608], [442, 1592], [481, 1615], [508, 1592], [532, 1592], [571, 1625], [602, 1596], [617, 1596], [629, 1608], [647, 1602], [678, 1617], [701, 1598], [723, 1610], [744, 1596], [777, 1621], [799, 1604], [838, 1600], [857, 1617], [877, 1611], [900, 1633], [959, 1598], [992, 1613], [1009, 1598], [1037, 1598], [1066, 1625], [1093, 1596], [1122, 1610], [1146, 1596], [1185, 1623], [1241, 1590], [1284, 1631], [1329, 1606], [1354, 1617], [1368, 1613], [1407, 1648], [1382, 1650], [1360, 1666], [1333, 1654], [1309, 1670], [1272, 1652], [1253, 1666], [1212, 1660], [1193, 1676], [1163, 1650], [1101, 1652], [1070, 1676], [1040, 1648], [1021, 1658], [998, 1647], [978, 1664], [933, 1664], [920, 1678], [885, 1650], [822, 1658], [779, 1647], [705, 1654], [686, 1668], [582, 1647], [543, 1662], [506, 1647], [491, 1647], [475, 1660], [415, 1650], [397, 1666], [380, 1666], [354, 1647], [327, 1672], [315, 1672], [288, 1647], [265, 1660], [247, 1650], [230, 1662], [200, 1650], [185, 1660], [173, 1654]], "baseline": [[171, 1648], [1204, 1647], [1249, 1648], [1270, 1654], [1408, 1650]], "id": "37eead36-ae8c-47ea-aa4b-69190b7cf186", "type": "baselines"}}, {"prediction": "\u0648\u0633\u062c\u0648\u062f\u0647\u0627\u060c \u0648\u0644\u0627 \u064a\u062d\u0636\u0631 \u0645\u0639 \u0642\u0631\u0627\u0621\u062a\u0647 \u0641\u064a\u0647\u0627 \u0628\u0627\u0644\u062e\u0634\u0648\u0639 \u0648\u0627\u0644\u062d\u0636\u0648\u0631\u060c \u0648\u0627\u0644\u062a\u062f\u0628\u0631 \u0648\u0627\u0644\u062a\u0631\u062a\u064a\u0644", "cuts": [[1292, 1292], [1267, 1267], [1231, 1231], [1213, 1213], [1194, 1194], [1170, 1170], [1152, 1158], [1139, 1139], [1109, 1121], [1097, 1103], [1072, 1079], [1060, 1066], [1030, 1042], [1024, 1024], [999, 999], [975, 975], [944, 944], [908, 914], [896, 896], [877, 877], [841, 847], [829, 829], [817, 817], [798, 798], [786, 786], [768, 768], [756, 756], [725, 731], [713, 713], [701, 701], [689, 689], [670, 670], [640, 652], [634, 634], [622, 622], [609, 609], [585, 585], [561, 561], [536, 536], [512, 512], [475, 481], [469, 469], [451, 451], [439, 439], [414, 414], [390, 390], [360, 360], [335, 335], [317, 317], [286, 293], [274, 274], [256, 262], [250, 250], [238, 238], [225, 225], [201, 201], [189, 189], [152, 165], [146, 146], [128, 128], [116, 122], [104, 104], [91, 91], [79, 79], [61, 61], [49, 49]], "confidences": [0.9998243451118469, 0.9999920129776001, 0.9999985694885254, 0.9999624490737915, 0.9999974966049194, 0.9999874830245972, 0.9999926090240479, 0.9999836683273315, 0.999990701675415, 0.9999855756759644, 0.99998939037323, 0.9999979734420776, 0.9996892213821411, 0.9999874830245972, 0.9997640252113342, 0.9999427795410156, 0.9993060827255249, 0.9446314573287964, 0.7036338448524475, 0.9967960715293884, 0.9986769556999207, 0.9841193556785583, 0.9994528889656067, 0.9409279823303223, 0.9999997615814209, 0.9999942779541016, 0.9934862852096558, 0.9999960660934448, 0.9999674558639526, 0.9067964553833008, 0.9972527623176575, 0.999915599822998, 0.9999898672103882, 0.9999929666519165, 0.9999849796295166, 0.9999780654907227, 0.9999867677688599, 0.999346911907196, 0.9999964237213135, 0.9825494289398193, 0.9995606541633606, 0.9960000514984131, 0.9999972581863403, 0.9731088280677795, 0.9996663331985474, 0.5042705535888672, 1.0, 0.9998852014541626, 0.9999935626983643, 0.9747305512428284, 0.7328495383262634, 0.9999992847442627, 0.9982652068138123, 0.9998162388801575, 1.0, 0.9999856948852539, 0.8613400459289551, 0.9995511174201965, 0.9720507264137268, 0.9999809265136719, 0.9999556541442871, 0.9989655017852783, 0.9860791563987732, 0.9992793202400208, 0.9793171882629395, 0.9999933242797852], "line": {"boundary": [[161, 1732], [165, 1686], [194, 1670], [237, 1684], [270, 1670], [319, 1707], [387, 1670], [409, 1674], [442, 1699], [461, 1686], [514, 1693], [532, 1678], [561, 1687], [588, 1670], [633, 1701], [656, 1684], [678, 1695], [701, 1680], [740, 1684], [752, 1674], [764, 1674], [787, 1695], [810, 1676], [830, 1689], [861, 1678], [885, 1699], [908, 1682], [945, 1678], [955, 1687], [980, 1678], [1003, 1699], [1046, 1693], [1070, 1713], [1115, 1682], [1175, 1695], [1200, 1674], [1226, 1674], [1261, 1703], [1296, 1676], [1348, 1695], [1440, 1693], [1469, 1726], [1456, 1726], [1432, 1746], [1389, 1734], [1347, 1748], [1321, 1732], [1280, 1732], [1270, 1725], [1233, 1744], [1193, 1725], [1171, 1742], [1142, 1730], [1074, 1748], [1060, 1736], [1033, 1758], [1007, 1754], [982, 1730], [949, 1748], [927, 1730], [904, 1734], [886, 1725], [851, 1744], [822, 1742], [803, 1725], [779, 1744], [760, 1730], [703, 1732], [668, 1756], [653, 1754], [627, 1730], [612, 1740], [584, 1728], [518, 1730], [489, 1742], [438, 1725], [411, 1740], [395, 1728], [364, 1728], [352, 1740], [325, 1742], [306, 1726], [286, 1740], [253, 1728], [228, 1744], [216, 1736], [179, 1744], [165, 1732]], "baseline": [[163, 1734], [214, 1732], [255, 1725], [364, 1726], [397, 1723], [419, 1723], [438, 1726], [1419, 1725], [1471, 1728]], "id": "10b8b309-9cbe-4e14-8758-f6d22c56c47e", "type": "baselines"}}, {"prediction": "\u0641\u0627\u062a\u0647\u0645\u0648\u0647 \u0628\u0642\u0633\u0627\u0648\u0629 \u0627\u0644\u0642\u0644\u0628\u060c \u0648\u0628\u0639\u062f\u0647 \u0639\u0646 \u0627\u0644\u0631\u0628 \u0639\u0632 \u0648\u062c\u0644.", "cuts": [[683, 683], [668, 673], [659, 659], [644, 644], [620, 620], [601, 606], [582, 582], [563, 567], [558, 558], [543, 543], [524, 524], [500, 500], [490, 490], [466, 466], [452, 452], [442, 447], [433, 433], [418, 418], [399, 404], [375, 375], [346, 346], [327, 332], [317, 322], [298, 303], [284, 284], [269, 269], [245, 245], [226, 231], [212, 212], [188, 192], [163, 168], [159, 159], [149, 149], [135, 135], [106, 106], [72, 77], [63, 63], [53, 58], [48, 48], [43, 43], [38, 38], [29, 34], [19, 19]], "confidences": [0.9739862084388733, 0.9999884366989136, 0.9997276663780212, 0.9999849796295166, 0.9990725517272949, 0.9999985694885254, 0.9983306527137756, 0.9980822801589966, 0.9998204112052917, 0.997825026512146, 0.9833168387413025, 0.9999716281890869, 0.999618411064148, 0.9978547692298889, 0.6419629454612732, 0.9951518774032593, 0.9999998807907104, 1.0, 0.9999918937683105, 0.9991405010223389, 0.9999971389770508, 1.0, 1.0, 0.9999991655349731, 0.9999963045120239, 0.9999998807907104, 0.9999983310699463, 0.999997615814209, 0.9999990463256836, 0.9999960660934448, 0.9999496936798096, 0.9999961853027344, 0.9999966621398926, 0.9999440908432007, 0.9999513626098633, 0.9999853372573853, 0.9999990463256836, 0.9999951124191284, 0.9946951270103455, 0.9999998807907104, 0.9999974966049194, 0.9999954700469971, 0.9960901141166687], "line": {"boundary": [[764, 1799], [766, 1769], [783, 1752], [812, 1746], [849, 1775], [879, 1767], [898, 1750], [929, 1769], [974, 1765], [986, 1773], [1025, 1762], [1048, 1771], [1103, 1767], [1122, 1777], [1150, 1752], [1189, 1752], [1233, 1767], [1253, 1754], [1272, 1773], [1288, 1758], [1304, 1758], [1321, 1775], [1384, 1775], [1426, 1756], [1463, 1783], [1469, 1802], [1415, 1810], [1401, 1822], [1368, 1812], [1354, 1824], [1327, 1812], [1306, 1822], [1292, 1810], [1253, 1810], [1231, 1822], [1206, 1806], [1146, 1810], [1130, 1824], [1093, 1806], [1072, 1820], [1048, 1820], [1035, 1808], [994, 1810], [980, 1802], [935, 1826], [912, 1808], [886, 1822], [851, 1822], [830, 1804], [785, 1812], [766, 1806]], "baseline": [[766, 1801], [846, 1801], [867, 1804], [1470, 1804]], "id": "80fb2329-0ad1-464c-b2b3-93a36d431628", "type": "baselines"}}, {"prediction": "\u0648\u0645\u062a\u0649 \u0648\u062c\u062f\u062a\u0645 \u0627\u0644\u0639\u0627\u0644\u0645 \u0644\u0627 \u0645\u0639\u0627\u0645\u0644\u0629 \u0644\u0647 \u0645\u0639 \u0631\u0628\u0647 \u0639\u0632 \u0648\u062c\u0644 \u060c \u062a\u0638\u0647\u0631 \u0639\u0644\u064a\u0647 \u0628\u0647\u062c\u062a\u0647\u0627 \u0648\u0627\u0654\u0646\u0648\u0627\u0631\u0647\u0627", "cuts": [[1236, 1236], [1214, 1214], [1197, 1197], [1186, 1186], [1142, 1153], [1131, 1136], [1103, 1103], [1086, 1086], [1064, 1064], [1042, 1042], [1014, 1020], [1009, 1009], [998, 998], [981, 981], [964, 964], [953, 953], [931, 937], [898, 909], [887, 892], [876, 881], [853, 859], [842, 842], [820, 820], [804, 809], [792, 792], [770, 770], [759, 759], [732, 743], [726, 726], [709, 709], [682, 687], [671, 671], [648, 648], [621, 626], [610, 610], [593, 593], [576, 576], [554, 560], [543, 543], [532, 538], [526, 526], [521, 521], [515, 515], [504, 510], [499, 499], [493, 493], [471, 477], [460, 460], [438, 443], [421, 421], [399, 405], [371, 377], [355, 355], [338, 338], [327, 327], [310, 310], [283, 288], [277, 277], [260, 260], [233, 233], [211, 211], [200, 200], [177, 183], [155, 166], [150, 150], [133, 133], [122, 127], [116, 116], [105, 105], [83, 89], [72, 72], [50, 50], [33, 33]], "confidences": [0.9999624490737915, 0.9999873638153076, 0.9999958276748657, 0.9999871253967285, 0.9999827146530151, 0.9999901056289673, 0.999958872795105, 0.519729733467102, 0.9999949932098389, 0.990899920463562, 0.9998747110366821, 0.9999719858169556, 0.9997586607933044, 0.9992938041687012, 0.9987748265266418, 0.9998395442962646, 0.9999606609344482, 0.9999958276748657, 0.999937891960144, 0.9987975358963013, 0.9998902082443237, 0.9642751216888428, 0.9995637536048889, 0.9999383687973022, 0.9973805546760559, 0.9987190961837769, 0.524466335773468, 0.9997130036354065, 0.9221267104148865, 0.9419159293174744, 0.9999997615814209, 0.9999914169311523, 0.9996623992919922, 0.999992847442627, 0.9999985694885254, 0.999937891960144, 0.9998610019683838, 0.9999767541885376, 0.9999998807907104, 0.8760433197021484, 0.9968647360801697, 0.9999954700469971, 0.999546229839325, 0.9998810291290283, 0.656394362449646, 0.9994986057281494, 0.9998539686203003, 0.9695175290107727, 0.9999977350234985, 0.9999958276748657, 0.9999986886978149, 0.9999974966049194, 0.9999914169311523, 0.9999973773956299, 0.9956705570220947, 0.9975166320800781, 0.9996503591537476, 0.9999966621398926, 0.9999432563781738, 0.9999326467514038, 0.960350751876831, 0.9999790191650391, 0.9999817609786987, 0.9995361566543579, 0.9995700716972351, 0.9970190525054932, 0.9999990463256836, 0.9999368190765381, 0.7645747065544128, 0.9985374212265015, 0.9979060888290405, 0.9999816417694092, 0.990431010723114], "line": {"boundary": [[144, 1867], [146, 1841], [161, 1828], [196, 1843], [214, 1828], [233, 1838], [263, 1816], [290, 1841], [315, 1826], [329, 1840], [399, 1840], [423, 1853], [473, 1828], [516, 1861], [559, 1828], [598, 1832], [614, 1845], [658, 1822], [693, 1845], [713, 1840], [746, 1847], [758, 1859], [775, 1845], [808, 1845], [820, 1855], [849, 1828], [871, 1840], [900, 1828], [920, 1838], [935, 1828], [957, 1847], [978, 1847], [1000, 1828], [1021, 1826], [1052, 1851], [1079, 1828], [1107, 1841], [1132, 1830], [1161, 1855], [1193, 1836], [1249, 1851], [1265, 1845], [1286, 1863], [1327, 1840], [1368, 1847], [1407, 1882], [1376, 1886], [1364, 1898], [1347, 1898], [1337, 1888], [1298, 1904], [1276, 1886], [1261, 1900], [1189, 1890], [1165, 1910], [1142, 1888], [1087, 1886], [1052, 1908], [1031, 1886], [1007, 1888], [994, 1877], [961, 1890], [898, 1890], [869, 1875], [853, 1888], [826, 1880], [781, 1910], [752, 1888], [717, 1896], [690, 1875], [668, 1888], [635, 1888], [606, 1873], [584, 1888], [524, 1900], [499, 1880], [460, 1896], [432, 1880], [409, 1894], [376, 1884], [329, 1894], [302, 1875], [276, 1894], [253, 1884], [237, 1896], [196, 1894], [148, 1869]], "baseline": [[146, 1869], [194, 1875], [323, 1873], [345, 1878], [499, 1882], [555, 1880], [590, 1873], [859, 1875], [900, 1871], [923, 1877], [1037, 1878], [1056, 1882], [1276, 1880], [1354, 1888], [1408, 1884]], "id": "54799679-1ac4-47fe-934f-83b31f13faa8", "type": "baselines"}}, {"prediction": "\u0648\u0633\u0643\u064a\u0646\u062a\u0647\u0627\u060c \u0645\u0646 \u062a\u0644\u0627\u0648\u0629 \u0648\u0635\u064a\u0627\u0645 \u0648\u0642\u064a\u0627\u0645\u060c \u0641\u0627\u0639\u0644\u0645\u0648\u0627 \u0627\u0654\u0646\u0647 \u0642\u0644\u064a\u0644 \u0627\u0644\u0646\u0635\u064a\u0628 \u0645\u0646 \u062b\u0645\u0631\u0629 \u0627\u0644\u0639\u0644\u0645 \u0627\u0655\u0630 \u062b\u0645\u0631\u0629", "cuts": [[1298, 1298], [1273, 1278], [1243, 1243], [1228, 1228], [1213, 1213], [1203, 1203], [1188, 1188], [1173, 1173], [1158, 1158], [1138, 1143], [1128, 1133], [1108, 1108], [1082, 1087], [1072, 1072], [1057, 1062], [1047, 1052], [1027, 1027], [1007, 1007], [987, 992], [977, 977], [947, 947], [922, 922], [907, 907], [892, 892], [872, 877], [862, 862], [842, 842], [827, 827], [817, 822], [797, 802], [782, 782], [762, 767], [757, 757], [742, 742], [727, 727], [707, 707], [687, 687], [667, 672], [651, 651], [636, 641], [626, 631], [621, 621], [616, 616], [601, 601], [581, 586], [571, 571], [556, 561], [546, 546], [531, 536], [501, 506], [491, 496], [481, 486], [471, 471], [451, 451], [426, 426], [396, 401], [366, 371], [356, 356], [331, 331], [306, 311], [296, 301], [281, 281], [261, 261], [246, 246], [226, 231], [216, 221], [205, 210], [190, 190], [175, 175], [150, 155], [135, 135], [125, 130], [115, 120], [110, 110], [90, 95], [80, 80], [65, 65], [45, 45], [30, 30]], "confidences": [0.9999417066574097, 0.9999945163726807, 0.9999974966049194, 0.999985933303833, 0.9999626874923706, 0.99998939037323, 0.9999910593032837, 0.9999959468841553, 0.756621778011322, 0.9993665814399719, 0.9999896287918091, 0.9500626921653748, 0.9996504783630371, 0.9999996423721313, 0.9999978542327881, 0.9999961853027344, 0.9998416900634766, 0.9999421834945679, 0.9999947547912598, 0.9550725221633911, 0.999901294708252, 0.999994158744812, 0.99993896484375, 0.9998705387115479, 0.9994296431541443, 0.9963602423667908, 0.9768261909484863, 0.9845020174980164, 0.9991129040718079, 0.9998034834861755, 0.9999189376831055, 0.9999998807907104, 0.9999986886978149, 0.9999935626983643, 0.9999996423721313, 0.9999974966049194, 0.999970555305481, 0.9999955892562866, 0.9999997615814209, 1.0, 0.9998965263366699, 0.9999996423721313, 0.9999357461929321, 0.7053399085998535, 0.9999959468841553, 0.9999908208847046, 0.9990590214729309, 0.9999946355819702, 0.9999847412109375, 0.9993951320648193, 0.9999172687530518, 0.9937519431114197, 0.9999966621398926, 0.9998257756233215, 0.514997661113739, 0.9999998807907104, 0.9999972581863403, 0.9961175918579102, 0.996932864189148, 0.9999979734420776, 0.9999998807907104, 0.9999791383743286, 0.9999998807907104, 0.9914675951004028, 0.9999983310699463, 0.9999983310699463, 0.9999997615814209, 0.9999996423721313, 0.9999963045120239, 0.9999997615814209, 0.9999983310699463, 0.9999831914901733, 1.0, 0.9999969005584717, 0.999922513961792, 0.6258425116539001, 0.9992828965187073, 0.9999939203262329, 0.9998599290847778], "line": {"boundary": [[152, 1951], [155, 1912], [163, 1906], [192, 1923], [214, 1904], [255, 1904], [288, 1923], [311, 1902], [329, 1912], [354, 1902], [368, 1912], [387, 1908], [409, 1925], [440, 1904], [460, 1921], [520, 1919], [543, 1929], [575, 1927], [617, 1904], [635, 1906], [653, 1921], [670, 1906], [684, 1912], [695, 1904], [734, 1919], [762, 1898], [795, 1908], [814, 1925], [840, 1906], [859, 1917], [873, 1908], [896, 1908], [914, 1923], [931, 1923], [955, 1906], [984, 1908], [1015, 1929], [1040, 1908], [1070, 1929], [1085, 1921], [1122, 1925], [1140, 1912], [1159, 1921], [1194, 1908], [1230, 1925], [1278, 1929], [1306, 1908], [1327, 1921], [1393, 1906], [1417, 1927], [1438, 1925], [1471, 1953], [1434, 1976], [1387, 1964], [1368, 1976], [1343, 1966], [1319, 1976], [1278, 1958], [1253, 1978], [1230, 1980], [1212, 1964], [1150, 1974], [1128, 1960], [1115, 1974], [1085, 1964], [1056, 1976], [1042, 1970], [1029, 1984], [1005, 1970], [951, 1972], [933, 1984], [902, 1958], [826, 1962], [795, 1976], [775, 1958], [742, 1962], [727, 1955], [658, 1980], [631, 1960], [534, 1974], [506, 1955], [463, 1976], [440, 1958], [387, 1972], [368, 1955], [313, 1960], [284, 1980], [233, 1955], [185, 1972], [155, 1955]], "baseline": [[153, 1953], [830, 1951], [916, 1953], [941, 1962], [961, 1958], [1419, 1960], [1473, 1955]], "id": "209f5fd7-70ce-4f0f-85ab-dadef361c176", "type": "baselines"}}, {"prediction": "\u0627\u0644\u0639\u0644\u0645 \u0627\u0644\u0645\u0639\u0627\u0645\u0644\u0629 \u060c \u0648\u0642\u0644\u064a\u0644 \u0627\u0644\u0646\u0635\u064a\u0628 \u0645\u0646 \u0627\u0644\u0645\u062d\u0628\u0629 \u0648\u0627\u0644\u062e\u0634\u064a\u0629\u060c \u0648( \u0627\u0655\u0646\u0645\u0627 \u064a\u062e\u0634\u0649 \u0627\u0644\u0644\u0647 \u0645\u0646 \u0639\u0628\u0627\u062f\u0648", "cuts": [[1303, 1303], [1290, 1290], [1272, 1272], [1260, 1260], [1235, 1235], [1211, 1217], [1198, 1204], [1186, 1192], [1168, 1168], [1149, 1149], [1131, 1137], [1118, 1118], [1106, 1106], [1088, 1088], [1081, 1081], [1075, 1075], [1051, 1057], [1038, 1038], [1014, 1014], [1002, 1002], [989, 989], [977, 977], [940, 946], [928, 934], [916, 922], [903, 903], [885, 885], [860, 860], [836, 836], [793, 799], [780, 780], [756, 762], [725, 731], [719, 719], [707, 713], [688, 688], [664, 664], [645, 645], [633, 633], [602, 608], [590, 596], [571, 578], [559, 565], [535, 535], [516, 516], [492, 492], [479, 479], [461, 461], [436, 442], [424, 424], [399, 399], [381, 381], [369, 375], [363, 363], [356, 356], [338, 338], [326, 326], [313, 320], [301, 301], [289, 289], [270, 270], [246, 252], [221, 227], [215, 215], [203, 209], [191, 191], [178, 184], [160, 166], [154, 154], [135, 135], [104, 111], [92, 92], [80, 80], [61, 61], [49, 49], [37, 37]], "confidences": [0.9999939203262329, 0.9962407350540161, 0.6148149967193604, 0.9995049238204956, 0.9999679327011108, 0.9998955726623535, 0.9999966621398926, 0.9999812841415405, 0.9999535083770752, 0.9999920129776001, 0.9999990463256836, 0.9946582913398743, 0.7702367901802063, 0.9436891078948975, 0.9403108358383179, 0.874081015586853, 0.9999908208847046, 0.9999887943267822, 0.999915599822998, 0.9999622106552124, 0.9999816417694092, 0.9999862909317017, 0.9991334080696106, 0.9935688972473145, 0.9999759197235107, 0.9654875993728638, 0.9996823072433472, 0.9990022778511047, 0.564159095287323, 0.9998770952224731, 0.9986807703971863, 0.9983581900596619, 0.9994006156921387, 0.9421390295028687, 0.9977754950523376, 0.9938074946403503, 0.9999985694885254, 0.9493253231048584, 0.9998681545257568, 0.9999979734420776, 0.9999966621398926, 0.9997482895851135, 0.9999991655349731, 0.9999998807907104, 0.9024093747138977, 0.8440074920654297, 0.9990659356117249, 0.9999818801879883, 0.98737633228302, 0.9999896287918091, 0.9998379945755005, 0.9999996423721313, 0.9999951124191284, 0.9999514818191528, 0.9996600151062012, 0.971716046333313, 0.9997962117195129, 0.999996542930603, 0.9999830722808838, 0.9999959468841553, 0.9999998807907104, 0.999964714050293, 0.9999979734420776, 0.5567030906677246, 0.999757707118988, 0.8596031069755554, 0.998282790184021, 0.7934605479240417, 0.9993875026702881, 0.9990580677986145, 0.9998717308044434, 0.9960359930992126, 0.998126208782196, 0.9995434880256653, 0.5599195957183838, 0.99636310338974], "line": {"boundary": [[152, 2046], [165, 2009], [218, 1982], [247, 2005], [269, 1986], [300, 2005], [331, 1984], [358, 1982], [378, 1999], [401, 1982], [440, 1980], [458, 1993], [483, 1982], [512, 1995], [530, 1986], [555, 2009], [582, 2019], [610, 1993], [637, 2003], [647, 1995], [686, 1999], [701, 1990], [740, 2013], [771, 1995], [789, 2011], [816, 2015], [851, 1993], [879, 2015], [894, 2007], [927, 2009], [939, 2019], [947, 2011], [966, 2019], [1011, 2017], [1050, 1992], [1089, 2017], [1109, 1999], [1124, 2003], [1138, 1995], [1157, 1997], [1191, 2025], [1222, 1997], [1237, 1995], [1255, 2005], [1270, 1997], [1300, 2015], [1329, 1993], [1362, 2019], [1391, 1995], [1409, 2005], [1430, 1995], [1463, 2021], [1469, 2040], [1438, 2056], [1395, 2056], [1378, 2071], [1347, 2054], [1230, 2058], [1196, 2048], [1171, 2068], [1144, 2058], [1093, 2071], [1072, 2052], [966, 2066], [939, 2044], [898, 2070], [865, 2052], [799, 2052], [777, 2066], [750, 2044], [725, 2064], [680, 2050], [623, 2062], [584, 2044], [567, 2060], [541, 2060], [528, 2071], [497, 2066], [483, 2054], [436, 2058], [424, 2050], [389, 2062], [368, 2060], [356, 2048], [309, 2048], [294, 2060], [259, 2058], [251, 2050], [228, 2060], [202, 2050], [187, 2058], [155, 2054]], "baseline": [[153, 2048], [491, 2048], [604, 2042], [816, 2042], [883, 2046], [1470, 2042]], "id": "d2a24751-e15b-4046-a50f-1b6734287128", "type": "baselines"}}, {"prediction": "\u0627\u0644\u0639\u0644\u0645\u0648\u0627).", "cuts": [[358, 365], [352, 352], [338, 338], [325, 325], [311, 311], [291, 291], [270, 277], [250, 250], [34, 41]], "confidences": [0.9943757057189941, 0.7291178107261658, 0.9799628257751465, 0.7520625591278076, 0.6908395886421204, 0.9932884573936462, 0.9999109506607056, 0.48956286907196045, 0.997548520565033], "line": {"boundary": [[1099, 2132], [1101, 2107], [1132, 2075], [1212, 2099], [1228, 2081], [1245, 2089], [1280, 2077], [1309, 2097], [1352, 2064], [1378, 2079], [1446, 2077], [1465, 2097], [1471, 2126], [1442, 2144], [1384, 2146], [1343, 2165], [1327, 2165], [1304, 2144], [1284, 2153], [1247, 2140], [1208, 2157], [1193, 2144], [1134, 2149], [1107, 2132]], "baseline": [[1101, 2134], [1473, 2128]], "id": "c9a4534a-dd8f-43bc-b94d-b6ffba2f07f9", "type": "baselines"}}], "regions": {"text": [{"id": "e85748f7-d261-4906-9d1d-9dc0074c03e1", "boundary": [[0, 0], [1655, 0], [1655, 2339], [0, 2339]], "imagename": null, "tags": null}]}} \ No newline at end of file diff --git a/tests/resources/records.json b/tests/resources/records.json index 4e328b55a..b7b102f96 100644 --- a/tests/resources/records.json +++ b/tests/resources/records.json @@ -1 +1 @@ -[{"prediction": "\u0645\u0627\u0644\u0633\u0644\u0627 \u0647\u064a\u0644\u0639 \u0647\u062a\u0627\u0632\u062c\u0639\u0645 \u064a\u0641", "cuts": [[[1437, 119], [1437, 256], [1449, 256], [1449, 119]], [[1484, 119], [1484, 256], [1496, 256], [1496, 119]], [[1508, 119], [1508, 256], [1520, 256], [1520, 119]], [[1568, 119], [1568, 256], [1568, 256], [1568, 119]], [[1603, 119], [1603, 256], [1603, 256], [1603, 119]], [[1615, 119], [1615, 256], [1627, 256], [1627, 119]], [[1639, 119], [1639, 256], [1639, 256], [1639, 119]], [[1663, 119], [1663, 256], [1674, 256], [1674, 119]], [[1698, 119], [1698, 256], [1698, 256], [1698, 119]], [[1722, 119], [1722, 256], [1734, 256], [1734, 119]], [[1746, 119], [1746, 256], [1758, 256], [1758, 119]], [[1793, 119], [1793, 256], [1805, 256], [1805, 119]], [[1817, 119], [1817, 256], [1829, 256], [1829, 119]], [[1853, 119], [1853, 256], [1853, 256], [1853, 119]], [[1876, 119], [1876, 256], [1888, 256], [1888, 119]], [[1924, 119], [1924, 256], [1936, 256], [1936, 119]], [[1959, 119], [1959, 256], [1971, 256], [1971, 119]], [[2007, 119], [2007, 256], [2019, 256], [2019, 119]], [[2054, 119], [2054, 256], [2054, 256], [2054, 119]], [[2078, 119], [2078, 256], [2090, 256], [2090, 119]], [[2149, 119], [2149, 256], [2149, 256], [2149, 119]], [[2161, 119], [2161, 256], [2173, 256], [2173, 119]]], "confidences": [0.9999573230743408, 0.8209527730941772, 0.9964040517807007, 0.667267382144928, 0.9973480701446533, 0.9998679161071777, 0.9999040365219116, 0.999890923500061, 0.8255242109298706, 0.9995707869529724, 0.9976646900177002, 0.9889178276062012, 0.9869346618652344, 0.9966957569122314, 0.9958117008209229, 0.980436384677887, 0.9956343770027161, 0.9984177350997925, 0.762851893901825, 0.9997056126594543, 0.9977449178695679, 0.9985793828964233], "line": [[1437, 119], [1437, 256], [2185, 256], [2185, 119]]}, {"prediction": "(294)", "cuts": [[[2838, 119], [2838, 265], [2838, 265], [2838, 119]], [[2904, 119], [2904, 265], [2904, 265], [2904, 119]], [[2954, 119], [2954, 265], [2954, 265], [2954, 119]], [[3019, 119], [3019, 265], [3019, 265], [3019, 119]], [[3069, 119], [3069, 265], [3085, 265], [3085, 119]]], "confidences": [0.5292237997055054, 0.9998055100440979, 0.9602159857749939, 0.9988068342208862, 0.994594395160675], "line": [[2821, 119], [2821, 265], [3119, 265], [3119, 119]]}, {"prediction": "\u0627\u0630\u0643\u0648 \u0627\u0630\u0643\u062a\u0642\u0648 \u0641 \u0646\u064a\u0646\u0645\u0654\u0648\u0645\u0644\u0627 \u0631\u064a\u0645\u0654\u0627 \u0627\u0647\u0628 \u0647\u0645\u0631\u0643\u0627 \u0649\u062a\u0644\u0627 \u0629\u0639\u0627\u0631\u062f\u0644\u0627 \u0647\u064a\u0644\u0627 \u0644\u0645\u062d \u062f\u0642\u0648 \u0629\u0646\u0633 \u0644\u0643 \u064a\u0641", "cuts": [[[358, 365], [358, 542], [358, 542], [358, 365]], [[402, 365], [402, 542], [402, 542], [402, 365]], [[460, 365], [460, 542], [474, 542], [474, 365]], [[648, 365], [648, 542], [663, 542], [663, 365]], [[677, 365], [677, 542], [706, 542], [706, 365]], [[735, 365], [735, 542], [750, 542], [750, 365]], [[779, 365], [779, 542], [779, 542], [779, 365]], [[852, 365], [852, 542], [852, 542], [852, 365]], [[939, 365], [939, 542], [939, 542], [939, 365]], [[997, 365], [997, 542], [997, 542], [997, 365]], [[1055, 365], [1055, 542], [1055, 542], [1055, 365]], [[1069, 365], [1069, 542], [1084, 542], [1084, 365]], [[1127, 365], [1127, 542], [1142, 542], [1142, 365]], [[1171, 365], [1171, 542], [1171, 542], [1171, 365]], [[1200, 365], [1200, 542], [1215, 542], [1215, 365]], [[1229, 365], [1229, 542], [1244, 542], [1244, 365]], [[1273, 365], [1273, 542], [1273, 542], [1273, 365]], [[1302, 365], [1302, 542], [1302, 542], [1302, 365]], [[1345, 365], [1345, 542], [1345, 542], [1345, 365]], [[1360, 365], [1360, 542], [1360, 542], [1360, 365]], [[1374, 365], [1374, 542], [1374, 542], [1374, 365]], [[1389, 365], [1389, 542], [1403, 542], [1403, 365]], [[1418, 365], [1418, 542], [1432, 542], [1432, 365]], [[1447, 365], [1447, 542], [1447, 542], [1447, 365]], [[1490, 365], [1490, 542], [1490, 542], [1490, 365]], [[1505, 365], [1505, 542], [1519, 542], [1519, 365]], [[1548, 365], [1548, 542], [1548, 542], [1548, 365]], [[1578, 365], [1578, 542], [1578, 542], [1578, 365]], [[1592, 365], [1592, 542], [1592, 542], [1592, 365]], [[1607, 365], [1607, 542], [1621, 542], [1621, 365]], [[1650, 365], [1650, 542], [1650, 542], [1650, 365]], [[1665, 365], [1665, 542], [1679, 542], [1679, 365]], [[1694, 365], [1694, 542], [1708, 542], [1708, 365]], [[1723, 365], [1723, 542], [1737, 542], [1737, 365]], [[1766, 365], [1766, 542], [1766, 542], [1766, 365]], [[1810, 365], [1810, 542], [1810, 542], [1810, 365]], [[1853, 365], [1853, 542], [1868, 542], [1868, 365]], [[1882, 365], [1882, 542], [1882, 542], [1882, 365]], [[1955, 365], [1955, 542], [1969, 542], [1969, 365]], [[1984, 365], [1984, 542], [1999, 542], [1999, 365]], [[2057, 365], [2057, 542], [2057, 542], [2057, 365]], [[2071, 365], [2071, 542], [2071, 542], [2071, 365]], [[2086, 365], [2086, 542], [2100, 542], [2100, 365]], [[2115, 365], [2115, 542], [2115, 542], [2115, 365]], [[2129, 365], [2129, 542], [2144, 542], [2144, 365]], [[2173, 365], [2173, 542], [2173, 542], [2173, 365]], [[2202, 365], [2202, 542], [2202, 542], [2202, 365]], [[2245, 365], [2245, 542], [2260, 542], [2260, 365]], [[2303, 365], [2303, 542], [2303, 542], [2303, 365]], [[2332, 365], [2332, 542], [2347, 542], [2347, 365]], [[2361, 365], [2361, 542], [2361, 542], [2361, 365]], [[2376, 365], [2376, 542], [2391, 542], [2391, 365]], [[2405, 365], [2405, 542], [2420, 542], [2420, 365]], [[2434, 365], [2434, 542], [2449, 542], [2449, 365]], [[2478, 365], [2478, 542], [2478, 542], [2478, 365]], [[2492, 365], [2492, 542], [2507, 542], [2507, 365]], [[2521, 365], [2521, 542], [2521, 542], [2521, 365]], [[2536, 365], [2536, 542], [2550, 542], [2550, 365]], [[2594, 365], [2594, 542], [2594, 542], [2594, 365]], [[2623, 365], [2623, 542], [2623, 542], [2623, 365]], [[2637, 365], [2637, 542], [2652, 542], [2652, 365]], [[2681, 365], [2681, 542], [2695, 542], [2695, 365]], [[2724, 365], [2724, 542], [2724, 542], [2724, 365]], [[2753, 365], [2753, 542], [2753, 542], [2753, 365]], [[2812, 365], [2812, 542], [2812, 542], [2812, 365]], [[2841, 365], [2841, 542], [2855, 542], [2855, 365]], [[2870, 365], [2870, 542], [2870, 542], [2870, 365]], [[2899, 365], [2899, 542], [2899, 542], [2899, 365]], [[2942, 365], [2942, 542], [2957, 542], [2957, 365]], [[2986, 365], [2986, 542], [3000, 542], [3000, 365]], [[3058, 365], [3058, 542], [3058, 542], [3058, 365]], [[3073, 365], [3073, 542], [3073, 542], [3073, 365]], [[3116, 365], [3116, 542], [3131, 542], [3131, 365]], [[3145, 365], [3145, 542], [3145, 542], [3145, 365]], [[3160, 365], [3160, 542], [3174, 542], [3174, 365]]], "confidences": [0.9912043213844299, 0.9701831340789795, 0.9999876022338867, 0.9710670113563538, 0.9999645948410034, 0.9999040365219116, 0.9999904632568359, 0.9276049733161926, 0.9949396848678589, 0.9172694683074951, 0.9944435954093933, 0.9998708963394165, 0.9994745850563049, 0.9999909400939941, 0.9929296374320984, 0.9996275901794434, 0.9654240012168884, 0.9978482723236084, 0.8331916928291321, 0.9914954900741577, 0.9862202405929565, 0.9967150688171387, 0.9994681477546692, 0.9422079920768738, 0.9996860027313232, 0.9978721141815186, 0.590356171131134, 0.9853919148445129, 0.9949153661727905, 0.9330083131790161, 0.902510941028595, 0.9991570711135864, 0.9937306642532349, 0.9962736368179321, 0.9969527721405029, 0.9955304265022278, 0.9975927472114563, 0.931627094745636, 0.9910709857940674, 0.994668185710907, 0.9977930784225464, 0.9947988986968994, 0.99834144115448, 0.9974583983421326, 0.9996436834335327, 0.9992924928665161, 0.9981145858764648, 0.9989506006240845, 0.9970920085906982, 0.9896613359451294, 0.6772571206092834, 0.9618531465530396, 0.9992746710777283, 0.998647153377533, 0.834701657295227, 0.9992173910140991, 0.9594298601150513, 0.9998399019241333, 0.9885919690132141, 0.9965444207191467, 0.9950836896896362, 0.8906474709510803, 0.8982319831848145, 0.520612359046936, 0.9998024106025696, 0.9991157650947571, 0.9997696280479431, 0.9865208864212036, 0.9990457892417908, 0.9984292387962341, 0.8027671575546265, 0.9970871806144714, 0.9498249292373657, 0.9898852109909058, 0.9846636056900024], "line": [[358, 365], [358, 542], [3189, 542], [3189, 365]]}, {"prediction": "\u0646\u0627\u0643 \u0646\u0627\u0641 \u0644\u0627\u062d\u0644\u0627 \u0647\u0630\u0647 \u0646\u0639 \u0646\u063a\u0634\u0643\u0627\u0644 \u0644\u0627\u0642\u0648 \u0627\u062f\u062f\u0634 \u064b\u0627\u0640\u0628\u0646\u063a \u0628\u0636\u063a\u0648 \u0643\u0644\u0630\u0644 \u062f\u064a\u0634\u0631\u0644\u0627 \u0637\u0627\u0634\u062a\u0633\u0627\u0641", "cuts": [[[364, 534], [364, 695], [377, 695], [377, 534]], [[405, 534], [405, 695], [405, 695], [405, 534]], [[419, 534], [419, 695], [419, 695], [419, 534]], [[460, 534], [460, 695], [487, 695], [487, 534]], [[514, 534], [514, 695], [528, 695], [528, 534]], [[555, 534], [555, 695], [555, 695], [555, 534]], [[569, 534], [569, 695], [583, 695], [583, 534]], [[610, 534], [610, 695], [624, 695], [624, 534]], [[665, 534], [665, 695], [665, 695], [665, 534]], [[679, 534], [679, 695], [692, 695], [692, 534]], [[720, 534], [720, 695], [720, 695], [720, 534]], [[733, 534], [733, 695], [747, 695], [747, 534]], [[774, 534], [774, 695], [788, 695], [788, 534]], [[802, 534], [802, 695], [816, 695], [816, 534]], [[829, 534], [829, 695], [843, 695], [843, 534]], [[884, 534], [884, 695], [884, 695], [884, 534]], [[911, 534], [911, 695], [925, 695], [925, 534]], [[952, 534], [952, 695], [966, 695], [966, 534]], [[1007, 534], [1007, 695], [1007, 695], [1007, 534]], [[1048, 534], [1048, 695], [1048, 695], [1048, 534]], [[1089, 534], [1089, 695], [1103, 695], [1103, 534]], [[1144, 534], [1144, 695], [1144, 695], [1144, 534]], [[1185, 534], [1185, 695], [1185, 695], [1185, 534]], [[1240, 534], [1240, 695], [1240, 695], [1240, 534]], [[1295, 534], [1295, 695], [1295, 695], [1295, 534]], [[1363, 534], [1363, 695], [1377, 695], [1377, 534]], [[1391, 534], [1391, 695], [1404, 695], [1404, 534]], [[1418, 534], [1418, 695], [1432, 695], [1432, 534]], [[1473, 534], [1473, 695], [1486, 695], [1486, 534]], [[1500, 534], [1500, 695], [1500, 695], [1500, 534]], [[1514, 534], [1514, 695], [1514, 695], [1514, 534]], [[1568, 534], [1568, 695], [1582, 695], [1582, 534]], [[1596, 534], [1596, 695], [1623, 695], [1623, 534]], [[1651, 534], [1651, 695], [1664, 695], [1664, 534]], [[1692, 534], [1692, 695], [1705, 695], [1705, 534]], [[1760, 534], [1760, 695], [1774, 695], [1774, 534]], [[1815, 534], [1815, 695], [1829, 695], [1829, 534]], [[1842, 534], [1842, 695], [1856, 695], [1856, 534]], [[1870, 534], [1870, 695], [1870, 695], [1870, 534]], [[1883, 534], [1883, 695], [1883, 695], [1883, 534]], [[1924, 534], [1924, 695], [1952, 695], [1952, 534]], [[2034, 534], [2034, 695], [2034, 695], [2034, 534]], [[2075, 534], [2075, 695], [2075, 695], [2075, 534]], [[2130, 534], [2130, 695], [2143, 695], [2143, 534]], [[2171, 534], [2171, 695], [2185, 695], [2185, 534]], [[2226, 534], [2226, 695], [2226, 695], [2226, 534]], [[2308, 534], [2308, 695], [2308, 695], [2308, 534]], [[2363, 534], [2363, 695], [2363, 695], [2363, 534]], [[2431, 534], [2431, 695], [2431, 695], [2431, 534]], [[2458, 534], [2458, 695], [2472, 695], [2472, 534]], [[2527, 534], [2527, 695], [2527, 695], [2527, 534]], [[2582, 534], [2582, 695], [2582, 695], [2582, 534]], [[2609, 534], [2609, 695], [2623, 695], [2623, 534]], [[2650, 534], [2650, 695], [2650, 695], [2650, 534]], [[2664, 534], [2664, 695], [2677, 695], [2677, 534]], [[2705, 534], [2705, 695], [2705, 695], [2705, 534]], [[2732, 534], [2732, 695], [2746, 695], [2746, 534]], [[2787, 534], [2787, 695], [2787, 695], [2787, 534]], [[2828, 534], [2828, 695], [2828, 695], [2828, 534]], [[2842, 534], [2842, 695], [2855, 695], [2855, 534]], [[2869, 534], [2869, 695], [2869, 695], [2869, 534]], [[2883, 534], [2883, 695], [2896, 695], [2896, 534]], [[2938, 534], [2938, 695], [2938, 695], [2938, 534]], [[2992, 534], [2992, 695], [2992, 695], [2992, 534]], [[3033, 534], [3033, 695], [3033, 695], [3033, 534]], [[3061, 534], [3061, 695], [3074, 695], [3074, 534]], [[3102, 534], [3102, 695], [3129, 695], [3129, 534]], [[3143, 534], [3143, 695], [3157, 695], [3157, 534]], [[3170, 534], [3170, 695], [3170, 695], [3170, 534]]], "confidences": [0.9985698461532593, 0.9999438524246216, 0.9994581341743469, 0.9928075075149536, 0.9999692440032959, 0.9775535464286804, 0.9994893074035645, 0.9992764592170715, 0.9999681711196899, 0.999976634979248, 0.5837891101837158, 0.9994819760322571, 0.9999487400054932, 0.6378462314605713, 0.9995593428611755, 0.9997476935386658, 0.9998375177383423, 0.9999092817306519, 0.9706422686576843, 0.9981306195259094, 0.999731719493866, 0.9984344840049744, 0.9999045133590698, 0.9998281002044678, 0.9965876340866089, 0.9981406927108765, 0.9998824596405029, 0.9994693398475647, 0.9985535740852356, 0.9371808171272278, 0.9999727010726929, 0.9998328685760498, 0.9986041188240051, 0.9998788833618164, 0.9969847798347473, 0.9979107975959778, 0.9678859114646912, 0.9965589642524719, 0.9958305954933167, 0.856151819229126, 0.9174501299858093, 0.977839469909668, 0.9722991585731506, 0.999629020690918, 0.9988741278648376, 0.5952739119529724, 0.8313284516334534, 0.6637697815895081, 0.9957481026649475, 0.9997797608375549, 0.99960857629776, 0.6831809282302856, 0.9987292885780334, 0.9998849630355835, 0.9514357447624207, 0.9951800107955933, 0.9758328795433044, 0.9967122077941895, 0.7513812184333801, 0.9999700784683228, 0.9964326620101929, 0.9970759153366089, 0.9622002243995667, 0.7601332068443298, 0.9990935325622559, 0.9990278482437134, 0.9987155199050903, 0.9829656481742859, 0.6010854244232178], "line": [[350, 534], [350, 695], [3184, 695], [3184, 534]]}, {"prediction": "\u0647\u064a\u062f\u064a \u0646\u064a\u0628 \u0644\u062b\u0645 \u0627\u0644\u0641 \u0646\u064a\u0637\u0642\u064a \u0646\u0628 \u0649\u0644\u0639 \u0631\u0627\u0636\u062d\u0627\u0628 \u062a\u0642\u0648\u0644\u0627 \u064a\u0641 \u0630\u0641\u0646\u0627\u0648 \u0647\u0633\u0641\u0646 \u062a\u0642\u0647\u0632\u0654\u0627 \u0644\u0648\u0642\u062a \u0627\u0645\u0643 \u0645\u0627\u0644\u0627", "cuts": [[[349, 703], [349, 875], [362, 875], [362, 703]], [[389, 703], [389, 875], [389, 875], [389, 703]], [[428, 703], [428, 875], [441, 875], [441, 703]], [[455, 703], [455, 875], [468, 875], [468, 703]], [[481, 703], [481, 875], [494, 875], [494, 703]], [[534, 703], [534, 875], [547, 875], [547, 703]], [[560, 703], [560, 875], [560, 875], [560, 703]], [[586, 703], [586, 875], [586, 875], [586, 703]], [[613, 703], [613, 875], [626, 875], [626, 703]], [[666, 703], [666, 875], [679, 875], [679, 703]], [[705, 703], [705, 875], [705, 875], [705, 703]], [[731, 703], [731, 875], [731, 875], [731, 703]], [[758, 703], [758, 875], [771, 875], [771, 703]], [[797, 703], [797, 875], [797, 875], [797, 703]], [[837, 703], [837, 875], [837, 875], [837, 703]], [[863, 703], [863, 875], [863, 875], [863, 703]], [[890, 703], [890, 875], [903, 875], [903, 703]], [[956, 703], [956, 875], [956, 875], [956, 703]], [[982, 703], [982, 875], [982, 875], [982, 703]], [[1008, 703], [1008, 875], [1022, 875], [1022, 703]], [[1074, 703], [1074, 875], [1074, 875], [1074, 703]], [[1101, 703], [1101, 875], [1101, 875], [1101, 703]], [[1127, 703], [1127, 875], [1140, 875], [1140, 703]], [[1180, 703], [1180, 875], [1193, 875], [1193, 703]], [[1206, 703], [1206, 875], [1219, 875], [1219, 703]], [[1233, 703], [1233, 875], [1246, 875], [1246, 703]], [[1312, 703], [1312, 875], [1312, 875], [1312, 703]], [[1325, 703], [1325, 875], [1338, 875], [1338, 703]], [[1351, 703], [1351, 875], [1351, 875], [1351, 703]], [[1391, 703], [1391, 875], [1404, 875], [1404, 703]], [[1444, 703], [1444, 875], [1444, 875], [1444, 703]], [[1470, 703], [1470, 875], [1483, 875], [1483, 703]], [[1509, 703], [1509, 875], [1523, 875], [1523, 703]], [[1575, 703], [1575, 875], [1589, 875], [1589, 703]], [[1628, 703], [1628, 875], [1641, 875], [1641, 703]], [[1655, 703], [1655, 875], [1655, 875], [1655, 703]], [[1681, 703], [1681, 875], [1694, 875], [1694, 703]], [[1747, 703], [1747, 875], [1747, 875], [1747, 703]], [[1800, 703], [1800, 875], [1800, 875], [1800, 703]], [[1839, 703], [1839, 875], [1852, 875], [1852, 703]], [[1865, 703], [1865, 875], [1879, 875], [1879, 703]], [[1892, 703], [1892, 875], [1905, 875], [1905, 703]], [[1918, 703], [1918, 875], [1918, 875], [1918, 703]], [[1958, 703], [1958, 875], [1958, 875], [1958, 703]], [[1971, 703], [1971, 875], [1971, 875], [1971, 703]], [[1997, 703], [1997, 875], [2011, 875], [2011, 703]], [[2050, 703], [2050, 875], [2050, 875], [2050, 703]], [[2090, 703], [2090, 875], [2090, 875], [2090, 703]], [[2116, 703], [2116, 875], [2116, 875], [2116, 703]], [[2156, 703], [2156, 875], [2156, 875], [2156, 703]], [[2195, 703], [2195, 875], [2208, 875], [2208, 703]], [[2222, 703], [2222, 875], [2235, 875], [2235, 703]], [[2261, 703], [2261, 875], [2261, 875], [2261, 703]], [[2301, 703], [2301, 875], [2327, 875], [2327, 703]], [[2353, 703], [2353, 875], [2367, 875], [2367, 703]], [[2380, 703], [2380, 875], [2393, 875], [2393, 703]], [[2406, 703], [2406, 875], [2419, 875], [2419, 703]], [[2472, 703], [2472, 875], [2472, 875], [2472, 703]], [[2538, 703], [2538, 875], [2538, 875], [2538, 703]], [[2564, 703], [2564, 875], [2578, 875], [2578, 703]], [[2617, 703], [2617, 875], [2630, 875], [2630, 703]], [[2643, 703], [2643, 875], [2657, 875], [2657, 703]], [[2670, 703], [2670, 875], [2670, 875], [2670, 703]], [[2683, 703], [2683, 875], [2696, 875], [2696, 703]], [[2749, 703], [2749, 875], [2749, 875], [2749, 703]], [[2789, 703], [2789, 875], [2789, 875], [2789, 703]], [[2828, 703], [2828, 875], [2828, 875], [2828, 703]], [[2854, 703], [2854, 875], [2854, 875], [2854, 703]], [[2881, 703], [2881, 875], [2894, 875], [2894, 703]], [[2907, 703], [2907, 875], [2907, 875], [2907, 703]], [[2920, 703], [2920, 875], [2934, 875], [2934, 703]], [[2947, 703], [2947, 875], [2947, 875], [2947, 703]], [[2973, 703], [2973, 875], [2986, 875], [2986, 703]], [[3065, 703], [3065, 875], [3065, 875], [3065, 703]], [[3105, 703], [3105, 875], [3118, 875], [3118, 703]], [[3131, 703], [3131, 875], [3145, 875], [3145, 703]], [[3158, 703], [3158, 875], [3171, 875], [3171, 703]]], "confidences": [0.9978576302528381, 0.9365259408950806, 0.9993323683738708, 0.9999881982803345, 0.995057225227356, 0.9951127171516418, 0.9600062966346741, 0.9987906813621521, 0.9990148544311523, 0.9999818801879883, 0.979132354259491, 0.9896356463432312, 0.9996901750564575, 0.9994376301765442, 0.9832384586334229, 0.9919760823249817, 0.9994718432426453, 0.99887615442276, 0.9999384880065918, 0.9999126195907593, 0.9968804121017456, 0.9998698234558105, 0.9996697902679443, 0.9999111890792847, 0.999935507774353, 0.9999529123306274, 0.9985625147819519, 0.9987615346908569, 0.9998716115951538, 0.9999983310699463, 0.9997947812080383, 0.9999533891677856, 0.9956774115562439, 0.9921483993530273, 0.9991742968559265, 0.8690173625946045, 0.9889227151870728, 0.990256130695343, 0.9499505758285522, 0.9942019581794739, 0.992321789264679, 0.9953250885009766, 0.9952391386032104, 0.9987290501594543, 0.9708223342895508, 0.9829660654067993, 0.998319685459137, 0.9980186223983765, 0.9733465313911438, 0.9576543569564819, 0.9972822666168213, 0.9974729418754578, 0.9998486042022705, 0.9998704195022583, 0.9851626753807068, 0.999782145023346, 0.9982467889785767, 0.5265796780586243, 0.9982032775878906, 0.9953908920288086, 0.9945998191833496, 0.9988899827003479, 0.932214617729187, 0.9992570281028748, 0.9942938685417175, 0.9976638555526733, 0.8540834784507751, 0.999862551689148, 0.999956488609314, 0.5673943161964417, 0.9989564418792725, 0.996831476688385, 0.9993277788162231, 0.9955874681472778, 0.995591402053833, 0.9929797053337097, 0.9888700246810913], "line": [[349, 703], [349, 875], [3171, 875], [3171, 703]]}, {"prediction": "\u0645\u0648\u062a\u062e\u0645 \u0637\u0641\u0633 \u064a\u0641 \u0649\u062f\u0646\u0639 \u0646\u064a\u0646\u0645\u0654\u0648\u0645\u0644\u0627 \u0631\u064a\u0645\u0654\u0627\u064a \u0649\u0647 \u0644\u0627\u0642 \u0627\u0647\u0628 \u0643\u062a\u0648\u0633\u0643 \u0649\u062a\u0644\u0627 \u0629\u0639\u0631\u0627\u062f\u0644\u0627 \u0644\u0639\u0641 \u0627\u0645 \u0647\u0644 \u0644\u0627\u0642", "cuts": [[[347, 875], [347, 1045], [360, 1045], [360, 875]], [[414, 875], [414, 1045], [414, 1045], [414, 875]], [[454, 875], [454, 1045], [467, 1045], [467, 875]], [[480, 875], [480, 1045], [480, 1045], [480, 875]], [[494, 875], [494, 1045], [494, 1045], [494, 875]], [[534, 875], [534, 1045], [534, 1045], [534, 875]], [[574, 875], [574, 1045], [574, 1045], [574, 875]], [[641, 875], [641, 1045], [641, 1045], [641, 875]], [[681, 875], [681, 1045], [694, 1045], [694, 875]], [[721, 875], [721, 1045], [721, 1045], [721, 875]], [[761, 875], [761, 1045], [761, 1045], [761, 875]], [[774, 875], [774, 1045], [774, 1045], [774, 875]], [[801, 875], [801, 1045], [814, 1045], [814, 875]], [[854, 875], [854, 1045], [854, 1045], [854, 875]], [[921, 875], [921, 1045], [921, 1045], [921, 875]], [[948, 875], [948, 1045], [948, 1045], [948, 875]], [[974, 875], [974, 1045], [974, 1045], [974, 875]], [[1014, 875], [1014, 1045], [1028, 1045], [1028, 875]], [[1054, 875], [1054, 1045], [1068, 1045], [1068, 875]], [[1081, 875], [1081, 1045], [1094, 1045], [1094, 875]], [[1121, 875], [1121, 1045], [1121, 1045], [1121, 875]], [[1148, 875], [1148, 1045], [1161, 1045], [1161, 875]], [[1201, 875], [1201, 1045], [1201, 1045], [1201, 875]], [[1214, 875], [1214, 1045], [1214, 1045], [1214, 875]], [[1228, 875], [1228, 1045], [1228, 1045], [1228, 875]], [[1241, 875], [1241, 1045], [1255, 1045], [1255, 875]], [[1268, 875], [1268, 1045], [1281, 1045], [1281, 875]], [[1295, 875], [1295, 1045], [1295, 1045], [1295, 875]], [[1335, 875], [1335, 1045], [1348, 1045], [1348, 875]], [[1361, 875], [1361, 1045], [1361, 1045], [1361, 875]], [[1388, 875], [1388, 1045], [1401, 1045], [1401, 875]], [[1415, 875], [1415, 1045], [1428, 1045], [1428, 875]], [[1441, 875], [1441, 1045], [1468, 1045], [1468, 875]], [[1481, 875], [1481, 1045], [1495, 1045], [1495, 875]], [[1508, 875], [1508, 1045], [1535, 1045], [1535, 875]], [[1602, 875], [1602, 1045], [1602, 1045], [1602, 875]], [[1615, 875], [1615, 1045], [1628, 1045], [1628, 875]], [[1655, 875], [1655, 1045], [1682, 1045], [1682, 875]], [[1735, 875], [1735, 1045], [1735, 1045], [1735, 875]], [[1748, 875], [1748, 1045], [1748, 1045], [1748, 875]], [[1775, 875], [1775, 1045], [1775, 1045], [1775, 875]], [[1802, 875], [1802, 1045], [1802, 1045], [1802, 875]], [[1828, 875], [1828, 1045], [1828, 1045], [1828, 875]], [[1882, 875], [1882, 1045], [1895, 1045], [1895, 875]], [[1922, 875], [1922, 1045], [1922, 1045], [1922, 875]], [[1948, 875], [1948, 1045], [1962, 1045], [1962, 875]], [[2015, 875], [2015, 1045], [2029, 1045], [2029, 875]], [[2082, 875], [2082, 1045], [2082, 1045], [2082, 875]], [[2122, 875], [2122, 1045], [2135, 1045], [2135, 875]], [[2175, 875], [2175, 1045], [2189, 1045], [2189, 875]], [[2242, 875], [2242, 1045], [2242, 1045], [2242, 875]], [[2295, 875], [2295, 1045], [2295, 1045], [2295, 875]], [[2336, 875], [2336, 1045], [2336, 1045], [2336, 875]], [[2349, 875], [2349, 1045], [2349, 1045], [2349, 875]], [[2362, 875], [2362, 1045], [2376, 1045], [2376, 875]], [[2389, 875], [2389, 1045], [2389, 1045], [2389, 875]], [[2402, 875], [2402, 1045], [2416, 1045], [2416, 875]], [[2429, 875], [2429, 1045], [2442, 1045], [2442, 875]], [[2456, 875], [2456, 1045], [2469, 1045], [2469, 875]], [[2522, 875], [2522, 1045], [2536, 1045], [2536, 875]], [[2549, 875], [2549, 1045], [2562, 1045], [2562, 875]], [[2589, 875], [2589, 1045], [2602, 1045], [2602, 875]], [[2629, 875], [2629, 1045], [2629, 1045], [2629, 875]], [[2642, 875], [2642, 1045], [2656, 1045], [2656, 875]], [[2669, 875], [2669, 1045], [2669, 1045], [2669, 875]], [[2776, 875], [2776, 1045], [2776, 1045], [2776, 875]], [[2803, 875], [2803, 1045], [2803, 1045], [2803, 875]], [[2843, 875], [2843, 1045], [2843, 1045], [2843, 875]], [[2869, 875], [2869, 1045], [2869, 1045], [2869, 875]], [[2896, 875], [2896, 1045], [2896, 1045], [2896, 875]], [[2923, 875], [2923, 1045], [2923, 1045], [2923, 875]], [[2963, 875], [2963, 1045], [2963, 1045], [2963, 875]], [[2989, 875], [2989, 1045], [2989, 1045], [2989, 875]], [[3016, 875], [3016, 1045], [3030, 1045], [3030, 875]], [[3043, 875], [3043, 1045], [3056, 1045], [3056, 875]], [[3123, 875], [3123, 1045], [3123, 1045], [3123, 875]], [[3136, 875], [3136, 1045], [3150, 1045], [3150, 875]], [[3163, 875], [3163, 1045], [3163, 1045], [3163, 875]]], "confidences": [0.9999685287475586, 0.7677904963493347, 0.9990272521972656, 0.9999896287918091, 0.9995161294937134, 0.9926141500473022, 0.9854110479354858, 0.9970456957817078, 0.9999581575393677, 0.9979928731918335, 0.9991545677185059, 0.9992891550064087, 0.9991033673286438, 0.9999771118164062, 0.9999916553497314, 0.6514611840248108, 0.7720828652381897, 0.9889744520187378, 0.9997015595436096, 0.9999628067016602, 0.8422739505767822, 0.9995865225791931, 0.9994408488273621, 0.9979164004325867, 0.9996676445007324, 0.9994599223136902, 0.9910250902175903, 0.9999783039093018, 0.9963022470474243, 0.6243562698364258, 0.9998552799224854, 0.9986860156059265, 0.999666690826416, 0.66583651304245, 0.9677010178565979, 0.9999728202819824, 0.9999947547912598, 0.9995187520980835, 0.9917859435081482, 0.9952231049537659, 0.988452136516571, 0.9948959946632385, 0.997393012046814, 0.984053909778595, 0.9514071345329285, 0.9919347763061523, 0.9954736828804016, 0.535146176815033, 0.9996613264083862, 0.9889037013053894, 0.9973983764648438, 0.7873133420944214, 0.814349353313446, 0.9971019625663757, 0.9976648092269897, 0.9264478087425232, 0.9916608929634094, 0.9994589686393738, 0.9972610473632812, 0.9991005659103394, 0.999413013458252, 0.9994602799415588, 0.9613319039344788, 0.9990511536598206, 0.9996521472930908, 0.7384001612663269, 0.9942810535430908, 0.9455984234809875, 0.9997734427452087, 0.9952616691589355, 0.9989475607872009, 0.9959900975227356, 0.9930391907691956, 0.9999250173568726, 0.998917818069458, 0.6285094618797302, 0.8354631662368774, 0.9813341498374939], "line": [[347, 875], [347, 1045], [3203, 1045], [3203, 875]]}, {"prediction": "\u0627\u0647\u062a\u0644\u0628\u0642\u0648 \u0627\u0647\u0628 \u0627\u0643\u0631\u0628\u062a \u0627\u0647\u064a\u0644\u0627 \u062a\u0631\u0638\u0646\u0648 \u0637\u0641\u0633\u0644\u0627 \u062a\u062d\u062a\u0641\u0648 \u062a\u062d\u0628\u0635\u0654\u0627 \u0627\u0644\u0641 \u0627\u0647\u0628 \u062a\u0638\u0641\u062a\u062d\u0627 \u062f\u0642 \u0628\u064a\u0637 \u0647\u064a\u0641\u0648", "cuts": [[[349, 1045], [349, 1187], [362, 1187], [362, 1045]], [[375, 1045], [375, 1187], [375, 1187], [375, 1045]], [[425, 1045], [425, 1187], [425, 1187], [425, 1045]], [[451, 1045], [451, 1187], [451, 1187], [451, 1045]], [[476, 1045], [476, 1187], [476, 1187], [476, 1045]], [[502, 1045], [502, 1187], [502, 1187], [502, 1045]], [[552, 1045], [552, 1187], [565, 1187], [565, 1045]], [[578, 1045], [578, 1187], [603, 1187], [603, 1045]], [[629, 1045], [629, 1187], [641, 1187], [641, 1045]], [[692, 1045], [692, 1187], [705, 1187], [705, 1045]], [[730, 1045], [730, 1187], [730, 1187], [730, 1045]], [[756, 1045], [756, 1187], [756, 1187], [756, 1045]], [[781, 1045], [781, 1187], [781, 1187], [781, 1045]], [[794, 1045], [794, 1187], [794, 1187], [794, 1045]], [[857, 1045], [857, 1187], [857, 1187], [857, 1045]], [[870, 1045], [870, 1187], [870, 1187], [870, 1045]], [[896, 1045], [896, 1187], [896, 1187], [896, 1045]], [[921, 1045], [921, 1187], [934, 1187], [934, 1045]], [[959, 1045], [959, 1187], [972, 1187], [972, 1045]], [[984, 1045], [984, 1187], [984, 1187], [984, 1045]], [[1023, 1045], [1023, 1187], [1023, 1187], [1023, 1045]], [[1048, 1045], [1048, 1187], [1048, 1187], [1048, 1045]], [[1061, 1045], [1061, 1187], [1073, 1187], [1073, 1045]], [[1086, 1045], [1086, 1187], [1086, 1187], [1086, 1045]], [[1137, 1045], [1137, 1187], [1137, 1187], [1137, 1045]], [[1213, 1045], [1213, 1187], [1213, 1187], [1213, 1045]], [[1239, 1045], [1239, 1187], [1239, 1187], [1239, 1045]], [[1289, 1045], [1289, 1187], [1289, 1187], [1289, 1045]], [[1340, 1045], [1340, 1187], [1340, 1187], [1340, 1045]], [[1366, 1045], [1366, 1187], [1378, 1187], [1378, 1045]], [[1417, 1045], [1417, 1187], [1417, 1187], [1417, 1045]], [[1480, 1045], [1480, 1187], [1480, 1187], [1480, 1045]], [[1518, 1045], [1518, 1187], [1531, 1187], [1531, 1045]], [[1556, 1045], [1556, 1187], [1556, 1187], [1556, 1045]], [[1569, 1045], [1569, 1187], [1582, 1187], [1582, 1045]], [[1594, 1045], [1594, 1187], [1607, 1187], [1607, 1045]], [[1645, 1045], [1645, 1187], [1645, 1187], [1645, 1045]], [[1709, 1045], [1709, 1187], [1709, 1187], [1709, 1045]], [[1760, 1045], [1760, 1187], [1760, 1187], [1760, 1045]], [[1785, 1045], [1785, 1187], [1785, 1187], [1785, 1045]], [[1836, 1045], [1836, 1187], [1836, 1187], [1836, 1045]], [[1861, 1045], [1861, 1187], [1874, 1187], [1874, 1045]], [[1925, 1045], [1925, 1187], [1925, 1187], [1925, 1045]], [[1988, 1045], [1988, 1187], [1988, 1187], [1988, 1045]], [[2039, 1045], [2039, 1187], [2039, 1187], [2039, 1045]], [[2090, 1045], [2090, 1187], [2090, 1187], [2090, 1045]], [[2128, 1045], [2128, 1187], [2128, 1187], [2128, 1045]], [[2141, 1045], [2141, 1187], [2154, 1187], [2154, 1045]], [[2166, 1045], [2166, 1187], [2179, 1187], [2179, 1045]], [[2204, 1045], [2204, 1187], [2204, 1187], [2204, 1045]], [[2243, 1045], [2243, 1187], [2243, 1187], [2243, 1045]], [[2268, 1045], [2268, 1187], [2268, 1187], [2268, 1045]], [[2293, 1045], [2293, 1187], [2306, 1187], [2306, 1045]], [[2319, 1045], [2319, 1187], [2331, 1187], [2331, 1045]], [[2344, 1045], [2344, 1187], [2357, 1187], [2357, 1045]], [[2370, 1045], [2370, 1187], [2370, 1187], [2370, 1045]], [[2395, 1045], [2395, 1187], [2408, 1187], [2408, 1045]], [[2471, 1045], [2471, 1187], [2471, 1187], [2471, 1045]], [[2522, 1045], [2522, 1187], [2522, 1187], [2522, 1045]], [[2586, 1045], [2586, 1187], [2586, 1187], [2586, 1045]], [[2611, 1045], [2611, 1187], [2611, 1187], [2611, 1045]], [[2649, 1045], [2649, 1187], [2649, 1187], [2649, 1045]], [[2700, 1045], [2700, 1187], [2713, 1187], [2713, 1045]], [[2725, 1045], [2725, 1187], [2725, 1187], [2725, 1045]], [[2764, 1045], [2764, 1187], [2776, 1187], [2776, 1045]], [[2789, 1045], [2789, 1187], [2802, 1187], [2802, 1045]], [[2827, 1045], [2827, 1187], [2827, 1187], [2827, 1045]], [[2865, 1045], [2865, 1187], [2878, 1187], [2878, 1045]], [[2929, 1045], [2929, 1187], [2929, 1187], [2929, 1045]], [[2967, 1045], [2967, 1187], [2967, 1187], [2967, 1045]], [[3005, 1045], [3005, 1187], [3030, 1187], [3030, 1045]], [[3056, 1045], [3056, 1187], [3056, 1187], [3056, 1045]], [[3094, 1045], [3094, 1187], [3094, 1187], [3094, 1045]], [[3119, 1045], [3119, 1187], [3119, 1187], [3119, 1045]], [[3170, 1045], [3170, 1187], [3170, 1187], [3170, 1045]]], "confidences": [0.9951897859573364, 0.9994397759437561, 0.9996225833892822, 0.9996881484985352, 0.9991863369941711, 0.522216796875, 0.9999700784683228, 0.9999351501464844, 0.9608615040779114, 0.9998836517333984, 0.9998319149017334, 0.9999574422836304, 0.9995219707489014, 0.9832330346107483, 0.9993361830711365, 0.9289281964302063, 0.9628921747207642, 0.9998667240142822, 0.9999779462814331, 0.9990992546081543, 0.9141392707824707, 0.9998242259025574, 0.9967435002326965, 0.9999244213104248, 0.9998732805252075, 0.9988711476325989, 0.9987514019012451, 0.9988617897033691, 0.6241702437400818, 0.979806125164032, 0.9399409294128418, 0.9999160766601562, 0.9905988574028015, 0.9990541338920593, 0.9999421834945679, 0.960566520690918, 0.9946881532669067, 0.9999477863311768, 0.833644688129425, 0.9959911704063416, 0.9991551637649536, 0.9996606111526489, 0.9877347350120544, 0.9679129123687744, 0.9596376419067383, 0.986365795135498, 0.9459453225135803, 0.9946697354316711, 0.9895763397216797, 0.9849644303321838, 0.903245210647583, 0.7387762665748596, 0.9958617687225342, 0.9985413551330566, 0.9970884919166565, 0.6466483473777771, 0.9444775581359863, 0.8205862045288086, 0.9942649006843567, 0.984862744808197, 0.9972048401832581, 0.8482417464256287, 0.9919762015342712, 0.9672141671180725, 0.9989006519317627, 0.9998776912689209, 0.9994097948074341, 0.9945313334465027, 0.9889154434204102, 0.9952592253684998, 0.999559223651886, 0.99980229139328, 0.9894945025444031, 0.7909385561943054, 0.9917729496955872], "line": [[349, 1045], [349, 1187], [3183, 1187], [3183, 1045]]}, {"prediction": "\u0645\u0639\u0646 \u0644\u0627\u0642 \u0629\u0639\u0627\u0633\u0644\u0627 \u0627\u0640\u0647\u0631\u0636\u062d\u0627 \u0644\u0627\u0642\u0641 \u0643\u0644\u0630 \u0644\u062b\u0645 \u062a\u0639\u0646\u0635 \u062a\u064a\u0633\u0645\u0654\u0627 \u0627\u0644\u0643\u0648 \u0627\u0647\u0639\u0636\u0648\u0645 \u0649\u0644\u0627 \u0627\u0647\u062f\u062f\u0631\u0648", "cuts": [[[342, 1217], [342, 1386], [354, 1386], [354, 1217]], [[404, 1217], [404, 1386], [404, 1386], [404, 1217]], [[428, 1217], [428, 1386], [428, 1386], [428, 1217]], [[453, 1217], [453, 1386], [465, 1386], [465, 1217]], [[527, 1217], [527, 1386], [527, 1386], [527, 1217]], [[539, 1217], [539, 1386], [539, 1386], [539, 1217]], [[564, 1217], [564, 1386], [564, 1386], [564, 1217]], [[589, 1217], [589, 1386], [613, 1386], [613, 1217]], [[638, 1217], [638, 1386], [650, 1386], [650, 1217]], [[675, 1217], [675, 1386], [675, 1386], [675, 1217]], [[712, 1217], [712, 1386], [724, 1386], [724, 1217]], [[761, 1217], [761, 1386], [774, 1386], [774, 1217]], [[786, 1217], [786, 1386], [798, 1386], [798, 1217]], [[811, 1217], [811, 1386], [823, 1386], [823, 1217]], [[835, 1217], [835, 1386], [835, 1386], [835, 1217]], [[860, 1217], [860, 1386], [872, 1386], [872, 1217]], [[909, 1217], [909, 1386], [922, 1386], [922, 1217]], [[1020, 1217], [1020, 1386], [1020, 1386], [1020, 1217]], [[1082, 1217], [1082, 1386], [1082, 1386], [1082, 1217]], [[1119, 1217], [1119, 1386], [1131, 1386], [1131, 1217]], [[1181, 1217], [1181, 1386], [1193, 1386], [1193, 1217]], [[1242, 1217], [1242, 1386], [1255, 1386], [1255, 1217]], [[1267, 1217], [1267, 1386], [1267, 1386], [1267, 1217]], [[1329, 1217], [1329, 1386], [1341, 1386], [1341, 1217]], [[1353, 1217], [1353, 1386], [1366, 1386], [1366, 1217]], [[1390, 1217], [1390, 1386], [1390, 1386], [1390, 1217]], [[1427, 1217], [1427, 1386], [1427, 1386], [1427, 1217]], [[1452, 1217], [1452, 1386], [1452, 1386], [1452, 1217]], [[1501, 1217], [1501, 1386], [1501, 1386], [1501, 1217]], [[1551, 1217], [1551, 1386], [1563, 1386], [1563, 1217]], [[1575, 1217], [1575, 1386], [1588, 1386], [1588, 1217]], [[1612, 1217], [1612, 1386], [1625, 1386], [1625, 1217]], [[1686, 1217], [1686, 1386], [1699, 1386], [1699, 1217]], [[1711, 1217], [1711, 1386], [1723, 1386], [1723, 1217]], [[1736, 1217], [1736, 1386], [1748, 1386], [1748, 1217]], [[1773, 1217], [1773, 1386], [1785, 1386], [1785, 1217]], [[1822, 1217], [1822, 1386], [1835, 1386], [1835, 1217]], [[1884, 1217], [1884, 1386], [1884, 1386], [1884, 1217]], [[1921, 1217], [1921, 1386], [1933, 1386], [1933, 1217]], [[1970, 1217], [1970, 1386], [1983, 1386], [1983, 1217]], [[2007, 1217], [2007, 1386], [2020, 1386], [2020, 1217]], [[2069, 1217], [2069, 1386], [2069, 1386], [2069, 1217]], [[2118, 1217], [2118, 1386], [2118, 1386], [2118, 1217]], [[2155, 1217], [2155, 1386], [2168, 1386], [2168, 1217]], [[2192, 1217], [2192, 1386], [2205, 1386], [2205, 1217]], [[2217, 1217], [2217, 1386], [2229, 1386], [2229, 1217]], [[2242, 1217], [2242, 1386], [2254, 1386], [2254, 1217]], [[2266, 1217], [2266, 1386], [2279, 1386], [2279, 1217]], [[2316, 1217], [2316, 1386], [2328, 1386], [2328, 1217]], [[2353, 1217], [2353, 1386], [2365, 1386], [2365, 1217]], [[2377, 1217], [2377, 1386], [2377, 1386], [2377, 1217]], [[2439, 1217], [2439, 1386], [2439, 1386], [2439, 1217]], [[2464, 1217], [2464, 1386], [2476, 1386], [2476, 1217]], [[2501, 1217], [2501, 1386], [2501, 1386], [2501, 1217]], [[2525, 1217], [2525, 1386], [2525, 1386], [2525, 1217]], [[2575, 1217], [2575, 1386], [2575, 1386], [2575, 1217]], [[2624, 1217], [2624, 1386], [2624, 1386], [2624, 1217]], [[2698, 1217], [2698, 1386], [2698, 1386], [2698, 1217]], [[2735, 1217], [2735, 1386], [2735, 1386], [2735, 1217]], [[2760, 1217], [2760, 1386], [2772, 1386], [2772, 1217]], [[2821, 1217], [2821, 1386], [2821, 1386], [2821, 1217]], [[2834, 1217], [2834, 1386], [2834, 1386], [2834, 1217]], [[2858, 1217], [2858, 1386], [2858, 1386], [2858, 1217]], [[2871, 1217], [2871, 1386], [2895, 1386], [2895, 1217]], [[2932, 1217], [2932, 1386], [2932, 1386], [2932, 1217]], [[2957, 1217], [2957, 1386], [2957, 1386], [2957, 1217]], [[3019, 1217], [3019, 1386], [3019, 1386], [3019, 1217]], [[3056, 1217], [3056, 1386], [3056, 1386], [3056, 1217]], [[3105, 1217], [3105, 1386], [3105, 1386], [3105, 1217]], [[3154, 1217], [3154, 1386], [3167, 1386], [3167, 1217]]], "confidences": [0.9998668432235718, 0.9939495325088501, 0.9999988079071045, 0.9995810389518738, 0.9973961114883423, 0.9925320744514465, 0.9987910389900208, 0.9998966455459595, 0.9999004602432251, 0.981810450553894, 0.99687659740448, 0.9998045563697815, 0.9998052716255188, 0.9931424856185913, 0.9988383650779724, 0.9999014139175415, 0.8104275465011597, 0.9999496936798096, 0.9994741082191467, 0.9992212057113647, 0.9998238682746887, 0.9998308420181274, 0.9993120431900024, 0.998877227306366, 0.999991774559021, 0.9525014758110046, 0.9995104074478149, 0.9983181953430176, 0.9150962829589844, 0.9546083807945251, 0.9991251826286316, 0.9999608993530273, 0.9821158647537231, 0.9985532164573669, 0.9999796152114868, 0.9997883439064026, 0.9903550148010254, 0.9928650856018066, 0.9961369633674622, 0.9909413456916809, 0.9947617650032043, 0.9631214737892151, 0.9943027496337891, 0.9706187844276428, 0.998685896396637, 0.9429393410682678, 0.9975836277008057, 0.9990416169166565, 0.9978206157684326, 0.9992419481277466, 0.9985764026641846, 0.9849389791488647, 0.9799551367759705, 0.9954046010971069, 0.9584742784500122, 0.9991908669471741, 0.994751513004303, 0.9992371797561646, 0.8995857834815979, 0.99809330701828, 0.999932050704956, 0.8690654039382935, 0.5987728834152222, 0.9988412261009216, 0.6917583346366882, 0.9999560117721558, 0.904447615146637, 0.9634166955947876, 0.9973717927932739, 0.9989099502563477], "line": [[342, 1217], [342, 1386], [3179, 1386], [3179, 1217]]}, {"prediction": "\u0630\u0640\u062e\u0646 \u0649\u0631\u0627\u062f \u0646\u0645 \u0646\u0627\u0644\u0641\u0644\u0627 \u062a\u064a\u0628\u0644\u0627 \u0649\u0644\u0627 \u0636\u0645\u0627 \u0647\u0644 \u0644\u0627\u0642\u0641 \u0647\u0645\u062f\u062e \u0636\u0639\u0628 \u0649\u0639\u062f\u062a\u0633\u0627\u0642 \u0646\u064a\u0646\u0645\u0654\u0648\u0645\u0644\u0627 \u0631\u064a\u0645\u0654\u0627\u0627\u064a", "cuts": [[[363, 1386], [363, 1518], [363, 1518], [363, 1386]], [[413, 1386], [413, 1518], [426, 1518], [426, 1386]], [[514, 1386], [514, 1518], [514, 1518], [514, 1386]], [[551, 1386], [551, 1518], [551, 1518], [551, 1386]], [[564, 1386], [564, 1518], [589, 1518], [589, 1386]], [[652, 1386], [652, 1518], [652, 1518], [652, 1386]], [[702, 1386], [702, 1518], [715, 1518], [715, 1386]], [[740, 1386], [740, 1518], [752, 1518], [752, 1386]], [[765, 1386], [765, 1518], [777, 1518], [777, 1386]], [[802, 1386], [802, 1518], [815, 1518], [815, 1386]], [[865, 1386], [865, 1518], [865, 1518], [865, 1386]], [[903, 1386], [903, 1518], [903, 1518], [903, 1386]], [[928, 1386], [928, 1518], [940, 1518], [940, 1386]], [[991, 1386], [991, 1518], [991, 1518], [991, 1386]], [[1041, 1386], [1041, 1518], [1053, 1518], [1053, 1386]], [[1066, 1386], [1066, 1518], [1078, 1518], [1078, 1386]], [[1116, 1386], [1116, 1518], [1116, 1518], [1116, 1386]], [[1141, 1386], [1141, 1518], [1141, 1518], [1141, 1386]], [[1154, 1386], [1154, 1518], [1166, 1518], [1166, 1386]], [[1179, 1386], [1179, 1518], [1191, 1518], [1191, 1386]], [[1229, 1386], [1229, 1518], [1229, 1518], [1229, 1386]], [[1279, 1386], [1279, 1518], [1279, 1518], [1279, 1386]], [[1304, 1386], [1304, 1518], [1304, 1518], [1304, 1386]], [[1329, 1386], [1329, 1518], [1342, 1518], [1342, 1386]], [[1354, 1386], [1354, 1518], [1354, 1518], [1354, 1386]], [[1367, 1386], [1367, 1518], [1380, 1518], [1380, 1386]], [[1417, 1386], [1417, 1518], [1417, 1518], [1417, 1386]], [[1430, 1386], [1430, 1518], [1442, 1518], [1442, 1386]], [[1455, 1386], [1455, 1518], [1467, 1518], [1467, 1386]], [[1480, 1386], [1480, 1518], [1480, 1518], [1480, 1386]], [[1568, 1386], [1568, 1518], [1568, 1518], [1568, 1386]], [[1618, 1386], [1618, 1518], [1631, 1518], [1631, 1386]], [[1668, 1386], [1668, 1518], [1668, 1518], [1668, 1386]], [[1681, 1386], [1681, 1518], [1693, 1518], [1693, 1386]], [[1731, 1386], [1731, 1518], [1743, 1518], [1743, 1386]], [[1769, 1386], [1769, 1518], [1769, 1518], [1769, 1386]], [[1781, 1386], [1781, 1518], [1794, 1518], [1794, 1386]], [[1844, 1386], [1844, 1518], [1844, 1518], [1844, 1386]], [[1869, 1386], [1869, 1518], [1869, 1518], [1869, 1386]], [[1907, 1386], [1907, 1518], [1907, 1518], [1907, 1386]], [[1932, 1386], [1932, 1518], [1932, 1518], [1932, 1386]], [[1957, 1386], [1957, 1518], [1969, 1518], [1969, 1386]], [[1982, 1386], [1982, 1518], [1994, 1518], [1994, 1386]], [[2032, 1386], [2032, 1518], [2032, 1518], [2032, 1386]], [[2082, 1386], [2082, 1518], [2082, 1518], [2082, 1386]], [[2120, 1386], [2120, 1518], [2132, 1518], [2132, 1386]], [[2158, 1386], [2158, 1518], [2170, 1518], [2170, 1386]], [[2245, 1386], [2245, 1518], [2258, 1518], [2258, 1386]], [[2296, 1386], [2296, 1518], [2308, 1518], [2308, 1386]], [[2333, 1386], [2333, 1518], [2333, 1518], [2333, 1386]], [[2358, 1386], [2358, 1518], [2371, 1518], [2371, 1386]], [[2421, 1386], [2421, 1518], [2434, 1518], [2434, 1386]], [[2459, 1386], [2459, 1518], [2459, 1518], [2459, 1386]], [[2521, 1386], [2521, 1518], [2521, 1518], [2521, 1386]], [[2546, 1386], [2546, 1518], [2546, 1518], [2546, 1386]], [[2597, 1386], [2597, 1518], [2597, 1518], [2597, 1386]], [[2622, 1386], [2622, 1518], [2634, 1518], [2634, 1386]], [[2647, 1386], [2647, 1518], [2647, 1518], [2647, 1386]], [[2672, 1386], [2672, 1518], [2697, 1518], [2697, 1386]], [[2735, 1386], [2735, 1518], [2747, 1518], [2747, 1386]], [[2772, 1386], [2772, 1518], [2772, 1518], [2772, 1386]], [[2797, 1386], [2797, 1518], [2810, 1518], [2810, 1386]], [[2835, 1386], [2835, 1518], [2835, 1518], [2835, 1386]], [[2873, 1386], [2873, 1518], [2873, 1518], [2873, 1386]], [[2885, 1386], [2885, 1518], [2898, 1518], [2898, 1386]], [[2910, 1386], [2910, 1518], [2910, 1518], [2910, 1386]], [[2923, 1386], [2923, 1518], [2923, 1518], [2923, 1386]], [[2948, 1386], [2948, 1518], [2961, 1518], [2961, 1386]], [[2973, 1386], [2973, 1518], [2973, 1518], [2973, 1386]], [[3011, 1386], [3011, 1518], [3023, 1518], [3023, 1386]], [[3036, 1386], [3036, 1518], [3036, 1518], [3036, 1386]], [[3073, 1386], [3073, 1518], [3073, 1518], [3073, 1386]], [[3099, 1386], [3099, 1518], [3099, 1518], [3099, 1386]], [[3111, 1386], [3111, 1518], [3111, 1518], [3111, 1386]], [[3136, 1386], [3136, 1518], [3149, 1518], [3149, 1386]], [[3161, 1386], [3161, 1518], [3161, 1518], [3161, 1386]]], "confidences": [0.9999401569366455, 0.9998226761817932, 0.9994499087333679, 0.9997947812080383, 0.9999337196350098, 0.9999203681945801, 0.9999982118606567, 0.9687603116035461, 0.9988839030265808, 0.998324453830719, 0.9755439758300781, 0.9999986886978149, 0.9999879598617554, 0.9918851852416992, 0.8710991144180298, 0.9998831748962402, 0.9799282550811768, 0.9640147686004639, 0.9995965361595154, 0.9998983144760132, 0.9994907379150391, 0.9999008178710938, 0.9999949932098389, 0.9999257326126099, 0.9849360585212708, 0.9992073178291321, 0.9999591112136841, 0.9999978542327881, 0.9979308843612671, 0.9989111423492432, 0.9988147020339966, 0.999931812286377, 0.9295510649681091, 0.9994879961013794, 0.92276930809021, 0.9905250668525696, 0.9945216178894043, 0.997312605381012, 0.9944058656692505, 0.7167887091636658, 0.9777094125747681, 0.9938310384750366, 0.9831866025924683, 0.9466024041175842, 0.9909795522689819, 0.9944062232971191, 0.9905561804771423, 0.9995214939117432, 0.9992606043815613, 0.9838502407073975, 0.9978698492050171, 0.9995675683021545, 0.9971568584442139, 0.9990284442901611, 0.9905042052268982, 0.9607644081115723, 0.9984911680221558, 0.7474943399429321, 0.9998549222946167, 0.9999047517776489, 0.9814210534095764, 0.9995946288108826, 0.999559223651886, 0.9958802461624146, 0.9985283613204956, 0.9995490908622742, 0.9966185092926025, 0.9981649518013, 0.8663971424102783, 0.9913069605827332, 0.9690742492675781, 0.9696444869041443, 0.9100111126899719, 0.9691933393478394, 0.9827510714530945, 0.9762600064277649], "line": [[338, 1386], [338, 1518], [3174, 1518], [3174, 1386]]}, {"prediction": "\u062b\u0628\u0644\u064a \u0645\u0644\u0641 \u0647\u0645\u062a\u062e\u0628 \u0647\u064a\u0641 \u0649\u0630\u0644\u0627 \u0637\u0641\u0633\u0644\u0627\u0628 \u0649\u0646\u0654\u064a\u062c\u0644 \u0641\u0627\u0644\u0641\u0644\u0627 \u0642\u0648\u062f\u0646\u0635\u0644\u0627 \u062d\u062a\u0641\u0627 \u0645\u062b \u0647\u062d\u062a\u0641\u0627\u0648 \u064a\u0641\u0632\u0627\u062e \u0646\u0645 \u0647\u062d\u0627\u062a\u0641\u0645", "cuts": [[[361, 1567], [361, 1720], [361, 1720], [361, 1567]], [[413, 1567], [413, 1720], [413, 1720], [413, 1567]], [[439, 1567], [439, 1720], [439, 1720], [439, 1567]], [[465, 1567], [465, 1720], [465, 1720], [465, 1567]], [[491, 1567], [491, 1720], [504, 1720], [504, 1567]], [[517, 1567], [517, 1720], [530, 1720], [530, 1567]], [[543, 1567], [543, 1720], [543, 1720], [543, 1567]], [[556, 1567], [556, 1720], [569, 1720], [569, 1567]], [[582, 1567], [582, 1720], [595, 1720], [595, 1567]], [[621, 1567], [621, 1720], [621, 1720], [621, 1567]], [[660, 1567], [660, 1720], [660, 1720], [660, 1567]], [[699, 1567], [699, 1720], [699, 1720], [699, 1567]], [[725, 1567], [725, 1720], [725, 1720], [725, 1567]], [[764, 1567], [764, 1720], [764, 1720], [764, 1567]], [[777, 1567], [777, 1720], [790, 1720], [790, 1567]], [[816, 1567], [816, 1720], [816, 1720], [816, 1567]], [[855, 1567], [855, 1720], [855, 1720], [855, 1567]], [[881, 1567], [881, 1720], [881, 1720], [881, 1567]], [[907, 1567], [907, 1720], [920, 1720], [920, 1567]], [[972, 1567], [972, 1720], [972, 1720], [972, 1567]], [[1024, 1567], [1024, 1720], [1024, 1720], [1024, 1567]], [[1051, 1567], [1051, 1720], [1051, 1720], [1051, 1567]], [[1077, 1567], [1077, 1720], [1077, 1720], [1077, 1567]], [[1090, 1567], [1090, 1720], [1103, 1720], [1103, 1567]], [[1142, 1567], [1142, 1720], [1155, 1720], [1155, 1567]], [[1207, 1567], [1207, 1720], [1207, 1720], [1207, 1567]], [[1259, 1567], [1259, 1720], [1272, 1720], [1272, 1567]], [[1298, 1567], [1298, 1720], [1311, 1720], [1311, 1567]], [[1324, 1567], [1324, 1720], [1337, 1720], [1337, 1567]], [[1350, 1567], [1350, 1720], [1350, 1720], [1350, 1567]], [[1363, 1567], [1363, 1720], [1376, 1720], [1376, 1567]], [[1415, 1567], [1415, 1720], [1415, 1720], [1415, 1567]], [[1428, 1567], [1428, 1720], [1441, 1720], [1441, 1567]], [[1454, 1567], [1454, 1720], [1454, 1720], [1454, 1567]], [[1467, 1567], [1467, 1720], [1467, 1720], [1467, 1567]], [[1493, 1567], [1493, 1720], [1506, 1720], [1506, 1567]], [[1519, 1567], [1519, 1720], [1519, 1720], [1519, 1567]], [[1545, 1567], [1545, 1720], [1558, 1720], [1558, 1567]], [[1597, 1567], [1597, 1720], [1597, 1720], [1597, 1567]], [[1636, 1567], [1636, 1720], [1662, 1720], [1662, 1567]], [[1675, 1567], [1675, 1720], [1675, 1720], [1675, 1567]], [[1714, 1567], [1714, 1720], [1714, 1720], [1714, 1567]], [[1740, 1567], [1740, 1720], [1740, 1720], [1740, 1567]], [[1753, 1567], [1753, 1720], [1753, 1720], [1753, 1567]], [[1766, 1567], [1766, 1720], [1779, 1720], [1779, 1567]], [[1831, 1567], [1831, 1720], [1831, 1720], [1831, 1567]], [[1883, 1567], [1883, 1720], [1883, 1720], [1883, 1567]], [[1935, 1567], [1935, 1720], [1935, 1720], [1935, 1567]], [[1961, 1567], [1961, 1720], [1961, 1720], [1961, 1567]], [[2013, 1567], [2013, 1720], [2013, 1720], [2013, 1567]], [[2052, 1567], [2052, 1720], [2052, 1720], [2052, 1567]], [[2065, 1567], [2065, 1720], [2078, 1720], [2078, 1567]], [[2091, 1567], [2091, 1720], [2104, 1720], [2104, 1567]], [[2130, 1567], [2130, 1720], [2130, 1720], [2130, 1567]], [[2182, 1567], [2182, 1720], [2182, 1720], [2182, 1567]], [[2208, 1567], [2208, 1720], [2208, 1720], [2208, 1567]], [[2234, 1567], [2234, 1720], [2247, 1720], [2247, 1567]], [[2260, 1567], [2260, 1720], [2260, 1720], [2260, 1567]], [[2286, 1567], [2286, 1720], [2299, 1720], [2299, 1567]], [[2338, 1567], [2338, 1720], [2338, 1720], [2338, 1567]], [[2364, 1567], [2364, 1720], [2377, 1720], [2377, 1567]], [[2390, 1567], [2390, 1720], [2390, 1720], [2390, 1567]], [[2429, 1567], [2429, 1720], [2429, 1720], [2429, 1567]], [[2482, 1567], [2482, 1720], [2482, 1720], [2482, 1567]], [[2508, 1567], [2508, 1720], [2508, 1720], [2508, 1567]], [[2534, 1567], [2534, 1720], [2534, 1720], [2534, 1567]], [[2586, 1567], [2586, 1720], [2586, 1720], [2586, 1567]], [[2612, 1567], [2612, 1720], [2625, 1720], [2625, 1567]], [[2651, 1567], [2651, 1720], [2651, 1720], [2651, 1567]], [[2664, 1567], [2664, 1720], [2664, 1720], [2664, 1567]], [[2716, 1567], [2716, 1720], [2716, 1720], [2716, 1567]], [[2742, 1567], [2742, 1720], [2742, 1720], [2742, 1567]], [[2768, 1567], [2768, 1720], [2781, 1720], [2781, 1567]], [[2807, 1567], [2807, 1720], [2820, 1720], [2820, 1567]], [[2859, 1567], [2859, 1720], [2859, 1720], [2859, 1567]], [[2898, 1567], [2898, 1720], [2898, 1720], [2898, 1567]], [[2924, 1567], [2924, 1720], [2937, 1720], [2937, 1567]], [[2963, 1567], [2963, 1720], [2963, 1720], [2963, 1567]], [[3002, 1567], [3002, 1720], [3002, 1720], [3002, 1567]], [[3054, 1567], [3054, 1720], [3054, 1720], [3054, 1567]], [[3080, 1567], [3080, 1720], [3080, 1720], [3080, 1567]], [[3119, 1567], [3119, 1720], [3119, 1720], [3119, 1567]], [[3145, 1567], [3145, 1720], [3158, 1720], [3158, 1567]]], "confidences": [0.9997847676277161, 0.8071998953819275, 0.9448097944259644, 0.5688155293464661, 0.9998739957809448, 0.9982267022132874, 0.9191160798072815, 0.9526958465576172, 0.999830961227417, 0.9999995231628418, 0.9993177652359009, 0.7347162365913391, 0.9999439716339111, 0.9998359680175781, 0.9999669790267944, 0.9836472868919373, 0.7263768315315247, 0.9997736811637878, 0.977462887763977, 0.9863080382347107, 0.9985411167144775, 0.9811128377914429, 0.8683425784111023, 0.9999358654022217, 0.9999024868011475, 0.9963384866714478, 0.9992992877960205, 0.9999573230743408, 0.9997846484184265, 0.9697093963623047, 0.9807730913162231, 0.9946768283843994, 0.9997909665107727, 0.9734904170036316, 0.9997913241386414, 0.998504638671875, 0.9990155696868896, 0.9989411234855652, 0.54173743724823, 0.9961016178131104, 0.9981348514556885, 0.9947880506515503, 0.9791303277015686, 0.9969891905784607, 0.9995549321174622, 0.8724766969680786, 0.9847140908241272, 0.9845319390296936, 0.9821130037307739, 0.9790477156639099, 0.838432252407074, 0.8714025616645813, 0.9906243681907654, 0.9986967444419861, 0.9988054037094116, 0.9993483424186707, 0.9957486987113953, 0.8649066090583801, 0.9977350234985352, 0.9995262622833252, 0.9965477585792542, 0.9975600242614746, 0.8291054368019104, 0.9957834482192993, 0.9864495396614075, 0.9921678900718689, 0.9627065658569336, 0.9999135732650757, 0.95207279920578, 0.9991065859794617, 0.9793131947517395, 0.8521793484687805, 0.9974838495254517, 0.9999358654022217, 0.9997324347496033, 0.9997784495353699, 0.9984983205795288, 0.9993114471435547, 0.8725327849388123, 0.9996665716171265, 0.9979007244110107, 0.9954906105995178, 0.9731282591819763], "line": [[335, 1567], [335, 1720], [3171, 1720], [3171, 1567]]}, {"prediction": "\u0647\u0640\u062d\u062a\u0641\u0648 \u0647\u0645\u062a\u062e \u0631\u0633\u0643\u0628 \u0631\u0645\u0654\u0627\u0641 \u062f\u064a\u0634\u0631\u0644\u0627 \u0649\u062f\u064a \u0646\u064a\u0628 \u0639\u0636\u0648\u0641 \u0627\u0645\u0648\u062a\u062d\u0645 \u0637\u0641\u0633\u0644\u0627\u0628 \u0621\u0627\u062c \u0646\u0627 \u0645\u0627\u0644\u063a\u0644\u0627", "cuts": [[[332, 1721], [332, 1880], [345, 1880], [345, 1721]], [[384, 1721], [384, 1880], [397, 1880], [397, 1721]], [[501, 1721], [501, 1880], [514, 1880], [514, 1721]], [[553, 1721], [553, 1880], [553, 1880], [553, 1721]], [[579, 1721], [579, 1880], [579, 1880], [579, 1721]], [[631, 1721], [631, 1880], [644, 1880], [644, 1721]], [[657, 1721], [657, 1880], [683, 1880], [683, 1721]], [[709, 1721], [709, 1880], [709, 1880], [709, 1721]], [[748, 1721], [748, 1880], [748, 1880], [748, 1721]], [[787, 1721], [787, 1880], [787, 1880], [787, 1721]], [[826, 1721], [826, 1880], [826, 1880], [826, 1721]], [[865, 1721], [865, 1880], [878, 1880], [878, 1721]], [[917, 1721], [917, 1880], [917, 1880], [917, 1721]], [[956, 1721], [956, 1880], [969, 1880], [969, 1721]], [[1021, 1721], [1021, 1880], [1021, 1880], [1021, 1721]], [[1100, 1721], [1100, 1880], [1100, 1880], [1100, 1721]], [[1126, 1721], [1126, 1880], [1139, 1880], [1139, 1721]], [[1178, 1721], [1178, 1880], [1191, 1880], [1191, 1721]], [[1217, 1721], [1217, 1880], [1217, 1880], [1217, 1721]], [[1243, 1721], [1243, 1880], [1243, 1880], [1243, 1721]], [[1256, 1721], [1256, 1880], [1269, 1880], [1269, 1721]], [[1282, 1721], [1282, 1880], [1282, 1880], [1282, 1721]], [[1308, 1721], [1308, 1880], [1321, 1880], [1321, 1721]], [[1373, 1721], [1373, 1880], [1386, 1880], [1386, 1721]], [[1412, 1721], [1412, 1880], [1412, 1880], [1412, 1721]], [[1451, 1721], [1451, 1880], [1464, 1880], [1464, 1721]], [[1503, 1721], [1503, 1880], [1503, 1880], [1503, 1721]], [[1516, 1721], [1516, 1880], [1529, 1880], [1529, 1721]], [[1542, 1721], [1542, 1880], [1555, 1880], [1555, 1721]], [[1568, 1721], [1568, 1880], [1568, 1880], [1568, 1721]], [[1659, 1721], [1659, 1880], [1659, 1880], [1659, 1721]], [[1711, 1721], [1711, 1880], [1724, 1880], [1724, 1721]], [[1776, 1721], [1776, 1880], [1776, 1880], [1776, 1721]], [[1802, 1721], [1802, 1880], [1815, 1880], [1815, 1721]], [[1841, 1721], [1841, 1880], [1854, 1880], [1854, 1721]], [[1867, 1721], [1867, 1880], [1867, 1880], [1867, 1721]], [[1893, 1721], [1893, 1880], [1893, 1880], [1893, 1721]], [[1919, 1721], [1919, 1880], [1932, 1880], [1932, 1721]], [[1997, 1721], [1997, 1880], [1997, 1880], [1997, 1721]], [[2062, 1721], [2062, 1880], [2062, 1880], [2062, 1721]], [[2127, 1721], [2127, 1880], [2140, 1880], [2140, 1721]], [[2166, 1721], [2166, 1880], [2166, 1880], [2166, 1721]], [[2192, 1721], [2192, 1880], [2205, 1880], [2205, 1721]], [[2244, 1721], [2244, 1880], [2257, 1880], [2257, 1721]], [[2270, 1721], [2270, 1880], [2283, 1880], [2283, 1721]], [[2322, 1721], [2322, 1880], [2335, 1880], [2335, 1721]], [[2361, 1721], [2361, 1880], [2361, 1880], [2361, 1721]], [[2400, 1721], [2400, 1880], [2400, 1880], [2400, 1721]], [[2413, 1721], [2413, 1880], [2413, 1880], [2413, 1721]], [[2452, 1721], [2452, 1880], [2452, 1880], [2452, 1721]], [[2492, 1721], [2492, 1880], [2505, 1880], [2505, 1721]], [[2557, 1721], [2557, 1880], [2557, 1880], [2557, 1721]], [[2609, 1721], [2609, 1880], [2622, 1880], [2622, 1721]], [[2648, 1721], [2648, 1880], [2648, 1880], [2648, 1721]], [[2661, 1721], [2661, 1880], [2674, 1880], [2674, 1721]], [[2687, 1721], [2687, 1880], [2700, 1880], [2700, 1721]], [[2713, 1721], [2713, 1880], [2726, 1880], [2726, 1721]], [[2765, 1721], [2765, 1880], [2765, 1880], [2765, 1721]], [[2791, 1721], [2791, 1880], [2804, 1880], [2804, 1721]], [[2817, 1721], [2817, 1880], [2830, 1880], [2830, 1721]], [[2869, 1721], [2869, 1880], [2882, 1880], [2882, 1721]], [[2908, 1721], [2908, 1880], [2921, 1880], [2921, 1721]], [[2947, 1721], [2947, 1880], [2960, 1880], [2960, 1721]], [[2973, 1721], [2973, 1880], [2986, 1880], [2986, 1721]], [[2999, 1721], [2999, 1880], [3012, 1880], [3012, 1721]], [[3038, 1721], [3038, 1880], [3064, 1880], [3064, 1721]], [[3077, 1721], [3077, 1880], [3077, 1880], [3077, 1721]], [[3103, 1721], [3103, 1880], [3116, 1880], [3116, 1721]], [[3142, 1721], [3142, 1880], [3142, 1880], [3142, 1721]], [[3155, 1721], [3155, 1880], [3168, 1880], [3168, 1721]]], "confidences": [0.9992807507514954, 0.9998190999031067, 0.9999728202819824, 0.9843649864196777, 0.9963064193725586, 0.9997701048851013, 0.9999123811721802, 0.9973879456520081, 0.999208390712738, 0.9719638824462891, 0.9987868666648865, 0.9970434308052063, 0.9997827410697937, 0.9999145269393921, 0.9791185259819031, 0.9905698299407959, 0.9965567588806152, 0.9983978867530823, 0.9999661445617676, 0.9999735355377197, 0.9999608993530273, 0.9998136162757874, 0.9998812675476074, 0.9741519093513489, 0.9999430179595947, 0.9712876081466675, 0.9996782541275024, 0.9999500513076782, 0.9999716281890869, 0.9999657869338989, 0.9887442588806152, 0.9972917437553406, 0.9999973773956299, 0.997856080532074, 0.9842910766601562, 0.9882177710533142, 0.9942337870597839, 0.9974427223205566, 0.8417571187019348, 0.96481853723526, 0.9993478655815125, 0.9817719459533691, 0.8848896622657776, 0.9665166735649109, 0.9880167245864868, 0.9940158128738403, 0.9972080588340759, 0.9924798011779785, 0.999430239200592, 0.9886217713356018, 0.9994122982025146, 0.9545918107032776, 0.9984832406044006, 0.9997020363807678, 0.9989163875579834, 0.9997730851173401, 0.9998658895492554, 0.9669737815856934, 0.9264388084411621, 0.9989351630210876, 0.9926611185073853, 0.9998636245727539, 0.9986826777458191, 0.9965075850486755, 0.9996894598007202, 0.9975529313087463, 0.9992383718490601, 0.9993176460266113, 0.9744951128959656, 0.9796627163887024], "line": [[332, 1721], [332, 1880], [3207, 1880], [3207, 1721]]}, {"prediction": "\u0647\u0628\u0636\u063a \u0646\u0645 \u062f\u064a\u0634\u0631\u0644\u0627 \u0646\u0643\u0633\u0641 \u0628\u064a\u0637\u0644\u0627 \u064a\u0641 \u0629\u0646\u0648\u0641\u062f\u0645 \u0629\u064a\u0648\u0637\u0645 \u0627\u0647\u0644\u0627\u062d \u0647\u064a\u0641 \u0629\u0639\u0627\u0631\u062f\u0644\u0627 \u0649\u0644\u0627 \u0631\u0638\u0646 \u062d\u062a\u0641 \u0627\u0644\u0646", "cuts": [[[331, 1906], [331, 2053], [331, 2053], [331, 1906]], [[370, 1906], [370, 2053], [370, 2053], [370, 1906]], [[409, 1906], [409, 2053], [409, 2053], [409, 1906]], [[473, 1906], [473, 2053], [473, 2053], [473, 1906]], [[512, 1906], [512, 2053], [525, 2053], [525, 1906]], [[563, 1906], [563, 2053], [563, 2053], [563, 1906]], [[602, 1906], [602, 2053], [602, 2053], [602, 1906]], [[628, 1906], [628, 2053], [641, 2053], [641, 1906]], [[679, 1906], [679, 2053], [679, 2053], [679, 1906]], [[705, 1906], [705, 2053], [705, 2053], [705, 1906]], [[757, 1906], [757, 2053], [757, 2053], [757, 1906]], [[795, 1906], [795, 2053], [808, 2053], [808, 1906]], [[821, 1906], [821, 2053], [821, 2053], [821, 1906]], [[834, 1906], [834, 2053], [847, 2053], [847, 1906]], [[860, 1906], [860, 2053], [873, 2053], [873, 1906]], [[924, 1906], [924, 2053], [937, 2053], [937, 1906]], [[976, 1906], [976, 2053], [976, 2053], [976, 1906]], [[1156, 1906], [1156, 2053], [1156, 2053], [1156, 1906]], [[1182, 1906], [1182, 2053], [1195, 2053], [1195, 1906]], [[1221, 1906], [1221, 2053], [1233, 2053], [1233, 1906]], [[1272, 1906], [1272, 2053], [1272, 2053], [1272, 1906]], [[1337, 1906], [1337, 2053], [1337, 2053], [1337, 1906]], [[1362, 1906], [1362, 2053], [1362, 2053], [1362, 1906]], [[1401, 1906], [1401, 2053], [1414, 2053], [1414, 1906]], [[1427, 1906], [1427, 2053], [1427, 2053], [1427, 1906]], [[1440, 1906], [1440, 2053], [1453, 2053], [1453, 1906]], [[1504, 1906], [1504, 2053], [1504, 2053], [1504, 1906]], [[1517, 1906], [1517, 2053], [1530, 2053], [1530, 1906]], [[1556, 1906], [1556, 2053], [1569, 2053], [1569, 1906]], [[1581, 1906], [1581, 2053], [1594, 2053], [1594, 1906]], [[1620, 1906], [1620, 2053], [1620, 2053], [1620, 1906]], [[1659, 1906], [1659, 2053], [1672, 2053], [1672, 1906]], [[1697, 1906], [1697, 2053], [1697, 2053], [1697, 1906]], [[1736, 1906], [1736, 2053], [1749, 2053], [1749, 1906]], [[1775, 1906], [1775, 2053], [1775, 2053], [1775, 1906]], [[1801, 1906], [1801, 2053], [1813, 2053], [1813, 1906]], [[1839, 1906], [1839, 2053], [1839, 2053], [1839, 1906]], [[1865, 1906], [1865, 2053], [1865, 2053], [1865, 1906]], [[1917, 1906], [1917, 2053], [1917, 2053], [1917, 1906]], [[1955, 1906], [1955, 2053], [1955, 2053], [1955, 1906]], [[2007, 1906], [2007, 2053], [2007, 2053], [2007, 1906]], [[2033, 1906], [2033, 2053], [2045, 2053], [2045, 1906]], [[2071, 1906], [2071, 2053], [2071, 2053], [2071, 1906]], [[2097, 1906], [2097, 2053], [2097, 2053], [2097, 1906]], [[2110, 1906], [2110, 2053], [2123, 2053], [2123, 1906]], [[2136, 1906], [2136, 2053], [2149, 2053], [2149, 1906]], [[2174, 1906], [2174, 2053], [2174, 2053], [2174, 1906]], [[2226, 1906], [2226, 2053], [2239, 2053], [2239, 1906]], [[2265, 1906], [2265, 2053], [2265, 2053], [2265, 1906]], [[2290, 1906], [2290, 2053], [2303, 2053], [2303, 1906]], [[2329, 1906], [2329, 2053], [2329, 2053], [2329, 1906]], [[2355, 1906], [2355, 2053], [2368, 2053], [2368, 1906]], [[2393, 1906], [2393, 2053], [2393, 2053], [2393, 1906]], [[2419, 1906], [2419, 2053], [2419, 2053], [2419, 1906]], [[2458, 1906], [2458, 2053], [2471, 2053], [2471, 1906]], [[2509, 1906], [2509, 2053], [2509, 2053], [2509, 1906]], [[2548, 1906], [2548, 2053], [2548, 2053], [2548, 1906]], [[2574, 1906], [2574, 2053], [2587, 2053], [2587, 1906]], [[2600, 1906], [2600, 2053], [2613, 2053], [2613, 1906]], [[2625, 1906], [2625, 2053], [2625, 2053], [2625, 1906]], [[2677, 1906], [2677, 2053], [2677, 2053], [2677, 1906]], [[2690, 1906], [2690, 2053], [2690, 2053], [2690, 1906]], [[2716, 1906], [2716, 2053], [2716, 2053], [2716, 1906]], [[2729, 1906], [2729, 2053], [2741, 2053], [2741, 1906]], [[2780, 1906], [2780, 2053], [2780, 2053], [2780, 1906]], [[2806, 1906], [2806, 2053], [2819, 2053], [2819, 1906]], [[2857, 1906], [2857, 2053], [2857, 2053], [2857, 1906]], [[2870, 1906], [2870, 2053], [2896, 2053], [2896, 1906]], [[2922, 1906], [2922, 2053], [2935, 2053], [2935, 1906]], [[2973, 1906], [2973, 2053], [2973, 2053], [2973, 1906]], [[2999, 1906], [2999, 2053], [2999, 2053], [2999, 1906]], [[3025, 1906], [3025, 2053], [3025, 2053], [3025, 1906]], [[3051, 1906], [3051, 2053], [3051, 2053], [3051, 1906]], [[3128, 1906], [3128, 2053], [3141, 2053], [3141, 1906]], [[3167, 1906], [3167, 2053], [3167, 2053], [3167, 1906]]], "confidences": [0.9159960746765137, 0.9976181387901306, 0.9996975660324097, 0.9877948760986328, 0.9998071789741516, 0.9193847179412842, 0.9977598190307617, 0.9999691247940063, 0.5213192105293274, 0.9998345375061035, 0.9993784427642822, 0.9995831847190857, 0.999843955039978, 0.9999074935913086, 0.6370336413383484, 0.9995335340499878, 0.9989326596260071, 0.9860919117927551, 0.999936580657959, 0.9919663667678833, 0.9993558526039124, 0.9862826466560364, 0.8228899836540222, 0.9858511090278625, 0.9946306943893433, 0.9999549388885498, 0.999767005443573, 0.9996095299720764, 0.9999788999557495, 0.9988093376159668, 0.5167495608329773, 0.9999475479125977, 0.9990980625152588, 0.9999123811721802, 0.6137158870697021, 0.9979343414306641, 0.9267524480819702, 0.7948201894760132, 0.9545791745185852, 0.9082372188568115, 0.9680109024047852, 0.995181143283844, 0.9705617427825928, 0.8799518942832947, 0.9786372184753418, 0.9424921274185181, 0.605963408946991, 0.9994131326675415, 0.9984038472175598, 0.9985394477844238, 0.9992857575416565, 0.9990177154541016, 0.9961357116699219, 0.9946891069412231, 0.9922365546226501, 0.9932458996772766, 0.9726880788803101, 0.9994227886199951, 0.9991672039031982, 0.9998785257339478, 0.999698281288147, 0.9973363280296326, 0.9942309856414795, 0.9929460883140564, 0.8134588599205017, 0.9999004602432251, 0.9500336647033691, 0.999967098236084, 0.9991039633750916, 0.9984264373779297, 0.8896018266677856, 0.7239580750465393, 0.9947216510772705, 0.9903062582015991, 0.8236287832260132], "line": [[331, 1906], [331, 2053], [3167, 2053], [3167, 1906]]}, {"prediction": "\u0627\u0647\u062f\u0639\u0628 \u0643\u064a\u0644\u0639 \u0642\u062f\u0635\u0627 \u0646\u0644\u0641 \u0627\u062f\u0634\u0627\u0631 \u0641\u0631\u0635\u0646\u0627\u0648 \u0627\u0647\u0646\u0627\u0643\u0645 \u0649\u0644\u0627 \u0627\u0647\u062f\u062f\u0631\u0627 \u0646\u064a\u0637\u0642\u064a \u0646\u0628 \u0649\u0644\u0639\u0644 \u0644\u0627\u0642 \u0645\u0654\u064a", "cuts": [[[334, 2060], [334, 2231], [334, 2231], [334, 2060]], [[360, 2060], [360, 2231], [360, 2231], [360, 2060]], [[425, 2060], [425, 2231], [425, 2231], [425, 2060]], [[451, 2060], [451, 2231], [451, 2231], [451, 2060]], [[477, 2060], [477, 2231], [490, 2231], [490, 2060]], [[503, 2060], [503, 2231], [516, 2231], [516, 2060]], [[569, 2060], [569, 2231], [569, 2231], [569, 2060]], [[621, 2060], [621, 2231], [634, 2231], [634, 2060]], [[647, 2060], [647, 2231], [647, 2231], [647, 2060]], [[673, 2060], [673, 2231], [686, 2231], [686, 2060]], [[712, 2060], [712, 2231], [738, 2231], [738, 2060]], [[803, 2060], [803, 2231], [803, 2231], [803, 2060]], [[842, 2060], [842, 2231], [855, 2231], [855, 2060]], [[894, 2060], [894, 2231], [907, 2231], [907, 2060]], [[946, 2060], [946, 2231], [959, 2231], [959, 2060]], [[972, 2060], [972, 2231], [972, 2231], [972, 2060]], [[1024, 2060], [1024, 2231], [1024, 2231], [1024, 2060]], [[1051, 2060], [1051, 2231], [1064, 2231], [1064, 2060]], [[1077, 2060], [1077, 2231], [1077, 2231], [1077, 2060]], [[1103, 2060], [1103, 2231], [1129, 2231], [1129, 2060]], [[1168, 2060], [1168, 2231], [1181, 2231], [1181, 2060]], [[1220, 2060], [1220, 2231], [1220, 2231], [1220, 2060]], [[1272, 2060], [1272, 2231], [1272, 2231], [1272, 2060]], [[1298, 2060], [1298, 2231], [1311, 2231], [1311, 2060]], [[1350, 2060], [1350, 2231], [1350, 2231], [1350, 2060]], [[1376, 2060], [1376, 2231], [1389, 2231], [1389, 2060]], [[1480, 2060], [1480, 2231], [1480, 2231], [1480, 2060]], [[1520, 2060], [1520, 2231], [1533, 2231], [1533, 2060]], [[1585, 2060], [1585, 2231], [1585, 2231], [1585, 2060]], [[1624, 2060], [1624, 2231], [1624, 2231], [1624, 2060]], [[1650, 2060], [1650, 2231], [1663, 2231], [1663, 2060]], [[1702, 2060], [1702, 2231], [1715, 2231], [1715, 2060]], [[1728, 2060], [1728, 2231], [1754, 2231], [1754, 2060]], [[1767, 2060], [1767, 2231], [1780, 2231], [1780, 2060]], [[1832, 2060], [1832, 2231], [1845, 2231], [1845, 2060]], [[1858, 2060], [1858, 2231], [1871, 2231], [1871, 2060]], [[1884, 2060], [1884, 2231], [1884, 2231], [1884, 2060]], [[1897, 2060], [1897, 2231], [1910, 2231], [1910, 2060]], [[1962, 2060], [1962, 2231], [1962, 2231], [1962, 2060]], [[1989, 2060], [1989, 2231], [2002, 2231], [2002, 2060]], [[2067, 2060], [2067, 2231], [2067, 2231], [2067, 2060]], [[2080, 2060], [2080, 2231], [2080, 2231], [2080, 2060]], [[2106, 2060], [2106, 2231], [2106, 2231], [2106, 2060]], [[2119, 2060], [2119, 2231], [2132, 2231], [2132, 2060]], [[2158, 2060], [2158, 2231], [2158, 2231], [2158, 2060]], [[2184, 2060], [2184, 2231], [2184, 2231], [2184, 2060]], [[2236, 2060], [2236, 2231], [2236, 2231], [2236, 2060]], [[2262, 2060], [2262, 2231], [2275, 2231], [2275, 2060]], [[2314, 2060], [2314, 2231], [2327, 2231], [2327, 2060]], [[2353, 2060], [2353, 2231], [2353, 2231], [2353, 2060]], [[2379, 2060], [2379, 2231], [2392, 2231], [2392, 2060]], [[2431, 2060], [2431, 2231], [2444, 2231], [2444, 2060]], [[2457, 2060], [2457, 2231], [2471, 2231], [2471, 2060]], [[2497, 2060], [2497, 2231], [2497, 2231], [2497, 2060]], [[2549, 2060], [2549, 2231], [2562, 2231], [2562, 2060]], [[2588, 2060], [2588, 2231], [2588, 2231], [2588, 2060]], [[2614, 2060], [2614, 2231], [2627, 2231], [2627, 2060]], [[2679, 2060], [2679, 2231], [2679, 2231], [2679, 2060]], [[2705, 2060], [2705, 2231], [2705, 2231], [2705, 2060]], [[2731, 2060], [2731, 2231], [2744, 2231], [2744, 2060]], [[2848, 2060], [2848, 2231], [2848, 2231], [2848, 2060]], [[2861, 2060], [2861, 2231], [2861, 2231], [2861, 2060]], [[2887, 2060], [2887, 2231], [2887, 2231], [2887, 2060]], [[2913, 2060], [2913, 2231], [2926, 2231], [2926, 2060]], [[2939, 2060], [2939, 2231], [2953, 2231], [2953, 2060]], [[3005, 2060], [3005, 2231], [3005, 2231], [3005, 2060]], [[3018, 2060], [3018, 2231], [3031, 2231], [3031, 2060]], [[3044, 2060], [3044, 2231], [3044, 2231], [3044, 2060]], [[3070, 2060], [3070, 2231], [3083, 2231], [3083, 2060]], [[3096, 2060], [3096, 2231], [3109, 2231], [3109, 2060]], [[3135, 2060], [3135, 2231], [3135, 2231], [3135, 2060]], [[3148, 2060], [3148, 2231], [3148, 2231], [3148, 2060]]], "confidences": [0.9586123824119568, 0.9986465573310852, 0.9566709995269775, 0.9922924041748047, 0.999371349811554, 0.999819815158844, 0.9982818365097046, 0.9999407529830933, 0.9542492032051086, 0.9999266862869263, 0.9977983236312866, 0.9082127213478088, 0.9984015822410583, 0.9999021291732788, 0.9999748468399048, 0.9999212026596069, 0.9956596493721008, 0.9997978806495667, 0.9995704293251038, 0.9999994039535522, 0.9999769926071167, 0.5398281216621399, 0.9999560117721558, 0.9999827146530151, 0.678408682346344, 0.999976634979248, 0.9999359846115112, 0.9980989098548889, 0.9837391376495361, 0.8357751369476318, 0.9999740123748779, 0.9798765182495117, 0.9975171089172363, 0.99134761095047, 0.9869094491004944, 0.8972664475440979, 0.9946538209915161, 0.9883899688720703, 0.9977121353149414, 0.9916154742240906, 0.9904273748397827, 0.9942137598991394, 0.9822350144386292, 0.9995124340057373, 0.6970008015632629, 0.9945922493934631, 0.9990953207015991, 0.9915691018104553, 0.9982545971870422, 0.9470371603965759, 0.9988258481025696, 0.9982693195343018, 0.9856593608856201, 0.9323335886001587, 0.9999984502792358, 0.9984018206596375, 0.9993857145309448, 0.9998714923858643, 0.6906755566596985, 0.9985949397087097, 0.994672954082489, 0.9955617189407349, 0.9956024885177612, 0.9990137815475464, 0.9867492318153381, 0.9986513257026672, 0.9989423155784607, 0.6353587508201599, 0.996551513671875, 0.9758750200271606, 0.8564420342445374, 0.5611870884895325], "line": [[334, 2060], [334, 2231], [3161, 2231], [3161, 2060]]}, {"prediction": "\u0648\u062d\" \u0628\u0631\u0636\u0641 \u0637\u0648\u0633 \u0641\u0644\u0627 \u0647\u0628 \u0649\u0639\u0627\u0633\u0644\u0627 \u0628\u0631\u0636\u0628 \u0645\u062f\u0642\u062a\u0648 \u0629\u064a\u0646\u0633 \u0629\u0632\u064a\u0627\u062c\u0628 \u0639\u0628\u062a\u064a \u0646\u0627 \u0631\u0645\u0654\u0627\u0648\u064b \u0627\u064a\u0639\u0627\u0633", "cuts": [[[354, 2227], [354, 2386], [354, 2386], [354, 2227]], [[393, 2227], [393, 2386], [406, 2386], [406, 2227]], [[459, 2227], [459, 2386], [459, 2386], [459, 2227]], [[472, 2227], [472, 2386], [486, 2386], [486, 2227]], [[551, 2227], [551, 2386], [551, 2386], [551, 2227]], [[617, 2227], [617, 2386], [631, 2386], [631, 2227]], [[670, 2227], [670, 2386], [670, 2386], [670, 2227]], [[710, 2227], [710, 2386], [710, 2386], [710, 2227]], [[736, 2227], [736, 2386], [763, 2386], [763, 2227]], [[815, 2227], [815, 2386], [815, 2386], [815, 2227]], [[881, 2227], [881, 2386], [881, 2386], [881, 2227]], [[921, 2227], [921, 2386], [934, 2386], [934, 2227]], [[961, 2227], [961, 2386], [987, 2386], [987, 2227]], [[1079, 2227], [1079, 2386], [1093, 2386], [1093, 2227]], [[1106, 2227], [1106, 2386], [1119, 2386], [1119, 2227]], [[1132, 2227], [1132, 2386], [1132, 2386], [1132, 2227]], [[1145, 2227], [1145, 2386], [1159, 2386], [1159, 2227]], [[1198, 2227], [1198, 2386], [1198, 2386], [1198, 2227]], [[1225, 2227], [1225, 2386], [1225, 2386], [1225, 2227]], [[1251, 2227], [1251, 2386], [1264, 2386], [1264, 2227]], [[1304, 2227], [1304, 2386], [1304, 2386], [1304, 2227]], [[1330, 2227], [1330, 2386], [1343, 2386], [1343, 2227]], [[1383, 2227], [1383, 2386], [1383, 2386], [1383, 2227]], [[1409, 2227], [1409, 2386], [1423, 2386], [1423, 2227]], [[1449, 2227], [1449, 2386], [1462, 2386], [1462, 2227]], [[1475, 2227], [1475, 2386], [1475, 2386], [1475, 2227]], [[1489, 2227], [1489, 2386], [1502, 2386], [1502, 2227]], [[1581, 2227], [1581, 2386], [1581, 2386], [1581, 2227]], [[1634, 2227], [1634, 2386], [1647, 2386], [1647, 2227]], [[1687, 2227], [1687, 2386], [1687, 2386], [1687, 2227]], [[1726, 2227], [1726, 2386], [1739, 2386], [1739, 2227]], [[1753, 2227], [1753, 2386], [1766, 2386], [1766, 2227]], [[1805, 2227], [1805, 2386], [1819, 2386], [1819, 2227]], [[1871, 2227], [1871, 2386], [1871, 2386], [1871, 2227]], [[1898, 2227], [1898, 2386], [1911, 2386], [1911, 2227]], [[1937, 2227], [1937, 2386], [1937, 2386], [1937, 2227]], [[1990, 2227], [1990, 2386], [1990, 2386], [1990, 2227]], [[2017, 2227], [2017, 2386], [2030, 2386], [2030, 2227]], [[2069, 2227], [2069, 2386], [2069, 2386], [2069, 2227]], [[2096, 2227], [2096, 2386], [2096, 2386], [2096, 2227]], [[2122, 2227], [2122, 2386], [2135, 2386], [2135, 2227]], [[2162, 2227], [2162, 2386], [2175, 2386], [2175, 2227]], [[2201, 2227], [2201, 2386], [2215, 2386], [2215, 2227]], [[2267, 2227], [2267, 2386], [2267, 2386], [2267, 2227]], [[2307, 2227], [2307, 2386], [2307, 2386], [2307, 2227]], [[2333, 2227], [2333, 2386], [2333, 2386], [2333, 2227]], [[2373, 2227], [2373, 2386], [2373, 2386], [2373, 2227]], [[2399, 2227], [2399, 2386], [2413, 2386], [2413, 2227]], [[2426, 2227], [2426, 2386], [2439, 2386], [2439, 2227]], [[2452, 2227], [2452, 2386], [2465, 2386], [2465, 2227]], [[2492, 2227], [2492, 2386], [2505, 2386], [2505, 2227]], [[2545, 2227], [2545, 2386], [2545, 2386], [2545, 2227]], [[2571, 2227], [2571, 2386], [2571, 2386], [2571, 2227]], [[2597, 2227], [2597, 2386], [2597, 2386], [2597, 2227]], [[2624, 2227], [2624, 2386], [2637, 2386], [2637, 2227]], [[2690, 2227], [2690, 2386], [2703, 2386], [2703, 2227]], [[2729, 2227], [2729, 2386], [2729, 2386], [2729, 2227]], [[2756, 2227], [2756, 2386], [2756, 2386], [2756, 2227]], [[2795, 2227], [2795, 2386], [2795, 2386], [2795, 2227]], [[2822, 2227], [2822, 2386], [2835, 2386], [2835, 2227]], [[2861, 2227], [2861, 2386], [2861, 2386], [2861, 2227]], [[2875, 2227], [2875, 2386], [2875, 2386], [2875, 2227]], [[2927, 2227], [2927, 2386], [2927, 2386], [2927, 2227]], [[2941, 2227], [2941, 2386], [2941, 2386], [2941, 2227]], [[2954, 2227], [2954, 2386], [2967, 2386], [2967, 2227]], [[2993, 2227], [2993, 2386], [3006, 2386], [3006, 2227]], [[3033, 2227], [3033, 2386], [3033, 2386], [3033, 2227]], [[3046, 2227], [3046, 2386], [3059, 2386], [3059, 2227]], [[3099, 2227], [3099, 2386], [3099, 2386], [3099, 2227]], [[3138, 2227], [3138, 2386], [3152, 2386], [3152, 2227]]], "confidences": [0.9823002219200134, 0.9972136616706848, 0.9999020099639893, 0.9994245767593384, 0.9984127283096313, 0.9963169097900391, 0.9989890456199646, 0.8085134625434875, 0.9999579191207886, 0.9999361038208008, 0.9942615032196045, 0.9278876185417175, 0.9998903274536133, 0.999955415725708, 0.9999262094497681, 0.9999407529830933, 0.9999767541885376, 0.9984294772148132, 0.8924553990364075, 0.9984814524650574, 0.9999494552612305, 0.9997356534004211, 0.9999189376831055, 0.9939833283424377, 0.9999998807907104, 0.9999184608459473, 0.9999300241470337, 0.573874831199646, 0.9986876845359802, 0.9068862199783325, 0.9941669702529907, 0.999953031539917, 0.997863233089447, 0.7316004037857056, 0.9987077713012695, 0.9907311797142029, 0.9943513870239258, 0.9962025284767151, 0.9970322847366333, 0.9568605422973633, 0.9899909496307373, 0.8733225464820862, 0.9993151426315308, 0.7688072919845581, 0.9994139671325684, 0.9989153146743774, 0.5057005286216736, 0.9989703893661499, 0.9991733431816101, 0.9928190112113953, 0.9984594583511353, 0.7554934024810791, 0.9958826303482056, 0.9795031547546387, 0.9996199607849121, 0.9911338686943054, 0.9956367611885071, 0.9094319939613342, 0.9807215929031372, 0.9925562739372253, 0.8968151211738586, 0.7551785707473755, 0.9919329285621643, 0.9729509353637695, 0.9989374279975891, 0.9944745898246765, 0.9989200830459595, 0.9993528723716736, 0.9896401166915894, 0.9964278340339661], "line": [[327, 2227], [327, 2386], [3165, 2386], [3165, 2227]]}, {"prediction": ". \u0643\u0644\u0630 \u064a\u0641 \u062a\u0627\u0645\u0641 \u0637\u0648\u0633 \u0629\u0627\u0645\u0633\u0645\u062e", "cuts": [[[2264, 2402], [2264, 2531], [2264, 2531], [2264, 2402]], [[2302, 2402], [2302, 2531], [2302, 2531], [2302, 2402]], [[2340, 2402], [2340, 2531], [2353, 2531], [2353, 2402]], [[2391, 2402], [2391, 2531], [2403, 2531], [2403, 2402]], [[2416, 2402], [2416, 2531], [2429, 2531], [2429, 2402]], [[2454, 2402], [2454, 2531], [2467, 2531], [2467, 2402]], [[2492, 2402], [2492, 2531], [2492, 2531], [2492, 2402]], [[2505, 2402], [2505, 2531], [2505, 2531], [2505, 2402]], [[2543, 2402], [2543, 2531], [2543, 2531], [2543, 2402]], [[2594, 2402], [2594, 2531], [2594, 2531], [2594, 2402]], [[2632, 2402], [2632, 2531], [2632, 2531], [2632, 2402]], [[2644, 2402], [2644, 2531], [2657, 2531], [2657, 2402]], [[2695, 2402], [2695, 2531], [2695, 2531], [2695, 2402]], [[2720, 2402], [2720, 2531], [2733, 2531], [2733, 2402]], [[2771, 2402], [2771, 2531], [2771, 2531], [2771, 2402]], [[2847, 2402], [2847, 2531], [2847, 2531], [2847, 2402]], [[2898, 2402], [2898, 2531], [2898, 2531], [2898, 2402]], [[2936, 2402], [2936, 2531], [2936, 2531], [2936, 2402]], [[2961, 2402], [2961, 2531], [2961, 2531], [2961, 2402]], [[3012, 2402], [3012, 2531], [3012, 2531], [3012, 2402]], [[3025, 2402], [3025, 2531], [3037, 2531], [3037, 2402]], [[3075, 2402], [3075, 2531], [3088, 2531], [3088, 2402]], [[3101, 2402], [3101, 2531], [3101, 2531], [3101, 2402]], [[3126, 2402], [3126, 2531], [3139, 2531], [3139, 2402]]], "confidences": [0.9456377625465393, 0.9999704360961914, 0.9999499320983887, 0.9995260238647461, 0.9999178647994995, 0.9999415874481201, 0.9920778870582581, 0.9997991919517517, 0.9995096921920776, 0.999977707862854, 0.8690007925033569, 0.9649189710617065, 0.9999690055847168, 0.9999949932098389, 0.9907584190368652, 0.9840313196182251, 0.9416587948799133, 0.998769223690033, 0.9915293455123901, 0.967819094657898, 0.9989644289016724, 0.9990310668945312, 0.9262909889221191, 0.9966443777084351], "line": [[2264, 2402], [2264, 2531], [3164, 2531], [3164, 2402]]}, {"prediction": "\u062d\u0633\u0645 \u064a\u0641 \u0627\u0646\u0628\u0627\u062d\u0654\u0627 \u0646\u064a\u0628 \u0629\u064a\u0627\u0648\u0631\u0644\u0627 \u062a\u0641\u0644\u062a\u062e\u0627 \u0644\u0627\u0642 \u0644\u0636\u0635\u0641\u0644\u0627 \u0646\u0628 \u062f\u0645\u062d \u0646\u0639 \u0644\u064a\u0639\u0627\u0645\u0633\u0627 \u0646\u0628 \u062f\u0645\u0639\u0645 \u0649\u0648\u0631\u0648", "cuts": [[[331, 2564], [331, 2740], [344, 2740], [344, 2564]], [[398, 2564], [398, 2740], [398, 2740], [398, 2564]], [[439, 2564], [439, 2740], [452, 2740], [452, 2564]], [[466, 2564], [466, 2740], [479, 2740], [479, 2564]], [[520, 2564], [520, 2740], [520, 2740], [520, 2564]], [[533, 2564], [533, 2740], [533, 2740], [533, 2564]], [[560, 2564], [560, 2740], [560, 2740], [560, 2564]], [[587, 2564], [587, 2740], [587, 2740], [587, 2564]], [[614, 2564], [614, 2740], [614, 2740], [614, 2564]], [[628, 2564], [628, 2740], [641, 2740], [641, 2564]], [[668, 2564], [668, 2740], [668, 2740], [668, 2564]], [[695, 2564], [695, 2740], [709, 2740], [709, 2564]], [[763, 2564], [763, 2740], [763, 2740], [763, 2564]], [[776, 2564], [776, 2740], [790, 2740], [790, 2564]], [[803, 2564], [803, 2740], [803, 2740], [803, 2564]], [[844, 2564], [844, 2740], [857, 2740], [857, 2564]], [[871, 2564], [871, 2740], [884, 2740], [884, 2564]], [[898, 2564], [898, 2740], [898, 2740], [898, 2564]], [[925, 2564], [925, 2740], [938, 2740], [938, 2564]], [[979, 2564], [979, 2740], [979, 2740], [979, 2564]], [[1006, 2564], [1006, 2740], [1006, 2740], [1006, 2564]], [[1046, 2564], [1046, 2740], [1046, 2740], [1046, 2564]], [[1100, 2564], [1100, 2740], [1100, 2740], [1100, 2564]], [[1141, 2564], [1141, 2740], [1141, 2740], [1141, 2564]], [[1154, 2564], [1154, 2740], [1168, 2740], [1168, 2564]], [[1181, 2564], [1181, 2740], [1181, 2740], [1181, 2564]], [[1195, 2564], [1195, 2740], [1208, 2740], [1208, 2564]], [[1249, 2564], [1249, 2740], [1249, 2740], [1249, 2564]], [[1316, 2564], [1316, 2740], [1316, 2740], [1316, 2564]], [[1343, 2564], [1343, 2740], [1343, 2740], [1343, 2564]], [[1370, 2564], [1370, 2740], [1384, 2740], [1384, 2564]], [[1411, 2564], [1411, 2740], [1424, 2740], [1424, 2564]], [[1465, 2564], [1465, 2740], [1478, 2740], [1478, 2564]], [[1492, 2564], [1492, 2740], [1492, 2740], [1492, 2564]], [[1546, 2564], [1546, 2740], [1559, 2740], [1559, 2564]], [[1573, 2564], [1573, 2740], [1573, 2740], [1573, 2564]], [[1586, 2564], [1586, 2740], [1586, 2740], [1586, 2564]], [[1613, 2564], [1613, 2740], [1627, 2740], [1627, 2564]], [[1667, 2564], [1667, 2740], [1681, 2740], [1681, 2564]], [[1721, 2564], [1721, 2740], [1721, 2740], [1721, 2564]], [[1735, 2564], [1735, 2740], [1735, 2740], [1735, 2564]], [[1775, 2564], [1775, 2740], [1789, 2740], [1789, 2564]], [[1802, 2564], [1802, 2740], [1816, 2740], [1816, 2564]], [[1829, 2564], [1829, 2740], [1829, 2740], [1829, 2564]], [[1843, 2564], [1843, 2740], [1856, 2740], [1856, 2564]], [[1897, 2564], [1897, 2740], [1897, 2740], [1897, 2564]], [[1924, 2564], [1924, 2740], [1924, 2740], [1924, 2564]], [[1951, 2564], [1951, 2740], [1964, 2740], [1964, 2564]], [[2005, 2564], [2005, 2740], [2005, 2740], [2005, 2564]], [[2032, 2564], [2032, 2740], [2045, 2740], [2045, 2564]], [[2059, 2564], [2059, 2740], [2059, 2740], [2059, 2564]], [[2086, 2564], [2086, 2740], [2086, 2740], [2086, 2564]], [[2113, 2564], [2113, 2740], [2113, 2740], [2113, 2564]], [[2153, 2564], [2153, 2740], [2153, 2740], [2153, 2564]], [[2194, 2564], [2194, 2740], [2207, 2740], [2207, 2564]], [[2248, 2564], [2248, 2740], [2248, 2740], [2248, 2564]], [[2275, 2564], [2275, 2740], [2288, 2740], [2288, 2564]], [[2302, 2564], [2302, 2740], [2315, 2740], [2315, 2564]], [[2356, 2564], [2356, 2740], [2356, 2740], [2356, 2564]], [[2369, 2564], [2369, 2740], [2383, 2740], [2383, 2564]], [[2410, 2564], [2410, 2740], [2423, 2740], [2423, 2564]], [[2450, 2564], [2450, 2740], [2450, 2740], [2450, 2564]], [[2464, 2564], [2464, 2740], [2477, 2740], [2477, 2564]], [[2504, 2564], [2504, 2740], [2518, 2740], [2518, 2564]], [[2545, 2564], [2545, 2740], [2545, 2740], [2545, 2564]], [[2572, 2564], [2572, 2740], [2572, 2740], [2572, 2564]], [[2612, 2564], [2612, 2740], [2612, 2740], [2612, 2564]], [[2639, 2564], [2639, 2740], [2653, 2740], [2653, 2564]], [[2666, 2564], [2666, 2740], [2666, 2740], [2666, 2564]], [[2680, 2564], [2680, 2740], [2680, 2740], [2680, 2564]], [[2707, 2564], [2707, 2740], [2720, 2740], [2720, 2564]], [[2761, 2564], [2761, 2740], [2761, 2740], [2761, 2564]], [[2828, 2564], [2828, 2740], [2828, 2740], [2828, 2564]], [[2869, 2564], [2869, 2740], [2882, 2740], [2882, 2564]], [[2923, 2564], [2923, 2740], [2936, 2740], [2936, 2564]]], "confidences": [0.9999998807907104, 0.9929130673408508, 0.9938241243362427, 0.9778679609298706, 0.9086365699768066, 0.654657781124115, 0.9912302494049072, 0.9998319149017334, 0.9415075182914734, 0.9910994172096252, 0.8468749523162842, 0.991156816482544, 0.9946617484092712, 0.9841130375862122, 0.5371823906898499, 0.9606072902679443, 0.9999836683273315, 0.982523500919342, 0.9999951124191284, 0.9997585415840149, 0.9992431402206421, 0.9998185038566589, 0.8639128804206848, 0.999992847442627, 0.9997803568840027, 0.9999289512634277, 0.9999123811721802, 0.8764679431915283, 0.99874347448349, 0.7210342288017273, 0.9998082518577576, 0.9998107552528381, 0.9988790154457092, 0.9528904557228088, 0.9895849823951721, 0.989848792552948, 0.9913495779037476, 0.9971917271614075, 0.9919770359992981, 0.9953035116195679, 0.9654654860496521, 0.956558108329773, 0.968718945980072, 0.9891232848167419, 0.9995982050895691, 0.5353143811225891, 0.9994182586669922, 0.9978064894676208, 0.9980046153068542, 0.9997578263282776, 0.9991948008537292, 0.999717652797699, 0.999285876750946, 0.9232087731361389, 0.9976062774658203, 0.9995843768119812, 0.9938365817070007, 0.998508870601654, 0.8162130117416382, 0.9771808385848999, 0.9998940229415894, 0.9971707463264465, 0.9978471994400024, 0.9858810901641846, 0.938630223274231, 0.9977301955223083, 0.9997122883796692, 0.99797123670578, 0.5294272303581238, 0.9924471378326416, 0.9994334578514099, 0.9998548030853271, 0.999200165271759, 0.9990027546882629, 0.9875484704971313], "line": [[317, 2564], [317, 2740], [2950, 2740], [2950, 2564]]}, {"prediction": "\u0639\u0628\u0627\u0640\u0635\u0654\u0627\u0644\u0627 \u0649\u0644\u0627 \u0646\u064a\u0628\u0639\u0643\u0644\u0627 \u0646\u0645 \u0645\u0654\u0627 \u0646\u064a\u0628\u0639\u0643\u0644\u0627 \u0649\u0644\u0627 \u0639\u0628\u0627\u0635\u0654\u0627\u0644\u0627 \u0646\u0645 \u0648\u0645\u0654\u0627 \u0621\u0648\u0636\u0648\u0644\u0627 \u0641 \u0646\u064a\u0644\u062c\u0631\u0644\u0627", "cuts": [[[335, 2733], [335, 2890], [335, 2890], [335, 2733]], [[396, 2733], [396, 2890], [396, 2890], [396, 2733]], [[442, 2733], [442, 2890], [442, 2890], [442, 2733]], [[488, 2733], [488, 2890], [519, 2890], [519, 2733]], [[611, 2733], [611, 2890], [611, 2890], [611, 2733]], [[657, 2733], [657, 2890], [657, 2890], [657, 2733]], [[673, 2733], [673, 2890], [673, 2890], [673, 2733]], [[688, 2733], [688, 2890], [704, 2890], [704, 2733]], [[719, 2733], [719, 2890], [734, 2890], [734, 2733]], [[750, 2733], [750, 2890], [750, 2890], [750, 2733]], [[796, 2733], [796, 2890], [796, 2890], [796, 2733]], [[811, 2733], [811, 2890], [827, 2890], [827, 2733]], [[842, 2733], [842, 2890], [842, 2890], [842, 2733]], [[857, 2733], [857, 2890], [873, 2890], [873, 2733]], [[904, 2733], [904, 2890], [904, 2890], [904, 2733]], [[934, 2733], [934, 2890], [934, 2890], [934, 2733]], [[965, 2733], [965, 2890], [965, 2890], [965, 2733]], [[996, 2733], [996, 2890], [996, 2890], [996, 2733]], [[1057, 2733], [1057, 2890], [1057, 2890], [1057, 2733]], [[1119, 2733], [1119, 2890], [1134, 2890], [1134, 2733]], [[1150, 2733], [1150, 2890], [1150, 2890], [1150, 2733]], [[1165, 2733], [1165, 2890], [1180, 2890], [1180, 2733]], [[1288, 2733], [1288, 2890], [1303, 2890], [1303, 2733]], [[1380, 2733], [1380, 2890], [1380, 2890], [1380, 2733]], [[1411, 2733], [1411, 2890], [1426, 2890], [1426, 2733]], [[1442, 2733], [1442, 2890], [1457, 2890], [1457, 2733]], [[1473, 2733], [1473, 2890], [1473, 2890], [1473, 2733]], [[1488, 2733], [1488, 2890], [1503, 2890], [1503, 2733]], [[1519, 2733], [1519, 2890], [1534, 2890], [1534, 2733]], [[1565, 2733], [1565, 2890], [1565, 2890], [1565, 2733]], [[1580, 2733], [1580, 2890], [1596, 2890], [1596, 2733]], [[1626, 2733], [1626, 2890], [1626, 2890], [1626, 2733]], [[1657, 2733], [1657, 2890], [1657, 2890], [1657, 2733]], [[1703, 2733], [1703, 2890], [1719, 2890], [1719, 2733]], [[1780, 2733], [1780, 2890], [1780, 2890], [1780, 2733]], [[1796, 2733], [1796, 2890], [1796, 2890], [1796, 2733]], [[1811, 2733], [1811, 2890], [1826, 2890], [1826, 2733]], [[1857, 2733], [1857, 2890], [1872, 2890], [1872, 2733]], [[1888, 2733], [1888, 2890], [1888, 2890], [1888, 2733]], [[1903, 2733], [1903, 2890], [1919, 2890], [1919, 2733]], [[1934, 2733], [1934, 2890], [1934, 2890], [1934, 2733]], [[1965, 2733], [1965, 2890], [1965, 2890], [1965, 2733]], [[2042, 2733], [2042, 2890], [2042, 2890], [2042, 2733]], [[2072, 2733], [2072, 2890], [2072, 2890], [2072, 2733]], [[2118, 2733], [2118, 2890], [2134, 2890], [2134, 2733]], [[2165, 2733], [2165, 2890], [2180, 2890], [2180, 2733]], [[2195, 2733], [2195, 2890], [2195, 2890], [2195, 2733]], [[2211, 2733], [2211, 2890], [2211, 2890], [2211, 2733]], [[2226, 2733], [2226, 2890], [2241, 2890], [2241, 2733]], [[2257, 2733], [2257, 2890], [2272, 2890], [2272, 2733]], [[2318, 2733], [2318, 2890], [2318, 2890], [2318, 2733]], [[2349, 2733], [2349, 2890], [2349, 2890], [2349, 2733]], [[2380, 2733], [2380, 2890], [2395, 2890], [2395, 2733]], [[2426, 2733], [2426, 2890], [2441, 2890], [2441, 2733]], [[2472, 2733], [2472, 2890], [2472, 2890], [2472, 2733]], [[2503, 2733], [2503, 2890], [2503, 2890], [2503, 2733]], [[2518, 2733], [2518, 2890], [2534, 2890], [2534, 2733]], [[2549, 2733], [2549, 2890], [2549, 2890], [2549, 2733]], [[2580, 2733], [2580, 2890], [2580, 2890], [2580, 2733]], [[2626, 2733], [2626, 2890], [2641, 2890], [2641, 2733]], [[2687, 2733], [2687, 2890], [2687, 2890], [2687, 2733]], [[2749, 2733], [2749, 2890], [2749, 2890], [2749, 2733]], [[2780, 2733], [2780, 2890], [2780, 2890], [2780, 2733]], [[2795, 2733], [2795, 2890], [2811, 2890], [2811, 2733]], [[2826, 2733], [2826, 2890], [2826, 2890], [2826, 2733]], [[2872, 2733], [2872, 2890], [2887, 2890], [2887, 2733]], [[2918, 2733], [2918, 2890], [2918, 2890], [2918, 2733]], [[2949, 2733], [2949, 2890], [2949, 2890], [2949, 2733]], [[2980, 2733], [2980, 2890], [2980, 2890], [2980, 2733]], [[3010, 2733], [3010, 2890], [3010, 2890], [3010, 2733]], [[3041, 2733], [3041, 2890], [3041, 2890], [3041, 2733]], [[3103, 2733], [3103, 2890], [3103, 2890], [3103, 2733]], [[3118, 2733], [3118, 2890], [3133, 2890], [3133, 2733]], [[3149, 2733], [3149, 2890], [3149, 2890], [3149, 2733]]], "confidences": [0.9999692440032959, 0.7431797385215759, 0.9892282485961914, 0.9999510049819946, 0.9351767897605896, 0.9993116855621338, 0.9982556700706482, 0.9992502331733704, 0.9992731213569641, 0.9999591112136841, 0.9965296387672424, 0.9999610185623169, 0.9995737671852112, 0.9993835687637329, 0.987348198890686, 0.7714233994483948, 0.9989540576934814, 0.9985584616661072, 0.9998059868812561, 0.9927629828453064, 0.9998949766159058, 0.9996819496154785, 0.9967392086982727, 0.999603807926178, 0.9831695556640625, 0.9224259257316589, 0.9802579283714294, 0.9334492683410645, 0.9981402158737183, 0.9999240636825562, 0.986011266708374, 0.9975579977035522, 0.9953357577323914, 0.9803675413131714, 0.9984625577926636, 0.9977602958679199, 0.9838416576385498, 0.9906837940216064, 0.9881343245506287, 0.9841576814651489, 0.9998107552528381, 0.9991499185562134, 0.9434433579444885, 0.9854111671447754, 0.8520838618278503, 0.9964721202850342, 0.9900435209274292, 0.9503771066665649, 0.9993019104003906, 0.9988412261009216, 0.99639892578125, 0.8105504512786865, 0.9876804351806641, 0.9993699193000793, 0.921359121799469, 0.9604810476303101, 0.995456337928772, 0.9368835687637329, 0.9467335343360901, 0.9949937462806702, 0.999894380569458, 0.9988865256309509, 0.9930035471916199, 0.9975302815437317, 0.9954912066459656, 0.824739933013916, 0.9803107380867004, 0.98244708776474, 0.9932101368904114, 0.9977071285247803, 0.5027340054512024, 0.9766203165054321, 0.9856435060501099, 0.9869322180747986], "line": [[319, 2733], [319, 2890], [3149, 2890], [3149, 2733]]}, {"prediction": "\u062f\u0642 \u0627\u0646\u0628\u0627\u062d\u0635\u0654\u0627 \u0646\u0627 \u0643\u0627\u062f\u0641 \u062a\u0644\u0639\u062c \u0645\u0627\u0644\u0633\u0644\u0627 \u0647\u064a\u0644\u0639 \u0649\u0633\u0648\u0645 \u0646\u0633\u062d\u0644\u0627 \u064a\u0627 \u0649\u0644\u0627 \u0646\u064a\u0637\u0642\u064a \u0646\u0628 \u0649\u0644\u0639 \u0628\u062a\u0643\u0641", "cuts": [[[330, 2903], [330, 3080], [343, 3080], [343, 2903]], [[407, 2903], [407, 3080], [407, 3080], [407, 2903]], [[433, 2903], [433, 3080], [446, 3080], [446, 2903]], [[459, 2903], [459, 3080], [472, 3080], [472, 2903]], [[485, 2903], [485, 3080], [497, 3080], [497, 2903]], [[510, 2903], [510, 3080], [510, 3080], [510, 2903]], [[549, 2903], [549, 3080], [549, 3080], [549, 2903]], [[575, 2903], [575, 3080], [588, 3080], [588, 2903]], [[613, 2903], [613, 3080], [613, 3080], [613, 2903]], [[639, 2903], [639, 3080], [652, 3080], [652, 2903]], [[665, 2903], [665, 3080], [665, 3080], [665, 2903]], [[678, 2903], [678, 3080], [691, 3080], [691, 2903]], [[716, 2903], [716, 3080], [729, 3080], [729, 2903]], [[768, 2903], [768, 3080], [768, 3080], [768, 2903]], [[781, 2903], [781, 3080], [807, 3080], [807, 2903]], [[845, 2903], [845, 3080], [845, 3080], [845, 2903]], [[897, 2903], [897, 3080], [897, 3080], [897, 2903]], [[935, 2903], [935, 3080], [948, 3080], [948, 2903]], [[974, 2903], [974, 3080], [974, 3080], [974, 2903]], [[1000, 2903], [1000, 3080], [1013, 3080], [1013, 2903]], [[1051, 2903], [1051, 3080], [1051, 3080], [1051, 2903]], [[1103, 2903], [1103, 3080], [1116, 3080], [1116, 2903]], [[1142, 2903], [1142, 3080], [1142, 3080], [1142, 2903]], [[1180, 2903], [1180, 3080], [1193, 3080], [1193, 2903]], [[1232, 2903], [1232, 3080], [1245, 3080], [1245, 2903]], [[1283, 2903], [1283, 3080], [1296, 3080], [1296, 2903]], [[1335, 2903], [1335, 3080], [1348, 3080], [1348, 2903]], [[1360, 2903], [1360, 3080], [1373, 3080], [1373, 2903]], [[1412, 2903], [1412, 3080], [1425, 3080], [1425, 2903]], [[1438, 2903], [1438, 3080], [1451, 3080], [1451, 2903]], [[1464, 2903], [1464, 3080], [1464, 3080], [1464, 2903]], [[1476, 2903], [1476, 3080], [1489, 3080], [1489, 2903]], [[1502, 2903], [1502, 3080], [1515, 3080], [1515, 2903]], [[1541, 2903], [1541, 3080], [1554, 3080], [1554, 2903]], [[1567, 2903], [1567, 3080], [1579, 3080], [1579, 2903]], [[1592, 2903], [1592, 3080], [1605, 3080], [1605, 2903]], [[1644, 2903], [1644, 3080], [1657, 3080], [1657, 2903]], [[1708, 2903], [1708, 3080], [1708, 3080], [1708, 2903]], [[1747, 2903], [1747, 3080], [1747, 3080], [1747, 2903]], [[1798, 2903], [1798, 3080], [1811, 3080], [1811, 2903]], [[1837, 2903], [1837, 3080], [1837, 3080], [1837, 2903]], [[1863, 2903], [1863, 3080], [1876, 3080], [1876, 2903]], [[1927, 2903], [1927, 3080], [1927, 3080], [1927, 2903]], [[1966, 2903], [1966, 3080], [1979, 3080], [1979, 2903]], [[2017, 2903], [2017, 3080], [2017, 3080], [2017, 2903]], [[2030, 2903], [2030, 3080], [2043, 3080], [2043, 2903]], [[2069, 2903], [2069, 3080], [2082, 3080], [2082, 2903]], [[2095, 2903], [2095, 3080], [2108, 3080], [2108, 2903]], [[2146, 2903], [2146, 3080], [2146, 3080], [2146, 2903]], [[2198, 2903], [2198, 3080], [2211, 3080], [2211, 2903]], [[2223, 2903], [2223, 3080], [2223, 3080], [2223, 2903]], [[2262, 2903], [2262, 3080], [2262, 3080], [2262, 2903]], [[2275, 2903], [2275, 3080], [2288, 3080], [2288, 2903]], [[2301, 2903], [2301, 3080], [2314, 3080], [2314, 2903]], [[2326, 2903], [2326, 3080], [2339, 3080], [2339, 2903]], [[2365, 2903], [2365, 3080], [2378, 3080], [2378, 2903]], [[2404, 2903], [2404, 3080], [2404, 3080], [2404, 2903]], [[2430, 2903], [2430, 3080], [2430, 3080], [2430, 2903]], [[2494, 2903], [2494, 3080], [2494, 3080], [2494, 2903]], [[2520, 2903], [2520, 3080], [2520, 3080], [2520, 2903]], [[2545, 2903], [2545, 3080], [2558, 3080], [2558, 2903]], [[2623, 2903], [2623, 3080], [2623, 3080], [2623, 2903]], [[2649, 2903], [2649, 3080], [2649, 3080], [2649, 2903]], [[2674, 2903], [2674, 3080], [2687, 3080], [2687, 2903]], [[2752, 2903], [2752, 3080], [2752, 3080], [2752, 2903]], [[2764, 2903], [2764, 3080], [2777, 3080], [2777, 2903]], [[2790, 2903], [2790, 3080], [2803, 3080], [2803, 2903]], [[2829, 2903], [2829, 3080], [2842, 3080], [2842, 2903]], [[2893, 2903], [2893, 3080], [2893, 3080], [2893, 2903]], [[2945, 2903], [2945, 3080], [2945, 3080], [2945, 2903]], [[2983, 2903], [2983, 3080], [2983, 3080], [2983, 2903]], [[3138, 2903], [3138, 3080], [3138, 3080], [3138, 2903]]], "confidences": [0.9998844861984253, 0.7102985382080078, 0.9393450021743774, 0.9997884631156921, 0.999976634979248, 0.9990993738174438, 0.9997594952583313, 0.9806336760520935, 0.9991434812545776, 0.999870777130127, 0.999963641166687, 0.9986358284950256, 0.9997819066047668, 0.9902760982513428, 0.9999171495437622, 0.870272159576416, 0.9959033131599426, 0.9999700784683228, 0.9983206391334534, 0.9999897480010986, 0.9983596205711365, 0.9999958276748657, 0.7078837156295776, 0.9993500113487244, 0.9999644756317139, 0.9912440776824951, 0.9898048639297485, 0.9963675737380981, 0.9999985694885254, 0.9996073842048645, 0.9969272017478943, 0.9999234676361084, 0.9996798038482666, 0.9999964237213135, 0.9991588592529297, 0.9990678429603577, 0.9999243021011353, 0.9791718125343323, 0.8421644568443298, 0.9974477291107178, 0.7698737978935242, 0.9559314250946045, 0.9906162619590759, 0.9658488631248474, 0.9968318343162537, 0.9971415400505066, 0.9973932504653931, 0.99928218126297, 0.9969269633293152, 0.9991139769554138, 0.994012176990509, 0.880149781703949, 0.9992927312850952, 0.9991368651390076, 0.9945836663246155, 0.9022614359855652, 0.9830822944641113, 0.9845501780509949, 0.9880629777908325, 0.9992579817771912, 0.9978712797164917, 0.9982649683952332, 0.996843695640564, 0.9984459280967712, 0.9971120357513428, 0.9952387809753418, 0.9999581575393677, 0.9986611604690552, 0.9999819993972778, 0.9977983832359314, 0.5938760638237, 0.49053633213043213], "line": [[317, 2903], [317, 3080], [3151, 3080], [3151, 2903]]}, {"prediction": "\u0646\u0655\u0627 \u062a\u0644\u0639\u0641 \u0647\u064a\u0644\u0639 \u0649\u0644\u0645\u0639 \u0646\u0648\u0643\u064a \u0627\u0645 \u0643\u0637\u062e\u0628 \u0649\u0644\u0627 \u0628\u062a\u0643\u062a \u0646\u0654\u0627 \u062a\u064a\u0654\u0627\u0631 \u0646\u0627\u0641 \u0646\u064a\u0644\u062c\u0631\u0644\u0627 \u062d\u0633\u0645 \u064a\u0641 \u0627\u0648\u0641\u0644\u062a\u062e\u0627", "cuts": [[[328, 3067], [328, 3246], [342, 3246], [342, 3067]], [[357, 3067], [357, 3246], [357, 3246], [357, 3067]], [[371, 3067], [371, 3246], [386, 3246], [386, 3067]], [[400, 3067], [400, 3246], [400, 3246], [400, 3067]], [[444, 3067], [444, 3246], [444, 3246], [444, 3067]], [[502, 3067], [502, 3246], [502, 3246], [502, 3067]], [[531, 3067], [531, 3246], [531, 3246], [531, 3067]], [[560, 3067], [560, 3246], [560, 3246], [560, 3067]], [[589, 3067], [589, 3246], [589, 3246], [589, 3067]], [[604, 3067], [604, 3246], [618, 3246], [618, 3067]], [[647, 3067], [647, 3246], [647, 3246], [647, 3067]], [[676, 3067], [676, 3246], [676, 3246], [676, 3067]], [[691, 3067], [691, 3246], [705, 3246], [705, 3067]], [[735, 3067], [735, 3246], [749, 3246], [749, 3067]], [[822, 3067], [822, 3246], [822, 3246], [822, 3067]], [[836, 3067], [836, 3246], [836, 3246], [836, 3067]], [[851, 3067], [851, 3246], [851, 3246], [851, 3067]], [[880, 3067], [880, 3246], [880, 3246], [880, 3067]], [[923, 3067], [923, 3246], [938, 3246], [938, 3067]], [[967, 3067], [967, 3246], [967, 3246], [967, 3067]], [[1025, 3067], [1025, 3246], [1025, 3246], [1025, 3067]], [[1054, 3067], [1054, 3246], [1054, 3246], [1054, 3067]], [[1098, 3067], [1098, 3246], [1098, 3246], [1098, 3067]], [[1127, 3067], [1127, 3246], [1141, 3246], [1141, 3067]], [[1170, 3067], [1170, 3246], [1185, 3246], [1185, 3067]], [[1200, 3067], [1200, 3246], [1214, 3246], [1214, 3067]], [[1243, 3067], [1243, 3246], [1243, 3246], [1243, 3067]], [[1301, 3067], [1301, 3246], [1301, 3246], [1301, 3067]], [[1359, 3067], [1359, 3246], [1359, 3246], [1359, 3067]], [[1403, 3067], [1403, 3246], [1403, 3246], [1403, 3067]], [[1432, 3067], [1432, 3246], [1432, 3246], [1432, 3067]], [[1461, 3067], [1461, 3246], [1476, 3246], [1476, 3067]], [[1548, 3067], [1548, 3246], [1548, 3246], [1548, 3067]], [[1563, 3067], [1563, 3246], [1563, 3246], [1563, 3067]], [[1577, 3067], [1577, 3246], [1592, 3246], [1592, 3067]], [[1606, 3067], [1606, 3246], [1621, 3246], [1621, 3067]], [[1665, 3067], [1665, 3246], [1665, 3246], [1665, 3067]], [[1723, 3067], [1723, 3246], [1723, 3246], [1723, 3067]], [[1781, 3067], [1781, 3246], [1781, 3246], [1781, 3067]], [[1839, 3067], [1839, 3246], [1839, 3246], [1839, 3067]], [[1868, 3067], [1868, 3246], [1883, 3246], [1883, 3067]], [[1926, 3067], [1926, 3246], [1941, 3246], [1941, 3067]], [[1970, 3067], [1970, 3246], [1970, 3246], [1970, 3067]], [[1984, 3067], [1984, 3246], [1984, 3246], [1984, 3067]], [[1999, 3067], [1999, 3246], [2013, 3246], [2013, 3067]], [[2057, 3067], [2057, 3246], [2057, 3246], [2057, 3067]], [[2115, 3067], [2115, 3246], [2115, 3246], [2115, 3067]], [[2130, 3067], [2130, 3246], [2144, 3246], [2144, 3067]], [[2159, 3067], [2159, 3246], [2159, 3246], [2159, 3067]], [[2202, 3067], [2202, 3246], [2202, 3246], [2202, 3067]], [[2231, 3067], [2231, 3246], [2231, 3246], [2231, 3067]], [[2260, 3067], [2260, 3246], [2275, 3246], [2275, 3067]], [[2304, 3067], [2304, 3246], [2304, 3246], [2304, 3067]], [[2319, 3067], [2319, 3246], [2319, 3246], [2319, 3067]], [[2348, 3067], [2348, 3246], [2362, 3246], [2362, 3067]], [[2391, 3067], [2391, 3246], [2391, 3246], [2391, 3067]], [[2420, 3067], [2420, 3246], [2420, 3246], [2420, 3067]], [[2449, 3067], [2449, 3246], [2449, 3246], [2449, 3067]], [[2478, 3067], [2478, 3246], [2493, 3246], [2493, 3067]], [[2537, 3067], [2537, 3246], [2551, 3246], [2551, 3067]], [[2566, 3067], [2566, 3246], [2566, 3246], [2566, 3067]], [[2580, 3067], [2580, 3246], [2595, 3246], [2595, 3067]], [[2609, 3067], [2609, 3246], [2624, 3246], [2624, 3067]], [[2653, 3067], [2653, 3246], [2653, 3246], [2653, 3067]], [[2711, 3067], [2711, 3246], [2711, 3246], [2711, 3067]], [[2755, 3067], [2755, 3246], [2755, 3246], [2755, 3067]], [[2769, 3067], [2769, 3246], [2784, 3246], [2784, 3067]], [[2827, 3067], [2827, 3246], [2827, 3246], [2827, 3067]], [[2842, 3067], [2842, 3246], [2842, 3246], [2842, 3067]], [[2871, 3067], [2871, 3246], [2871, 3246], [2871, 3067]], [[2900, 3067], [2900, 3246], [2900, 3246], [2900, 3067]], [[2943, 3067], [2943, 3246], [2958, 3246], [2958, 3067]], [[2987, 3067], [2987, 3246], [3002, 3246], [3002, 3067]], [[3016, 3067], [3016, 3246], [3016, 3246], [3016, 3067]], [[3045, 3067], [3045, 3246], [3045, 3246], [3045, 3067]], [[3089, 3067], [3089, 3246], [3089, 3246], [3089, 3067]], [[3132, 3067], [3132, 3246], [3147, 3246], [3147, 3067]]], "confidences": [0.9998571872711182, 0.9720284938812256, 0.9996850490570068, 0.9983483552932739, 0.9983503818511963, 0.9421389102935791, 0.9757440090179443, 0.9998825788497925, 0.995537281036377, 0.9998735189437866, 0.979082465171814, 0.9991683959960938, 0.8312473297119141, 0.9998272061347961, 0.9998739957809448, 0.9941480159759521, 0.6946954131126404, 0.9902309775352478, 0.99941086769104, 0.9953650236129761, 0.9976621866226196, 0.9999786615371704, 0.6234442591667175, 0.9998052716255188, 0.9999556541442871, 0.9997043013572693, 0.9890322089195251, 0.9971346855163574, 0.9940102100372314, 0.9995328187942505, 0.999966025352478, 0.7640908360481262, 0.4993588328361511, 0.9998918771743774, 0.9999830722808838, 0.9678281545639038, 0.9903719425201416, 0.9858986139297485, 0.6294997930526733, 0.971433699131012, 0.9831180572509766, 0.9937307834625244, 0.9823363423347473, 0.9849645495414734, 0.9930417537689209, 0.9968459010124207, 0.9991890788078308, 0.9997627139091492, 0.9964436888694763, 0.8590208292007446, 0.9995020627975464, 0.9917978048324585, 0.9393941760063171, 0.9994710087776184, 0.9996200799942017, 0.6683350205421448, 0.7901033759117126, 0.9907926917076111, 0.9704153537750244, 0.9975791573524475, 0.999360978603363, 0.9991258978843689, 0.9997497200965881, 0.8905563354492188, 0.9997245669364929, 0.9991188645362854, 0.9998225569725037, 0.9893819093704224, 0.997583270072937, 0.9980733394622803, 0.9992850422859192, 0.9999197721481323, 0.9919565916061401, 0.9748324751853943, 0.8973613381385803, 0.9983094930648804, 0.9631760120391846], "line": [[313, 3067], [313, 3246], [3147, 3246], [3147, 3067]]}, {"prediction": "\u0621\u0648\u0636\u0648\u0644\u0627 \u064a\u0641 \u0641\u0627\u0644\u062a\u062e\u0655\u0627\u0644\u0627 \u0646\u0645 \u062a\u0631\u0643\u0630 \u0627\u0645 \u062a\u0645\u0647\u0641 \u060c \u0639. \u0646\u0633\u062d\u0644\u0627 \u0648\u0628\u0654\u0627 \u0647\u064a\u0644\u0627 \u0628\u062a\u0643\u0642 \u0649\u0644\u0627\u0639\u062a \u0647\u062a\u0627 \u0621\u0627\u0634", "cuts": [[[318, 3226], [318, 3412], [318, 3412], [318, 3226]], [[375, 3226], [375, 3412], [375, 3412], [375, 3226]], [[418, 3226], [418, 3412], [418, 3412], [418, 3226]], [[489, 3226], [489, 3412], [503, 3412], [503, 3226]], [[517, 3226], [517, 3412], [517, 3412], [517, 3226]], [[546, 3226], [546, 3412], [546, 3412], [546, 3226]], [[560, 3226], [560, 3412], [574, 3412], [574, 3226]], [[603, 3226], [603, 3412], [603, 3412], [603, 3226]], [[617, 3226], [617, 3412], [617, 3412], [617, 3226]], [[646, 3226], [646, 3412], [660, 3412], [660, 3226]], [[745, 3226], [745, 3412], [745, 3412], [745, 3226]], [[774, 3226], [774, 3412], [788, 3412], [788, 3226]], [[802, 3226], [802, 3412], [802, 3412], [802, 3226]], [[831, 3226], [831, 3412], [831, 3412], [831, 3226]], [[873, 3226], [873, 3412], [888, 3412], [888, 3226]], [[930, 3226], [930, 3412], [930, 3412], [930, 3226]], [[945, 3226], [945, 3412], [945, 3412], [945, 3226]], [[959, 3226], [959, 3412], [973, 3412], [973, 3226]], [[987, 3226], [987, 3412], [1001, 3412], [1001, 3226]], [[1016, 3226], [1016, 3412], [1030, 3412], [1030, 3226]], [[1101, 3226], [1101, 3412], [1101, 3412], [1101, 3226]], [[1130, 3226], [1130, 3412], [1144, 3412], [1144, 3226]], [[1172, 3226], [1172, 3412], [1172, 3412], [1172, 3226]], [[1215, 3226], [1215, 3412], [1215, 3412], [1215, 3226]], [[1286, 3226], [1286, 3412], [1286, 3412], [1286, 3226]], [[1315, 3226], [1315, 3412], [1315, 3412], [1315, 3226]], [[1386, 3226], [1386, 3412], [1386, 3412], [1386, 3226]], [[1414, 3226], [1414, 3412], [1443, 3412], [1443, 3226]], [[1471, 3226], [1471, 3412], [1471, 3412], [1471, 3226]], [[1500, 3226], [1500, 3412], [1500, 3412], [1500, 3226]], [[1528, 3226], [1528, 3412], [1542, 3412], [1542, 3226]], [[1585, 3226], [1585, 3412], [1585, 3412], [1585, 3226]], [[1642, 3226], [1642, 3412], [1642, 3412], [1642, 3226]], [[1685, 3226], [1685, 3412], [1685, 3412], [1685, 3226]], [[1727, 3226], [1727, 3412], [1727, 3412], [1727, 3226]], [[1756, 3226], [1756, 3412], [1770, 3412], [1770, 3226]], [[1799, 3226], [1799, 3412], [1799, 3412], [1799, 3226]], [[1827, 3226], [1827, 3412], [1827, 3412], [1827, 3226]], [[1870, 3226], [1870, 3412], [1870, 3412], [1870, 3226]], [[1941, 3226], [1941, 3412], [1941, 3412], [1941, 3226]], [[1969, 3226], [1969, 3412], [1984, 3412], [1984, 3226]], [[2026, 3226], [2026, 3412], [2026, 3412], [2026, 3226]], [[2069, 3226], [2069, 3412], [2083, 3412], [2083, 3226]], [[2112, 3226], [2112, 3412], [2112, 3412], [2112, 3226]], [[2126, 3226], [2126, 3412], [2140, 3412], [2140, 3226]], [[2169, 3226], [2169, 3412], [2183, 3412], [2183, 3226]], [[2197, 3226], [2197, 3412], [2211, 3412], [2211, 3226]], [[2240, 3226], [2240, 3412], [2254, 3412], [2254, 3226]], [[2268, 3226], [2268, 3412], [2268, 3412], [2268, 3226]], [[2297, 3226], [2297, 3412], [2297, 3412], [2297, 3226]], [[2311, 3226], [2311, 3412], [2311, 3412], [2311, 3226]], [[2339, 3226], [2339, 3412], [2354, 3412], [2354, 3226]], [[2382, 3226], [2382, 3412], [2382, 3412], [2382, 3226]], [[2411, 3226], [2411, 3412], [2411, 3412], [2411, 3226]], [[2439, 3226], [2439, 3412], [2439, 3412], [2439, 3226]], [[2453, 3226], [2453, 3412], [2468, 3412], [2468, 3226]], [[2482, 3226], [2482, 3412], [2482, 3412], [2482, 3226]], [[2539, 3226], [2539, 3412], [2539, 3412], [2539, 3226]], [[2596, 3226], [2596, 3412], [2596, 3412], [2596, 3226]], [[2638, 3226], [2638, 3412], [2638, 3412], [2638, 3226]], [[2681, 3226], [2681, 3412], [2681, 3412], [2681, 3226]], [[2710, 3226], [2710, 3412], [2724, 3412], [2724, 3226]], [[2766, 3226], [2766, 3412], [2766, 3412], [2766, 3226]], [[2781, 3226], [2781, 3412], [2781, 3412], [2781, 3226]], [[2795, 3226], [2795, 3412], [2809, 3412], [2809, 3226]], [[2823, 3226], [2823, 3412], [2838, 3412], [2838, 3226]], [[2866, 3226], [2866, 3412], [2866, 3412], [2866, 3226]], [[2880, 3226], [2880, 3412], [2895, 3412], [2895, 3226]], [[2923, 3226], [2923, 3412], [2923, 3412], [2923, 3226]], [[2952, 3226], [2952, 3412], [2952, 3412], [2952, 3226]], [[2994, 3226], [2994, 3412], [2994, 3412], [2994, 3226]], [[3008, 3226], [3008, 3412], [3023, 3412], [3023, 3226]], [[3051, 3226], [3051, 3412], [3051, 3412], [3051, 3226]], [[3080, 3226], [3080, 3412], [3094, 3412], [3094, 3226]], [[3137, 3226], [3137, 3412], [3137, 3412], [3137, 3226]]], "confidences": [0.9989814162254333, 0.9999122619628906, 0.9998594522476196, 0.9999371767044067, 0.9949018955230713, 0.8918391466140747, 0.9331813454627991, 0.9998503923416138, 0.999738872051239, 0.9999352693557739, 0.9999396800994873, 0.9997422099113464, 0.9998968839645386, 0.999890923500061, 0.9986469149589539, 0.9979827404022217, 0.9983227849006653, 0.9980936646461487, 0.9998340606689453, 0.9999415874481201, 0.9813975095748901, 0.9996932744979858, 0.9956713914871216, 0.9996687173843384, 0.9994198083877563, 0.732080340385437, 0.840719997882843, 0.9999840259552002, 0.9996098875999451, 0.9923226237297058, 0.9988951086997986, 0.5162858963012695, 0.5562438368797302, 0.799801766872406, 0.9747372269630432, 0.9793431758880615, 0.9933470487594604, 0.9947622418403625, 0.9987406134605408, 0.9820605516433716, 0.9616630673408508, 0.9937961101531982, 0.9977521300315857, 0.996338963508606, 0.9950332641601562, 0.8663762807846069, 0.9994631409645081, 0.9986950755119324, 0.8317818641662598, 0.9994366765022278, 0.9611929655075073, 0.9998297691345215, 0.9822288751602173, 0.9975958466529846, 0.9939688444137573, 0.9993873834609985, 0.998565137386322, 0.9918579459190369, 0.9916180968284607, 0.9998064637184143, 0.8910688161849976, 0.9938943982124329, 0.9996296167373657, 0.9683365821838379, 0.8659057021141052, 0.999840497970581, 0.9998772144317627, 0.9978265166282654, 0.9994264841079712, 0.9970901012420654, 0.9989050626754761, 0.9994168281555176, 0.9968239068984985, 0.9972999691963196, 0.8729744553565979], "line": [[318, 3226], [318, 3412], [3151, 3412], [3151, 3226]]}, {"prediction": "\u0644\u063a\u062a\u0648 \u0627\u0654\u064a\u0627\u0644\u062b \u0643\u0647\u062c\u0648 \u0644\u0633\u063a\u062a\u0648 \u0627\u062b\u0627\u0644\u062b \u0642\u0634\u0646\u062a\u0633\u062a\u0648 \u0627\u0627\u0644\u062b \u0636\u0645\u0636\u0645\u062a \u0646\u0654\u0627 \u0643\u0644\u0630 \u064a\u0641 \u0647\u0628\u0643\u0631\u0645\u0653\u0627 \u0649\u0630\u0644\u0627\u0648", "cuts": [[[340, 3404], [340, 3582], [369, 3582], [369, 3404]], [[398, 3404], [398, 3582], [398, 3582], [398, 3404]], [[441, 3404], [441, 3582], [441, 3582], [441, 3404]], [[484, 3404], [484, 3582], [498, 3582], [498, 3404]], [[513, 3404], [513, 3582], [527, 3582], [527, 3404]], [[556, 3404], [556, 3582], [556, 3582], [556, 3404]], [[570, 3404], [570, 3582], [570, 3582], [570, 3404]], [[585, 3404], [585, 3582], [585, 3582], [585, 3404]], [[613, 3404], [613, 3582], [628, 3582], [628, 3404]], [[642, 3404], [642, 3582], [642, 3582], [642, 3404]], [[671, 3404], [671, 3582], [671, 3582], [671, 3404]], [[700, 3404], [700, 3582], [714, 3582], [714, 3404]], [[757, 3404], [757, 3582], [757, 3582], [757, 3404]], [[800, 3404], [800, 3582], [815, 3582], [815, 3404]], [[844, 3404], [844, 3582], [858, 3582], [858, 3404]], [[930, 3404], [930, 3582], [930, 3582], [930, 3404]], [[959, 3404], [959, 3582], [973, 3582], [973, 3404]], [[1016, 3404], [1016, 3582], [1031, 3582], [1031, 3404]], [[1059, 3404], [1059, 3582], [1074, 3582], [1074, 3404]], [[1117, 3404], [1117, 3582], [1117, 3582], [1117, 3404]], [[1146, 3404], [1146, 3582], [1160, 3582], [1160, 3404]], [[1203, 3404], [1203, 3582], [1203, 3582], [1203, 3404]], [[1232, 3404], [1232, 3582], [1246, 3582], [1246, 3404]], [[1261, 3404], [1261, 3582], [1275, 3582], [1275, 3404]], [[1290, 3404], [1290, 3582], [1290, 3582], [1290, 3404]], [[1318, 3404], [1318, 3582], [1333, 3582], [1333, 3404]], [[1347, 3404], [1347, 3582], [1362, 3582], [1362, 3404]], [[1376, 3404], [1376, 3582], [1390, 3582], [1390, 3404]], [[1405, 3404], [1405, 3582], [1419, 3582], [1419, 3404]], [[1462, 3404], [1462, 3582], [1462, 3582], [1462, 3404]], [[1520, 3404], [1520, 3582], [1520, 3582], [1520, 3404]], [[1549, 3404], [1549, 3582], [1549, 3582], [1549, 3404]], [[1577, 3404], [1577, 3582], [1577, 3582], [1577, 3404]], [[1621, 3404], [1621, 3582], [1635, 3582], [1635, 3404]], [[1664, 3404], [1664, 3582], [1664, 3582], [1664, 3404]], [[1707, 3404], [1707, 3582], [1721, 3582], [1721, 3404]], [[1736, 3404], [1736, 3582], [1764, 3582], [1764, 3404]], [[1779, 3404], [1779, 3582], [1793, 3582], [1793, 3404]], [[1836, 3404], [1836, 3582], [1851, 3582], [1851, 3404]], [[1880, 3404], [1880, 3582], [1880, 3582], [1880, 3404]], [[1908, 3404], [1908, 3582], [1908, 3582], [1908, 3404]], [[1923, 3404], [1923, 3582], [1937, 3582], [1937, 3404]], [[2009, 3404], [2009, 3582], [2009, 3582], [2009, 3404]], [[2067, 3404], [2067, 3582], [2067, 3582], [2067, 3404]], [[2124, 3404], [2124, 3582], [2124, 3582], [2124, 3404]], [[2182, 3404], [2182, 3582], [2182, 3582], [2182, 3404]], [[2211, 3404], [2211, 3582], [2211, 3582], [2211, 3404]], [[2239, 3404], [2239, 3582], [2254, 3582], [2254, 3404]], [[2282, 3404], [2282, 3582], [2282, 3582], [2282, 3404]], [[2311, 3404], [2311, 3582], [2311, 3582], [2311, 3404]], [[2326, 3404], [2326, 3582], [2326, 3582], [2326, 3404]], [[2340, 3404], [2340, 3582], [2354, 3582], [2354, 3404]], [[2398, 3404], [2398, 3582], [2398, 3582], [2398, 3404]], [[2455, 3404], [2455, 3582], [2455, 3582], [2455, 3404]], [[2484, 3404], [2484, 3582], [2484, 3582], [2484, 3404]], [[2513, 3404], [2513, 3582], [2513, 3582], [2513, 3404]], [[2556, 3404], [2556, 3582], [2556, 3582], [2556, 3404]], [[2570, 3404], [2570, 3582], [2570, 3582], [2570, 3404]], [[2599, 3404], [2599, 3582], [2613, 3582], [2613, 3404]], [[2628, 3404], [2628, 3582], [2628, 3582], [2628, 3404]], [[2657, 3404], [2657, 3582], [2657, 3582], [2657, 3404]], [[2714, 3404], [2714, 3582], [2714, 3582], [2714, 3404]], [[2772, 3404], [2772, 3582], [2786, 3582], [2786, 3404]], [[2800, 3404], [2800, 3582], [2815, 3582], [2815, 3404]], [[2858, 3404], [2858, 3582], [2858, 3582], [2858, 3404]], [[2872, 3404], [2872, 3582], [2887, 3582], [2887, 3404]], [[2901, 3404], [2901, 3582], [2916, 3582], [2916, 3404]], [[2988, 3404], [2988, 3582], [2988, 3582], [2988, 3404]], [[3031, 3404], [3031, 3582], [3031, 3582], [3031, 3404]], [[3059, 3404], [3059, 3582], [3074, 3582], [3074, 3404]], [[3088, 3404], [3088, 3582], [3088, 3582], [3088, 3404]], [[3131, 3404], [3131, 3582], [3131, 3582], [3131, 3404]]], "confidences": [0.9967783093452454, 0.9999089241027832, 0.6990695595741272, 0.9999850988388062, 0.9994284510612488, 0.9905351996421814, 0.9979711174964905, 0.9925774931907654, 0.9997119307518005, 0.9998630285263062, 0.9991826415061951, 0.9995831847190857, 0.8739675283432007, 0.9986891150474548, 0.9998999834060669, 0.712096631526947, 0.9542328119277954, 0.9998071789741516, 0.9994687438011169, 0.9119349718093872, 0.9997267127037048, 0.9990538954734802, 0.9999830722808838, 0.9995676875114441, 0.9998180270195007, 0.9542503952980042, 0.9904987215995789, 0.9882673621177673, 0.9979777932167053, 0.9933269023895264, 0.9998037219047546, 0.9610075354576111, 0.9995414018630981, 0.9869469404220581, 0.9698469638824463, 0.9878268837928772, 0.9939728379249573, 0.8041937947273254, 0.9983476400375366, 0.792262077331543, 0.9819558262825012, 0.9396706223487854, 0.997104823589325, 0.9705645442008972, 0.9958399534225464, 0.9978986978530884, 0.8801516890525818, 0.9995551705360413, 0.9966424703598022, 0.9960938096046448, 0.9987300038337708, 0.9998194575309753, 0.563930094242096, 0.9453648328781128, 0.9340890049934387, 0.5644081830978394, 0.9911462664604187, 0.9987581968307495, 0.8091545701026917, 0.9992177486419678, 0.9971036314964294, 0.999757707118988, 0.999749481678009, 0.9985652565956116, 0.9988590478897095, 0.9889073371887207, 0.9370191693305969, 0.9869567155838013, 0.5738163590431213, 0.9979482293128967, 0.9877589344978333, 0.9431320428848267], "line": [[311, 3404], [311, 3582], [3146, 3582], [3146, 3404]]}, {"prediction": "\u0631\u0647\u0627\u0638 \u062d\u0633\u0645\u062a\u0648 \u0647\u0644\u0643 \u0643\u0633\u0654\u0627\u0631 \u062d\u0633\u0645\u062a\u0648 \u0646\u064a\u0642\u0641\u0631\u0645\u0644\u0627 \u0649\u0644\u0627 \u0643\u0639\u0628\u0627\u0635\u0654\u0627 \u0646\u0645 \u0643\u062f\u064a \u0644\u0633\u063a\u062a\u0648 \u0643\u062a\u064a\u062d\u0644 \u0631\u0639\u0634", "cuts": [[[321, 3580], [321, 3731], [335, 3731], [335, 3580]], [[363, 3580], [363, 3731], [363, 3731], [363, 3580]], [[405, 3580], [405, 3731], [405, 3731], [405, 3580]], [[434, 3580], [434, 3731], [434, 3731], [434, 3580]], [[476, 3580], [476, 3731], [504, 3731], [504, 3580]], [[546, 3580], [546, 3731], [560, 3731], [560, 3580]], [[616, 3580], [616, 3731], [616, 3731], [616, 3580]], [[644, 3580], [644, 3731], [658, 3731], [658, 3580]], [[672, 3580], [672, 3731], [672, 3731], [672, 3580]], [[729, 3580], [729, 3731], [729, 3731], [729, 3580]], [[757, 3580], [757, 3731], [771, 3731], [771, 3580]], [[785, 3580], [785, 3731], [799, 3731], [799, 3580]], [[813, 3580], [813, 3731], [827, 3731], [827, 3580]], [[841, 3580], [841, 3731], [841, 3731], [841, 3580]], [[883, 3580], [883, 3731], [883, 3731], [883, 3580]], [[925, 3580], [925, 3731], [925, 3731], [925, 3580]], [[995, 3580], [995, 3731], [1010, 3731], [1010, 3580]], [[1024, 3580], [1024, 3731], [1038, 3731], [1038, 3580]], [[1052, 3580], [1052, 3731], [1052, 3731], [1052, 3580]], [[1094, 3580], [1094, 3731], [1094, 3731], [1094, 3580]], [[1122, 3580], [1122, 3731], [1136, 3731], [1136, 3580]], [[1178, 3580], [1178, 3731], [1178, 3731], [1178, 3580]], [[1234, 3580], [1234, 3731], [1248, 3731], [1248, 3580]], [[1262, 3580], [1262, 3731], [1276, 3731], [1276, 3580]], [[1291, 3580], [1291, 3731], [1305, 3731], [1305, 3580]], [[1347, 3580], [1347, 3731], [1361, 3731], [1361, 3580]], [[1375, 3580], [1375, 3731], [1389, 3731], [1389, 3580]], [[1417, 3580], [1417, 3731], [1431, 3731], [1431, 3580]], [[1445, 3580], [1445, 3731], [1459, 3731], [1459, 3580]], [[1501, 3580], [1501, 3731], [1501, 3731], [1501, 3580]], [[1529, 3580], [1529, 3731], [1529, 3731], [1529, 3580]], [[1571, 3580], [1571, 3731], [1586, 3731], [1586, 3580]], [[1600, 3580], [1600, 3731], [1600, 3731], [1600, 3580]], [[1614, 3580], [1614, 3731], [1628, 3731], [1628, 3580]], [[1642, 3580], [1642, 3731], [1642, 3731], [1642, 3580]], [[1656, 3580], [1656, 3731], [1670, 3731], [1670, 3580]], [[1712, 3580], [1712, 3731], [1712, 3731], [1712, 3580]], [[1726, 3580], [1726, 3731], [1726, 3731], [1726, 3580]], [[1754, 3580], [1754, 3731], [1754, 3731], [1754, 3580]], [[1768, 3580], [1768, 3731], [1782, 3731], [1782, 3580]], [[1824, 3580], [1824, 3731], [1824, 3731], [1824, 3580]], [[1881, 3580], [1881, 3731], [1881, 3731], [1881, 3580]], [[1909, 3580], [1909, 3731], [1923, 3731], [1923, 3580]], [[1951, 3580], [1951, 3731], [1951, 3731], [1951, 3580]], [[2007, 3580], [2007, 3731], [2007, 3731], [2007, 3580]], [[2035, 3580], [2035, 3731], [2049, 3731], [2049, 3580]], [[2063, 3580], [2063, 3731], [2063, 3731], [2063, 3580]], [[2077, 3580], [2077, 3731], [2091, 3731], [2091, 3580]], [[2232, 3580], [2232, 3731], [2232, 3731], [2232, 3580]], [[2316, 3580], [2316, 3731], [2330, 3731], [2330, 3580]], [[2358, 3580], [2358, 3731], [2358, 3731], [2358, 3580]], [[2400, 3580], [2400, 3731], [2400, 3731], [2400, 3580]], [[2457, 3580], [2457, 3731], [2457, 3731], [2457, 3580]], [[2485, 3580], [2485, 3731], [2485, 3731], [2485, 3580]], [[2513, 3580], [2513, 3731], [2527, 3731], [2527, 3580]], [[2569, 3580], [2569, 3731], [2569, 3731], [2569, 3580]], [[2611, 3580], [2611, 3731], [2625, 3731], [2625, 3580]], [[2653, 3580], [2653, 3731], [2653, 3731], [2653, 3580]], [[2695, 3580], [2695, 3731], [2695, 3731], [2695, 3580]], [[2737, 3580], [2737, 3731], [2752, 3731], [2752, 3580]], [[2766, 3580], [2766, 3731], [2780, 3731], [2780, 3580]], [[2822, 3580], [2822, 3731], [2822, 3731], [2822, 3580]], [[2878, 3580], [2878, 3731], [2878, 3731], [2878, 3580]], [[2906, 3580], [2906, 3731], [2920, 3731], [2920, 3580]], [[2948, 3580], [2948, 3731], [2948, 3731], [2948, 3580]], [[2962, 3580], [2962, 3731], [2962, 3731], [2962, 3580]], [[3004, 3580], [3004, 3731], [3004, 3731], [3004, 3580]], [[3032, 3580], [3032, 3731], [3047, 3731], [3047, 3580]], [[3075, 3580], [3075, 3731], [3075, 3731], [3075, 3580]], [[3131, 3580], [3131, 3731], [3131, 3731], [3131, 3580]]], "confidences": [0.998375415802002, 0.7084844708442688, 0.9999047517776489, 0.9999986886978149, 0.9975200295448303, 0.9999372959136963, 0.9996757507324219, 0.9996148347854614, 0.9969292283058167, 0.9986764788627625, 0.9608417749404907, 0.9995263814926147, 0.9976219534873962, 0.9999587535858154, 0.9711738228797913, 0.9985242486000061, 0.8398705720901489, 0.9996516704559326, 0.9961230158805847, 0.9993791580200195, 0.9999798536300659, 0.9998527765274048, 0.9999849796295166, 0.9962425231933594, 0.9955328702926636, 0.9989853501319885, 0.999942421913147, 0.9898174405097961, 0.9999264478683472, 0.999687910079956, 0.9997530579566956, 0.9998797178268433, 0.9882998466491699, 0.9634631872177124, 0.999843955039978, 0.9989407658576965, 0.9994811415672302, 0.9873757362365723, 0.996773898601532, 0.9990653395652771, 0.9880833029747009, 0.9579822421073914, 0.9963617920875549, 0.968079149723053, 0.9967735409736633, 0.9786046743392944, 0.6416178941726685, 0.9990584254264832, 0.99955815076828, 0.9967063069343567, 0.998286783695221, 0.6909951567649841, 0.7128172516822815, 0.9613966941833496, 0.9996551275253296, 0.9983021020889282, 0.999221682548523, 0.9992075562477112, 0.9989655017852783, 0.9996083378791809, 0.9999632835388184, 0.936977207660675, 0.9235370755195618, 0.9997147917747498, 0.667002260684967, 0.9975676536560059, 0.9907772541046143, 0.9969243407249451, 0.9055776000022888, 0.7779210209846497], "line": [[307, 3580], [307, 3731], [3145, 3731], [3145, 3580]]}, {"prediction": "\u0644\u0635\u0648 \u0627\u0644\u0641 \u0647\u0631\u064a\u063a \u0649\u0644\u0627 \u0643\u0644\u0630 \u0641\u0644\u0627\u062e \u0627\u0644\u0648 \u0627\u0645\u062b\u0627\u0644\u062b \u0646\u064a\u0628\u0639\u0643\u0644\u0627 \u0649\u0644\u0627 \u0643\u064a\u0644\u062c\u0631 \u0644\u0633\u063a\u062a\u0648\u0627\u0645\u0647\u0646\u0637\u0627\u0628\u0648 \u0643\u064a\u0646\u0630\u0627", "cuts": [[[361, 3744], [361, 3918], [375, 3918], [375, 3744]], [[417, 3744], [417, 3918], [417, 3918], [417, 3744]], [[487, 3744], [487, 3918], [487, 3918], [487, 3744]], [[515, 3744], [515, 3918], [529, 3918], [529, 3744]], [[543, 3744], [543, 3918], [557, 3918], [557, 3744]], [[585, 3744], [585, 3918], [599, 3918], [599, 3744]], [[613, 3744], [613, 3918], [613, 3918], [613, 3744]], [[641, 3744], [641, 3918], [655, 3918], [655, 3744]], [[683, 3744], [683, 3918], [683, 3918], [683, 3744]], [[739, 3744], [739, 3918], [739, 3918], [739, 3744]], [[753, 3744], [753, 3918], [767, 3918], [767, 3744]], [[795, 3744], [795, 3918], [795, 3918], [795, 3744]], [[837, 3744], [837, 3918], [837, 3918], [837, 3744]], [[879, 3744], [879, 3918], [879, 3918], [879, 3744]], [[893, 3744], [893, 3918], [907, 3918], [907, 3744]], [[921, 3744], [921, 3918], [921, 3918], [921, 3744]], [[935, 3744], [935, 3918], [963, 3918], [963, 3744]], [[1019, 3744], [1019, 3918], [1019, 3918], [1019, 3744]], [[1075, 3744], [1075, 3918], [1075, 3918], [1075, 3744]], [[1103, 3744], [1103, 3918], [1103, 3918], [1103, 3744]], [[1131, 3744], [1131, 3918], [1145, 3918], [1145, 3744]], [[1215, 3744], [1215, 3918], [1229, 3918], [1229, 3744]], [[1243, 3744], [1243, 3918], [1257, 3918], [1257, 3744]], [[1271, 3744], [1271, 3918], [1285, 3918], [1285, 3744]], [[1299, 3744], [1299, 3918], [1313, 3918], [1313, 3744]], [[1383, 3744], [1383, 3918], [1397, 3918], [1397, 3744]], [[1411, 3744], [1411, 3918], [1439, 3918], [1439, 3744]], [[1453, 3744], [1453, 3918], [1467, 3918], [1467, 3744]], [[1509, 3744], [1509, 3918], [1509, 3918], [1509, 3744]], [[1537, 3744], [1537, 3918], [1537, 3918], [1537, 3744]], [[1551, 3744], [1551, 3918], [1551, 3918], [1551, 3744]], [[1565, 3744], [1565, 3918], [1565, 3918], [1565, 3744]], [[1579, 3744], [1579, 3918], [1579, 3918], [1579, 3744]], [[1607, 3744], [1607, 3918], [1621, 3918], [1621, 3744]], [[1635, 3744], [1635, 3918], [1649, 3918], [1649, 3744]], [[1663, 3744], [1663, 3918], [1663, 3918], [1663, 3744]], [[1691, 3744], [1691, 3918], [1719, 3918], [1719, 3744]], [[1747, 3744], [1747, 3918], [1761, 3918], [1761, 3744]], [[1775, 3744], [1775, 3918], [1789, 3918], [1789, 3744]], [[1803, 3744], [1803, 3918], [1817, 3918], [1817, 3744]], [[1845, 3744], [1845, 3918], [1845, 3918], [1845, 3744]], [[1873, 3744], [1873, 3918], [1873, 3918], [1873, 3744]], [[1943, 3744], [1943, 3918], [1957, 3918], [1957, 3744]], [[1971, 3744], [1971, 3918], [1971, 3918], [1971, 3744]], [[1985, 3744], [1985, 3918], [1999, 3918], [1999, 3744]], [[2041, 3744], [2041, 3918], [2041, 3918], [2041, 3744]], [[2055, 3744], [2055, 3918], [2055, 3918], [2055, 3744]], [[2083, 3744], [2083, 3918], [2083, 3918], [2083, 3744]], [[2097, 3744], [2097, 3918], [2111, 3918], [2111, 3744]], [[2153, 3744], [2153, 3918], [2153, 3918], [2153, 3744]], [[2209, 3744], [2209, 3918], [2209, 3918], [2209, 3744]], [[2237, 3744], [2237, 3918], [2237, 3918], [2237, 3744]], [[2265, 3744], [2265, 3918], [2279, 3918], [2279, 3744]], [[2335, 3744], [2335, 3918], [2349, 3918], [2349, 3744]], [[2363, 3744], [2363, 3918], [2377, 3918], [2377, 3744]], [[2419, 3744], [2419, 3918], [2433, 3918], [2433, 3744]], [[2475, 3744], [2475, 3918], [2475, 3918], [2475, 3744]], [[2517, 3744], [2517, 3918], [2517, 3918], [2517, 3744]], [[2545, 3744], [2545, 3918], [2545, 3918], [2545, 3744]], [[2601, 3744], [2601, 3918], [2601, 3918], [2601, 3744]], [[2643, 3744], [2643, 3918], [2643, 3918], [2643, 3744]], [[2657, 3744], [2657, 3918], [2671, 3918], [2671, 3744]], [[2699, 3744], [2699, 3918], [2713, 3918], [2713, 3744]], [[2741, 3744], [2741, 3918], [2741, 3918], [2741, 3744]], [[2769, 3744], [2769, 3918], [2769, 3918], [2769, 3744]], [[2811, 3744], [2811, 3918], [2825, 3918], [2825, 3744]], [[2839, 3744], [2839, 3918], [2839, 3918], [2839, 3744]], [[2881, 3744], [2881, 3918], [2895, 3918], [2895, 3744]], [[2923, 3744], [2923, 3918], [2923, 3918], [2923, 3744]], [[2965, 3744], [2965, 3918], [2979, 3918], [2979, 3744]], [[3021, 3744], [3021, 3918], [3035, 3918], [3035, 3744]], [[3049, 3744], [3049, 3918], [3049, 3918], [3049, 3744]], [[3077, 3744], [3077, 3918], [3091, 3918], [3091, 3744]], [[3119, 3744], [3119, 3918], [3133, 3918], [3133, 3744]]], "confidences": [0.9999858140945435, 0.9996674060821533, 0.9778488278388977, 0.999860405921936, 0.9980079531669617, 0.9999980926513672, 0.9997289776802063, 0.9999096393585205, 0.9999042749404907, 0.9987836480140686, 0.999509334564209, 0.8447310328483582, 0.9997413754463196, 0.9830690622329712, 0.9998390674591064, 0.8895362615585327, 0.9999918937683105, 0.9990143775939941, 0.9999878406524658, 0.9991787075996399, 0.9999074935913086, 0.999941349029541, 0.9998675584793091, 0.9998282194137573, 0.99846351146698, 0.9999916553497314, 0.9999760389328003, 0.9508774876594543, 0.9997891783714294, 0.9323558807373047, 0.9861510396003723, 0.9936410784721375, 0.9994382262229919, 0.9971796274185181, 0.9985735416412354, 0.9859241843223572, 0.9663385152816772, 0.9317023158073425, 0.9961659908294678, 0.9577418565750122, 0.9928492307662964, 0.9323335886001587, 0.9990610480308533, 0.9972244501113892, 0.9995347261428833, 0.9952410459518433, 0.9984773993492126, 0.999396800994873, 0.9987291693687439, 0.8243120908737183, 0.9955060482025146, 0.7022139430046082, 0.9865391850471497, 0.9992412328720093, 0.9976450800895691, 0.9810840487480164, 0.9991329312324524, 0.9990897178649902, 0.9992324113845825, 0.998461127281189, 0.9994111061096191, 0.9990944862365723, 0.9991432428359985, 0.9482150077819824, 0.9927623867988586, 0.9994828701019287, 0.953675389289856, 0.9985700845718384, 0.9993718266487122, 0.94927978515625, 0.9973020553588867, 0.9952284097671509, 0.9812739491462708, 0.9950826168060303], "line": [[333, 3744], [333, 3918], [3133, 3918], [3133, 3744]]}, {"prediction": "\u0649\u0627\u0644\u0648\u0645\u0644\u0627\u0642 \u0645\u062b \u0647\u0641\u0627\u0644\u062e \u0649\u0644\u0639 \u0629\u0628\u0627\u0635\u0639\u0644\u0627 \u0639\u0645\u062c\u0654\u0627 \u0627\u0645 \u0647\u064a\u0641 \u0647\u0644 \u0645\u0633\u0631 \u0645 \u0628\u062c\u0639\u062a \u0646\u064a\u0637\u0642\u064a \u0646\u0628 \u0649\u0644\u0639 \u0649\u0644\u0627 \u0628\u0646\u0627\u062a\u0643\u0644\u0627", "cuts": [[[374, 3912], [374, 4081], [374, 4081], [374, 3912]], [[416, 3912], [416, 4081], [430, 4081], [430, 3912]], [[444, 3912], [444, 4081], [458, 4081], [458, 3912]], [[486, 3912], [486, 4081], [500, 4081], [500, 3912]], [[528, 3912], [528, 4081], [528, 4081], [528, 3912]], [[598, 3912], [598, 4081], [598, 4081], [598, 3912]], [[612, 3912], [612, 4081], [612, 4081], [612, 3912]], [[641, 3912], [641, 4081], [641, 4081], [641, 3912]], [[669, 3912], [669, 4081], [683, 4081], [683, 3912]], [[697, 3912], [697, 4081], [711, 4081], [711, 3912]], [[739, 3912], [739, 4081], [739, 4081], [739, 3912]], [[767, 3912], [767, 4081], [767, 4081], [767, 3912]], [[781, 3912], [781, 4081], [795, 4081], [795, 3912]], [[823, 3912], [823, 4081], [823, 4081], [823, 3912]], [[851, 3912], [851, 4081], [865, 4081], [865, 3912]], [[879, 3912], [879, 4081], [893, 4081], [893, 3912]], [[921, 3912], [921, 4081], [935, 4081], [935, 3912]], [[963, 3912], [963, 4081], [977, 4081], [977, 3912]], [[1019, 3912], [1019, 4081], [1019, 4081], [1019, 3912]], [[1033, 3912], [1033, 4081], [1033, 4081], [1033, 3912]], [[1047, 3912], [1047, 4081], [1061, 4081], [1061, 3912]], [[1089, 3912], [1089, 4081], [1103, 4081], [1103, 3912]], [[1117, 3912], [1117, 4081], [1117, 4081], [1117, 3912]], [[1145, 3912], [1145, 4081], [1145, 4081], [1145, 3912]], [[1173, 3912], [1173, 4081], [1187, 4081], [1187, 3912]], [[1229, 3912], [1229, 4081], [1229, 4081], [1229, 3912]], [[1271, 3912], [1271, 4081], [1285, 4081], [1285, 3912]], [[1313, 3912], [1313, 4081], [1313, 4081], [1313, 3912]], [[1327, 3912], [1327, 4081], [1327, 4081], [1327, 3912]], [[1341, 3912], [1341, 4081], [1356, 4081], [1356, 3912]], [[1370, 3912], [1370, 4081], [1384, 4081], [1384, 3912]], [[1412, 3912], [1412, 4081], [1426, 4081], [1426, 3912]], [[1440, 3912], [1440, 4081], [1454, 4081], [1454, 3912]], [[1482, 3912], [1482, 4081], [1482, 4081], [1482, 3912]], [[1496, 3912], [1496, 4081], [1496, 4081], [1496, 3912]], [[1524, 3912], [1524, 4081], [1524, 4081], [1524, 3912]], [[1538, 3912], [1538, 4081], [1538, 4081], [1538, 3912]], [[1594, 3912], [1594, 4081], [1608, 4081], [1608, 3912]], [[1636, 3912], [1636, 4081], [1650, 4081], [1650, 3912]], [[1664, 3912], [1664, 4081], [1664, 4081], [1664, 3912]], [[1692, 3912], [1692, 4081], [1692, 4081], [1692, 3912]], [[1720, 3912], [1720, 4081], [1720, 4081], [1720, 3912]], [[1748, 3912], [1748, 4081], [1762, 4081], [1762, 3912]], [[1776, 3912], [1776, 4081], [1776, 4081], [1776, 3912]], [[1804, 3912], [1804, 4081], [1818, 4081], [1818, 3912]], [[1832, 3912], [1832, 4081], [1832, 4081], [1832, 3912]], [[1846, 3912], [1846, 4081], [1860, 4081], [1860, 3912]], [[1916, 3912], [1916, 4081], [1916, 4081], [1916, 3912]], [[1972, 3912], [1972, 4081], [1972, 4081], [1972, 3912]], [[1986, 3912], [1986, 4081], [2000, 4081], [2000, 3912]], [[2042, 3912], [2042, 4081], [2042, 4081], [2042, 3912]], [[2084, 3912], [2084, 4081], [2099, 4081], [2099, 3912]], [[2141, 3912], [2141, 4081], [2141, 4081], [2141, 3912]], [[2197, 3912], [2197, 4081], [2211, 4081], [2211, 3912]], [[2253, 3912], [2253, 4081], [2253, 4081], [2253, 3912]], [[2281, 3912], [2281, 4081], [2281, 4081], [2281, 3912]], [[2309, 3912], [2309, 4081], [2323, 4081], [2323, 3912]], [[2351, 3912], [2351, 4081], [2365, 4081], [2365, 3912]], [[2379, 3912], [2379, 4081], [2379, 4081], [2379, 3912]], [[2407, 3912], [2407, 4081], [2421, 4081], [2421, 3912]], [[2463, 3912], [2463, 4081], [2477, 4081], [2477, 3912]], [[2491, 3912], [2491, 4081], [2505, 4081], [2505, 3912]], [[2519, 3912], [2519, 4081], [2519, 4081], [2519, 3912]], [[2547, 3912], [2547, 4081], [2561, 4081], [2561, 3912]], [[2575, 3912], [2575, 4081], [2575, 4081], [2575, 3912]], [[2603, 3912], [2603, 4081], [2617, 4081], [2617, 3912]], [[2687, 3912], [2687, 4081], [2687, 4081], [2687, 3912]], [[2701, 3912], [2701, 4081], [2701, 4081], [2701, 3912]], [[2715, 3912], [2715, 4081], [2729, 4081], [2729, 3912]], [[2771, 3912], [2771, 4081], [2771, 4081], [2771, 3912]], [[2813, 3912], [2813, 4081], [2813, 4081], [2813, 3912]], [[2828, 3912], [2828, 4081], [2842, 4081], [2842, 3912]], [[2856, 3912], [2856, 4081], [2856, 4081], [2856, 3912]], [[2870, 3912], [2870, 4081], [2870, 4081], [2870, 3912]], [[2912, 3912], [2912, 4081], [2912, 4081], [2912, 3912]], [[2926, 3912], [2926, 4081], [2926, 4081], [2926, 3912]], [[2954, 3912], [2954, 4081], [2954, 4081], [2954, 3912]], [[2982, 3912], [2982, 4081], [2982, 4081], [2982, 3912]], [[3024, 3912], [3024, 4081], [3024, 4081], [3024, 3912]], [[3108, 3912], [3108, 4081], [3108, 4081], [3108, 3912]], [[3122, 3912], [3122, 4081], [3136, 4081], [3136, 3912]]], "confidences": [0.991776704788208, 0.9966753721237183, 0.9947142004966736, 0.9186890721321106, 0.9635366797447205, 0.9986220598220825, 0.9777283668518066, 0.9586911797523499, 0.9998502731323242, 0.9988514184951782, 0.9999862909317017, 0.46282950043678284, 0.973861575126648, 0.42882752418518066, 0.9648494124412537, 0.9557760953903198, 0.9976227879524231, 0.5231557488441467, 0.7373040914535522, 0.997160792350769, 0.9977774024009705, 0.9998714923858643, 0.9994800686836243, 0.5984565019607544, 0.9999616146087646, 0.953172504901886, 0.9998903274536133, 0.5577518939971924, 0.6261033415794373, 0.9999628067016602, 0.9987449645996094, 0.9979907274246216, 0.9999592304229736, 0.9994562268257141, 0.776331901550293, 0.9890751838684082, 0.9983356595039368, 0.986653745174408, 0.9932841062545776, 0.9770086407661438, 0.8269332051277161, 0.9864249229431152, 0.9786643981933594, 0.9915361404418945, 0.9968346953392029, 0.9864396452903748, 0.9924086332321167, 0.998105525970459, 0.9923842549324036, 0.9835927486419678, 0.9957876801490784, 0.9960395097732544, 0.9908498525619507, 0.9995176792144775, 0.9677330255508423, 0.9952746629714966, 0.9993299245834351, 0.998761773109436, 0.9985032081604004, 0.9977524876594543, 0.9977297186851501, 0.9998428821563721, 0.9996432065963745, 0.9972556233406067, 0.8950211405754089, 0.982606828212738, 0.9643605351448059, 0.9769435524940491, 0.9995658993721008, 0.9992498755455017, 0.7895783185958862, 0.9948700666427612, 0.8788118362426758, 0.9814590215682983, 0.9998113512992859, 0.8790767788887024, 0.7014104723930359, 0.7737085819244385, 0.9973267316818237, 0.9496884942054749, 0.9877499938011169], "line": [[304, 3912], [304, 4081], [3136, 4081], [3136, 3912]]}, {"prediction": "\u0647\u064a\u0644\u0639 \u0627\u0645 \u0641\u0644\u0627\u062e\u064a\u0648 \u062f\u062d\u0644\u0627 \u0627\u0630\u0647 \u0649\u0644\u0639 \u0647\u0648\u0636\u0648 \u064a\u0641 \u0644\u0645\u0639\u064a \u0646\u0627\u0643\u0641 \u0647\u0631\u0645\u0654\u0627 \u0644\u062b\u062a\u0645 \u0627\u0646\u0654\u0627\u0648 \u0644\u0627\u0642 \u0627\u0645\u0628 \u0645\u0644\u0639\u0654\u0627", "cuts": [[[305, 4078], [305, 4239], [305, 4239], [305, 4078]], [[348, 4078], [348, 4239], [348, 4239], [348, 4078]], [[362, 4078], [362, 4239], [376, 4239], [376, 4078]], [[390, 4078], [390, 4239], [404, 4239], [404, 4078]], [[447, 4078], [447, 4239], [447, 4239], [447, 4078]], [[461, 4078], [461, 4239], [475, 4239], [475, 4078]], [[489, 4078], [489, 4239], [489, 4239], [489, 4078]], [[518, 4078], [518, 4239], [532, 4239], [532, 4078]], [[617, 4078], [617, 4239], [617, 4239], [617, 4078]], [[645, 4078], [645, 4239], [645, 4239], [645, 4078]], [[659, 4078], [659, 4239], [674, 4239], [674, 4078]], [[702, 4078], [702, 4239], [702, 4239], [702, 4078]], [[730, 4078], [730, 4239], [744, 4239], [744, 4078]], [[787, 4078], [787, 4239], [801, 4239], [801, 4078]], [[815, 4078], [815, 4239], [830, 4239], [830, 4078]], [[872, 4078], [872, 4239], [872, 4239], [872, 4078]], [[900, 4078], [900, 4239], [900, 4239], [900, 4078]], [[915, 4078], [915, 4239], [929, 4239], [929, 4078]], [[957, 4078], [957, 4239], [971, 4239], [971, 4078]], [[985, 4078], [985, 4239], [1000, 4239], [1000, 4078]], [[1028, 4078], [1028, 4239], [1042, 4239], [1042, 4078]], [[1085, 4078], [1085, 4239], [1085, 4239], [1085, 4078]], [[1113, 4078], [1113, 4239], [1113, 4239], [1113, 4078]], [[1156, 4078], [1156, 4239], [1170, 4239], [1170, 4078]], [[1198, 4078], [1198, 4239], [1198, 4239], [1198, 4078]], [[1212, 4078], [1212, 4239], [1212, 4239], [1212, 4078]], [[1241, 4078], [1241, 4239], [1241, 4239], [1241, 4078]], [[1283, 4078], [1283, 4239], [1297, 4239], [1297, 4078]], [[1311, 4078], [1311, 4239], [1311, 4239], [1311, 4078]], [[1397, 4078], [1397, 4239], [1397, 4239], [1397, 4078]], [[1439, 4078], [1439, 4239], [1453, 4239], [1453, 4078]], [[1524, 4078], [1524, 4239], [1524, 4239], [1524, 4078]], [[1552, 4078], [1552, 4239], [1552, 4239], [1552, 4078]], [[1595, 4078], [1595, 4239], [1595, 4239], [1595, 4078]], [[1609, 4078], [1609, 4239], [1609, 4239], [1609, 4078]], [[1638, 4078], [1638, 4239], [1652, 4239], [1652, 4078]], [[1694, 4078], [1694, 4239], [1694, 4239], [1694, 4078]], [[1723, 4078], [1723, 4239], [1723, 4239], [1723, 4078]], [[1765, 4078], [1765, 4239], [1765, 4239], [1765, 4078]], [[1793, 4078], [1793, 4239], [1793, 4239], [1793, 4078]], [[1822, 4078], [1822, 4239], [1836, 4239], [1836, 4078]], [[1978, 4078], [1978, 4239], [1978, 4239], [1978, 4078]], [[2049, 4078], [2049, 4239], [2063, 4239], [2063, 4078]], [[2077, 4078], [2077, 4239], [2077, 4239], [2077, 4078]], [[2120, 4078], [2120, 4239], [2120, 4239], [2120, 4078]], [[2148, 4078], [2148, 4239], [2162, 4239], [2162, 4078]], [[2205, 4078], [2205, 4239], [2205, 4239], [2205, 4078]], [[2247, 4078], [2247, 4239], [2261, 4239], [2261, 4078]], [[2290, 4078], [2290, 4239], [2290, 4239], [2290, 4078]], [[2304, 4078], [2304, 4239], [2318, 4239], [2318, 4078]], [[2332, 4078], [2332, 4239], [2332, 4239], [2332, 4078]], [[2346, 4078], [2346, 4239], [2361, 4239], [2361, 4078]], [[2403, 4078], [2403, 4239], [2417, 4239], [2417, 4078]], [[2431, 4078], [2431, 4239], [2446, 4239], [2446, 4078]], [[2474, 4078], [2474, 4239], [2474, 4239], [2474, 4078]], [[2488, 4078], [2488, 4239], [2502, 4239], [2502, 4078]], [[2531, 4078], [2531, 4239], [2531, 4239], [2531, 4078]], [[2545, 4078], [2545, 4239], [2559, 4239], [2559, 4078]], [[2573, 4078], [2573, 4239], [2573, 4239], [2573, 4078]], [[2601, 4078], [2601, 4239], [2601, 4239], [2601, 4078]], [[2616, 4078], [2616, 4239], [2616, 4239], [2616, 4078]], [[2672, 4078], [2672, 4239], [2672, 4239], [2672, 4078]], [[2701, 4078], [2701, 4239], [2701, 4239], [2701, 4078]], [[2757, 4078], [2757, 4239], [2757, 4239], [2757, 4078]], [[2772, 4078], [2772, 4239], [2772, 4239], [2772, 4078]], [[2786, 4078], [2786, 4239], [2786, 4239], [2786, 4078]], [[2814, 4078], [2814, 4239], [2842, 4239], [2842, 4078]], [[2871, 4078], [2871, 4239], [2871, 4239], [2871, 4078]], [[2928, 4078], [2928, 4239], [2942, 4239], [2942, 4078]], [[2970, 4078], [2970, 4239], [2970, 4239], [2970, 4078]], [[2984, 4078], [2984, 4239], [2998, 4239], [2998, 4078]], [[3013, 4078], [3013, 4239], [3027, 4239], [3027, 4078]], [[3041, 4078], [3041, 4239], [3041, 4239], [3041, 4078]], [[3055, 4078], [3055, 4239], [3069, 4239], [3069, 4078]], [[3098, 4078], [3098, 4239], [3098, 4239], [3098, 4078]], [[3112, 4078], [3112, 4239], [3112, 4239], [3112, 4078]]], "confidences": [0.7411984801292419, 0.6705909371376038, 0.9994827508926392, 0.9999536275863647, 0.9637630581855774, 0.990339457988739, 0.9970928430557251, 0.9842832088470459, 0.9999134540557861, 0.8257625699043274, 0.9998522996902466, 0.9860520362854004, 0.8672003149986267, 0.999895453453064, 0.9987590312957764, 0.6380735635757446, 0.9966011047363281, 0.9999879598617554, 0.9917520880699158, 0.9998186230659485, 0.9985596537590027, 0.9999741315841675, 0.9970946311950684, 0.9999897480010986, 0.9996445178985596, 0.9999563694000244, 0.9980692267417908, 0.9994539618492126, 0.5508994460105896, 0.9075180888175964, 0.6853848099708557, 0.8571422696113586, 0.7955424189567566, 0.8907495737075806, 0.9879714846611023, 0.9994396567344666, 0.848770260810852, 0.9981479644775391, 0.909099280834198, 0.9869056344032288, 0.9833765625953674, 0.9991958737373352, 0.9672496318817139, 0.9993138313293457, 0.5518977046012878, 0.8995003700256348, 0.9811897277832031, 0.9994994401931763, 0.9018236994743347, 0.9840490818023682, 0.9967617392539978, 0.9858490228652954, 0.7903525233268738, 0.996831476688385, 0.9998800754547119, 0.9991426467895508, 0.9994601607322693, 0.9994288086891174, 0.9615521430969238, 0.8211493492126465, 0.9944064617156982, 0.9838215112686157, 0.9928451180458069, 0.9992262125015259, 0.9864301681518555, 0.980707585811615, 0.9878180027008057, 0.7048366665840149, 0.9973539113998413, 0.8788666129112244, 0.9915334582328796, 0.9992111921310425, 0.9873242378234863, 0.9851594567298889, 0.5106502771377563, 0.9952054023742676], "line": [[305, 4078], [305, 4239], [3126, 4239], [3126, 4078]]}, {"prediction": "\u062f\u064a\u0634\u0631\u0644\u0627 \u0649\u0644\u0627 \u0646\u064a\u0637\u0642\u064a \u0646\u0628 \u0649\u0644\u0639! \u064a\u0645\u0639\u0633\u0648 \u0645\u0627\u0644\u0633\u0644\u0627 \u0647\u064a\u0644\u0639 \u0646\u0633\u062d\u0644\u0627 \u064a\u0628\u0654\u0627 \u0645\u0627\u0644 \u0627\u0644\u0627\u062b\u062a\u0645\u0655\u0627 \u0629\u0640\u0639\u064a\u0634\u0644\u0627 \u0639\u064a\u0645\u062c", "cuts": [[[316, 4249], [316, 4413], [331, 4413], [331, 4249]], [[359, 4249], [359, 4413], [359, 4413], [359, 4249]], [[402, 4249], [402, 4413], [402, 4413], [402, 4249]], [[444, 4249], [444, 4413], [444, 4413], [444, 4249]], [[459, 4249], [459, 4413], [473, 4413], [473, 4249]], [[487, 4249], [487, 4413], [501, 4413], [501, 4249]], [[515, 4249], [515, 4413], [530, 4413], [530, 4249]], [[587, 4249], [587, 4413], [587, 4413], [587, 4249]], [[601, 4249], [601, 4413], [601, 4413], [601, 4249]], [[629, 4249], [629, 4413], [629, 4413], [629, 4249]], [[643, 4249], [643, 4413], [658, 4413], [658, 4249]], [[686, 4249], [686, 4413], [700, 4413], [700, 4249]], [[715, 4249], [715, 4413], [715, 4413], [715, 4249]], [[743, 4249], [743, 4413], [757, 4413], [757, 4249]], [[800, 4249], [800, 4413], [814, 4413], [814, 4249]], [[828, 4249], [828, 4413], [843, 4413], [843, 4249]], [[857, 4249], [857, 4413], [871, 4413], [871, 4249]], [[914, 4249], [914, 4413], [914, 4413], [914, 4249]], [[942, 4249], [942, 4413], [942, 4413], [942, 4249]], [[971, 4249], [971, 4413], [985, 4413], [985, 4249]], [[1070, 4249], [1070, 4413], [1070, 4413], [1070, 4249]], [[1084, 4249], [1084, 4413], [1084, 4413], [1084, 4249]], [[1113, 4249], [1113, 4413], [1113, 4413], [1113, 4249]], [[1141, 4249], [1141, 4413], [1141, 4413], [1141, 4249]], [[1155, 4249], [1155, 4413], [1184, 4413], [1184, 4249]], [[1212, 4249], [1212, 4413], [1226, 4413], [1226, 4249]], [[1255, 4249], [1255, 4413], [1255, 4413], [1255, 4249]], [[1269, 4249], [1269, 4413], [1269, 4413], [1269, 4249]], [[1312, 4249], [1312, 4413], [1326, 4413], [1326, 4249]], [[1369, 4249], [1369, 4413], [1383, 4413], [1383, 4249]], [[1397, 4249], [1397, 4413], [1411, 4413], [1411, 4249]], [[1426, 4249], [1426, 4413], [1440, 4413], [1440, 4249]], [[1482, 4249], [1482, 4413], [1482, 4413], [1482, 4249]], [[1497, 4249], [1497, 4413], [1511, 4413], [1511, 4249]], [[1553, 4249], [1553, 4413], [1553, 4413], [1553, 4249]], [[1582, 4249], [1582, 4413], [1582, 4413], [1582, 4249]], [[1596, 4249], [1596, 4413], [1610, 4413], [1610, 4249]], [[1625, 4249], [1625, 4413], [1639, 4413], [1639, 4249]], [[1653, 4249], [1653, 4413], [1653, 4413], [1653, 4249]], [[1681, 4249], [1681, 4413], [1681, 4413], [1681, 4249]], [[1710, 4249], [1710, 4413], [1710, 4413], [1710, 4249]], [[1738, 4249], [1738, 4413], [1738, 4413], [1738, 4249]], [[1781, 4249], [1781, 4413], [1795, 4413], [1795, 4249]], [[1824, 4249], [1824, 4413], [1824, 4413], [1824, 4249]], [[1866, 4249], [1866, 4413], [1895, 4413], [1895, 4249]], [[1923, 4249], [1923, 4413], [1923, 4413], [1923, 4249]], [[1937, 4249], [1937, 4413], [1952, 4413], [1952, 4249]], [[1980, 4249], [1980, 4413], [1994, 4413], [1994, 4249]], [[2008, 4249], [2008, 4413], [2023, 4413], [2023, 4249]], [[2065, 4249], [2065, 4413], [2065, 4413], [2065, 4249]], [[2080, 4249], [2080, 4413], [2080, 4413], [2080, 4249]], [[2108, 4249], [2108, 4413], [2108, 4413], [2108, 4249]], [[2122, 4249], [2122, 4413], [2122, 4413], [2122, 4249]], [[2136, 4249], [2136, 4413], [2151, 4413], [2151, 4249]], [[2222, 4249], [2222, 4413], [2236, 4413], [2236, 4249]], [[2264, 4249], [2264, 4413], [2279, 4413], [2279, 4249]], [[2293, 4249], [2293, 4413], [2307, 4413], [2307, 4249]], [[2321, 4249], [2321, 4413], [2336, 4413], [2336, 4249]], [[2378, 4249], [2378, 4413], [2378, 4413], [2378, 4249]], [[2407, 4249], [2407, 4413], [2407, 4413], [2407, 4249]], [[2435, 4249], [2435, 4413], [2435, 4413], [2435, 4249]], [[2463, 4249], [2463, 4413], [2463, 4413], [2463, 4249]], [[2492, 4249], [2492, 4413], [2492, 4413], [2492, 4249]], [[2520, 4249], [2520, 4413], [2520, 4413], [2520, 4249]], [[2549, 4249], [2549, 4413], [2549, 4413], [2549, 4249]], [[2563, 4249], [2563, 4413], [2563, 4413], [2563, 4249]], [[2577, 4249], [2577, 4413], [2591, 4413], [2591, 4249]], [[2606, 4249], [2606, 4413], [2606, 4413], [2606, 4249]], [[2663, 4249], [2663, 4413], [2677, 4413], [2677, 4249]], [[2762, 4249], [2762, 4413], [2762, 4413], [2762, 4249]], [[2805, 4249], [2805, 4413], [2805, 4413], [2805, 4249]], [[2847, 4249], [2847, 4413], [2847, 4413], [2847, 4249]], [[2876, 4249], [2876, 4413], [2876, 4413], [2876, 4249]], [[2890, 4249], [2890, 4413], [2904, 4413], [2904, 4249]], [[2919, 4249], [2919, 4413], [2933, 4413], [2933, 4249]], [[2975, 4249], [2975, 4413], [2990, 4413], [2990, 4249]], [[3061, 4249], [3061, 4413], [3061, 4413], [3061, 4249]], [[3089, 4249], [3089, 4413], [3089, 4413], [3089, 4249]], [[3103, 4249], [3103, 4413], [3103, 4413], [3103, 4249]]], "confidences": [0.9999558925628662, 0.9997685551643372, 0.9997368454933167, 0.9977124929428101, 0.999941349029541, 0.9980744123458862, 0.9950352907180786, 0.9224417805671692, 0.8580173254013062, 0.9524855613708496, 0.9989435076713562, 0.9999842643737793, 0.9986186027526855, 0.9998856782913208, 0.9995737671852112, 0.9912926554679871, 0.9999467134475708, 0.9999945163726807, 0.9995242357254028, 0.9999788999557495, 0.9609777331352234, 0.9153129458427429, 0.9045202136039734, 0.9997207522392273, 0.9395636916160583, 0.9894552826881409, 0.9998144507408142, 0.9997990727424622, 0.9999853372573853, 0.9999799728393555, 1.0, 0.9706733822822571, 0.9758889675140381, 0.9932337403297424, 0.8476508259773254, 0.9795082807540894, 0.9796251058578491, 0.9970026612281799, 0.4895252287387848, 0.5919338464736938, 0.9867957830429077, 0.7614318132400513, 0.9896360039710999, 0.9846451878547668, 0.9974541068077087, 0.5357677340507507, 0.9974640607833862, 0.9990847110748291, 0.9961497783660889, 0.9180766940116882, 0.8193199634552002, 0.9990463852882385, 0.9899054169654846, 0.9957371950149536, 0.9989354014396667, 0.9109645485877991, 0.9999076128005981, 0.999586284160614, 0.7132347226142883, 0.9970788955688477, 0.9983876943588257, 0.6264116764068604, 0.9988951086997986, 0.9250513315200806, 0.8208836913108826, 0.8228710293769836, 0.9901149272918701, 0.997815728187561, 0.9998468160629272, 0.9990491271018982, 0.9956784844398499, 0.7115011811256409, 0.9978455305099487, 0.9982227683067322, 0.9991169571876526, 0.9916975498199463, 0.9556155204772949, 0.9978026747703552, 0.9827321171760559], "line": [[302, 4249], [302, 4413], [3132, 4413], [3132, 4249]]}, {"prediction": ".\u0649\u0644\u0639 \u064a\u0641 \u0644\u0648\u0642\u0644\u0627 \u0649\u062f\u0646\u0639 \u0631\u062b\u0643 \u062f\u0642 \u0647\u062a\u0635\u0627\u062e \u0636\u0639\u0628\u0627 \u062f\u064a\u0634\u0631\u0644\u0627 \u0644\u0627\u0642\u0641 \u0643\u0644 \u0641\u0627\u0627\u062e\u0645 \u064a\u0636\u0641\u0627\u0631 \u0647\u0627 \u0647\u0644 \u0644\u064a\u0642\u0648", "cuts": [[[297, 4427], [297, 4585], [297, 4585], [297, 4427]], [[350, 4427], [350, 4585], [350, 4585], [350, 4427]], [[363, 4427], [363, 4585], [376, 4585], [376, 4427]], [[389, 4427], [389, 4585], [389, 4585], [389, 4427]], [[429, 4427], [429, 4585], [442, 4585], [442, 4427]], [[468, 4427], [468, 4585], [468, 4585], [468, 4427]], [[481, 4427], [481, 4585], [481, 4585], [481, 4427]], [[508, 4427], [508, 4585], [534, 4585], [534, 4427]], [[587, 4427], [587, 4585], [600, 4585], [600, 4427]], [[626, 4427], [626, 4585], [639, 4585], [639, 4427]], [[679, 4427], [679, 4585], [679, 4585], [679, 4427]], [[705, 4427], [705, 4585], [705, 4585], [705, 4427]], [[718, 4427], [718, 4585], [718, 4585], [718, 4427]], [[732, 4427], [732, 4585], [745, 4585], [745, 4427]], [[797, 4427], [797, 4585], [797, 4585], [797, 4427]], [[850, 4427], [850, 4585], [863, 4585], [863, 4427]], [[890, 4427], [890, 4585], [890, 4585], [890, 4427]], [[916, 4427], [916, 4585], [916, 4585], [916, 4427]], [[955, 4427], [955, 4585], [969, 4585], [969, 4427]], [[995, 4427], [995, 4585], [1008, 4585], [1008, 4427]], [[1021, 4427], [1021, 4585], [1021, 4585], [1021, 4427]], [[1048, 4427], [1048, 4585], [1061, 4585], [1061, 4427]], [[1219, 4427], [1219, 4585], [1219, 4585], [1219, 4427]], [[1245, 4427], [1245, 4585], [1258, 4585], [1258, 4427]], [[1285, 4427], [1285, 4585], [1285, 4585], [1285, 4427]], [[1311, 4427], [1311, 4585], [1324, 4585], [1324, 4427]], [[1337, 4427], [1337, 4585], [1337, 4585], [1337, 4427]], [[1364, 4427], [1364, 4585], [1364, 4585], [1364, 4427]], [[1416, 4427], [1416, 4585], [1429, 4585], [1429, 4427]], [[1456, 4427], [1456, 4585], [1469, 4585], [1469, 4427]], [[1495, 4427], [1495, 4585], [1495, 4585], [1495, 4427]], [[1535, 4427], [1535, 4585], [1548, 4585], [1548, 4427]], [[1614, 4427], [1614, 4585], [1614, 4585], [1614, 4427]], [[1666, 4427], [1666, 4585], [1666, 4585], [1666, 4427]], [[1693, 4427], [1693, 4585], [1706, 4585], [1706, 4427]], [[1732, 4427], [1732, 4585], [1732, 4585], [1732, 4427]], [[1745, 4427], [1745, 4585], [1759, 4585], [1759, 4427]], [[1798, 4427], [1798, 4585], [1798, 4585], [1798, 4427]], [[1824, 4427], [1824, 4585], [1824, 4585], [1824, 4427]], [[1877, 4427], [1877, 4585], [1877, 4585], [1877, 4427]], [[1917, 4427], [1917, 4585], [1930, 4585], [1930, 4427]], [[1943, 4427], [1943, 4585], [1943, 4585], [1943, 4427]], [[1969, 4427], [1969, 4585], [1969, 4585], [1969, 4427]], [[1982, 4427], [1982, 4585], [1996, 4585], [1996, 4427]], [[2048, 4427], [2048, 4585], [2048, 4585], [2048, 4427]], [[2075, 4427], [2075, 4585], [2075, 4585], [2075, 4427]], [[2101, 4427], [2101, 4585], [2114, 4585], [2114, 4427]], [[2140, 4427], [2140, 4585], [2140, 4585], [2140, 4427]], [[2154, 4427], [2154, 4585], [2167, 4585], [2167, 4427]], [[2206, 4427], [2206, 4585], [2219, 4585], [2219, 4427]], [[2272, 4427], [2272, 4585], [2272, 4585], [2272, 4427]], [[2285, 4427], [2285, 4585], [2298, 4585], [2298, 4427]], [[2364, 4427], [2364, 4585], [2377, 4585], [2377, 4427]], [[2404, 4427], [2404, 4585], [2404, 4585], [2404, 4427]], [[2430, 4427], [2430, 4585], [2430, 4585], [2430, 4427]], [[2456, 4427], [2456, 4585], [2456, 4585], [2456, 4427]], [[2470, 4427], [2470, 4585], [2483, 4585], [2483, 4427]], [[2509, 4427], [2509, 4585], [2522, 4585], [2522, 4427]], [[2562, 4427], [2562, 4585], [2562, 4585], [2562, 4427]], [[2601, 4427], [2601, 4585], [2601, 4585], [2601, 4427]], [[2654, 4427], [2654, 4585], [2654, 4585], [2654, 4427]], [[2680, 4427], [2680, 4585], [2680, 4585], [2680, 4427]], [[2720, 4427], [2720, 4585], [2733, 4585], [2733, 4427]], [[2759, 4427], [2759, 4585], [2759, 4585], [2759, 4427]], [[2786, 4427], [2786, 4585], [2786, 4585], [2786, 4427]], [[2838, 4427], [2838, 4585], [2851, 4585], [2851, 4427]], [[2865, 4427], [2865, 4585], [2865, 4585], [2865, 4427]], [[2891, 4427], [2891, 4585], [2891, 4585], [2891, 4427]], [[2917, 4427], [2917, 4585], [2930, 4585], [2930, 4427]], [[2944, 4427], [2944, 4585], [2957, 4585], [2957, 4427]], [[2996, 4427], [2996, 4585], [2996, 4585], [2996, 4427]], [[3023, 4427], [3023, 4585], [3023, 4585], [3023, 4427]], [[3049, 4427], [3049, 4585], [3049, 4585], [3049, 4427]], [[3102, 4427], [3102, 4585], [3102, 4585], [3102, 4427]]], "confidences": [0.8309170007705688, 0.9710848927497864, 0.9978118538856506, 0.9952423572540283, 0.9998619556427002, 0.9853734970092773, 0.9041633605957031, 0.9999642372131348, 0.9992184638977051, 0.987206757068634, 0.9999744892120361, 0.9998753070831299, 0.9998530149459839, 0.9993923902511597, 0.9804210662841797, 0.9999669790267944, 0.9681344628334045, 0.7007457613945007, 0.9999014139175415, 0.9938947558403015, 0.9991243481636047, 0.9994795918464661, 0.9906601309776306, 0.9998043179512024, 0.9999531507492065, 0.9996731281280518, 0.9904140830039978, 0.8484129309654236, 0.9998654127120972, 0.9998722076416016, 0.884601891040802, 0.9997289776802063, 0.5172210335731506, 0.9777674078941345, 0.9999169111251831, 0.49916383624076843, 0.9641693830490112, 0.9937703013420105, 0.7306916117668152, 0.9957754015922546, 0.9984592199325562, 0.943457841873169, 0.6241721510887146, 0.9954598546028137, 0.9971427321434021, 0.8888192176818848, 0.9960846900939941, 0.9893342852592468, 0.9664679169654846, 0.9914203882217407, 0.9974316954612732, 0.9991137385368347, 0.9997954964637756, 0.9750413298606873, 0.9770325422286987, 0.9967040419578552, 0.9981144666671753, 0.9363402724266052, 0.9950194358825684, 0.8760650753974915, 0.9999531507492065, 0.9947760105133057, 0.99937504529953, 0.9894686341285706, 0.9319698810577393, 0.9841182231903076, 0.998865008354187, 0.9998772144317627, 0.9999456405639648, 0.9940637946128845, 0.8951064348220825, 0.9981452226638794, 0.9989315867424011, 0.9663097262382507], "line": [[297, 4427], [297, 4585], [3128, 4585], [3128, 4427]]}] \ No newline at end of file +[{"prediction": "\u0645\u0627\u0644\u0633\u0644\u0627 \u0647\u064a\u0644\u0639 \u0647\u062a\u0627\u0632\u062c\u0639\u0645 \u064a\u0641", "cuts": [[[1437, 119], [1437, 256], [1449, 256], [1449, 119]], [[1484, 119], [1484, 256], [1496, 256], [1496, 119]], [[1508, 119], [1508, 256], [1520, 256], [1520, 119]], [[1568, 119], [1568, 256], [1568, 256], [1568, 119]], [[1603, 119], [1603, 256], [1603, 256], [1603, 119]], [[1615, 119], [1615, 256], [1627, 256], [1627, 119]], [[1639, 119], [1639, 256], [1639, 256], [1639, 119]], [[1663, 119], [1663, 256], [1674, 256], [1674, 119]], [[1698, 119], [1698, 256], [1698, 256], [1698, 119]], [[1722, 119], [1722, 256], [1734, 256], [1734, 119]], [[1746, 119], [1746, 256], [1758, 256], [1758, 119]], [[1793, 119], [1793, 256], [1805, 256], [1805, 119]], [[1817, 119], [1817, 256], [1829, 256], [1829, 119]], [[1853, 119], [1853, 256], [1853, 256], [1853, 119]], [[1876, 119], [1876, 256], [1888, 256], [1888, 119]], [[1924, 119], [1924, 256], [1936, 256], [1936, 119]], [[1959, 119], [1959, 256], [1971, 256], [1971, 119]], [[2007, 119], [2007, 256], [2019, 256], [2019, 119]], [[2054, 119], [2054, 256], [2054, 256], [2054, 119]], [[2078, 119], [2078, 256], [2090, 256], [2090, 119]], [[2149, 119], [2149, 256], [2149, 256], [2149, 119]], [[2161, 119], [2161, 256], [2173, 256], [2173, 119]]], "confidences": [0.9999573230743408, 0.8209527730941772, 0.9964040517807007, 0.667267382144928, 0.9973480701446533, 0.9998679161071777, 0.9999040365219116, 0.999890923500061, 0.8255242109298706, 0.9995707869529724, 0.9976646900177002, 0.9889178276062012, 0.9869346618652344, 0.9966957569122314, 0.9958117008209229, 0.980436384677887, 0.9956343770027161, 0.9984177350997925, 0.762851893901825, 0.9997056126594543, 0.9977449178695679, 0.9985793828964233], "line": {"id": "544a086f-68f6-4550-85d6-4ce103941809", "bbox": {"id": "45b2dd04-e945-4788-92b2-83a9ed26092d", "bbox": [[1437, 119], [1437, 256], [2185, 256], [2185, 119]]}, "type": "bbox"}}, {"prediction": "(294)", "cuts": [[[2838, 119], [2838, 265], [2838, 265], [2838, 119]], [[2904, 119], [2904, 265], [2904, 265], [2904, 119]], [[2954, 119], [2954, 265], [2954, 265], [2954, 119]], [[3019, 119], [3019, 265], [3019, 265], [3019, 119]], [[3069, 119], [3069, 265], [3085, 265], [3085, 119]]], "confidences": [0.5292237997055054, 0.9998055100440979, 0.9602159857749939, 0.9988068342208862, 0.994594395160675], "line": {"id": "f12082ff-d64b-4ff4-9c28-9a35ad7ddeaf", "bbox": {"id": "def66cf7-3d4d-41f2-9d7b-b5009f125834", "bbox": [[2821, 119], [2821, 265], [3119, 265], [3119, 119]]}, "type": "bbox"}}, {"prediction": "\u0627\u0630\u0643\u0648 \u0627\u0630\u0643\u062a\u0642\u0648 \u0641 \u0646\u064a\u0646\u0645\u0654\u0648\u0645\u0644\u0627 \u0631\u064a\u0645\u0654\u0627 \u0627\u0647\u0628 \u0647\u0645\u0631\u0643\u0627 \u0649\u062a\u0644\u0627 \u0629\u0639\u0627\u0631\u062f\u0644\u0627 \u0647\u064a\u0644\u0627 \u0644\u0645\u062d \u062f\u0642\u0648 \u0629\u0646\u0633 \u0644\u0643 \u064a\u0641", "cuts": [[[358, 365], [358, 542], [358, 542], [358, 365]], [[402, 365], [402, 542], [402, 542], [402, 365]], [[460, 365], [460, 542], [474, 542], [474, 365]], [[648, 365], [648, 542], [663, 542], [663, 365]], [[677, 365], [677, 542], [706, 542], [706, 365]], [[735, 365], [735, 542], [750, 542], [750, 365]], [[779, 365], [779, 542], [779, 542], [779, 365]], [[852, 365], [852, 542], [852, 542], [852, 365]], [[939, 365], [939, 542], [939, 542], [939, 365]], [[997, 365], [997, 542], [997, 542], [997, 365]], [[1055, 365], [1055, 542], [1055, 542], [1055, 365]], [[1069, 365], [1069, 542], [1084, 542], [1084, 365]], [[1127, 365], [1127, 542], [1142, 542], [1142, 365]], [[1171, 365], [1171, 542], [1171, 542], [1171, 365]], [[1200, 365], [1200, 542], [1215, 542], [1215, 365]], [[1229, 365], [1229, 542], [1244, 542], [1244, 365]], [[1273, 365], [1273, 542], [1273, 542], [1273, 365]], [[1302, 365], [1302, 542], [1302, 542], [1302, 365]], [[1345, 365], [1345, 542], [1345, 542], [1345, 365]], [[1360, 365], [1360, 542], [1360, 542], [1360, 365]], [[1374, 365], [1374, 542], [1374, 542], [1374, 365]], [[1389, 365], [1389, 542], [1403, 542], [1403, 365]], [[1418, 365], [1418, 542], [1432, 542], [1432, 365]], [[1447, 365], [1447, 542], [1447, 542], [1447, 365]], [[1490, 365], [1490, 542], [1490, 542], [1490, 365]], [[1505, 365], [1505, 542], [1519, 542], [1519, 365]], [[1548, 365], [1548, 542], [1548, 542], [1548, 365]], [[1578, 365], [1578, 542], [1578, 542], [1578, 365]], [[1592, 365], [1592, 542], [1592, 542], [1592, 365]], [[1607, 365], [1607, 542], [1621, 542], [1621, 365]], [[1650, 365], [1650, 542], [1650, 542], [1650, 365]], [[1665, 365], [1665, 542], [1679, 542], [1679, 365]], [[1694, 365], [1694, 542], [1708, 542], [1708, 365]], [[1723, 365], [1723, 542], [1737, 542], [1737, 365]], [[1766, 365], [1766, 542], [1766, 542], [1766, 365]], [[1810, 365], [1810, 542], [1810, 542], [1810, 365]], [[1853, 365], [1853, 542], [1868, 542], [1868, 365]], [[1882, 365], [1882, 542], [1882, 542], [1882, 365]], [[1955, 365], [1955, 542], [1969, 542], [1969, 365]], [[1984, 365], [1984, 542], [1999, 542], [1999, 365]], [[2057, 365], [2057, 542], [2057, 542], [2057, 365]], [[2071, 365], [2071, 542], [2071, 542], [2071, 365]], [[2086, 365], [2086, 542], [2100, 542], [2100, 365]], [[2115, 365], [2115, 542], [2115, 542], [2115, 365]], [[2129, 365], [2129, 542], [2144, 542], [2144, 365]], [[2173, 365], [2173, 542], [2173, 542], [2173, 365]], [[2202, 365], [2202, 542], [2202, 542], [2202, 365]], [[2245, 365], [2245, 542], [2260, 542], [2260, 365]], [[2303, 365], [2303, 542], [2303, 542], [2303, 365]], [[2332, 365], [2332, 542], [2347, 542], [2347, 365]], [[2361, 365], [2361, 542], [2361, 542], [2361, 365]], [[2376, 365], [2376, 542], [2391, 542], [2391, 365]], [[2405, 365], [2405, 542], [2420, 542], [2420, 365]], [[2434, 365], [2434, 542], [2449, 542], [2449, 365]], [[2478, 365], [2478, 542], [2478, 542], [2478, 365]], [[2492, 365], [2492, 542], [2507, 542], [2507, 365]], [[2521, 365], [2521, 542], [2521, 542], [2521, 365]], [[2536, 365], [2536, 542], [2550, 542], [2550, 365]], [[2594, 365], [2594, 542], [2594, 542], [2594, 365]], [[2623, 365], [2623, 542], [2623, 542], [2623, 365]], [[2637, 365], [2637, 542], [2652, 542], [2652, 365]], [[2681, 365], [2681, 542], [2695, 542], [2695, 365]], [[2724, 365], [2724, 542], [2724, 542], [2724, 365]], [[2753, 365], [2753, 542], [2753, 542], [2753, 365]], [[2812, 365], [2812, 542], [2812, 542], [2812, 365]], [[2841, 365], [2841, 542], [2855, 542], [2855, 365]], [[2870, 365], [2870, 542], [2870, 542], [2870, 365]], [[2899, 365], [2899, 542], [2899, 542], [2899, 365]], [[2942, 365], [2942, 542], [2957, 542], [2957, 365]], [[2986, 365], [2986, 542], [3000, 542], [3000, 365]], [[3058, 365], [3058, 542], [3058, 542], [3058, 365]], [[3073, 365], [3073, 542], [3073, 542], [3073, 365]], [[3116, 365], [3116, 542], [3131, 542], [3131, 365]], [[3145, 365], [3145, 542], [3145, 542], [3145, 365]], [[3160, 365], [3160, 542], [3174, 542], [3174, 365]]], "confidences": [0.9912043213844299, 0.9701831340789795, 0.9999876022338867, 0.9710670113563538, 0.9999645948410034, 0.9999040365219116, 0.9999904632568359, 0.9276049733161926, 0.9949396848678589, 0.9172694683074951, 0.9944435954093933, 0.9998708963394165, 0.9994745850563049, 0.9999909400939941, 0.9929296374320984, 0.9996275901794434, 0.9654240012168884, 0.9978482723236084, 0.8331916928291321, 0.9914954900741577, 0.9862202405929565, 0.9967150688171387, 0.9994681477546692, 0.9422079920768738, 0.9996860027313232, 0.9978721141815186, 0.590356171131134, 0.9853919148445129, 0.9949153661727905, 0.9330083131790161, 0.902510941028595, 0.9991570711135864, 0.9937306642532349, 0.9962736368179321, 0.9969527721405029, 0.9955304265022278, 0.9975927472114563, 0.931627094745636, 0.9910709857940674, 0.994668185710907, 0.9977930784225464, 0.9947988986968994, 0.99834144115448, 0.9974583983421326, 0.9996436834335327, 0.9992924928665161, 0.9981145858764648, 0.9989506006240845, 0.9970920085906982, 0.9896613359451294, 0.6772571206092834, 0.9618531465530396, 0.9992746710777283, 0.998647153377533, 0.834701657295227, 0.9992173910140991, 0.9594298601150513, 0.9998399019241333, 0.9885919690132141, 0.9965444207191467, 0.9950836896896362, 0.8906474709510803, 0.8982319831848145, 0.520612359046936, 0.9998024106025696, 0.9991157650947571, 0.9997696280479431, 0.9865208864212036, 0.9990457892417908, 0.9984292387962341, 0.8027671575546265, 0.9970871806144714, 0.9498249292373657, 0.9898852109909058, 0.9846636056900024], "line": {"id": "29eebc2f-71f1-43f9-bd09-feb4c3945229", "bbox": {"id": "b242cdcb-5ce1-4582-a71d-58c9ad54da92", "bbox": [[358, 365], [358, 542], [3189, 542], [3189, 365]]}, "type": "bbox"}}, {"prediction": "\u0646\u0627\u0643 \u0646\u0627\u0641 \u0644\u0627\u062d\u0644\u0627 \u0647\u0630\u0647 \u0646\u0639 \u0646\u063a\u0634\u0643\u0627\u0644 \u0644\u0627\u0642\u0648 \u0627\u062f\u062f\u0634 \u064b\u0627\u0640\u0628\u0646\u063a \u0628\u0636\u063a\u0648 \u0643\u0644\u0630\u0644 \u062f\u064a\u0634\u0631\u0644\u0627 \u0637\u0627\u0634\u062a\u0633\u0627\u0641", "cuts": [[[364, 534], [364, 695], [377, 695], [377, 534]], [[405, 534], [405, 695], [405, 695], [405, 534]], [[419, 534], [419, 695], [419, 695], [419, 534]], [[460, 534], [460, 695], [487, 695], [487, 534]], [[514, 534], [514, 695], [528, 695], [528, 534]], [[555, 534], [555, 695], [555, 695], [555, 534]], [[569, 534], [569, 695], [583, 695], [583, 534]], [[610, 534], [610, 695], [624, 695], [624, 534]], [[665, 534], [665, 695], [665, 695], [665, 534]], [[679, 534], [679, 695], [692, 695], [692, 534]], [[720, 534], [720, 695], [720, 695], [720, 534]], [[733, 534], [733, 695], [747, 695], [747, 534]], [[774, 534], [774, 695], [788, 695], [788, 534]], [[802, 534], [802, 695], [816, 695], [816, 534]], [[829, 534], [829, 695], [843, 695], [843, 534]], [[884, 534], [884, 695], [884, 695], [884, 534]], [[911, 534], [911, 695], [925, 695], [925, 534]], [[952, 534], [952, 695], [966, 695], [966, 534]], [[1007, 534], [1007, 695], [1007, 695], [1007, 534]], [[1048, 534], [1048, 695], [1048, 695], [1048, 534]], [[1089, 534], [1089, 695], [1103, 695], [1103, 534]], [[1144, 534], [1144, 695], [1144, 695], [1144, 534]], [[1185, 534], [1185, 695], [1185, 695], [1185, 534]], [[1240, 534], [1240, 695], [1240, 695], [1240, 534]], [[1295, 534], [1295, 695], [1295, 695], [1295, 534]], [[1363, 534], [1363, 695], [1377, 695], [1377, 534]], [[1391, 534], [1391, 695], [1404, 695], [1404, 534]], [[1418, 534], [1418, 695], [1432, 695], [1432, 534]], [[1473, 534], [1473, 695], [1486, 695], [1486, 534]], [[1500, 534], [1500, 695], [1500, 695], [1500, 534]], [[1514, 534], [1514, 695], [1514, 695], [1514, 534]], [[1568, 534], [1568, 695], [1582, 695], [1582, 534]], [[1596, 534], [1596, 695], [1623, 695], [1623, 534]], [[1651, 534], [1651, 695], [1664, 695], [1664, 534]], [[1692, 534], [1692, 695], [1705, 695], [1705, 534]], [[1760, 534], [1760, 695], [1774, 695], [1774, 534]], [[1815, 534], [1815, 695], [1829, 695], [1829, 534]], [[1842, 534], [1842, 695], [1856, 695], [1856, 534]], [[1870, 534], [1870, 695], [1870, 695], [1870, 534]], [[1883, 534], [1883, 695], [1883, 695], [1883, 534]], [[1924, 534], [1924, 695], [1952, 695], [1952, 534]], [[2034, 534], [2034, 695], [2034, 695], [2034, 534]], [[2075, 534], [2075, 695], [2075, 695], [2075, 534]], [[2130, 534], [2130, 695], [2143, 695], [2143, 534]], [[2171, 534], [2171, 695], [2185, 695], [2185, 534]], [[2226, 534], [2226, 695], [2226, 695], [2226, 534]], [[2308, 534], [2308, 695], [2308, 695], [2308, 534]], [[2363, 534], [2363, 695], [2363, 695], [2363, 534]], [[2431, 534], [2431, 695], [2431, 695], [2431, 534]], [[2458, 534], [2458, 695], [2472, 695], [2472, 534]], [[2527, 534], [2527, 695], [2527, 695], [2527, 534]], [[2582, 534], [2582, 695], [2582, 695], [2582, 534]], [[2609, 534], [2609, 695], [2623, 695], [2623, 534]], [[2650, 534], [2650, 695], [2650, 695], [2650, 534]], [[2664, 534], [2664, 695], [2677, 695], [2677, 534]], [[2705, 534], [2705, 695], [2705, 695], [2705, 534]], [[2732, 534], [2732, 695], [2746, 695], [2746, 534]], [[2787, 534], [2787, 695], [2787, 695], [2787, 534]], [[2828, 534], [2828, 695], [2828, 695], [2828, 534]], [[2842, 534], [2842, 695], [2855, 695], [2855, 534]], [[2869, 534], [2869, 695], [2869, 695], [2869, 534]], [[2883, 534], [2883, 695], [2896, 695], [2896, 534]], [[2938, 534], [2938, 695], [2938, 695], [2938, 534]], [[2992, 534], [2992, 695], [2992, 695], [2992, 534]], [[3033, 534], [3033, 695], [3033, 695], [3033, 534]], [[3061, 534], [3061, 695], [3074, 695], [3074, 534]], [[3102, 534], [3102, 695], [3129, 695], [3129, 534]], [[3143, 534], [3143, 695], [3157, 695], [3157, 534]], [[3170, 534], [3170, 695], [3170, 695], [3170, 534]]], "confidences": [0.9985698461532593, 0.9999438524246216, 0.9994581341743469, 0.9928075075149536, 0.9999692440032959, 0.9775535464286804, 0.9994893074035645, 0.9992764592170715, 0.9999681711196899, 0.999976634979248, 0.5837891101837158, 0.9994819760322571, 0.9999487400054932, 0.6378462314605713, 0.9995593428611755, 0.9997476935386658, 0.9998375177383423, 0.9999092817306519, 0.9706422686576843, 0.9981306195259094, 0.999731719493866, 0.9984344840049744, 0.9999045133590698, 0.9998281002044678, 0.9965876340866089, 0.9981406927108765, 0.9998824596405029, 0.9994693398475647, 0.9985535740852356, 0.9371808171272278, 0.9999727010726929, 0.9998328685760498, 0.9986041188240051, 0.9998788833618164, 0.9969847798347473, 0.9979107975959778, 0.9678859114646912, 0.9965589642524719, 0.9958305954933167, 0.856151819229126, 0.9174501299858093, 0.977839469909668, 0.9722991585731506, 0.999629020690918, 0.9988741278648376, 0.5952739119529724, 0.8313284516334534, 0.6637697815895081, 0.9957481026649475, 0.9997797608375549, 0.99960857629776, 0.6831809282302856, 0.9987292885780334, 0.9998849630355835, 0.9514357447624207, 0.9951800107955933, 0.9758328795433044, 0.9967122077941895, 0.7513812184333801, 0.9999700784683228, 0.9964326620101929, 0.9970759153366089, 0.9622002243995667, 0.7601332068443298, 0.9990935325622559, 0.9990278482437134, 0.9987155199050903, 0.9829656481742859, 0.6010854244232178], "line": {"id": "a88881fc-289b-41a3-8b0c-4f3a2ee6c7c7", "bbox": {"id": "38c5c359-ee24-49b1-916a-232a74148c17", "bbox": [[350, 534], [350, 695], [3184, 695], [3184, 534]]}, "type": "bbox"}}, {"prediction": "\u0647\u064a\u062f\u064a \u0646\u064a\u0628 \u0644\u062b\u0645 \u0627\u0644\u0641 \u0646\u064a\u0637\u0642\u064a \u0646\u0628 \u0649\u0644\u0639 \u0631\u0627\u0636\u062d\u0627\u0628 \u062a\u0642\u0648\u0644\u0627 \u064a\u0641 \u0630\u0641\u0646\u0627\u0648 \u0647\u0633\u0641\u0646 \u062a\u0642\u0647\u0632\u0654\u0627 \u0644\u0648\u0642\u062a \u0627\u0645\u0643 \u0645\u0627\u0644\u0627", "cuts": [[[349, 703], [349, 875], [362, 875], [362, 703]], [[389, 703], [389, 875], [389, 875], [389, 703]], [[428, 703], [428, 875], [441, 875], [441, 703]], [[455, 703], [455, 875], [468, 875], [468, 703]], [[481, 703], [481, 875], [494, 875], [494, 703]], [[534, 703], [534, 875], [547, 875], [547, 703]], [[560, 703], [560, 875], [560, 875], [560, 703]], [[586, 703], [586, 875], [586, 875], [586, 703]], [[613, 703], [613, 875], [626, 875], [626, 703]], [[666, 703], [666, 875], [679, 875], [679, 703]], [[705, 703], [705, 875], [705, 875], [705, 703]], [[731, 703], [731, 875], [731, 875], [731, 703]], [[758, 703], [758, 875], [771, 875], [771, 703]], [[797, 703], [797, 875], [797, 875], [797, 703]], [[837, 703], [837, 875], [837, 875], [837, 703]], [[863, 703], [863, 875], [863, 875], [863, 703]], [[890, 703], [890, 875], [903, 875], [903, 703]], [[956, 703], [956, 875], [956, 875], [956, 703]], [[982, 703], [982, 875], [982, 875], [982, 703]], [[1008, 703], [1008, 875], [1022, 875], [1022, 703]], [[1074, 703], [1074, 875], [1074, 875], [1074, 703]], [[1101, 703], [1101, 875], [1101, 875], [1101, 703]], [[1127, 703], [1127, 875], [1140, 875], [1140, 703]], [[1180, 703], [1180, 875], [1193, 875], [1193, 703]], [[1206, 703], [1206, 875], [1219, 875], [1219, 703]], [[1233, 703], [1233, 875], [1246, 875], [1246, 703]], [[1312, 703], [1312, 875], [1312, 875], [1312, 703]], [[1325, 703], [1325, 875], [1338, 875], [1338, 703]], [[1351, 703], [1351, 875], [1351, 875], [1351, 703]], [[1391, 703], [1391, 875], [1404, 875], [1404, 703]], [[1444, 703], [1444, 875], [1444, 875], [1444, 703]], [[1470, 703], [1470, 875], [1483, 875], [1483, 703]], [[1509, 703], [1509, 875], [1523, 875], [1523, 703]], [[1575, 703], [1575, 875], [1589, 875], [1589, 703]], [[1628, 703], [1628, 875], [1641, 875], [1641, 703]], [[1655, 703], [1655, 875], [1655, 875], [1655, 703]], [[1681, 703], [1681, 875], [1694, 875], [1694, 703]], [[1747, 703], [1747, 875], [1747, 875], [1747, 703]], [[1800, 703], [1800, 875], [1800, 875], [1800, 703]], [[1839, 703], [1839, 875], [1852, 875], [1852, 703]], [[1865, 703], [1865, 875], [1879, 875], [1879, 703]], [[1892, 703], [1892, 875], [1905, 875], [1905, 703]], [[1918, 703], [1918, 875], [1918, 875], [1918, 703]], [[1958, 703], [1958, 875], [1958, 875], [1958, 703]], [[1971, 703], [1971, 875], [1971, 875], [1971, 703]], [[1997, 703], [1997, 875], [2011, 875], [2011, 703]], [[2050, 703], [2050, 875], [2050, 875], [2050, 703]], [[2090, 703], [2090, 875], [2090, 875], [2090, 703]], [[2116, 703], [2116, 875], [2116, 875], [2116, 703]], [[2156, 703], [2156, 875], [2156, 875], [2156, 703]], [[2195, 703], [2195, 875], [2208, 875], [2208, 703]], [[2222, 703], [2222, 875], [2235, 875], [2235, 703]], [[2261, 703], [2261, 875], [2261, 875], [2261, 703]], [[2301, 703], [2301, 875], [2327, 875], [2327, 703]], [[2353, 703], [2353, 875], [2367, 875], [2367, 703]], [[2380, 703], [2380, 875], [2393, 875], [2393, 703]], [[2406, 703], [2406, 875], [2419, 875], [2419, 703]], [[2472, 703], [2472, 875], [2472, 875], [2472, 703]], [[2538, 703], [2538, 875], [2538, 875], [2538, 703]], [[2564, 703], [2564, 875], [2578, 875], [2578, 703]], [[2617, 703], [2617, 875], [2630, 875], [2630, 703]], [[2643, 703], [2643, 875], [2657, 875], [2657, 703]], [[2670, 703], [2670, 875], [2670, 875], [2670, 703]], [[2683, 703], [2683, 875], [2696, 875], [2696, 703]], [[2749, 703], [2749, 875], [2749, 875], [2749, 703]], [[2789, 703], [2789, 875], [2789, 875], [2789, 703]], [[2828, 703], [2828, 875], [2828, 875], [2828, 703]], [[2854, 703], [2854, 875], [2854, 875], [2854, 703]], [[2881, 703], [2881, 875], [2894, 875], [2894, 703]], [[2907, 703], [2907, 875], [2907, 875], [2907, 703]], [[2920, 703], [2920, 875], [2934, 875], [2934, 703]], [[2947, 703], [2947, 875], [2947, 875], [2947, 703]], [[2973, 703], [2973, 875], [2986, 875], [2986, 703]], [[3065, 703], [3065, 875], [3065, 875], [3065, 703]], [[3105, 703], [3105, 875], [3118, 875], [3118, 703]], [[3131, 703], [3131, 875], [3145, 875], [3145, 703]], [[3158, 703], [3158, 875], [3171, 875], [3171, 703]]], "confidences": [0.9978576302528381, 0.9365259408950806, 0.9993323683738708, 0.9999881982803345, 0.995057225227356, 0.9951127171516418, 0.9600062966346741, 0.9987906813621521, 0.9990148544311523, 0.9999818801879883, 0.979132354259491, 0.9896356463432312, 0.9996901750564575, 0.9994376301765442, 0.9832384586334229, 0.9919760823249817, 0.9994718432426453, 0.99887615442276, 0.9999384880065918, 0.9999126195907593, 0.9968804121017456, 0.9998698234558105, 0.9996697902679443, 0.9999111890792847, 0.999935507774353, 0.9999529123306274, 0.9985625147819519, 0.9987615346908569, 0.9998716115951538, 0.9999983310699463, 0.9997947812080383, 0.9999533891677856, 0.9956774115562439, 0.9921483993530273, 0.9991742968559265, 0.8690173625946045, 0.9889227151870728, 0.990256130695343, 0.9499505758285522, 0.9942019581794739, 0.992321789264679, 0.9953250885009766, 0.9952391386032104, 0.9987290501594543, 0.9708223342895508, 0.9829660654067993, 0.998319685459137, 0.9980186223983765, 0.9733465313911438, 0.9576543569564819, 0.9972822666168213, 0.9974729418754578, 0.9998486042022705, 0.9998704195022583, 0.9851626753807068, 0.999782145023346, 0.9982467889785767, 0.5265796780586243, 0.9982032775878906, 0.9953908920288086, 0.9945998191833496, 0.9988899827003479, 0.932214617729187, 0.9992570281028748, 0.9942938685417175, 0.9976638555526733, 0.8540834784507751, 0.999862551689148, 0.999956488609314, 0.5673943161964417, 0.9989564418792725, 0.996831476688385, 0.9993277788162231, 0.9955874681472778, 0.995591402053833, 0.9929797053337097, 0.9888700246810913], "line": {"id": "c636855b-1e00-4c7e-b95f-825b53acf325", "bbox": {"id": "a0cfd45b-cfc3-4068-ac25-417781cf2437", "bbox": [[349, 703], [349, 875], [3171, 875], [3171, 703]]}, "type": "bbox"}}, {"prediction": "\u0645\u0648\u062a\u062e\u0645 \u0637\u0641\u0633 \u064a\u0641 \u0649\u062f\u0646\u0639 \u0646\u064a\u0646\u0645\u0654\u0648\u0645\u0644\u0627 \u0631\u064a\u0645\u0654\u0627\u064a \u0649\u0647 \u0644\u0627\u0642 \u0627\u0647\u0628 \u0643\u062a\u0648\u0633\u0643 \u0649\u062a\u0644\u0627 \u0629\u0639\u0631\u0627\u062f\u0644\u0627 \u0644\u0639\u0641 \u0627\u0645 \u0647\u0644 \u0644\u0627\u0642", "cuts": [[[347, 875], [347, 1045], [360, 1045], [360, 875]], [[414, 875], [414, 1045], [414, 1045], [414, 875]], [[454, 875], [454, 1045], [467, 1045], [467, 875]], [[480, 875], [480, 1045], [480, 1045], [480, 875]], [[494, 875], [494, 1045], [494, 1045], [494, 875]], [[534, 875], [534, 1045], [534, 1045], [534, 875]], [[574, 875], [574, 1045], [574, 1045], [574, 875]], [[641, 875], [641, 1045], [641, 1045], [641, 875]], [[681, 875], [681, 1045], [694, 1045], [694, 875]], [[721, 875], [721, 1045], [721, 1045], [721, 875]], [[761, 875], [761, 1045], [761, 1045], [761, 875]], [[774, 875], [774, 1045], [774, 1045], [774, 875]], [[801, 875], [801, 1045], [814, 1045], [814, 875]], [[854, 875], [854, 1045], [854, 1045], [854, 875]], [[921, 875], [921, 1045], [921, 1045], [921, 875]], [[948, 875], [948, 1045], [948, 1045], [948, 875]], [[974, 875], [974, 1045], [974, 1045], [974, 875]], [[1014, 875], [1014, 1045], [1028, 1045], [1028, 875]], [[1054, 875], [1054, 1045], [1068, 1045], [1068, 875]], [[1081, 875], [1081, 1045], [1094, 1045], [1094, 875]], [[1121, 875], [1121, 1045], [1121, 1045], [1121, 875]], [[1148, 875], [1148, 1045], [1161, 1045], [1161, 875]], [[1201, 875], [1201, 1045], [1201, 1045], [1201, 875]], [[1214, 875], [1214, 1045], [1214, 1045], [1214, 875]], [[1228, 875], [1228, 1045], [1228, 1045], [1228, 875]], [[1241, 875], [1241, 1045], [1255, 1045], [1255, 875]], [[1268, 875], [1268, 1045], [1281, 1045], [1281, 875]], [[1295, 875], [1295, 1045], [1295, 1045], [1295, 875]], [[1335, 875], [1335, 1045], [1348, 1045], [1348, 875]], [[1361, 875], [1361, 1045], [1361, 1045], [1361, 875]], [[1388, 875], [1388, 1045], [1401, 1045], [1401, 875]], [[1415, 875], [1415, 1045], [1428, 1045], [1428, 875]], [[1441, 875], [1441, 1045], [1468, 1045], [1468, 875]], [[1481, 875], [1481, 1045], [1495, 1045], [1495, 875]], [[1508, 875], [1508, 1045], [1535, 1045], [1535, 875]], [[1602, 875], [1602, 1045], [1602, 1045], [1602, 875]], [[1615, 875], [1615, 1045], [1628, 1045], [1628, 875]], [[1655, 875], [1655, 1045], [1682, 1045], [1682, 875]], [[1735, 875], [1735, 1045], [1735, 1045], [1735, 875]], [[1748, 875], [1748, 1045], [1748, 1045], [1748, 875]], [[1775, 875], [1775, 1045], [1775, 1045], [1775, 875]], [[1802, 875], [1802, 1045], [1802, 1045], [1802, 875]], [[1828, 875], [1828, 1045], [1828, 1045], [1828, 875]], [[1882, 875], [1882, 1045], [1895, 1045], [1895, 875]], [[1922, 875], [1922, 1045], [1922, 1045], [1922, 875]], [[1948, 875], [1948, 1045], [1962, 1045], [1962, 875]], [[2015, 875], [2015, 1045], [2029, 1045], [2029, 875]], [[2082, 875], [2082, 1045], [2082, 1045], [2082, 875]], [[2122, 875], [2122, 1045], [2135, 1045], [2135, 875]], [[2175, 875], [2175, 1045], [2189, 1045], [2189, 875]], [[2242, 875], [2242, 1045], [2242, 1045], [2242, 875]], [[2295, 875], [2295, 1045], [2295, 1045], [2295, 875]], [[2336, 875], [2336, 1045], [2336, 1045], [2336, 875]], [[2349, 875], [2349, 1045], [2349, 1045], [2349, 875]], [[2362, 875], [2362, 1045], [2376, 1045], [2376, 875]], [[2389, 875], [2389, 1045], [2389, 1045], [2389, 875]], [[2402, 875], [2402, 1045], [2416, 1045], [2416, 875]], [[2429, 875], [2429, 1045], [2442, 1045], [2442, 875]], [[2456, 875], [2456, 1045], [2469, 1045], [2469, 875]], [[2522, 875], [2522, 1045], [2536, 1045], [2536, 875]], [[2549, 875], [2549, 1045], [2562, 1045], [2562, 875]], [[2589, 875], [2589, 1045], [2602, 1045], [2602, 875]], [[2629, 875], [2629, 1045], [2629, 1045], [2629, 875]], [[2642, 875], [2642, 1045], [2656, 1045], [2656, 875]], [[2669, 875], [2669, 1045], [2669, 1045], [2669, 875]], [[2776, 875], [2776, 1045], [2776, 1045], [2776, 875]], [[2803, 875], [2803, 1045], [2803, 1045], [2803, 875]], [[2843, 875], [2843, 1045], [2843, 1045], [2843, 875]], [[2869, 875], [2869, 1045], [2869, 1045], [2869, 875]], [[2896, 875], [2896, 1045], [2896, 1045], [2896, 875]], [[2923, 875], [2923, 1045], [2923, 1045], [2923, 875]], [[2963, 875], [2963, 1045], [2963, 1045], [2963, 875]], [[2989, 875], [2989, 1045], [2989, 1045], [2989, 875]], [[3016, 875], [3016, 1045], [3030, 1045], [3030, 875]], [[3043, 875], [3043, 1045], [3056, 1045], [3056, 875]], [[3123, 875], [3123, 1045], [3123, 1045], [3123, 875]], [[3136, 875], [3136, 1045], [3150, 1045], [3150, 875]], [[3163, 875], [3163, 1045], [3163, 1045], [3163, 875]]], "confidences": [0.9999685287475586, 0.7677904963493347, 0.9990272521972656, 0.9999896287918091, 0.9995161294937134, 0.9926141500473022, 0.9854110479354858, 0.9970456957817078, 0.9999581575393677, 0.9979928731918335, 0.9991545677185059, 0.9992891550064087, 0.9991033673286438, 0.9999771118164062, 0.9999916553497314, 0.6514611840248108, 0.7720828652381897, 0.9889744520187378, 0.9997015595436096, 0.9999628067016602, 0.8422739505767822, 0.9995865225791931, 0.9994408488273621, 0.9979164004325867, 0.9996676445007324, 0.9994599223136902, 0.9910250902175903, 0.9999783039093018, 0.9963022470474243, 0.6243562698364258, 0.9998552799224854, 0.9986860156059265, 0.999666690826416, 0.66583651304245, 0.9677010178565979, 0.9999728202819824, 0.9999947547912598, 0.9995187520980835, 0.9917859435081482, 0.9952231049537659, 0.988452136516571, 0.9948959946632385, 0.997393012046814, 0.984053909778595, 0.9514071345329285, 0.9919347763061523, 0.9954736828804016, 0.535146176815033, 0.9996613264083862, 0.9889037013053894, 0.9973983764648438, 0.7873133420944214, 0.814349353313446, 0.9971019625663757, 0.9976648092269897, 0.9264478087425232, 0.9916608929634094, 0.9994589686393738, 0.9972610473632812, 0.9991005659103394, 0.999413013458252, 0.9994602799415588, 0.9613319039344788, 0.9990511536598206, 0.9996521472930908, 0.7384001612663269, 0.9942810535430908, 0.9455984234809875, 0.9997734427452087, 0.9952616691589355, 0.9989475607872009, 0.9959900975227356, 0.9930391907691956, 0.9999250173568726, 0.998917818069458, 0.6285094618797302, 0.8354631662368774, 0.9813341498374939], "line": {"id": "38f2a4bc-b8fb-468b-b735-144a5381e35d", "bbox": {"id": "fc8c2130-6470-4ef8-b1b9-d226bc5b64cd", "bbox": [[347, 875], [347, 1045], [3203, 1045], [3203, 875]]}, "type": "bbox"}}, {"prediction": "\u0627\u0647\u062a\u0644\u0628\u0642\u0648 \u0627\u0647\u0628 \u0627\u0643\u0631\u0628\u062a \u0627\u0647\u064a\u0644\u0627 \u062a\u0631\u0638\u0646\u0648 \u0637\u0641\u0633\u0644\u0627 \u062a\u062d\u062a\u0641\u0648 \u062a\u062d\u0628\u0635\u0654\u0627 \u0627\u0644\u0641 \u0627\u0647\u0628 \u062a\u0638\u0641\u062a\u062d\u0627 \u062f\u0642 \u0628\u064a\u0637 \u0647\u064a\u0641\u0648", "cuts": [[[349, 1045], [349, 1187], [362, 1187], [362, 1045]], [[375, 1045], [375, 1187], [375, 1187], [375, 1045]], [[425, 1045], [425, 1187], [425, 1187], [425, 1045]], [[451, 1045], [451, 1187], [451, 1187], [451, 1045]], [[476, 1045], [476, 1187], [476, 1187], [476, 1045]], [[502, 1045], [502, 1187], [502, 1187], [502, 1045]], [[552, 1045], [552, 1187], [565, 1187], [565, 1045]], [[578, 1045], [578, 1187], [603, 1187], [603, 1045]], [[629, 1045], [629, 1187], [641, 1187], [641, 1045]], [[692, 1045], [692, 1187], [705, 1187], [705, 1045]], [[730, 1045], [730, 1187], [730, 1187], [730, 1045]], [[756, 1045], [756, 1187], [756, 1187], [756, 1045]], [[781, 1045], [781, 1187], [781, 1187], [781, 1045]], [[794, 1045], [794, 1187], [794, 1187], [794, 1045]], [[857, 1045], [857, 1187], [857, 1187], [857, 1045]], [[870, 1045], [870, 1187], [870, 1187], [870, 1045]], [[896, 1045], [896, 1187], [896, 1187], [896, 1045]], [[921, 1045], [921, 1187], [934, 1187], [934, 1045]], [[959, 1045], [959, 1187], [972, 1187], [972, 1045]], [[984, 1045], [984, 1187], [984, 1187], [984, 1045]], [[1023, 1045], [1023, 1187], [1023, 1187], [1023, 1045]], [[1048, 1045], [1048, 1187], [1048, 1187], [1048, 1045]], [[1061, 1045], [1061, 1187], [1073, 1187], [1073, 1045]], [[1086, 1045], [1086, 1187], [1086, 1187], [1086, 1045]], [[1137, 1045], [1137, 1187], [1137, 1187], [1137, 1045]], [[1213, 1045], [1213, 1187], [1213, 1187], [1213, 1045]], [[1239, 1045], [1239, 1187], [1239, 1187], [1239, 1045]], [[1289, 1045], [1289, 1187], [1289, 1187], [1289, 1045]], [[1340, 1045], [1340, 1187], [1340, 1187], [1340, 1045]], [[1366, 1045], [1366, 1187], [1378, 1187], [1378, 1045]], [[1417, 1045], [1417, 1187], [1417, 1187], [1417, 1045]], [[1480, 1045], [1480, 1187], [1480, 1187], [1480, 1045]], [[1518, 1045], [1518, 1187], [1531, 1187], [1531, 1045]], [[1556, 1045], [1556, 1187], [1556, 1187], [1556, 1045]], [[1569, 1045], [1569, 1187], [1582, 1187], [1582, 1045]], [[1594, 1045], [1594, 1187], [1607, 1187], [1607, 1045]], [[1645, 1045], [1645, 1187], [1645, 1187], [1645, 1045]], [[1709, 1045], [1709, 1187], [1709, 1187], [1709, 1045]], [[1760, 1045], [1760, 1187], [1760, 1187], [1760, 1045]], [[1785, 1045], [1785, 1187], [1785, 1187], [1785, 1045]], [[1836, 1045], [1836, 1187], [1836, 1187], [1836, 1045]], [[1861, 1045], [1861, 1187], [1874, 1187], [1874, 1045]], [[1925, 1045], [1925, 1187], [1925, 1187], [1925, 1045]], [[1988, 1045], [1988, 1187], [1988, 1187], [1988, 1045]], [[2039, 1045], [2039, 1187], [2039, 1187], [2039, 1045]], [[2090, 1045], [2090, 1187], [2090, 1187], [2090, 1045]], [[2128, 1045], [2128, 1187], [2128, 1187], [2128, 1045]], [[2141, 1045], [2141, 1187], [2154, 1187], [2154, 1045]], [[2166, 1045], [2166, 1187], [2179, 1187], [2179, 1045]], [[2204, 1045], [2204, 1187], [2204, 1187], [2204, 1045]], [[2243, 1045], [2243, 1187], [2243, 1187], [2243, 1045]], [[2268, 1045], [2268, 1187], [2268, 1187], [2268, 1045]], [[2293, 1045], [2293, 1187], [2306, 1187], [2306, 1045]], [[2319, 1045], [2319, 1187], [2331, 1187], [2331, 1045]], [[2344, 1045], [2344, 1187], [2357, 1187], [2357, 1045]], [[2370, 1045], [2370, 1187], [2370, 1187], [2370, 1045]], [[2395, 1045], [2395, 1187], [2408, 1187], [2408, 1045]], [[2471, 1045], [2471, 1187], [2471, 1187], [2471, 1045]], [[2522, 1045], [2522, 1187], [2522, 1187], [2522, 1045]], [[2586, 1045], [2586, 1187], [2586, 1187], [2586, 1045]], [[2611, 1045], [2611, 1187], [2611, 1187], [2611, 1045]], [[2649, 1045], [2649, 1187], [2649, 1187], [2649, 1045]], [[2700, 1045], [2700, 1187], [2713, 1187], [2713, 1045]], [[2725, 1045], [2725, 1187], [2725, 1187], [2725, 1045]], [[2764, 1045], [2764, 1187], [2776, 1187], [2776, 1045]], [[2789, 1045], [2789, 1187], [2802, 1187], [2802, 1045]], [[2827, 1045], [2827, 1187], [2827, 1187], [2827, 1045]], [[2865, 1045], [2865, 1187], [2878, 1187], [2878, 1045]], [[2929, 1045], [2929, 1187], [2929, 1187], [2929, 1045]], [[2967, 1045], [2967, 1187], [2967, 1187], [2967, 1045]], [[3005, 1045], [3005, 1187], [3030, 1187], [3030, 1045]], [[3056, 1045], [3056, 1187], [3056, 1187], [3056, 1045]], [[3094, 1045], [3094, 1187], [3094, 1187], [3094, 1045]], [[3119, 1045], [3119, 1187], [3119, 1187], [3119, 1045]], [[3170, 1045], [3170, 1187], [3170, 1187], [3170, 1045]]], "confidences": [0.9951897859573364, 0.9994397759437561, 0.9996225833892822, 0.9996881484985352, 0.9991863369941711, 0.522216796875, 0.9999700784683228, 0.9999351501464844, 0.9608615040779114, 0.9998836517333984, 0.9998319149017334, 0.9999574422836304, 0.9995219707489014, 0.9832330346107483, 0.9993361830711365, 0.9289281964302063, 0.9628921747207642, 0.9998667240142822, 0.9999779462814331, 0.9990992546081543, 0.9141392707824707, 0.9998242259025574, 0.9967435002326965, 0.9999244213104248, 0.9998732805252075, 0.9988711476325989, 0.9987514019012451, 0.9988617897033691, 0.6241702437400818, 0.979806125164032, 0.9399409294128418, 0.9999160766601562, 0.9905988574028015, 0.9990541338920593, 0.9999421834945679, 0.960566520690918, 0.9946881532669067, 0.9999477863311768, 0.833644688129425, 0.9959911704063416, 0.9991551637649536, 0.9996606111526489, 0.9877347350120544, 0.9679129123687744, 0.9596376419067383, 0.986365795135498, 0.9459453225135803, 0.9946697354316711, 0.9895763397216797, 0.9849644303321838, 0.903245210647583, 0.7387762665748596, 0.9958617687225342, 0.9985413551330566, 0.9970884919166565, 0.6466483473777771, 0.9444775581359863, 0.8205862045288086, 0.9942649006843567, 0.984862744808197, 0.9972048401832581, 0.8482417464256287, 0.9919762015342712, 0.9672141671180725, 0.9989006519317627, 0.9998776912689209, 0.9994097948074341, 0.9945313334465027, 0.9889154434204102, 0.9952592253684998, 0.999559223651886, 0.99980229139328, 0.9894945025444031, 0.7909385561943054, 0.9917729496955872], "line": {"id": "c8db96dd-e5f0-47e4-9da3-f34c7489857b", "bbox": {"id": "355dd1af-317b-4282-b778-48a722db18de", "bbox": [[349, 1045], [349, 1187], [3183, 1187], [3183, 1045]]}, "type": "bbox"}}, {"prediction": "\u0645\u0639\u0646 \u0644\u0627\u0642 \u0629\u0639\u0627\u0633\u0644\u0627 \u0627\u0640\u0647\u0631\u0636\u062d\u0627 \u0644\u0627\u0642\u0641 \u0643\u0644\u0630 \u0644\u062b\u0645 \u062a\u0639\u0646\u0635 \u062a\u064a\u0633\u0645\u0654\u0627 \u0627\u0644\u0643\u0648 \u0627\u0647\u0639\u0636\u0648\u0645 \u0649\u0644\u0627 \u0627\u0647\u062f\u062f\u0631\u0648", "cuts": [[[342, 1217], [342, 1386], [354, 1386], [354, 1217]], [[404, 1217], [404, 1386], [404, 1386], [404, 1217]], [[428, 1217], [428, 1386], [428, 1386], [428, 1217]], [[453, 1217], [453, 1386], [465, 1386], [465, 1217]], [[527, 1217], [527, 1386], [527, 1386], [527, 1217]], [[539, 1217], [539, 1386], [539, 1386], [539, 1217]], [[564, 1217], [564, 1386], [564, 1386], [564, 1217]], [[589, 1217], [589, 1386], [613, 1386], [613, 1217]], [[638, 1217], [638, 1386], [650, 1386], [650, 1217]], [[675, 1217], [675, 1386], [675, 1386], [675, 1217]], [[712, 1217], [712, 1386], [724, 1386], [724, 1217]], [[761, 1217], [761, 1386], [774, 1386], [774, 1217]], [[786, 1217], [786, 1386], [798, 1386], [798, 1217]], [[811, 1217], [811, 1386], [823, 1386], [823, 1217]], [[835, 1217], [835, 1386], [835, 1386], [835, 1217]], [[860, 1217], [860, 1386], [872, 1386], [872, 1217]], [[909, 1217], [909, 1386], [922, 1386], [922, 1217]], [[1020, 1217], [1020, 1386], [1020, 1386], [1020, 1217]], [[1082, 1217], [1082, 1386], [1082, 1386], [1082, 1217]], [[1119, 1217], [1119, 1386], [1131, 1386], [1131, 1217]], [[1181, 1217], [1181, 1386], [1193, 1386], [1193, 1217]], [[1242, 1217], [1242, 1386], [1255, 1386], [1255, 1217]], [[1267, 1217], [1267, 1386], [1267, 1386], [1267, 1217]], [[1329, 1217], [1329, 1386], [1341, 1386], [1341, 1217]], [[1353, 1217], [1353, 1386], [1366, 1386], [1366, 1217]], [[1390, 1217], [1390, 1386], [1390, 1386], [1390, 1217]], [[1427, 1217], [1427, 1386], [1427, 1386], [1427, 1217]], [[1452, 1217], [1452, 1386], [1452, 1386], [1452, 1217]], [[1501, 1217], [1501, 1386], [1501, 1386], [1501, 1217]], [[1551, 1217], [1551, 1386], [1563, 1386], [1563, 1217]], [[1575, 1217], [1575, 1386], [1588, 1386], [1588, 1217]], [[1612, 1217], [1612, 1386], [1625, 1386], [1625, 1217]], [[1686, 1217], [1686, 1386], [1699, 1386], [1699, 1217]], [[1711, 1217], [1711, 1386], [1723, 1386], [1723, 1217]], [[1736, 1217], [1736, 1386], [1748, 1386], [1748, 1217]], [[1773, 1217], [1773, 1386], [1785, 1386], [1785, 1217]], [[1822, 1217], [1822, 1386], [1835, 1386], [1835, 1217]], [[1884, 1217], [1884, 1386], [1884, 1386], [1884, 1217]], [[1921, 1217], [1921, 1386], [1933, 1386], [1933, 1217]], [[1970, 1217], [1970, 1386], [1983, 1386], [1983, 1217]], [[2007, 1217], [2007, 1386], [2020, 1386], [2020, 1217]], [[2069, 1217], [2069, 1386], [2069, 1386], [2069, 1217]], [[2118, 1217], [2118, 1386], [2118, 1386], [2118, 1217]], [[2155, 1217], [2155, 1386], [2168, 1386], [2168, 1217]], [[2192, 1217], [2192, 1386], [2205, 1386], [2205, 1217]], [[2217, 1217], [2217, 1386], [2229, 1386], [2229, 1217]], [[2242, 1217], [2242, 1386], [2254, 1386], [2254, 1217]], [[2266, 1217], [2266, 1386], [2279, 1386], [2279, 1217]], [[2316, 1217], [2316, 1386], [2328, 1386], [2328, 1217]], [[2353, 1217], [2353, 1386], [2365, 1386], [2365, 1217]], [[2377, 1217], [2377, 1386], [2377, 1386], [2377, 1217]], [[2439, 1217], [2439, 1386], [2439, 1386], [2439, 1217]], [[2464, 1217], [2464, 1386], [2476, 1386], [2476, 1217]], [[2501, 1217], [2501, 1386], [2501, 1386], [2501, 1217]], [[2525, 1217], [2525, 1386], [2525, 1386], [2525, 1217]], [[2575, 1217], [2575, 1386], [2575, 1386], [2575, 1217]], [[2624, 1217], [2624, 1386], [2624, 1386], [2624, 1217]], [[2698, 1217], [2698, 1386], [2698, 1386], [2698, 1217]], [[2735, 1217], [2735, 1386], [2735, 1386], [2735, 1217]], [[2760, 1217], [2760, 1386], [2772, 1386], [2772, 1217]], [[2821, 1217], [2821, 1386], [2821, 1386], [2821, 1217]], [[2834, 1217], [2834, 1386], [2834, 1386], [2834, 1217]], [[2858, 1217], [2858, 1386], [2858, 1386], [2858, 1217]], [[2871, 1217], [2871, 1386], [2895, 1386], [2895, 1217]], [[2932, 1217], [2932, 1386], [2932, 1386], [2932, 1217]], [[2957, 1217], [2957, 1386], [2957, 1386], [2957, 1217]], [[3019, 1217], [3019, 1386], [3019, 1386], [3019, 1217]], [[3056, 1217], [3056, 1386], [3056, 1386], [3056, 1217]], [[3105, 1217], [3105, 1386], [3105, 1386], [3105, 1217]], [[3154, 1217], [3154, 1386], [3167, 1386], [3167, 1217]]], "confidences": [0.9998668432235718, 0.9939495325088501, 0.9999988079071045, 0.9995810389518738, 0.9973961114883423, 0.9925320744514465, 0.9987910389900208, 0.9998966455459595, 0.9999004602432251, 0.981810450553894, 0.99687659740448, 0.9998045563697815, 0.9998052716255188, 0.9931424856185913, 0.9988383650779724, 0.9999014139175415, 0.8104275465011597, 0.9999496936798096, 0.9994741082191467, 0.9992212057113647, 0.9998238682746887, 0.9998308420181274, 0.9993120431900024, 0.998877227306366, 0.999991774559021, 0.9525014758110046, 0.9995104074478149, 0.9983181953430176, 0.9150962829589844, 0.9546083807945251, 0.9991251826286316, 0.9999608993530273, 0.9821158647537231, 0.9985532164573669, 0.9999796152114868, 0.9997883439064026, 0.9903550148010254, 0.9928650856018066, 0.9961369633674622, 0.9909413456916809, 0.9947617650032043, 0.9631214737892151, 0.9943027496337891, 0.9706187844276428, 0.998685896396637, 0.9429393410682678, 0.9975836277008057, 0.9990416169166565, 0.9978206157684326, 0.9992419481277466, 0.9985764026641846, 0.9849389791488647, 0.9799551367759705, 0.9954046010971069, 0.9584742784500122, 0.9991908669471741, 0.994751513004303, 0.9992371797561646, 0.8995857834815979, 0.99809330701828, 0.999932050704956, 0.8690654039382935, 0.5987728834152222, 0.9988412261009216, 0.6917583346366882, 0.9999560117721558, 0.904447615146637, 0.9634166955947876, 0.9973717927932739, 0.9989099502563477], "line": {"id": "b58db9b3-ccd7-449f-9d44-0bacf7a3be3e", "bbox": {"id": "309303ba-7ddf-4f08-bb5f-8487c14139dc", "bbox": [[342, 1217], [342, 1386], [3179, 1386], [3179, 1217]]}, "type": "bbox"}}, {"prediction": "\u0630\u0640\u062e\u0646 \u0649\u0631\u0627\u062f \u0646\u0645 \u0646\u0627\u0644\u0641\u0644\u0627 \u062a\u064a\u0628\u0644\u0627 \u0649\u0644\u0627 \u0636\u0645\u0627 \u0647\u0644 \u0644\u0627\u0642\u0641 \u0647\u0645\u062f\u062e \u0636\u0639\u0628 \u0649\u0639\u062f\u062a\u0633\u0627\u0642 \u0646\u064a\u0646\u0645\u0654\u0648\u0645\u0644\u0627 \u0631\u064a\u0645\u0654\u0627\u0627\u064a", "cuts": [[[363, 1386], [363, 1518], [363, 1518], [363, 1386]], [[413, 1386], [413, 1518], [426, 1518], [426, 1386]], [[514, 1386], [514, 1518], [514, 1518], [514, 1386]], [[551, 1386], [551, 1518], [551, 1518], [551, 1386]], [[564, 1386], [564, 1518], [589, 1518], [589, 1386]], [[652, 1386], [652, 1518], [652, 1518], [652, 1386]], [[702, 1386], [702, 1518], [715, 1518], [715, 1386]], [[740, 1386], [740, 1518], [752, 1518], [752, 1386]], [[765, 1386], [765, 1518], [777, 1518], [777, 1386]], [[802, 1386], [802, 1518], [815, 1518], [815, 1386]], [[865, 1386], [865, 1518], [865, 1518], [865, 1386]], [[903, 1386], [903, 1518], [903, 1518], [903, 1386]], [[928, 1386], [928, 1518], [940, 1518], [940, 1386]], [[991, 1386], [991, 1518], [991, 1518], [991, 1386]], [[1041, 1386], [1041, 1518], [1053, 1518], [1053, 1386]], [[1066, 1386], [1066, 1518], [1078, 1518], [1078, 1386]], [[1116, 1386], [1116, 1518], [1116, 1518], [1116, 1386]], [[1141, 1386], [1141, 1518], [1141, 1518], [1141, 1386]], [[1154, 1386], [1154, 1518], [1166, 1518], [1166, 1386]], [[1179, 1386], [1179, 1518], [1191, 1518], [1191, 1386]], [[1229, 1386], [1229, 1518], [1229, 1518], [1229, 1386]], [[1279, 1386], [1279, 1518], [1279, 1518], [1279, 1386]], [[1304, 1386], [1304, 1518], [1304, 1518], [1304, 1386]], [[1329, 1386], [1329, 1518], [1342, 1518], [1342, 1386]], [[1354, 1386], [1354, 1518], [1354, 1518], [1354, 1386]], [[1367, 1386], [1367, 1518], [1380, 1518], [1380, 1386]], [[1417, 1386], [1417, 1518], [1417, 1518], [1417, 1386]], [[1430, 1386], [1430, 1518], [1442, 1518], [1442, 1386]], [[1455, 1386], [1455, 1518], [1467, 1518], [1467, 1386]], [[1480, 1386], [1480, 1518], [1480, 1518], [1480, 1386]], [[1568, 1386], [1568, 1518], [1568, 1518], [1568, 1386]], [[1618, 1386], [1618, 1518], [1631, 1518], [1631, 1386]], [[1668, 1386], [1668, 1518], [1668, 1518], [1668, 1386]], [[1681, 1386], [1681, 1518], [1693, 1518], [1693, 1386]], [[1731, 1386], [1731, 1518], [1743, 1518], [1743, 1386]], [[1769, 1386], [1769, 1518], [1769, 1518], [1769, 1386]], [[1781, 1386], [1781, 1518], [1794, 1518], [1794, 1386]], [[1844, 1386], [1844, 1518], [1844, 1518], [1844, 1386]], [[1869, 1386], [1869, 1518], [1869, 1518], [1869, 1386]], [[1907, 1386], [1907, 1518], [1907, 1518], [1907, 1386]], [[1932, 1386], [1932, 1518], [1932, 1518], [1932, 1386]], [[1957, 1386], [1957, 1518], [1969, 1518], [1969, 1386]], [[1982, 1386], [1982, 1518], [1994, 1518], [1994, 1386]], [[2032, 1386], [2032, 1518], [2032, 1518], [2032, 1386]], [[2082, 1386], [2082, 1518], [2082, 1518], [2082, 1386]], [[2120, 1386], [2120, 1518], [2132, 1518], [2132, 1386]], [[2158, 1386], [2158, 1518], [2170, 1518], [2170, 1386]], [[2245, 1386], [2245, 1518], [2258, 1518], [2258, 1386]], [[2296, 1386], [2296, 1518], [2308, 1518], [2308, 1386]], [[2333, 1386], [2333, 1518], [2333, 1518], [2333, 1386]], [[2358, 1386], [2358, 1518], [2371, 1518], [2371, 1386]], [[2421, 1386], [2421, 1518], [2434, 1518], [2434, 1386]], [[2459, 1386], [2459, 1518], [2459, 1518], [2459, 1386]], [[2521, 1386], [2521, 1518], [2521, 1518], [2521, 1386]], [[2546, 1386], [2546, 1518], [2546, 1518], [2546, 1386]], [[2597, 1386], [2597, 1518], [2597, 1518], [2597, 1386]], [[2622, 1386], [2622, 1518], [2634, 1518], [2634, 1386]], [[2647, 1386], [2647, 1518], [2647, 1518], [2647, 1386]], [[2672, 1386], [2672, 1518], [2697, 1518], [2697, 1386]], [[2735, 1386], [2735, 1518], [2747, 1518], [2747, 1386]], [[2772, 1386], [2772, 1518], [2772, 1518], [2772, 1386]], [[2797, 1386], [2797, 1518], [2810, 1518], [2810, 1386]], [[2835, 1386], [2835, 1518], [2835, 1518], [2835, 1386]], [[2873, 1386], [2873, 1518], [2873, 1518], [2873, 1386]], [[2885, 1386], [2885, 1518], [2898, 1518], [2898, 1386]], [[2910, 1386], [2910, 1518], [2910, 1518], [2910, 1386]], [[2923, 1386], [2923, 1518], [2923, 1518], [2923, 1386]], [[2948, 1386], [2948, 1518], [2961, 1518], [2961, 1386]], [[2973, 1386], [2973, 1518], [2973, 1518], [2973, 1386]], [[3011, 1386], [3011, 1518], [3023, 1518], [3023, 1386]], [[3036, 1386], [3036, 1518], [3036, 1518], [3036, 1386]], [[3073, 1386], [3073, 1518], [3073, 1518], [3073, 1386]], [[3099, 1386], [3099, 1518], [3099, 1518], [3099, 1386]], [[3111, 1386], [3111, 1518], [3111, 1518], [3111, 1386]], [[3136, 1386], [3136, 1518], [3149, 1518], [3149, 1386]], [[3161, 1386], [3161, 1518], [3161, 1518], [3161, 1386]]], "confidences": [0.9999401569366455, 0.9998226761817932, 0.9994499087333679, 0.9997947812080383, 0.9999337196350098, 0.9999203681945801, 0.9999982118606567, 0.9687603116035461, 0.9988839030265808, 0.998324453830719, 0.9755439758300781, 0.9999986886978149, 0.9999879598617554, 0.9918851852416992, 0.8710991144180298, 0.9998831748962402, 0.9799282550811768, 0.9640147686004639, 0.9995965361595154, 0.9998983144760132, 0.9994907379150391, 0.9999008178710938, 0.9999949932098389, 0.9999257326126099, 0.9849360585212708, 0.9992073178291321, 0.9999591112136841, 0.9999978542327881, 0.9979308843612671, 0.9989111423492432, 0.9988147020339966, 0.999931812286377, 0.9295510649681091, 0.9994879961013794, 0.92276930809021, 0.9905250668525696, 0.9945216178894043, 0.997312605381012, 0.9944058656692505, 0.7167887091636658, 0.9777094125747681, 0.9938310384750366, 0.9831866025924683, 0.9466024041175842, 0.9909795522689819, 0.9944062232971191, 0.9905561804771423, 0.9995214939117432, 0.9992606043815613, 0.9838502407073975, 0.9978698492050171, 0.9995675683021545, 0.9971568584442139, 0.9990284442901611, 0.9905042052268982, 0.9607644081115723, 0.9984911680221558, 0.7474943399429321, 0.9998549222946167, 0.9999047517776489, 0.9814210534095764, 0.9995946288108826, 0.999559223651886, 0.9958802461624146, 0.9985283613204956, 0.9995490908622742, 0.9966185092926025, 0.9981649518013, 0.8663971424102783, 0.9913069605827332, 0.9690742492675781, 0.9696444869041443, 0.9100111126899719, 0.9691933393478394, 0.9827510714530945, 0.9762600064277649], "line": {"id": "aed3d49e-51e9-4a1b-8666-61970246b6ed", "bbox": {"id": "418e5e37-02e7-45d7-8862-21caf565b075", "bbox": [[338, 1386], [338, 1518], [3174, 1518], [3174, 1386]]}, "type": "bbox"}}, {"prediction": "\u062b\u0628\u0644\u064a \u0645\u0644\u0641 \u0647\u0645\u062a\u062e\u0628 \u0647\u064a\u0641 \u0649\u0630\u0644\u0627 \u0637\u0641\u0633\u0644\u0627\u0628 \u0649\u0646\u0654\u064a\u062c\u0644 \u0641\u0627\u0644\u0641\u0644\u0627 \u0642\u0648\u062f\u0646\u0635\u0644\u0627 \u062d\u062a\u0641\u0627 \u0645\u062b \u0647\u062d\u062a\u0641\u0627\u0648 \u064a\u0641\u0632\u0627\u062e \u0646\u0645 \u0647\u062d\u0627\u062a\u0641\u0645", "cuts": [[[361, 1567], [361, 1720], [361, 1720], [361, 1567]], [[413, 1567], [413, 1720], [413, 1720], [413, 1567]], [[439, 1567], [439, 1720], [439, 1720], [439, 1567]], [[465, 1567], [465, 1720], [465, 1720], [465, 1567]], [[491, 1567], [491, 1720], [504, 1720], [504, 1567]], [[517, 1567], [517, 1720], [530, 1720], [530, 1567]], [[543, 1567], [543, 1720], [543, 1720], [543, 1567]], [[556, 1567], [556, 1720], [569, 1720], [569, 1567]], [[582, 1567], [582, 1720], [595, 1720], [595, 1567]], [[621, 1567], [621, 1720], [621, 1720], [621, 1567]], [[660, 1567], [660, 1720], [660, 1720], [660, 1567]], [[699, 1567], [699, 1720], [699, 1720], [699, 1567]], [[725, 1567], [725, 1720], [725, 1720], [725, 1567]], [[764, 1567], [764, 1720], [764, 1720], [764, 1567]], [[777, 1567], [777, 1720], [790, 1720], [790, 1567]], [[816, 1567], [816, 1720], [816, 1720], [816, 1567]], [[855, 1567], [855, 1720], [855, 1720], [855, 1567]], [[881, 1567], [881, 1720], [881, 1720], [881, 1567]], [[907, 1567], [907, 1720], [920, 1720], [920, 1567]], [[972, 1567], [972, 1720], [972, 1720], [972, 1567]], [[1024, 1567], [1024, 1720], [1024, 1720], [1024, 1567]], [[1051, 1567], [1051, 1720], [1051, 1720], [1051, 1567]], [[1077, 1567], [1077, 1720], [1077, 1720], [1077, 1567]], [[1090, 1567], [1090, 1720], [1103, 1720], [1103, 1567]], [[1142, 1567], [1142, 1720], [1155, 1720], [1155, 1567]], [[1207, 1567], [1207, 1720], [1207, 1720], [1207, 1567]], [[1259, 1567], [1259, 1720], [1272, 1720], [1272, 1567]], [[1298, 1567], [1298, 1720], [1311, 1720], [1311, 1567]], [[1324, 1567], [1324, 1720], [1337, 1720], [1337, 1567]], [[1350, 1567], [1350, 1720], [1350, 1720], [1350, 1567]], [[1363, 1567], [1363, 1720], [1376, 1720], [1376, 1567]], [[1415, 1567], [1415, 1720], [1415, 1720], [1415, 1567]], [[1428, 1567], [1428, 1720], [1441, 1720], [1441, 1567]], [[1454, 1567], [1454, 1720], [1454, 1720], [1454, 1567]], [[1467, 1567], [1467, 1720], [1467, 1720], [1467, 1567]], [[1493, 1567], [1493, 1720], [1506, 1720], [1506, 1567]], [[1519, 1567], [1519, 1720], [1519, 1720], [1519, 1567]], [[1545, 1567], [1545, 1720], [1558, 1720], [1558, 1567]], [[1597, 1567], [1597, 1720], [1597, 1720], [1597, 1567]], [[1636, 1567], [1636, 1720], [1662, 1720], [1662, 1567]], [[1675, 1567], [1675, 1720], [1675, 1720], [1675, 1567]], [[1714, 1567], [1714, 1720], [1714, 1720], [1714, 1567]], [[1740, 1567], [1740, 1720], [1740, 1720], [1740, 1567]], [[1753, 1567], [1753, 1720], [1753, 1720], [1753, 1567]], [[1766, 1567], [1766, 1720], [1779, 1720], [1779, 1567]], [[1831, 1567], [1831, 1720], [1831, 1720], [1831, 1567]], [[1883, 1567], [1883, 1720], [1883, 1720], [1883, 1567]], [[1935, 1567], [1935, 1720], [1935, 1720], [1935, 1567]], [[1961, 1567], [1961, 1720], [1961, 1720], [1961, 1567]], [[2013, 1567], [2013, 1720], [2013, 1720], [2013, 1567]], [[2052, 1567], [2052, 1720], [2052, 1720], [2052, 1567]], [[2065, 1567], [2065, 1720], [2078, 1720], [2078, 1567]], [[2091, 1567], [2091, 1720], [2104, 1720], [2104, 1567]], [[2130, 1567], [2130, 1720], [2130, 1720], [2130, 1567]], [[2182, 1567], [2182, 1720], [2182, 1720], [2182, 1567]], [[2208, 1567], [2208, 1720], [2208, 1720], [2208, 1567]], [[2234, 1567], [2234, 1720], [2247, 1720], [2247, 1567]], [[2260, 1567], [2260, 1720], [2260, 1720], [2260, 1567]], [[2286, 1567], [2286, 1720], [2299, 1720], [2299, 1567]], [[2338, 1567], [2338, 1720], [2338, 1720], [2338, 1567]], [[2364, 1567], [2364, 1720], [2377, 1720], [2377, 1567]], [[2390, 1567], [2390, 1720], [2390, 1720], [2390, 1567]], [[2429, 1567], [2429, 1720], [2429, 1720], [2429, 1567]], [[2482, 1567], [2482, 1720], [2482, 1720], [2482, 1567]], [[2508, 1567], [2508, 1720], [2508, 1720], [2508, 1567]], [[2534, 1567], [2534, 1720], [2534, 1720], [2534, 1567]], [[2586, 1567], [2586, 1720], [2586, 1720], [2586, 1567]], [[2612, 1567], [2612, 1720], [2625, 1720], [2625, 1567]], [[2651, 1567], [2651, 1720], [2651, 1720], [2651, 1567]], [[2664, 1567], [2664, 1720], [2664, 1720], [2664, 1567]], [[2716, 1567], [2716, 1720], [2716, 1720], [2716, 1567]], [[2742, 1567], [2742, 1720], [2742, 1720], [2742, 1567]], [[2768, 1567], [2768, 1720], [2781, 1720], [2781, 1567]], [[2807, 1567], [2807, 1720], [2820, 1720], [2820, 1567]], [[2859, 1567], [2859, 1720], [2859, 1720], [2859, 1567]], [[2898, 1567], [2898, 1720], [2898, 1720], [2898, 1567]], [[2924, 1567], [2924, 1720], [2937, 1720], [2937, 1567]], [[2963, 1567], [2963, 1720], [2963, 1720], [2963, 1567]], [[3002, 1567], [3002, 1720], [3002, 1720], [3002, 1567]], [[3054, 1567], [3054, 1720], [3054, 1720], [3054, 1567]], [[3080, 1567], [3080, 1720], [3080, 1720], [3080, 1567]], [[3119, 1567], [3119, 1720], [3119, 1720], [3119, 1567]], [[3145, 1567], [3145, 1720], [3158, 1720], [3158, 1567]]], "confidences": [0.9997847676277161, 0.8071998953819275, 0.9448097944259644, 0.5688155293464661, 0.9998739957809448, 0.9982267022132874, 0.9191160798072815, 0.9526958465576172, 0.999830961227417, 0.9999995231628418, 0.9993177652359009, 0.7347162365913391, 0.9999439716339111, 0.9998359680175781, 0.9999669790267944, 0.9836472868919373, 0.7263768315315247, 0.9997736811637878, 0.977462887763977, 0.9863080382347107, 0.9985411167144775, 0.9811128377914429, 0.8683425784111023, 0.9999358654022217, 0.9999024868011475, 0.9963384866714478, 0.9992992877960205, 0.9999573230743408, 0.9997846484184265, 0.9697093963623047, 0.9807730913162231, 0.9946768283843994, 0.9997909665107727, 0.9734904170036316, 0.9997913241386414, 0.998504638671875, 0.9990155696868896, 0.9989411234855652, 0.54173743724823, 0.9961016178131104, 0.9981348514556885, 0.9947880506515503, 0.9791303277015686, 0.9969891905784607, 0.9995549321174622, 0.8724766969680786, 0.9847140908241272, 0.9845319390296936, 0.9821130037307739, 0.9790477156639099, 0.838432252407074, 0.8714025616645813, 0.9906243681907654, 0.9986967444419861, 0.9988054037094116, 0.9993483424186707, 0.9957486987113953, 0.8649066090583801, 0.9977350234985352, 0.9995262622833252, 0.9965477585792542, 0.9975600242614746, 0.8291054368019104, 0.9957834482192993, 0.9864495396614075, 0.9921678900718689, 0.9627065658569336, 0.9999135732650757, 0.95207279920578, 0.9991065859794617, 0.9793131947517395, 0.8521793484687805, 0.9974838495254517, 0.9999358654022217, 0.9997324347496033, 0.9997784495353699, 0.9984983205795288, 0.9993114471435547, 0.8725327849388123, 0.9996665716171265, 0.9979007244110107, 0.9954906105995178, 0.9731282591819763], "line": {"id": "41e5ddb1-2a4c-4034-a3c4-f260d6680319", "bbox": {"id": "54a56c8d-b064-49ff-bda6-5fc0cd5ecec7", "bbox": [[335, 1567], [335, 1720], [3171, 1720], [3171, 1567]]}, "type": "bbox"}}, {"prediction": "\u0647\u0640\u062d\u062a\u0641\u0648 \u0647\u0645\u062a\u062e \u0631\u0633\u0643\u0628 \u0631\u0645\u0654\u0627\u0641 \u062f\u064a\u0634\u0631\u0644\u0627 \u0649\u062f\u064a \u0646\u064a\u0628 \u0639\u0636\u0648\u0641 \u0627\u0645\u0648\u062a\u062d\u0645 \u0637\u0641\u0633\u0644\u0627\u0628 \u0621\u0627\u062c \u0646\u0627 \u0645\u0627\u0644\u063a\u0644\u0627", "cuts": [[[332, 1721], [332, 1880], [345, 1880], [345, 1721]], [[384, 1721], [384, 1880], [397, 1880], [397, 1721]], [[501, 1721], [501, 1880], [514, 1880], [514, 1721]], [[553, 1721], [553, 1880], [553, 1880], [553, 1721]], [[579, 1721], [579, 1880], [579, 1880], [579, 1721]], [[631, 1721], [631, 1880], [644, 1880], [644, 1721]], [[657, 1721], [657, 1880], [683, 1880], [683, 1721]], [[709, 1721], [709, 1880], [709, 1880], [709, 1721]], [[748, 1721], [748, 1880], [748, 1880], [748, 1721]], [[787, 1721], [787, 1880], [787, 1880], [787, 1721]], [[826, 1721], [826, 1880], [826, 1880], [826, 1721]], [[865, 1721], [865, 1880], [878, 1880], [878, 1721]], [[917, 1721], [917, 1880], [917, 1880], [917, 1721]], [[956, 1721], [956, 1880], [969, 1880], [969, 1721]], [[1021, 1721], [1021, 1880], [1021, 1880], [1021, 1721]], [[1100, 1721], [1100, 1880], [1100, 1880], [1100, 1721]], [[1126, 1721], [1126, 1880], [1139, 1880], [1139, 1721]], [[1178, 1721], [1178, 1880], [1191, 1880], [1191, 1721]], [[1217, 1721], [1217, 1880], [1217, 1880], [1217, 1721]], [[1243, 1721], [1243, 1880], [1243, 1880], [1243, 1721]], [[1256, 1721], [1256, 1880], [1269, 1880], [1269, 1721]], [[1282, 1721], [1282, 1880], [1282, 1880], [1282, 1721]], [[1308, 1721], [1308, 1880], [1321, 1880], [1321, 1721]], [[1373, 1721], [1373, 1880], [1386, 1880], [1386, 1721]], [[1412, 1721], [1412, 1880], [1412, 1880], [1412, 1721]], [[1451, 1721], [1451, 1880], [1464, 1880], [1464, 1721]], [[1503, 1721], [1503, 1880], [1503, 1880], [1503, 1721]], [[1516, 1721], [1516, 1880], [1529, 1880], [1529, 1721]], [[1542, 1721], [1542, 1880], [1555, 1880], [1555, 1721]], [[1568, 1721], [1568, 1880], [1568, 1880], [1568, 1721]], [[1659, 1721], [1659, 1880], [1659, 1880], [1659, 1721]], [[1711, 1721], [1711, 1880], [1724, 1880], [1724, 1721]], [[1776, 1721], [1776, 1880], [1776, 1880], [1776, 1721]], [[1802, 1721], [1802, 1880], [1815, 1880], [1815, 1721]], [[1841, 1721], [1841, 1880], [1854, 1880], [1854, 1721]], [[1867, 1721], [1867, 1880], [1867, 1880], [1867, 1721]], [[1893, 1721], [1893, 1880], [1893, 1880], [1893, 1721]], [[1919, 1721], [1919, 1880], [1932, 1880], [1932, 1721]], [[1997, 1721], [1997, 1880], [1997, 1880], [1997, 1721]], [[2062, 1721], [2062, 1880], [2062, 1880], [2062, 1721]], [[2127, 1721], [2127, 1880], [2140, 1880], [2140, 1721]], [[2166, 1721], [2166, 1880], [2166, 1880], [2166, 1721]], [[2192, 1721], [2192, 1880], [2205, 1880], [2205, 1721]], [[2244, 1721], [2244, 1880], [2257, 1880], [2257, 1721]], [[2270, 1721], [2270, 1880], [2283, 1880], [2283, 1721]], [[2322, 1721], [2322, 1880], [2335, 1880], [2335, 1721]], [[2361, 1721], [2361, 1880], [2361, 1880], [2361, 1721]], [[2400, 1721], [2400, 1880], [2400, 1880], [2400, 1721]], [[2413, 1721], [2413, 1880], [2413, 1880], [2413, 1721]], [[2452, 1721], [2452, 1880], [2452, 1880], [2452, 1721]], [[2492, 1721], [2492, 1880], [2505, 1880], [2505, 1721]], [[2557, 1721], [2557, 1880], [2557, 1880], [2557, 1721]], [[2609, 1721], [2609, 1880], [2622, 1880], [2622, 1721]], [[2648, 1721], [2648, 1880], [2648, 1880], [2648, 1721]], [[2661, 1721], [2661, 1880], [2674, 1880], [2674, 1721]], [[2687, 1721], [2687, 1880], [2700, 1880], [2700, 1721]], [[2713, 1721], [2713, 1880], [2726, 1880], [2726, 1721]], [[2765, 1721], [2765, 1880], [2765, 1880], [2765, 1721]], [[2791, 1721], [2791, 1880], [2804, 1880], [2804, 1721]], [[2817, 1721], [2817, 1880], [2830, 1880], [2830, 1721]], [[2869, 1721], [2869, 1880], [2882, 1880], [2882, 1721]], [[2908, 1721], [2908, 1880], [2921, 1880], [2921, 1721]], [[2947, 1721], [2947, 1880], [2960, 1880], [2960, 1721]], [[2973, 1721], [2973, 1880], [2986, 1880], [2986, 1721]], [[2999, 1721], [2999, 1880], [3012, 1880], [3012, 1721]], [[3038, 1721], [3038, 1880], [3064, 1880], [3064, 1721]], [[3077, 1721], [3077, 1880], [3077, 1880], [3077, 1721]], [[3103, 1721], [3103, 1880], [3116, 1880], [3116, 1721]], [[3142, 1721], [3142, 1880], [3142, 1880], [3142, 1721]], [[3155, 1721], [3155, 1880], [3168, 1880], [3168, 1721]]], "confidences": [0.9992807507514954, 0.9998190999031067, 0.9999728202819824, 0.9843649864196777, 0.9963064193725586, 0.9997701048851013, 0.9999123811721802, 0.9973879456520081, 0.999208390712738, 0.9719638824462891, 0.9987868666648865, 0.9970434308052063, 0.9997827410697937, 0.9999145269393921, 0.9791185259819031, 0.9905698299407959, 0.9965567588806152, 0.9983978867530823, 0.9999661445617676, 0.9999735355377197, 0.9999608993530273, 0.9998136162757874, 0.9998812675476074, 0.9741519093513489, 0.9999430179595947, 0.9712876081466675, 0.9996782541275024, 0.9999500513076782, 0.9999716281890869, 0.9999657869338989, 0.9887442588806152, 0.9972917437553406, 0.9999973773956299, 0.997856080532074, 0.9842910766601562, 0.9882177710533142, 0.9942337870597839, 0.9974427223205566, 0.8417571187019348, 0.96481853723526, 0.9993478655815125, 0.9817719459533691, 0.8848896622657776, 0.9665166735649109, 0.9880167245864868, 0.9940158128738403, 0.9972080588340759, 0.9924798011779785, 0.999430239200592, 0.9886217713356018, 0.9994122982025146, 0.9545918107032776, 0.9984832406044006, 0.9997020363807678, 0.9989163875579834, 0.9997730851173401, 0.9998658895492554, 0.9669737815856934, 0.9264388084411621, 0.9989351630210876, 0.9926611185073853, 0.9998636245727539, 0.9986826777458191, 0.9965075850486755, 0.9996894598007202, 0.9975529313087463, 0.9992383718490601, 0.9993176460266113, 0.9744951128959656, 0.9796627163887024], "line": {"id": "8ce8c4c0-0eea-4355-b607-845c7b522a80", "bbox": {"id": "f4d8e81c-2263-4b82-8a7f-4ee5afe0205b", "bbox": [[332, 1721], [332, 1880], [3207, 1880], [3207, 1721]]}, "type": "bbox"}}, {"prediction": "\u0647\u0628\u0636\u063a \u0646\u0645 \u062f\u064a\u0634\u0631\u0644\u0627 \u0646\u0643\u0633\u0641 \u0628\u064a\u0637\u0644\u0627 \u064a\u0641 \u0629\u0646\u0648\u0641\u062f\u0645 \u0629\u064a\u0648\u0637\u0645 \u0627\u0647\u0644\u0627\u062d \u0647\u064a\u0641 \u0629\u0639\u0627\u0631\u062f\u0644\u0627 \u0649\u0644\u0627 \u0631\u0638\u0646 \u062d\u062a\u0641 \u0627\u0644\u0646", "cuts": [[[331, 1906], [331, 2053], [331, 2053], [331, 1906]], [[370, 1906], [370, 2053], [370, 2053], [370, 1906]], [[409, 1906], [409, 2053], [409, 2053], [409, 1906]], [[473, 1906], [473, 2053], [473, 2053], [473, 1906]], [[512, 1906], [512, 2053], [525, 2053], [525, 1906]], [[563, 1906], [563, 2053], [563, 2053], [563, 1906]], [[602, 1906], [602, 2053], [602, 2053], [602, 1906]], [[628, 1906], [628, 2053], [641, 2053], [641, 1906]], [[679, 1906], [679, 2053], [679, 2053], [679, 1906]], [[705, 1906], [705, 2053], [705, 2053], [705, 1906]], [[757, 1906], [757, 2053], [757, 2053], [757, 1906]], [[795, 1906], [795, 2053], [808, 2053], [808, 1906]], [[821, 1906], [821, 2053], [821, 2053], [821, 1906]], [[834, 1906], [834, 2053], [847, 2053], [847, 1906]], [[860, 1906], [860, 2053], [873, 2053], [873, 1906]], [[924, 1906], [924, 2053], [937, 2053], [937, 1906]], [[976, 1906], [976, 2053], [976, 2053], [976, 1906]], [[1156, 1906], [1156, 2053], [1156, 2053], [1156, 1906]], [[1182, 1906], [1182, 2053], [1195, 2053], [1195, 1906]], [[1221, 1906], [1221, 2053], [1233, 2053], [1233, 1906]], [[1272, 1906], [1272, 2053], [1272, 2053], [1272, 1906]], [[1337, 1906], [1337, 2053], [1337, 2053], [1337, 1906]], [[1362, 1906], [1362, 2053], [1362, 2053], [1362, 1906]], [[1401, 1906], [1401, 2053], [1414, 2053], [1414, 1906]], [[1427, 1906], [1427, 2053], [1427, 2053], [1427, 1906]], [[1440, 1906], [1440, 2053], [1453, 2053], [1453, 1906]], [[1504, 1906], [1504, 2053], [1504, 2053], [1504, 1906]], [[1517, 1906], [1517, 2053], [1530, 2053], [1530, 1906]], [[1556, 1906], [1556, 2053], [1569, 2053], [1569, 1906]], [[1581, 1906], [1581, 2053], [1594, 2053], [1594, 1906]], [[1620, 1906], [1620, 2053], [1620, 2053], [1620, 1906]], [[1659, 1906], [1659, 2053], [1672, 2053], [1672, 1906]], [[1697, 1906], [1697, 2053], [1697, 2053], [1697, 1906]], [[1736, 1906], [1736, 2053], [1749, 2053], [1749, 1906]], [[1775, 1906], [1775, 2053], [1775, 2053], [1775, 1906]], [[1801, 1906], [1801, 2053], [1813, 2053], [1813, 1906]], [[1839, 1906], [1839, 2053], [1839, 2053], [1839, 1906]], [[1865, 1906], [1865, 2053], [1865, 2053], [1865, 1906]], [[1917, 1906], [1917, 2053], [1917, 2053], [1917, 1906]], [[1955, 1906], [1955, 2053], [1955, 2053], [1955, 1906]], [[2007, 1906], [2007, 2053], [2007, 2053], [2007, 1906]], [[2033, 1906], [2033, 2053], [2045, 2053], [2045, 1906]], [[2071, 1906], [2071, 2053], [2071, 2053], [2071, 1906]], [[2097, 1906], [2097, 2053], [2097, 2053], [2097, 1906]], [[2110, 1906], [2110, 2053], [2123, 2053], [2123, 1906]], [[2136, 1906], [2136, 2053], [2149, 2053], [2149, 1906]], [[2174, 1906], [2174, 2053], [2174, 2053], [2174, 1906]], [[2226, 1906], [2226, 2053], [2239, 2053], [2239, 1906]], [[2265, 1906], [2265, 2053], [2265, 2053], [2265, 1906]], [[2290, 1906], [2290, 2053], [2303, 2053], [2303, 1906]], [[2329, 1906], [2329, 2053], [2329, 2053], [2329, 1906]], [[2355, 1906], [2355, 2053], [2368, 2053], [2368, 1906]], [[2393, 1906], [2393, 2053], [2393, 2053], [2393, 1906]], [[2419, 1906], [2419, 2053], [2419, 2053], [2419, 1906]], [[2458, 1906], [2458, 2053], [2471, 2053], [2471, 1906]], [[2509, 1906], [2509, 2053], [2509, 2053], [2509, 1906]], [[2548, 1906], [2548, 2053], [2548, 2053], [2548, 1906]], [[2574, 1906], [2574, 2053], [2587, 2053], [2587, 1906]], [[2600, 1906], [2600, 2053], [2613, 2053], [2613, 1906]], [[2625, 1906], [2625, 2053], [2625, 2053], [2625, 1906]], [[2677, 1906], [2677, 2053], [2677, 2053], [2677, 1906]], [[2690, 1906], [2690, 2053], [2690, 2053], [2690, 1906]], [[2716, 1906], [2716, 2053], [2716, 2053], [2716, 1906]], [[2729, 1906], [2729, 2053], [2741, 2053], [2741, 1906]], [[2780, 1906], [2780, 2053], [2780, 2053], [2780, 1906]], [[2806, 1906], [2806, 2053], [2819, 2053], [2819, 1906]], [[2857, 1906], [2857, 2053], [2857, 2053], [2857, 1906]], [[2870, 1906], [2870, 2053], [2896, 2053], [2896, 1906]], [[2922, 1906], [2922, 2053], [2935, 2053], [2935, 1906]], [[2973, 1906], [2973, 2053], [2973, 2053], [2973, 1906]], [[2999, 1906], [2999, 2053], [2999, 2053], [2999, 1906]], [[3025, 1906], [3025, 2053], [3025, 2053], [3025, 1906]], [[3051, 1906], [3051, 2053], [3051, 2053], [3051, 1906]], [[3128, 1906], [3128, 2053], [3141, 2053], [3141, 1906]], [[3167, 1906], [3167, 2053], [3167, 2053], [3167, 1906]]], "confidences": [0.9159960746765137, 0.9976181387901306, 0.9996975660324097, 0.9877948760986328, 0.9998071789741516, 0.9193847179412842, 0.9977598190307617, 0.9999691247940063, 0.5213192105293274, 0.9998345375061035, 0.9993784427642822, 0.9995831847190857, 0.999843955039978, 0.9999074935913086, 0.6370336413383484, 0.9995335340499878, 0.9989326596260071, 0.9860919117927551, 0.999936580657959, 0.9919663667678833, 0.9993558526039124, 0.9862826466560364, 0.8228899836540222, 0.9858511090278625, 0.9946306943893433, 0.9999549388885498, 0.999767005443573, 0.9996095299720764, 0.9999788999557495, 0.9988093376159668, 0.5167495608329773, 0.9999475479125977, 0.9990980625152588, 0.9999123811721802, 0.6137158870697021, 0.9979343414306641, 0.9267524480819702, 0.7948201894760132, 0.9545791745185852, 0.9082372188568115, 0.9680109024047852, 0.995181143283844, 0.9705617427825928, 0.8799518942832947, 0.9786372184753418, 0.9424921274185181, 0.605963408946991, 0.9994131326675415, 0.9984038472175598, 0.9985394477844238, 0.9992857575416565, 0.9990177154541016, 0.9961357116699219, 0.9946891069412231, 0.9922365546226501, 0.9932458996772766, 0.9726880788803101, 0.9994227886199951, 0.9991672039031982, 0.9998785257339478, 0.999698281288147, 0.9973363280296326, 0.9942309856414795, 0.9929460883140564, 0.8134588599205017, 0.9999004602432251, 0.9500336647033691, 0.999967098236084, 0.9991039633750916, 0.9984264373779297, 0.8896018266677856, 0.7239580750465393, 0.9947216510772705, 0.9903062582015991, 0.8236287832260132], "line": {"id": "2ffb72d3-6dee-479c-8a17-2cd3427ce6b9", "bbox": {"id": "1cd9447c-d76c-448d-b261-db779a35a0d2", "bbox": [[331, 1906], [331, 2053], [3167, 2053], [3167, 1906]]}, "type": "bbox"}}, {"prediction": "\u0627\u0647\u062f\u0639\u0628 \u0643\u064a\u0644\u0639 \u0642\u062f\u0635\u0627 \u0646\u0644\u0641 \u0627\u062f\u0634\u0627\u0631 \u0641\u0631\u0635\u0646\u0627\u0648 \u0627\u0647\u0646\u0627\u0643\u0645 \u0649\u0644\u0627 \u0627\u0647\u062f\u062f\u0631\u0627 \u0646\u064a\u0637\u0642\u064a \u0646\u0628 \u0649\u0644\u0639\u0644 \u0644\u0627\u0642 \u0645\u0654\u064a", "cuts": [[[334, 2060], [334, 2231], [334, 2231], [334, 2060]], [[360, 2060], [360, 2231], [360, 2231], [360, 2060]], [[425, 2060], [425, 2231], [425, 2231], [425, 2060]], [[451, 2060], [451, 2231], [451, 2231], [451, 2060]], [[477, 2060], [477, 2231], [490, 2231], [490, 2060]], [[503, 2060], [503, 2231], [516, 2231], [516, 2060]], [[569, 2060], [569, 2231], [569, 2231], [569, 2060]], [[621, 2060], [621, 2231], [634, 2231], [634, 2060]], [[647, 2060], [647, 2231], [647, 2231], [647, 2060]], [[673, 2060], [673, 2231], [686, 2231], [686, 2060]], [[712, 2060], [712, 2231], [738, 2231], [738, 2060]], [[803, 2060], [803, 2231], [803, 2231], [803, 2060]], [[842, 2060], [842, 2231], [855, 2231], [855, 2060]], [[894, 2060], [894, 2231], [907, 2231], [907, 2060]], [[946, 2060], [946, 2231], [959, 2231], [959, 2060]], [[972, 2060], [972, 2231], [972, 2231], [972, 2060]], [[1024, 2060], [1024, 2231], [1024, 2231], [1024, 2060]], [[1051, 2060], [1051, 2231], [1064, 2231], [1064, 2060]], [[1077, 2060], [1077, 2231], [1077, 2231], [1077, 2060]], [[1103, 2060], [1103, 2231], [1129, 2231], [1129, 2060]], [[1168, 2060], [1168, 2231], [1181, 2231], [1181, 2060]], [[1220, 2060], [1220, 2231], [1220, 2231], [1220, 2060]], [[1272, 2060], [1272, 2231], [1272, 2231], [1272, 2060]], [[1298, 2060], [1298, 2231], [1311, 2231], [1311, 2060]], [[1350, 2060], [1350, 2231], [1350, 2231], [1350, 2060]], [[1376, 2060], [1376, 2231], [1389, 2231], [1389, 2060]], [[1480, 2060], [1480, 2231], [1480, 2231], [1480, 2060]], [[1520, 2060], [1520, 2231], [1533, 2231], [1533, 2060]], [[1585, 2060], [1585, 2231], [1585, 2231], [1585, 2060]], [[1624, 2060], [1624, 2231], [1624, 2231], [1624, 2060]], [[1650, 2060], [1650, 2231], [1663, 2231], [1663, 2060]], [[1702, 2060], [1702, 2231], [1715, 2231], [1715, 2060]], [[1728, 2060], [1728, 2231], [1754, 2231], [1754, 2060]], [[1767, 2060], [1767, 2231], [1780, 2231], [1780, 2060]], [[1832, 2060], [1832, 2231], [1845, 2231], [1845, 2060]], [[1858, 2060], [1858, 2231], [1871, 2231], [1871, 2060]], [[1884, 2060], [1884, 2231], [1884, 2231], [1884, 2060]], [[1897, 2060], [1897, 2231], [1910, 2231], [1910, 2060]], [[1962, 2060], [1962, 2231], [1962, 2231], [1962, 2060]], [[1989, 2060], [1989, 2231], [2002, 2231], [2002, 2060]], [[2067, 2060], [2067, 2231], [2067, 2231], [2067, 2060]], [[2080, 2060], [2080, 2231], [2080, 2231], [2080, 2060]], [[2106, 2060], [2106, 2231], [2106, 2231], [2106, 2060]], [[2119, 2060], [2119, 2231], [2132, 2231], [2132, 2060]], [[2158, 2060], [2158, 2231], [2158, 2231], [2158, 2060]], [[2184, 2060], [2184, 2231], [2184, 2231], [2184, 2060]], [[2236, 2060], [2236, 2231], [2236, 2231], [2236, 2060]], [[2262, 2060], [2262, 2231], [2275, 2231], [2275, 2060]], [[2314, 2060], [2314, 2231], [2327, 2231], [2327, 2060]], [[2353, 2060], [2353, 2231], [2353, 2231], [2353, 2060]], [[2379, 2060], [2379, 2231], [2392, 2231], [2392, 2060]], [[2431, 2060], [2431, 2231], [2444, 2231], [2444, 2060]], [[2457, 2060], [2457, 2231], [2471, 2231], [2471, 2060]], [[2497, 2060], [2497, 2231], [2497, 2231], [2497, 2060]], [[2549, 2060], [2549, 2231], [2562, 2231], [2562, 2060]], [[2588, 2060], [2588, 2231], [2588, 2231], [2588, 2060]], [[2614, 2060], [2614, 2231], [2627, 2231], [2627, 2060]], [[2679, 2060], [2679, 2231], [2679, 2231], [2679, 2060]], [[2705, 2060], [2705, 2231], [2705, 2231], [2705, 2060]], [[2731, 2060], [2731, 2231], [2744, 2231], [2744, 2060]], [[2848, 2060], [2848, 2231], [2848, 2231], [2848, 2060]], [[2861, 2060], [2861, 2231], [2861, 2231], [2861, 2060]], [[2887, 2060], [2887, 2231], [2887, 2231], [2887, 2060]], [[2913, 2060], [2913, 2231], [2926, 2231], [2926, 2060]], [[2939, 2060], [2939, 2231], [2953, 2231], [2953, 2060]], [[3005, 2060], [3005, 2231], [3005, 2231], [3005, 2060]], [[3018, 2060], [3018, 2231], [3031, 2231], [3031, 2060]], [[3044, 2060], [3044, 2231], [3044, 2231], [3044, 2060]], [[3070, 2060], [3070, 2231], [3083, 2231], [3083, 2060]], [[3096, 2060], [3096, 2231], [3109, 2231], [3109, 2060]], [[3135, 2060], [3135, 2231], [3135, 2231], [3135, 2060]], [[3148, 2060], [3148, 2231], [3148, 2231], [3148, 2060]]], "confidences": [0.9586123824119568, 0.9986465573310852, 0.9566709995269775, 0.9922924041748047, 0.999371349811554, 0.999819815158844, 0.9982818365097046, 0.9999407529830933, 0.9542492032051086, 0.9999266862869263, 0.9977983236312866, 0.9082127213478088, 0.9984015822410583, 0.9999021291732788, 0.9999748468399048, 0.9999212026596069, 0.9956596493721008, 0.9997978806495667, 0.9995704293251038, 0.9999994039535522, 0.9999769926071167, 0.5398281216621399, 0.9999560117721558, 0.9999827146530151, 0.678408682346344, 0.999976634979248, 0.9999359846115112, 0.9980989098548889, 0.9837391376495361, 0.8357751369476318, 0.9999740123748779, 0.9798765182495117, 0.9975171089172363, 0.99134761095047, 0.9869094491004944, 0.8972664475440979, 0.9946538209915161, 0.9883899688720703, 0.9977121353149414, 0.9916154742240906, 0.9904273748397827, 0.9942137598991394, 0.9822350144386292, 0.9995124340057373, 0.6970008015632629, 0.9945922493934631, 0.9990953207015991, 0.9915691018104553, 0.9982545971870422, 0.9470371603965759, 0.9988258481025696, 0.9982693195343018, 0.9856593608856201, 0.9323335886001587, 0.9999984502792358, 0.9984018206596375, 0.9993857145309448, 0.9998714923858643, 0.6906755566596985, 0.9985949397087097, 0.994672954082489, 0.9955617189407349, 0.9956024885177612, 0.9990137815475464, 0.9867492318153381, 0.9986513257026672, 0.9989423155784607, 0.6353587508201599, 0.996551513671875, 0.9758750200271606, 0.8564420342445374, 0.5611870884895325], "line": {"id": "6367bc3c-1ea2-485c-b40b-66b98d9c09f7", "bbox": {"id": "6cc9b689-db87-48a8-9782-5edff5a1fdaa", "bbox": [[334, 2060], [334, 2231], [3161, 2231], [3161, 2060]]}, "type": "bbox"}}, {"prediction": "\u0648\u062d\" \u0628\u0631\u0636\u0641 \u0637\u0648\u0633 \u0641\u0644\u0627 \u0647\u0628 \u0649\u0639\u0627\u0633\u0644\u0627 \u0628\u0631\u0636\u0628 \u0645\u062f\u0642\u062a\u0648 \u0629\u064a\u0646\u0633 \u0629\u0632\u064a\u0627\u062c\u0628 \u0639\u0628\u062a\u064a \u0646\u0627 \u0631\u0645\u0654\u0627\u0648\u064b \u0627\u064a\u0639\u0627\u0633", "cuts": [[[354, 2227], [354, 2386], [354, 2386], [354, 2227]], [[393, 2227], [393, 2386], [406, 2386], [406, 2227]], [[459, 2227], [459, 2386], [459, 2386], [459, 2227]], [[472, 2227], [472, 2386], [486, 2386], [486, 2227]], [[551, 2227], [551, 2386], [551, 2386], [551, 2227]], [[617, 2227], [617, 2386], [631, 2386], [631, 2227]], [[670, 2227], [670, 2386], [670, 2386], [670, 2227]], [[710, 2227], [710, 2386], [710, 2386], [710, 2227]], [[736, 2227], [736, 2386], [763, 2386], [763, 2227]], [[815, 2227], [815, 2386], [815, 2386], [815, 2227]], [[881, 2227], [881, 2386], [881, 2386], [881, 2227]], [[921, 2227], [921, 2386], [934, 2386], [934, 2227]], [[961, 2227], [961, 2386], [987, 2386], [987, 2227]], [[1079, 2227], [1079, 2386], [1093, 2386], [1093, 2227]], [[1106, 2227], [1106, 2386], [1119, 2386], [1119, 2227]], [[1132, 2227], [1132, 2386], [1132, 2386], [1132, 2227]], [[1145, 2227], [1145, 2386], [1159, 2386], [1159, 2227]], [[1198, 2227], [1198, 2386], [1198, 2386], [1198, 2227]], [[1225, 2227], [1225, 2386], [1225, 2386], [1225, 2227]], [[1251, 2227], [1251, 2386], [1264, 2386], [1264, 2227]], [[1304, 2227], [1304, 2386], [1304, 2386], [1304, 2227]], [[1330, 2227], [1330, 2386], [1343, 2386], [1343, 2227]], [[1383, 2227], [1383, 2386], [1383, 2386], [1383, 2227]], [[1409, 2227], [1409, 2386], [1423, 2386], [1423, 2227]], [[1449, 2227], [1449, 2386], [1462, 2386], [1462, 2227]], [[1475, 2227], [1475, 2386], [1475, 2386], [1475, 2227]], [[1489, 2227], [1489, 2386], [1502, 2386], [1502, 2227]], [[1581, 2227], [1581, 2386], [1581, 2386], [1581, 2227]], [[1634, 2227], [1634, 2386], [1647, 2386], [1647, 2227]], [[1687, 2227], [1687, 2386], [1687, 2386], [1687, 2227]], [[1726, 2227], [1726, 2386], [1739, 2386], [1739, 2227]], [[1753, 2227], [1753, 2386], [1766, 2386], [1766, 2227]], [[1805, 2227], [1805, 2386], [1819, 2386], [1819, 2227]], [[1871, 2227], [1871, 2386], [1871, 2386], [1871, 2227]], [[1898, 2227], [1898, 2386], [1911, 2386], [1911, 2227]], [[1937, 2227], [1937, 2386], [1937, 2386], [1937, 2227]], [[1990, 2227], [1990, 2386], [1990, 2386], [1990, 2227]], [[2017, 2227], [2017, 2386], [2030, 2386], [2030, 2227]], [[2069, 2227], [2069, 2386], [2069, 2386], [2069, 2227]], [[2096, 2227], [2096, 2386], [2096, 2386], [2096, 2227]], [[2122, 2227], [2122, 2386], [2135, 2386], [2135, 2227]], [[2162, 2227], [2162, 2386], [2175, 2386], [2175, 2227]], [[2201, 2227], [2201, 2386], [2215, 2386], [2215, 2227]], [[2267, 2227], [2267, 2386], [2267, 2386], [2267, 2227]], [[2307, 2227], [2307, 2386], [2307, 2386], [2307, 2227]], [[2333, 2227], [2333, 2386], [2333, 2386], [2333, 2227]], [[2373, 2227], [2373, 2386], [2373, 2386], [2373, 2227]], [[2399, 2227], [2399, 2386], [2413, 2386], [2413, 2227]], [[2426, 2227], [2426, 2386], [2439, 2386], [2439, 2227]], [[2452, 2227], [2452, 2386], [2465, 2386], [2465, 2227]], [[2492, 2227], [2492, 2386], [2505, 2386], [2505, 2227]], [[2545, 2227], [2545, 2386], [2545, 2386], [2545, 2227]], [[2571, 2227], [2571, 2386], [2571, 2386], [2571, 2227]], [[2597, 2227], [2597, 2386], [2597, 2386], [2597, 2227]], [[2624, 2227], [2624, 2386], [2637, 2386], [2637, 2227]], [[2690, 2227], [2690, 2386], [2703, 2386], [2703, 2227]], [[2729, 2227], [2729, 2386], [2729, 2386], [2729, 2227]], [[2756, 2227], [2756, 2386], [2756, 2386], [2756, 2227]], [[2795, 2227], [2795, 2386], [2795, 2386], [2795, 2227]], [[2822, 2227], [2822, 2386], [2835, 2386], [2835, 2227]], [[2861, 2227], [2861, 2386], [2861, 2386], [2861, 2227]], [[2875, 2227], [2875, 2386], [2875, 2386], [2875, 2227]], [[2927, 2227], [2927, 2386], [2927, 2386], [2927, 2227]], [[2941, 2227], [2941, 2386], [2941, 2386], [2941, 2227]], [[2954, 2227], [2954, 2386], [2967, 2386], [2967, 2227]], [[2993, 2227], [2993, 2386], [3006, 2386], [3006, 2227]], [[3033, 2227], [3033, 2386], [3033, 2386], [3033, 2227]], [[3046, 2227], [3046, 2386], [3059, 2386], [3059, 2227]], [[3099, 2227], [3099, 2386], [3099, 2386], [3099, 2227]], [[3138, 2227], [3138, 2386], [3152, 2386], [3152, 2227]]], "confidences": [0.9823002219200134, 0.9972136616706848, 0.9999020099639893, 0.9994245767593384, 0.9984127283096313, 0.9963169097900391, 0.9989890456199646, 0.8085134625434875, 0.9999579191207886, 0.9999361038208008, 0.9942615032196045, 0.9278876185417175, 0.9998903274536133, 0.999955415725708, 0.9999262094497681, 0.9999407529830933, 0.9999767541885376, 0.9984294772148132, 0.8924553990364075, 0.9984814524650574, 0.9999494552612305, 0.9997356534004211, 0.9999189376831055, 0.9939833283424377, 0.9999998807907104, 0.9999184608459473, 0.9999300241470337, 0.573874831199646, 0.9986876845359802, 0.9068862199783325, 0.9941669702529907, 0.999953031539917, 0.997863233089447, 0.7316004037857056, 0.9987077713012695, 0.9907311797142029, 0.9943513870239258, 0.9962025284767151, 0.9970322847366333, 0.9568605422973633, 0.9899909496307373, 0.8733225464820862, 0.9993151426315308, 0.7688072919845581, 0.9994139671325684, 0.9989153146743774, 0.5057005286216736, 0.9989703893661499, 0.9991733431816101, 0.9928190112113953, 0.9984594583511353, 0.7554934024810791, 0.9958826303482056, 0.9795031547546387, 0.9996199607849121, 0.9911338686943054, 0.9956367611885071, 0.9094319939613342, 0.9807215929031372, 0.9925562739372253, 0.8968151211738586, 0.7551785707473755, 0.9919329285621643, 0.9729509353637695, 0.9989374279975891, 0.9944745898246765, 0.9989200830459595, 0.9993528723716736, 0.9896401166915894, 0.9964278340339661], "line": {"id": "17dc5df6-cefa-422b-a8e4-8f0698a21cc7", "bbox": {"id": "a43b40bb-088e-4263-80ad-eacd02545773", "bbox": [[327, 2227], [327, 2386], [3165, 2386], [3165, 2227]]}, "type": "bbox"}}, {"prediction": ". \u0643\u0644\u0630 \u064a\u0641 \u062a\u0627\u0645\u0641 \u0637\u0648\u0633 \u0629\u0627\u0645\u0633\u0645\u062e", "cuts": [[[2264, 2402], [2264, 2531], [2264, 2531], [2264, 2402]], [[2302, 2402], [2302, 2531], [2302, 2531], [2302, 2402]], [[2340, 2402], [2340, 2531], [2353, 2531], [2353, 2402]], [[2391, 2402], [2391, 2531], [2403, 2531], [2403, 2402]], [[2416, 2402], [2416, 2531], [2429, 2531], [2429, 2402]], [[2454, 2402], [2454, 2531], [2467, 2531], [2467, 2402]], [[2492, 2402], [2492, 2531], [2492, 2531], [2492, 2402]], [[2505, 2402], [2505, 2531], [2505, 2531], [2505, 2402]], [[2543, 2402], [2543, 2531], [2543, 2531], [2543, 2402]], [[2594, 2402], [2594, 2531], [2594, 2531], [2594, 2402]], [[2632, 2402], [2632, 2531], [2632, 2531], [2632, 2402]], [[2644, 2402], [2644, 2531], [2657, 2531], [2657, 2402]], [[2695, 2402], [2695, 2531], [2695, 2531], [2695, 2402]], [[2720, 2402], [2720, 2531], [2733, 2531], [2733, 2402]], [[2771, 2402], [2771, 2531], [2771, 2531], [2771, 2402]], [[2847, 2402], [2847, 2531], [2847, 2531], [2847, 2402]], [[2898, 2402], [2898, 2531], [2898, 2531], [2898, 2402]], [[2936, 2402], [2936, 2531], [2936, 2531], [2936, 2402]], [[2961, 2402], [2961, 2531], [2961, 2531], [2961, 2402]], [[3012, 2402], [3012, 2531], [3012, 2531], [3012, 2402]], [[3025, 2402], [3025, 2531], [3037, 2531], [3037, 2402]], [[3075, 2402], [3075, 2531], [3088, 2531], [3088, 2402]], [[3101, 2402], [3101, 2531], [3101, 2531], [3101, 2402]], [[3126, 2402], [3126, 2531], [3139, 2531], [3139, 2402]]], "confidences": [0.9456377625465393, 0.9999704360961914, 0.9999499320983887, 0.9995260238647461, 0.9999178647994995, 0.9999415874481201, 0.9920778870582581, 0.9997991919517517, 0.9995096921920776, 0.999977707862854, 0.8690007925033569, 0.9649189710617065, 0.9999690055847168, 0.9999949932098389, 0.9907584190368652, 0.9840313196182251, 0.9416587948799133, 0.998769223690033, 0.9915293455123901, 0.967819094657898, 0.9989644289016724, 0.9990310668945312, 0.9262909889221191, 0.9966443777084351], "line": {"id": "ac974425-9af8-4614-9250-c83dab27ee65", "bbox": {"id": "c8d2000e-5c3d-4401-adc6-7597ab2ef2b7", "bbox": [[2264, 2402], [2264, 2531], [3164, 2531], [3164, 2402]]}, "type": "bbox"}}, {"prediction": "\u062d\u0633\u0645 \u064a\u0641 \u0627\u0646\u0628\u0627\u062d\u0654\u0627 \u0646\u064a\u0628 \u0629\u064a\u0627\u0648\u0631\u0644\u0627 \u062a\u0641\u0644\u062a\u062e\u0627 \u0644\u0627\u0642 \u0644\u0636\u0635\u0641\u0644\u0627 \u0646\u0628 \u062f\u0645\u062d \u0646\u0639 \u0644\u064a\u0639\u0627\u0645\u0633\u0627 \u0646\u0628 \u062f\u0645\u0639\u0645 \u0649\u0648\u0631\u0648", "cuts": [[[331, 2564], [331, 2740], [344, 2740], [344, 2564]], [[398, 2564], [398, 2740], [398, 2740], [398, 2564]], [[439, 2564], [439, 2740], [452, 2740], [452, 2564]], [[466, 2564], [466, 2740], [479, 2740], [479, 2564]], [[520, 2564], [520, 2740], [520, 2740], [520, 2564]], [[533, 2564], [533, 2740], [533, 2740], [533, 2564]], [[560, 2564], [560, 2740], [560, 2740], [560, 2564]], [[587, 2564], [587, 2740], [587, 2740], [587, 2564]], [[614, 2564], [614, 2740], [614, 2740], [614, 2564]], [[628, 2564], [628, 2740], [641, 2740], [641, 2564]], [[668, 2564], [668, 2740], [668, 2740], [668, 2564]], [[695, 2564], [695, 2740], [709, 2740], [709, 2564]], [[763, 2564], [763, 2740], [763, 2740], [763, 2564]], [[776, 2564], [776, 2740], [790, 2740], [790, 2564]], [[803, 2564], [803, 2740], [803, 2740], [803, 2564]], [[844, 2564], [844, 2740], [857, 2740], [857, 2564]], [[871, 2564], [871, 2740], [884, 2740], [884, 2564]], [[898, 2564], [898, 2740], [898, 2740], [898, 2564]], [[925, 2564], [925, 2740], [938, 2740], [938, 2564]], [[979, 2564], [979, 2740], [979, 2740], [979, 2564]], [[1006, 2564], [1006, 2740], [1006, 2740], [1006, 2564]], [[1046, 2564], [1046, 2740], [1046, 2740], [1046, 2564]], [[1100, 2564], [1100, 2740], [1100, 2740], [1100, 2564]], [[1141, 2564], [1141, 2740], [1141, 2740], [1141, 2564]], [[1154, 2564], [1154, 2740], [1168, 2740], [1168, 2564]], [[1181, 2564], [1181, 2740], [1181, 2740], [1181, 2564]], [[1195, 2564], [1195, 2740], [1208, 2740], [1208, 2564]], [[1249, 2564], [1249, 2740], [1249, 2740], [1249, 2564]], [[1316, 2564], [1316, 2740], [1316, 2740], [1316, 2564]], [[1343, 2564], [1343, 2740], [1343, 2740], [1343, 2564]], [[1370, 2564], [1370, 2740], [1384, 2740], [1384, 2564]], [[1411, 2564], [1411, 2740], [1424, 2740], [1424, 2564]], [[1465, 2564], [1465, 2740], [1478, 2740], [1478, 2564]], [[1492, 2564], [1492, 2740], [1492, 2740], [1492, 2564]], [[1546, 2564], [1546, 2740], [1559, 2740], [1559, 2564]], [[1573, 2564], [1573, 2740], [1573, 2740], [1573, 2564]], [[1586, 2564], [1586, 2740], [1586, 2740], [1586, 2564]], [[1613, 2564], [1613, 2740], [1627, 2740], [1627, 2564]], [[1667, 2564], [1667, 2740], [1681, 2740], [1681, 2564]], [[1721, 2564], [1721, 2740], [1721, 2740], [1721, 2564]], [[1735, 2564], [1735, 2740], [1735, 2740], [1735, 2564]], [[1775, 2564], [1775, 2740], [1789, 2740], [1789, 2564]], [[1802, 2564], [1802, 2740], [1816, 2740], [1816, 2564]], [[1829, 2564], [1829, 2740], [1829, 2740], [1829, 2564]], [[1843, 2564], [1843, 2740], [1856, 2740], [1856, 2564]], [[1897, 2564], [1897, 2740], [1897, 2740], [1897, 2564]], [[1924, 2564], [1924, 2740], [1924, 2740], [1924, 2564]], [[1951, 2564], [1951, 2740], [1964, 2740], [1964, 2564]], [[2005, 2564], [2005, 2740], [2005, 2740], [2005, 2564]], [[2032, 2564], [2032, 2740], [2045, 2740], [2045, 2564]], [[2059, 2564], [2059, 2740], [2059, 2740], [2059, 2564]], [[2086, 2564], [2086, 2740], [2086, 2740], [2086, 2564]], [[2113, 2564], [2113, 2740], [2113, 2740], [2113, 2564]], [[2153, 2564], [2153, 2740], [2153, 2740], [2153, 2564]], [[2194, 2564], [2194, 2740], [2207, 2740], [2207, 2564]], [[2248, 2564], [2248, 2740], [2248, 2740], [2248, 2564]], [[2275, 2564], [2275, 2740], [2288, 2740], [2288, 2564]], [[2302, 2564], [2302, 2740], [2315, 2740], [2315, 2564]], [[2356, 2564], [2356, 2740], [2356, 2740], [2356, 2564]], [[2369, 2564], [2369, 2740], [2383, 2740], [2383, 2564]], [[2410, 2564], [2410, 2740], [2423, 2740], [2423, 2564]], [[2450, 2564], [2450, 2740], [2450, 2740], [2450, 2564]], [[2464, 2564], [2464, 2740], [2477, 2740], [2477, 2564]], [[2504, 2564], [2504, 2740], [2518, 2740], [2518, 2564]], [[2545, 2564], [2545, 2740], [2545, 2740], [2545, 2564]], [[2572, 2564], [2572, 2740], [2572, 2740], [2572, 2564]], [[2612, 2564], [2612, 2740], [2612, 2740], [2612, 2564]], [[2639, 2564], [2639, 2740], [2653, 2740], [2653, 2564]], [[2666, 2564], [2666, 2740], [2666, 2740], [2666, 2564]], [[2680, 2564], [2680, 2740], [2680, 2740], [2680, 2564]], [[2707, 2564], [2707, 2740], [2720, 2740], [2720, 2564]], [[2761, 2564], [2761, 2740], [2761, 2740], [2761, 2564]], [[2828, 2564], [2828, 2740], [2828, 2740], [2828, 2564]], [[2869, 2564], [2869, 2740], [2882, 2740], [2882, 2564]], [[2923, 2564], [2923, 2740], [2936, 2740], [2936, 2564]]], "confidences": [0.9999998807907104, 0.9929130673408508, 0.9938241243362427, 0.9778679609298706, 0.9086365699768066, 0.654657781124115, 0.9912302494049072, 0.9998319149017334, 0.9415075182914734, 0.9910994172096252, 0.8468749523162842, 0.991156816482544, 0.9946617484092712, 0.9841130375862122, 0.5371823906898499, 0.9606072902679443, 0.9999836683273315, 0.982523500919342, 0.9999951124191284, 0.9997585415840149, 0.9992431402206421, 0.9998185038566589, 0.8639128804206848, 0.999992847442627, 0.9997803568840027, 0.9999289512634277, 0.9999123811721802, 0.8764679431915283, 0.99874347448349, 0.7210342288017273, 0.9998082518577576, 0.9998107552528381, 0.9988790154457092, 0.9528904557228088, 0.9895849823951721, 0.989848792552948, 0.9913495779037476, 0.9971917271614075, 0.9919770359992981, 0.9953035116195679, 0.9654654860496521, 0.956558108329773, 0.968718945980072, 0.9891232848167419, 0.9995982050895691, 0.5353143811225891, 0.9994182586669922, 0.9978064894676208, 0.9980046153068542, 0.9997578263282776, 0.9991948008537292, 0.999717652797699, 0.999285876750946, 0.9232087731361389, 0.9976062774658203, 0.9995843768119812, 0.9938365817070007, 0.998508870601654, 0.8162130117416382, 0.9771808385848999, 0.9998940229415894, 0.9971707463264465, 0.9978471994400024, 0.9858810901641846, 0.938630223274231, 0.9977301955223083, 0.9997122883796692, 0.99797123670578, 0.5294272303581238, 0.9924471378326416, 0.9994334578514099, 0.9998548030853271, 0.999200165271759, 0.9990027546882629, 0.9875484704971313], "line": {"id": "0fff3cfd-bf34-4a3f-9116-b5b5510ee6db", "bbox": {"id": "b994b599-c779-44fd-a687-82f7c6ed4464", "bbox": [[317, 2564], [317, 2740], [2950, 2740], [2950, 2564]]}, "type": "bbox"}}, {"prediction": "\u0639\u0628\u0627\u0640\u0635\u0654\u0627\u0644\u0627 \u0649\u0644\u0627 \u0646\u064a\u0628\u0639\u0643\u0644\u0627 \u0646\u0645 \u0645\u0654\u0627 \u0646\u064a\u0628\u0639\u0643\u0644\u0627 \u0649\u0644\u0627 \u0639\u0628\u0627\u0635\u0654\u0627\u0644\u0627 \u0646\u0645 \u0648\u0645\u0654\u0627 \u0621\u0648\u0636\u0648\u0644\u0627 \u0641 \u0646\u064a\u0644\u062c\u0631\u0644\u0627", "cuts": [[[335, 2733], [335, 2890], [335, 2890], [335, 2733]], [[396, 2733], [396, 2890], [396, 2890], [396, 2733]], [[442, 2733], [442, 2890], [442, 2890], [442, 2733]], [[488, 2733], [488, 2890], [519, 2890], [519, 2733]], [[611, 2733], [611, 2890], [611, 2890], [611, 2733]], [[657, 2733], [657, 2890], [657, 2890], [657, 2733]], [[673, 2733], [673, 2890], [673, 2890], [673, 2733]], [[688, 2733], [688, 2890], [704, 2890], [704, 2733]], [[719, 2733], [719, 2890], [734, 2890], [734, 2733]], [[750, 2733], [750, 2890], [750, 2890], [750, 2733]], [[796, 2733], [796, 2890], [796, 2890], [796, 2733]], [[811, 2733], [811, 2890], [827, 2890], [827, 2733]], [[842, 2733], [842, 2890], [842, 2890], [842, 2733]], [[857, 2733], [857, 2890], [873, 2890], [873, 2733]], [[904, 2733], [904, 2890], [904, 2890], [904, 2733]], [[934, 2733], [934, 2890], [934, 2890], [934, 2733]], [[965, 2733], [965, 2890], [965, 2890], [965, 2733]], [[996, 2733], [996, 2890], [996, 2890], [996, 2733]], [[1057, 2733], [1057, 2890], [1057, 2890], [1057, 2733]], [[1119, 2733], [1119, 2890], [1134, 2890], [1134, 2733]], [[1150, 2733], [1150, 2890], [1150, 2890], [1150, 2733]], [[1165, 2733], [1165, 2890], [1180, 2890], [1180, 2733]], [[1288, 2733], [1288, 2890], [1303, 2890], [1303, 2733]], [[1380, 2733], [1380, 2890], [1380, 2890], [1380, 2733]], [[1411, 2733], [1411, 2890], [1426, 2890], [1426, 2733]], [[1442, 2733], [1442, 2890], [1457, 2890], [1457, 2733]], [[1473, 2733], [1473, 2890], [1473, 2890], [1473, 2733]], [[1488, 2733], [1488, 2890], [1503, 2890], [1503, 2733]], [[1519, 2733], [1519, 2890], [1534, 2890], [1534, 2733]], [[1565, 2733], [1565, 2890], [1565, 2890], [1565, 2733]], [[1580, 2733], [1580, 2890], [1596, 2890], [1596, 2733]], [[1626, 2733], [1626, 2890], [1626, 2890], [1626, 2733]], [[1657, 2733], [1657, 2890], [1657, 2890], [1657, 2733]], [[1703, 2733], [1703, 2890], [1719, 2890], [1719, 2733]], [[1780, 2733], [1780, 2890], [1780, 2890], [1780, 2733]], [[1796, 2733], [1796, 2890], [1796, 2890], [1796, 2733]], [[1811, 2733], [1811, 2890], [1826, 2890], [1826, 2733]], [[1857, 2733], [1857, 2890], [1872, 2890], [1872, 2733]], [[1888, 2733], [1888, 2890], [1888, 2890], [1888, 2733]], [[1903, 2733], [1903, 2890], [1919, 2890], [1919, 2733]], [[1934, 2733], [1934, 2890], [1934, 2890], [1934, 2733]], [[1965, 2733], [1965, 2890], [1965, 2890], [1965, 2733]], [[2042, 2733], [2042, 2890], [2042, 2890], [2042, 2733]], [[2072, 2733], [2072, 2890], [2072, 2890], [2072, 2733]], [[2118, 2733], [2118, 2890], [2134, 2890], [2134, 2733]], [[2165, 2733], [2165, 2890], [2180, 2890], [2180, 2733]], [[2195, 2733], [2195, 2890], [2195, 2890], [2195, 2733]], [[2211, 2733], [2211, 2890], [2211, 2890], [2211, 2733]], [[2226, 2733], [2226, 2890], [2241, 2890], [2241, 2733]], [[2257, 2733], [2257, 2890], [2272, 2890], [2272, 2733]], [[2318, 2733], [2318, 2890], [2318, 2890], [2318, 2733]], [[2349, 2733], [2349, 2890], [2349, 2890], [2349, 2733]], [[2380, 2733], [2380, 2890], [2395, 2890], [2395, 2733]], [[2426, 2733], [2426, 2890], [2441, 2890], [2441, 2733]], [[2472, 2733], [2472, 2890], [2472, 2890], [2472, 2733]], [[2503, 2733], [2503, 2890], [2503, 2890], [2503, 2733]], [[2518, 2733], [2518, 2890], [2534, 2890], [2534, 2733]], [[2549, 2733], [2549, 2890], [2549, 2890], [2549, 2733]], [[2580, 2733], [2580, 2890], [2580, 2890], [2580, 2733]], [[2626, 2733], [2626, 2890], [2641, 2890], [2641, 2733]], [[2687, 2733], [2687, 2890], [2687, 2890], [2687, 2733]], [[2749, 2733], [2749, 2890], [2749, 2890], [2749, 2733]], [[2780, 2733], [2780, 2890], [2780, 2890], [2780, 2733]], [[2795, 2733], [2795, 2890], [2811, 2890], [2811, 2733]], [[2826, 2733], [2826, 2890], [2826, 2890], [2826, 2733]], [[2872, 2733], [2872, 2890], [2887, 2890], [2887, 2733]], [[2918, 2733], [2918, 2890], [2918, 2890], [2918, 2733]], [[2949, 2733], [2949, 2890], [2949, 2890], [2949, 2733]], [[2980, 2733], [2980, 2890], [2980, 2890], [2980, 2733]], [[3010, 2733], [3010, 2890], [3010, 2890], [3010, 2733]], [[3041, 2733], [3041, 2890], [3041, 2890], [3041, 2733]], [[3103, 2733], [3103, 2890], [3103, 2890], [3103, 2733]], [[3118, 2733], [3118, 2890], [3133, 2890], [3133, 2733]], [[3149, 2733], [3149, 2890], [3149, 2890], [3149, 2733]]], "confidences": [0.9999692440032959, 0.7431797385215759, 0.9892282485961914, 0.9999510049819946, 0.9351767897605896, 0.9993116855621338, 0.9982556700706482, 0.9992502331733704, 0.9992731213569641, 0.9999591112136841, 0.9965296387672424, 0.9999610185623169, 0.9995737671852112, 0.9993835687637329, 0.987348198890686, 0.7714233994483948, 0.9989540576934814, 0.9985584616661072, 0.9998059868812561, 0.9927629828453064, 0.9998949766159058, 0.9996819496154785, 0.9967392086982727, 0.999603807926178, 0.9831695556640625, 0.9224259257316589, 0.9802579283714294, 0.9334492683410645, 0.9981402158737183, 0.9999240636825562, 0.986011266708374, 0.9975579977035522, 0.9953357577323914, 0.9803675413131714, 0.9984625577926636, 0.9977602958679199, 0.9838416576385498, 0.9906837940216064, 0.9881343245506287, 0.9841576814651489, 0.9998107552528381, 0.9991499185562134, 0.9434433579444885, 0.9854111671447754, 0.8520838618278503, 0.9964721202850342, 0.9900435209274292, 0.9503771066665649, 0.9993019104003906, 0.9988412261009216, 0.99639892578125, 0.8105504512786865, 0.9876804351806641, 0.9993699193000793, 0.921359121799469, 0.9604810476303101, 0.995456337928772, 0.9368835687637329, 0.9467335343360901, 0.9949937462806702, 0.999894380569458, 0.9988865256309509, 0.9930035471916199, 0.9975302815437317, 0.9954912066459656, 0.824739933013916, 0.9803107380867004, 0.98244708776474, 0.9932101368904114, 0.9977071285247803, 0.5027340054512024, 0.9766203165054321, 0.9856435060501099, 0.9869322180747986], "line": {"id": "1a39d194-b0dc-457d-bee7-a462a1a46aa4", "bbox": {"id": "c990a4e6-7864-4f81-95fd-21d5e2521022", "bbox": [[319, 2733], [319, 2890], [3149, 2890], [3149, 2733]]}, "type": "bbox"}}, {"prediction": "\u062f\u0642 \u0627\u0646\u0628\u0627\u062d\u0635\u0654\u0627 \u0646\u0627 \u0643\u0627\u062f\u0641 \u062a\u0644\u0639\u062c \u0645\u0627\u0644\u0633\u0644\u0627 \u0647\u064a\u0644\u0639 \u0649\u0633\u0648\u0645 \u0646\u0633\u062d\u0644\u0627 \u064a\u0627 \u0649\u0644\u0627 \u0646\u064a\u0637\u0642\u064a \u0646\u0628 \u0649\u0644\u0639 \u0628\u062a\u0643\u0641", "cuts": [[[330, 2903], [330, 3080], [343, 3080], [343, 2903]], [[407, 2903], [407, 3080], [407, 3080], [407, 2903]], [[433, 2903], [433, 3080], [446, 3080], [446, 2903]], [[459, 2903], [459, 3080], [472, 3080], [472, 2903]], [[485, 2903], [485, 3080], [497, 3080], [497, 2903]], [[510, 2903], [510, 3080], [510, 3080], [510, 2903]], [[549, 2903], [549, 3080], [549, 3080], [549, 2903]], [[575, 2903], [575, 3080], [588, 3080], [588, 2903]], [[613, 2903], [613, 3080], [613, 3080], [613, 2903]], [[639, 2903], [639, 3080], [652, 3080], [652, 2903]], [[665, 2903], [665, 3080], [665, 3080], [665, 2903]], [[678, 2903], [678, 3080], [691, 3080], [691, 2903]], [[716, 2903], [716, 3080], [729, 3080], [729, 2903]], [[768, 2903], [768, 3080], [768, 3080], [768, 2903]], [[781, 2903], [781, 3080], [807, 3080], [807, 2903]], [[845, 2903], [845, 3080], [845, 3080], [845, 2903]], [[897, 2903], [897, 3080], [897, 3080], [897, 2903]], [[935, 2903], [935, 3080], [948, 3080], [948, 2903]], [[974, 2903], [974, 3080], [974, 3080], [974, 2903]], [[1000, 2903], [1000, 3080], [1013, 3080], [1013, 2903]], [[1051, 2903], [1051, 3080], [1051, 3080], [1051, 2903]], [[1103, 2903], [1103, 3080], [1116, 3080], [1116, 2903]], [[1142, 2903], [1142, 3080], [1142, 3080], [1142, 2903]], [[1180, 2903], [1180, 3080], [1193, 3080], [1193, 2903]], [[1232, 2903], [1232, 3080], [1245, 3080], [1245, 2903]], [[1283, 2903], [1283, 3080], [1296, 3080], [1296, 2903]], [[1335, 2903], [1335, 3080], [1348, 3080], [1348, 2903]], [[1360, 2903], [1360, 3080], [1373, 3080], [1373, 2903]], [[1412, 2903], [1412, 3080], [1425, 3080], [1425, 2903]], [[1438, 2903], [1438, 3080], [1451, 3080], [1451, 2903]], [[1464, 2903], [1464, 3080], [1464, 3080], [1464, 2903]], [[1476, 2903], [1476, 3080], [1489, 3080], [1489, 2903]], [[1502, 2903], [1502, 3080], [1515, 3080], [1515, 2903]], [[1541, 2903], [1541, 3080], [1554, 3080], [1554, 2903]], [[1567, 2903], [1567, 3080], [1579, 3080], [1579, 2903]], [[1592, 2903], [1592, 3080], [1605, 3080], [1605, 2903]], [[1644, 2903], [1644, 3080], [1657, 3080], [1657, 2903]], [[1708, 2903], [1708, 3080], [1708, 3080], [1708, 2903]], [[1747, 2903], [1747, 3080], [1747, 3080], [1747, 2903]], [[1798, 2903], [1798, 3080], [1811, 3080], [1811, 2903]], [[1837, 2903], [1837, 3080], [1837, 3080], [1837, 2903]], [[1863, 2903], [1863, 3080], [1876, 3080], [1876, 2903]], [[1927, 2903], [1927, 3080], [1927, 3080], [1927, 2903]], [[1966, 2903], [1966, 3080], [1979, 3080], [1979, 2903]], [[2017, 2903], [2017, 3080], [2017, 3080], [2017, 2903]], [[2030, 2903], [2030, 3080], [2043, 3080], [2043, 2903]], [[2069, 2903], [2069, 3080], [2082, 3080], [2082, 2903]], [[2095, 2903], [2095, 3080], [2108, 3080], [2108, 2903]], [[2146, 2903], [2146, 3080], [2146, 3080], [2146, 2903]], [[2198, 2903], [2198, 3080], [2211, 3080], [2211, 2903]], [[2223, 2903], [2223, 3080], [2223, 3080], [2223, 2903]], [[2262, 2903], [2262, 3080], [2262, 3080], [2262, 2903]], [[2275, 2903], [2275, 3080], [2288, 3080], [2288, 2903]], [[2301, 2903], [2301, 3080], [2314, 3080], [2314, 2903]], [[2326, 2903], [2326, 3080], [2339, 3080], [2339, 2903]], [[2365, 2903], [2365, 3080], [2378, 3080], [2378, 2903]], [[2404, 2903], [2404, 3080], [2404, 3080], [2404, 2903]], [[2430, 2903], [2430, 3080], [2430, 3080], [2430, 2903]], [[2494, 2903], [2494, 3080], [2494, 3080], [2494, 2903]], [[2520, 2903], [2520, 3080], [2520, 3080], [2520, 2903]], [[2545, 2903], [2545, 3080], [2558, 3080], [2558, 2903]], [[2623, 2903], [2623, 3080], [2623, 3080], [2623, 2903]], [[2649, 2903], [2649, 3080], [2649, 3080], [2649, 2903]], [[2674, 2903], [2674, 3080], [2687, 3080], [2687, 2903]], [[2752, 2903], [2752, 3080], [2752, 3080], [2752, 2903]], [[2764, 2903], [2764, 3080], [2777, 3080], [2777, 2903]], [[2790, 2903], [2790, 3080], [2803, 3080], [2803, 2903]], [[2829, 2903], [2829, 3080], [2842, 3080], [2842, 2903]], [[2893, 2903], [2893, 3080], [2893, 3080], [2893, 2903]], [[2945, 2903], [2945, 3080], [2945, 3080], [2945, 2903]], [[2983, 2903], [2983, 3080], [2983, 3080], [2983, 2903]], [[3138, 2903], [3138, 3080], [3138, 3080], [3138, 2903]]], "confidences": [0.9998844861984253, 0.7102985382080078, 0.9393450021743774, 0.9997884631156921, 0.999976634979248, 0.9990993738174438, 0.9997594952583313, 0.9806336760520935, 0.9991434812545776, 0.999870777130127, 0.999963641166687, 0.9986358284950256, 0.9997819066047668, 0.9902760982513428, 0.9999171495437622, 0.870272159576416, 0.9959033131599426, 0.9999700784683228, 0.9983206391334534, 0.9999897480010986, 0.9983596205711365, 0.9999958276748657, 0.7078837156295776, 0.9993500113487244, 0.9999644756317139, 0.9912440776824951, 0.9898048639297485, 0.9963675737380981, 0.9999985694885254, 0.9996073842048645, 0.9969272017478943, 0.9999234676361084, 0.9996798038482666, 0.9999964237213135, 0.9991588592529297, 0.9990678429603577, 0.9999243021011353, 0.9791718125343323, 0.8421644568443298, 0.9974477291107178, 0.7698737978935242, 0.9559314250946045, 0.9906162619590759, 0.9658488631248474, 0.9968318343162537, 0.9971415400505066, 0.9973932504653931, 0.99928218126297, 0.9969269633293152, 0.9991139769554138, 0.994012176990509, 0.880149781703949, 0.9992927312850952, 0.9991368651390076, 0.9945836663246155, 0.9022614359855652, 0.9830822944641113, 0.9845501780509949, 0.9880629777908325, 0.9992579817771912, 0.9978712797164917, 0.9982649683952332, 0.996843695640564, 0.9984459280967712, 0.9971120357513428, 0.9952387809753418, 0.9999581575393677, 0.9986611604690552, 0.9999819993972778, 0.9977983832359314, 0.5938760638237, 0.49053633213043213], "line": {"id": "bf9737fe-552e-4ece-9185-1692f325de6a", "bbox": {"id": "e8c7a301-87be-4cbb-b4cd-ca098110a82c", "bbox": [[317, 2903], [317, 3080], [3151, 3080], [3151, 2903]]}, "type": "bbox"}}, {"prediction": "\u0646\u0655\u0627 \u062a\u0644\u0639\u0641 \u0647\u064a\u0644\u0639 \u0649\u0644\u0645\u0639 \u0646\u0648\u0643\u064a \u0627\u0645 \u0643\u0637\u062e\u0628 \u0649\u0644\u0627 \u0628\u062a\u0643\u062a \u0646\u0654\u0627 \u062a\u064a\u0654\u0627\u0631 \u0646\u0627\u0641 \u0646\u064a\u0644\u062c\u0631\u0644\u0627 \u062d\u0633\u0645 \u064a\u0641 \u0627\u0648\u0641\u0644\u062a\u062e\u0627", "cuts": [[[328, 3067], [328, 3246], [342, 3246], [342, 3067]], [[357, 3067], [357, 3246], [357, 3246], [357, 3067]], [[371, 3067], [371, 3246], [386, 3246], [386, 3067]], [[400, 3067], [400, 3246], [400, 3246], [400, 3067]], [[444, 3067], [444, 3246], [444, 3246], [444, 3067]], [[502, 3067], [502, 3246], [502, 3246], [502, 3067]], [[531, 3067], [531, 3246], [531, 3246], [531, 3067]], [[560, 3067], [560, 3246], [560, 3246], [560, 3067]], [[589, 3067], [589, 3246], [589, 3246], [589, 3067]], [[604, 3067], [604, 3246], [618, 3246], [618, 3067]], [[647, 3067], [647, 3246], [647, 3246], [647, 3067]], [[676, 3067], [676, 3246], [676, 3246], [676, 3067]], [[691, 3067], [691, 3246], [705, 3246], [705, 3067]], [[735, 3067], [735, 3246], [749, 3246], [749, 3067]], [[822, 3067], [822, 3246], [822, 3246], [822, 3067]], [[836, 3067], [836, 3246], [836, 3246], [836, 3067]], [[851, 3067], [851, 3246], [851, 3246], [851, 3067]], [[880, 3067], [880, 3246], [880, 3246], [880, 3067]], [[923, 3067], [923, 3246], [938, 3246], [938, 3067]], [[967, 3067], [967, 3246], [967, 3246], [967, 3067]], [[1025, 3067], [1025, 3246], [1025, 3246], [1025, 3067]], [[1054, 3067], [1054, 3246], [1054, 3246], [1054, 3067]], [[1098, 3067], [1098, 3246], [1098, 3246], [1098, 3067]], [[1127, 3067], [1127, 3246], [1141, 3246], [1141, 3067]], [[1170, 3067], [1170, 3246], [1185, 3246], [1185, 3067]], [[1200, 3067], [1200, 3246], [1214, 3246], [1214, 3067]], [[1243, 3067], [1243, 3246], [1243, 3246], [1243, 3067]], [[1301, 3067], [1301, 3246], [1301, 3246], [1301, 3067]], [[1359, 3067], [1359, 3246], [1359, 3246], [1359, 3067]], [[1403, 3067], [1403, 3246], [1403, 3246], [1403, 3067]], [[1432, 3067], [1432, 3246], [1432, 3246], [1432, 3067]], [[1461, 3067], [1461, 3246], [1476, 3246], [1476, 3067]], [[1548, 3067], [1548, 3246], [1548, 3246], [1548, 3067]], [[1563, 3067], [1563, 3246], [1563, 3246], [1563, 3067]], [[1577, 3067], [1577, 3246], [1592, 3246], [1592, 3067]], [[1606, 3067], [1606, 3246], [1621, 3246], [1621, 3067]], [[1665, 3067], [1665, 3246], [1665, 3246], [1665, 3067]], [[1723, 3067], [1723, 3246], [1723, 3246], [1723, 3067]], [[1781, 3067], [1781, 3246], [1781, 3246], [1781, 3067]], [[1839, 3067], [1839, 3246], [1839, 3246], [1839, 3067]], [[1868, 3067], [1868, 3246], [1883, 3246], [1883, 3067]], [[1926, 3067], [1926, 3246], [1941, 3246], [1941, 3067]], [[1970, 3067], [1970, 3246], [1970, 3246], [1970, 3067]], [[1984, 3067], [1984, 3246], [1984, 3246], [1984, 3067]], [[1999, 3067], [1999, 3246], [2013, 3246], [2013, 3067]], [[2057, 3067], [2057, 3246], [2057, 3246], [2057, 3067]], [[2115, 3067], [2115, 3246], [2115, 3246], [2115, 3067]], [[2130, 3067], [2130, 3246], [2144, 3246], [2144, 3067]], [[2159, 3067], [2159, 3246], [2159, 3246], [2159, 3067]], [[2202, 3067], [2202, 3246], [2202, 3246], [2202, 3067]], [[2231, 3067], [2231, 3246], [2231, 3246], [2231, 3067]], [[2260, 3067], [2260, 3246], [2275, 3246], [2275, 3067]], [[2304, 3067], [2304, 3246], [2304, 3246], [2304, 3067]], [[2319, 3067], [2319, 3246], [2319, 3246], [2319, 3067]], [[2348, 3067], [2348, 3246], [2362, 3246], [2362, 3067]], [[2391, 3067], [2391, 3246], [2391, 3246], [2391, 3067]], [[2420, 3067], [2420, 3246], [2420, 3246], [2420, 3067]], [[2449, 3067], [2449, 3246], [2449, 3246], [2449, 3067]], [[2478, 3067], [2478, 3246], [2493, 3246], [2493, 3067]], [[2537, 3067], [2537, 3246], [2551, 3246], [2551, 3067]], [[2566, 3067], [2566, 3246], [2566, 3246], [2566, 3067]], [[2580, 3067], [2580, 3246], [2595, 3246], [2595, 3067]], [[2609, 3067], [2609, 3246], [2624, 3246], [2624, 3067]], [[2653, 3067], [2653, 3246], [2653, 3246], [2653, 3067]], [[2711, 3067], [2711, 3246], [2711, 3246], [2711, 3067]], [[2755, 3067], [2755, 3246], [2755, 3246], [2755, 3067]], [[2769, 3067], [2769, 3246], [2784, 3246], [2784, 3067]], [[2827, 3067], [2827, 3246], [2827, 3246], [2827, 3067]], [[2842, 3067], [2842, 3246], [2842, 3246], [2842, 3067]], [[2871, 3067], [2871, 3246], [2871, 3246], [2871, 3067]], [[2900, 3067], [2900, 3246], [2900, 3246], [2900, 3067]], [[2943, 3067], [2943, 3246], [2958, 3246], [2958, 3067]], [[2987, 3067], [2987, 3246], [3002, 3246], [3002, 3067]], [[3016, 3067], [3016, 3246], [3016, 3246], [3016, 3067]], [[3045, 3067], [3045, 3246], [3045, 3246], [3045, 3067]], [[3089, 3067], [3089, 3246], [3089, 3246], [3089, 3067]], [[3132, 3067], [3132, 3246], [3147, 3246], [3147, 3067]]], "confidences": [0.9998571872711182, 0.9720284938812256, 0.9996850490570068, 0.9983483552932739, 0.9983503818511963, 0.9421389102935791, 0.9757440090179443, 0.9998825788497925, 0.995537281036377, 0.9998735189437866, 0.979082465171814, 0.9991683959960938, 0.8312473297119141, 0.9998272061347961, 0.9998739957809448, 0.9941480159759521, 0.6946954131126404, 0.9902309775352478, 0.99941086769104, 0.9953650236129761, 0.9976621866226196, 0.9999786615371704, 0.6234442591667175, 0.9998052716255188, 0.9999556541442871, 0.9997043013572693, 0.9890322089195251, 0.9971346855163574, 0.9940102100372314, 0.9995328187942505, 0.999966025352478, 0.7640908360481262, 0.4993588328361511, 0.9998918771743774, 0.9999830722808838, 0.9678281545639038, 0.9903719425201416, 0.9858986139297485, 0.6294997930526733, 0.971433699131012, 0.9831180572509766, 0.9937307834625244, 0.9823363423347473, 0.9849645495414734, 0.9930417537689209, 0.9968459010124207, 0.9991890788078308, 0.9997627139091492, 0.9964436888694763, 0.8590208292007446, 0.9995020627975464, 0.9917978048324585, 0.9393941760063171, 0.9994710087776184, 0.9996200799942017, 0.6683350205421448, 0.7901033759117126, 0.9907926917076111, 0.9704153537750244, 0.9975791573524475, 0.999360978603363, 0.9991258978843689, 0.9997497200965881, 0.8905563354492188, 0.9997245669364929, 0.9991188645362854, 0.9998225569725037, 0.9893819093704224, 0.997583270072937, 0.9980733394622803, 0.9992850422859192, 0.9999197721481323, 0.9919565916061401, 0.9748324751853943, 0.8973613381385803, 0.9983094930648804, 0.9631760120391846], "line": {"id": "73f026fc-a2a6-43c9-9ad2-fc1ca3a13b49", "bbox": {"id": "e5df36d3-31b0-4a3c-ba0c-0a3f00af668b", "bbox": [[313, 3067], [313, 3246], [3147, 3246], [3147, 3067]]}, "type": "bbox"}}, {"prediction": "\u0621\u0648\u0636\u0648\u0644\u0627 \u064a\u0641 \u0641\u0627\u0644\u062a\u062e\u0655\u0627\u0644\u0627 \u0646\u0645 \u062a\u0631\u0643\u0630 \u0627\u0645 \u062a\u0645\u0647\u0641 \u060c \u0639. \u0646\u0633\u062d\u0644\u0627 \u0648\u0628\u0654\u0627 \u0647\u064a\u0644\u0627 \u0628\u062a\u0643\u0642 \u0649\u0644\u0627\u0639\u062a \u0647\u062a\u0627 \u0621\u0627\u0634", "cuts": [[[318, 3226], [318, 3412], [318, 3412], [318, 3226]], [[375, 3226], [375, 3412], [375, 3412], [375, 3226]], [[418, 3226], [418, 3412], [418, 3412], [418, 3226]], [[489, 3226], [489, 3412], [503, 3412], [503, 3226]], [[517, 3226], [517, 3412], [517, 3412], [517, 3226]], [[546, 3226], [546, 3412], [546, 3412], [546, 3226]], [[560, 3226], [560, 3412], [574, 3412], [574, 3226]], [[603, 3226], [603, 3412], [603, 3412], [603, 3226]], [[617, 3226], [617, 3412], [617, 3412], [617, 3226]], [[646, 3226], [646, 3412], [660, 3412], [660, 3226]], [[745, 3226], [745, 3412], [745, 3412], [745, 3226]], [[774, 3226], [774, 3412], [788, 3412], [788, 3226]], [[802, 3226], [802, 3412], [802, 3412], [802, 3226]], [[831, 3226], [831, 3412], [831, 3412], [831, 3226]], [[873, 3226], [873, 3412], [888, 3412], [888, 3226]], [[930, 3226], [930, 3412], [930, 3412], [930, 3226]], [[945, 3226], [945, 3412], [945, 3412], [945, 3226]], [[959, 3226], [959, 3412], [973, 3412], [973, 3226]], [[987, 3226], [987, 3412], [1001, 3412], [1001, 3226]], [[1016, 3226], [1016, 3412], [1030, 3412], [1030, 3226]], [[1101, 3226], [1101, 3412], [1101, 3412], [1101, 3226]], [[1130, 3226], [1130, 3412], [1144, 3412], [1144, 3226]], [[1172, 3226], [1172, 3412], [1172, 3412], [1172, 3226]], [[1215, 3226], [1215, 3412], [1215, 3412], [1215, 3226]], [[1286, 3226], [1286, 3412], [1286, 3412], [1286, 3226]], [[1315, 3226], [1315, 3412], [1315, 3412], [1315, 3226]], [[1386, 3226], [1386, 3412], [1386, 3412], [1386, 3226]], [[1414, 3226], [1414, 3412], [1443, 3412], [1443, 3226]], [[1471, 3226], [1471, 3412], [1471, 3412], [1471, 3226]], [[1500, 3226], [1500, 3412], [1500, 3412], [1500, 3226]], [[1528, 3226], [1528, 3412], [1542, 3412], [1542, 3226]], [[1585, 3226], [1585, 3412], [1585, 3412], [1585, 3226]], [[1642, 3226], [1642, 3412], [1642, 3412], [1642, 3226]], [[1685, 3226], [1685, 3412], [1685, 3412], [1685, 3226]], [[1727, 3226], [1727, 3412], [1727, 3412], [1727, 3226]], [[1756, 3226], [1756, 3412], [1770, 3412], [1770, 3226]], [[1799, 3226], [1799, 3412], [1799, 3412], [1799, 3226]], [[1827, 3226], [1827, 3412], [1827, 3412], [1827, 3226]], [[1870, 3226], [1870, 3412], [1870, 3412], [1870, 3226]], [[1941, 3226], [1941, 3412], [1941, 3412], [1941, 3226]], [[1969, 3226], [1969, 3412], [1984, 3412], [1984, 3226]], [[2026, 3226], [2026, 3412], [2026, 3412], [2026, 3226]], [[2069, 3226], [2069, 3412], [2083, 3412], [2083, 3226]], [[2112, 3226], [2112, 3412], [2112, 3412], [2112, 3226]], [[2126, 3226], [2126, 3412], [2140, 3412], [2140, 3226]], [[2169, 3226], [2169, 3412], [2183, 3412], [2183, 3226]], [[2197, 3226], [2197, 3412], [2211, 3412], [2211, 3226]], [[2240, 3226], [2240, 3412], [2254, 3412], [2254, 3226]], [[2268, 3226], [2268, 3412], [2268, 3412], [2268, 3226]], [[2297, 3226], [2297, 3412], [2297, 3412], [2297, 3226]], [[2311, 3226], [2311, 3412], [2311, 3412], [2311, 3226]], [[2339, 3226], [2339, 3412], [2354, 3412], [2354, 3226]], [[2382, 3226], [2382, 3412], [2382, 3412], [2382, 3226]], [[2411, 3226], [2411, 3412], [2411, 3412], [2411, 3226]], [[2439, 3226], [2439, 3412], [2439, 3412], [2439, 3226]], [[2453, 3226], [2453, 3412], [2468, 3412], [2468, 3226]], [[2482, 3226], [2482, 3412], [2482, 3412], [2482, 3226]], [[2539, 3226], [2539, 3412], [2539, 3412], [2539, 3226]], [[2596, 3226], [2596, 3412], [2596, 3412], [2596, 3226]], [[2638, 3226], [2638, 3412], [2638, 3412], [2638, 3226]], [[2681, 3226], [2681, 3412], [2681, 3412], [2681, 3226]], [[2710, 3226], [2710, 3412], [2724, 3412], [2724, 3226]], [[2766, 3226], [2766, 3412], [2766, 3412], [2766, 3226]], [[2781, 3226], [2781, 3412], [2781, 3412], [2781, 3226]], [[2795, 3226], [2795, 3412], [2809, 3412], [2809, 3226]], [[2823, 3226], [2823, 3412], [2838, 3412], [2838, 3226]], [[2866, 3226], [2866, 3412], [2866, 3412], [2866, 3226]], [[2880, 3226], [2880, 3412], [2895, 3412], [2895, 3226]], [[2923, 3226], [2923, 3412], [2923, 3412], [2923, 3226]], [[2952, 3226], [2952, 3412], [2952, 3412], [2952, 3226]], [[2994, 3226], [2994, 3412], [2994, 3412], [2994, 3226]], [[3008, 3226], [3008, 3412], [3023, 3412], [3023, 3226]], [[3051, 3226], [3051, 3412], [3051, 3412], [3051, 3226]], [[3080, 3226], [3080, 3412], [3094, 3412], [3094, 3226]], [[3137, 3226], [3137, 3412], [3137, 3412], [3137, 3226]]], "confidences": [0.9989814162254333, 0.9999122619628906, 0.9998594522476196, 0.9999371767044067, 0.9949018955230713, 0.8918391466140747, 0.9331813454627991, 0.9998503923416138, 0.999738872051239, 0.9999352693557739, 0.9999396800994873, 0.9997422099113464, 0.9998968839645386, 0.999890923500061, 0.9986469149589539, 0.9979827404022217, 0.9983227849006653, 0.9980936646461487, 0.9998340606689453, 0.9999415874481201, 0.9813975095748901, 0.9996932744979858, 0.9956713914871216, 0.9996687173843384, 0.9994198083877563, 0.732080340385437, 0.840719997882843, 0.9999840259552002, 0.9996098875999451, 0.9923226237297058, 0.9988951086997986, 0.5162858963012695, 0.5562438368797302, 0.799801766872406, 0.9747372269630432, 0.9793431758880615, 0.9933470487594604, 0.9947622418403625, 0.9987406134605408, 0.9820605516433716, 0.9616630673408508, 0.9937961101531982, 0.9977521300315857, 0.996338963508606, 0.9950332641601562, 0.8663762807846069, 0.9994631409645081, 0.9986950755119324, 0.8317818641662598, 0.9994366765022278, 0.9611929655075073, 0.9998297691345215, 0.9822288751602173, 0.9975958466529846, 0.9939688444137573, 0.9993873834609985, 0.998565137386322, 0.9918579459190369, 0.9916180968284607, 0.9998064637184143, 0.8910688161849976, 0.9938943982124329, 0.9996296167373657, 0.9683365821838379, 0.8659057021141052, 0.999840497970581, 0.9998772144317627, 0.9978265166282654, 0.9994264841079712, 0.9970901012420654, 0.9989050626754761, 0.9994168281555176, 0.9968239068984985, 0.9972999691963196, 0.8729744553565979], "line": {"id": "3b529273-591b-4f91-98ef-3ee8753dbb54", "bbox": {"id": "3bb7ac42-61d6-4a41-8c37-8b6403c24061", "bbox": [[318, 3226], [318, 3412], [3151, 3412], [3151, 3226]]}, "type": "bbox"}}, {"prediction": "\u0644\u063a\u062a\u0648 \u0627\u0654\u064a\u0627\u0644\u062b \u0643\u0647\u062c\u0648 \u0644\u0633\u063a\u062a\u0648 \u0627\u062b\u0627\u0644\u062b \u0642\u0634\u0646\u062a\u0633\u062a\u0648 \u0627\u0627\u0644\u062b \u0636\u0645\u0636\u0645\u062a \u0646\u0654\u0627 \u0643\u0644\u0630 \u064a\u0641 \u0647\u0628\u0643\u0631\u0645\u0653\u0627 \u0649\u0630\u0644\u0627\u0648", "cuts": [[[340, 3404], [340, 3582], [369, 3582], [369, 3404]], [[398, 3404], [398, 3582], [398, 3582], [398, 3404]], [[441, 3404], [441, 3582], [441, 3582], [441, 3404]], [[484, 3404], [484, 3582], [498, 3582], [498, 3404]], [[513, 3404], [513, 3582], [527, 3582], [527, 3404]], [[556, 3404], [556, 3582], [556, 3582], [556, 3404]], [[570, 3404], [570, 3582], [570, 3582], [570, 3404]], [[585, 3404], [585, 3582], [585, 3582], [585, 3404]], [[613, 3404], [613, 3582], [628, 3582], [628, 3404]], [[642, 3404], [642, 3582], [642, 3582], [642, 3404]], [[671, 3404], [671, 3582], [671, 3582], [671, 3404]], [[700, 3404], [700, 3582], [714, 3582], [714, 3404]], [[757, 3404], [757, 3582], [757, 3582], [757, 3404]], [[800, 3404], [800, 3582], [815, 3582], [815, 3404]], [[844, 3404], [844, 3582], [858, 3582], [858, 3404]], [[930, 3404], [930, 3582], [930, 3582], [930, 3404]], [[959, 3404], [959, 3582], [973, 3582], [973, 3404]], [[1016, 3404], [1016, 3582], [1031, 3582], [1031, 3404]], [[1059, 3404], [1059, 3582], [1074, 3582], [1074, 3404]], [[1117, 3404], [1117, 3582], [1117, 3582], [1117, 3404]], [[1146, 3404], [1146, 3582], [1160, 3582], [1160, 3404]], [[1203, 3404], [1203, 3582], [1203, 3582], [1203, 3404]], [[1232, 3404], [1232, 3582], [1246, 3582], [1246, 3404]], [[1261, 3404], [1261, 3582], [1275, 3582], [1275, 3404]], [[1290, 3404], [1290, 3582], [1290, 3582], [1290, 3404]], [[1318, 3404], [1318, 3582], [1333, 3582], [1333, 3404]], [[1347, 3404], [1347, 3582], [1362, 3582], [1362, 3404]], [[1376, 3404], [1376, 3582], [1390, 3582], [1390, 3404]], [[1405, 3404], [1405, 3582], [1419, 3582], [1419, 3404]], [[1462, 3404], [1462, 3582], [1462, 3582], [1462, 3404]], [[1520, 3404], [1520, 3582], [1520, 3582], [1520, 3404]], [[1549, 3404], [1549, 3582], [1549, 3582], [1549, 3404]], [[1577, 3404], [1577, 3582], [1577, 3582], [1577, 3404]], [[1621, 3404], [1621, 3582], [1635, 3582], [1635, 3404]], [[1664, 3404], [1664, 3582], [1664, 3582], [1664, 3404]], [[1707, 3404], [1707, 3582], [1721, 3582], [1721, 3404]], [[1736, 3404], [1736, 3582], [1764, 3582], [1764, 3404]], [[1779, 3404], [1779, 3582], [1793, 3582], [1793, 3404]], [[1836, 3404], [1836, 3582], [1851, 3582], [1851, 3404]], [[1880, 3404], [1880, 3582], [1880, 3582], [1880, 3404]], [[1908, 3404], [1908, 3582], [1908, 3582], [1908, 3404]], [[1923, 3404], [1923, 3582], [1937, 3582], [1937, 3404]], [[2009, 3404], [2009, 3582], [2009, 3582], [2009, 3404]], [[2067, 3404], [2067, 3582], [2067, 3582], [2067, 3404]], [[2124, 3404], [2124, 3582], [2124, 3582], [2124, 3404]], [[2182, 3404], [2182, 3582], [2182, 3582], [2182, 3404]], [[2211, 3404], [2211, 3582], [2211, 3582], [2211, 3404]], [[2239, 3404], [2239, 3582], [2254, 3582], [2254, 3404]], [[2282, 3404], [2282, 3582], [2282, 3582], [2282, 3404]], [[2311, 3404], [2311, 3582], [2311, 3582], [2311, 3404]], [[2326, 3404], [2326, 3582], [2326, 3582], [2326, 3404]], [[2340, 3404], [2340, 3582], [2354, 3582], [2354, 3404]], [[2398, 3404], [2398, 3582], [2398, 3582], [2398, 3404]], [[2455, 3404], [2455, 3582], [2455, 3582], [2455, 3404]], [[2484, 3404], [2484, 3582], [2484, 3582], [2484, 3404]], [[2513, 3404], [2513, 3582], [2513, 3582], [2513, 3404]], [[2556, 3404], [2556, 3582], [2556, 3582], [2556, 3404]], [[2570, 3404], [2570, 3582], [2570, 3582], [2570, 3404]], [[2599, 3404], [2599, 3582], [2613, 3582], [2613, 3404]], [[2628, 3404], [2628, 3582], [2628, 3582], [2628, 3404]], [[2657, 3404], [2657, 3582], [2657, 3582], [2657, 3404]], [[2714, 3404], [2714, 3582], [2714, 3582], [2714, 3404]], [[2772, 3404], [2772, 3582], [2786, 3582], [2786, 3404]], [[2800, 3404], [2800, 3582], [2815, 3582], [2815, 3404]], [[2858, 3404], [2858, 3582], [2858, 3582], [2858, 3404]], [[2872, 3404], [2872, 3582], [2887, 3582], [2887, 3404]], [[2901, 3404], [2901, 3582], [2916, 3582], [2916, 3404]], [[2988, 3404], [2988, 3582], [2988, 3582], [2988, 3404]], [[3031, 3404], [3031, 3582], [3031, 3582], [3031, 3404]], [[3059, 3404], [3059, 3582], [3074, 3582], [3074, 3404]], [[3088, 3404], [3088, 3582], [3088, 3582], [3088, 3404]], [[3131, 3404], [3131, 3582], [3131, 3582], [3131, 3404]]], "confidences": [0.9967783093452454, 0.9999089241027832, 0.6990695595741272, 0.9999850988388062, 0.9994284510612488, 0.9905351996421814, 0.9979711174964905, 0.9925774931907654, 0.9997119307518005, 0.9998630285263062, 0.9991826415061951, 0.9995831847190857, 0.8739675283432007, 0.9986891150474548, 0.9998999834060669, 0.712096631526947, 0.9542328119277954, 0.9998071789741516, 0.9994687438011169, 0.9119349718093872, 0.9997267127037048, 0.9990538954734802, 0.9999830722808838, 0.9995676875114441, 0.9998180270195007, 0.9542503952980042, 0.9904987215995789, 0.9882673621177673, 0.9979777932167053, 0.9933269023895264, 0.9998037219047546, 0.9610075354576111, 0.9995414018630981, 0.9869469404220581, 0.9698469638824463, 0.9878268837928772, 0.9939728379249573, 0.8041937947273254, 0.9983476400375366, 0.792262077331543, 0.9819558262825012, 0.9396706223487854, 0.997104823589325, 0.9705645442008972, 0.9958399534225464, 0.9978986978530884, 0.8801516890525818, 0.9995551705360413, 0.9966424703598022, 0.9960938096046448, 0.9987300038337708, 0.9998194575309753, 0.563930094242096, 0.9453648328781128, 0.9340890049934387, 0.5644081830978394, 0.9911462664604187, 0.9987581968307495, 0.8091545701026917, 0.9992177486419678, 0.9971036314964294, 0.999757707118988, 0.999749481678009, 0.9985652565956116, 0.9988590478897095, 0.9889073371887207, 0.9370191693305969, 0.9869567155838013, 0.5738163590431213, 0.9979482293128967, 0.9877589344978333, 0.9431320428848267], "line": {"id": "3ef1722f-de38-475f-abac-a1829fffd3b1", "bbox": {"id": "35baa9ea-e0c2-4349-8f5f-e1faa768e791", "bbox": [[311, 3404], [311, 3582], [3146, 3582], [3146, 3404]]}, "type": "bbox"}}, {"prediction": "\u0631\u0647\u0627\u0638 \u062d\u0633\u0645\u062a\u0648 \u0647\u0644\u0643 \u0643\u0633\u0654\u0627\u0631 \u062d\u0633\u0645\u062a\u0648 \u0646\u064a\u0642\u0641\u0631\u0645\u0644\u0627 \u0649\u0644\u0627 \u0643\u0639\u0628\u0627\u0635\u0654\u0627 \u0646\u0645 \u0643\u062f\u064a \u0644\u0633\u063a\u062a\u0648 \u0643\u062a\u064a\u062d\u0644 \u0631\u0639\u0634", "cuts": [[[321, 3580], [321, 3731], [335, 3731], [335, 3580]], [[363, 3580], [363, 3731], [363, 3731], [363, 3580]], [[405, 3580], [405, 3731], [405, 3731], [405, 3580]], [[434, 3580], [434, 3731], [434, 3731], [434, 3580]], [[476, 3580], [476, 3731], [504, 3731], [504, 3580]], [[546, 3580], [546, 3731], [560, 3731], [560, 3580]], [[616, 3580], [616, 3731], [616, 3731], [616, 3580]], [[644, 3580], [644, 3731], [658, 3731], [658, 3580]], [[672, 3580], [672, 3731], [672, 3731], [672, 3580]], [[729, 3580], [729, 3731], [729, 3731], [729, 3580]], [[757, 3580], [757, 3731], [771, 3731], [771, 3580]], [[785, 3580], [785, 3731], [799, 3731], [799, 3580]], [[813, 3580], [813, 3731], [827, 3731], [827, 3580]], [[841, 3580], [841, 3731], [841, 3731], [841, 3580]], [[883, 3580], [883, 3731], [883, 3731], [883, 3580]], [[925, 3580], [925, 3731], [925, 3731], [925, 3580]], [[995, 3580], [995, 3731], [1010, 3731], [1010, 3580]], [[1024, 3580], [1024, 3731], [1038, 3731], [1038, 3580]], [[1052, 3580], [1052, 3731], [1052, 3731], [1052, 3580]], [[1094, 3580], [1094, 3731], [1094, 3731], [1094, 3580]], [[1122, 3580], [1122, 3731], [1136, 3731], [1136, 3580]], [[1178, 3580], [1178, 3731], [1178, 3731], [1178, 3580]], [[1234, 3580], [1234, 3731], [1248, 3731], [1248, 3580]], [[1262, 3580], [1262, 3731], [1276, 3731], [1276, 3580]], [[1291, 3580], [1291, 3731], [1305, 3731], [1305, 3580]], [[1347, 3580], [1347, 3731], [1361, 3731], [1361, 3580]], [[1375, 3580], [1375, 3731], [1389, 3731], [1389, 3580]], [[1417, 3580], [1417, 3731], [1431, 3731], [1431, 3580]], [[1445, 3580], [1445, 3731], [1459, 3731], [1459, 3580]], [[1501, 3580], [1501, 3731], [1501, 3731], [1501, 3580]], [[1529, 3580], [1529, 3731], [1529, 3731], [1529, 3580]], [[1571, 3580], [1571, 3731], [1586, 3731], [1586, 3580]], [[1600, 3580], [1600, 3731], [1600, 3731], [1600, 3580]], [[1614, 3580], [1614, 3731], [1628, 3731], [1628, 3580]], [[1642, 3580], [1642, 3731], [1642, 3731], [1642, 3580]], [[1656, 3580], [1656, 3731], [1670, 3731], [1670, 3580]], [[1712, 3580], [1712, 3731], [1712, 3731], [1712, 3580]], [[1726, 3580], [1726, 3731], [1726, 3731], [1726, 3580]], [[1754, 3580], [1754, 3731], [1754, 3731], [1754, 3580]], [[1768, 3580], [1768, 3731], [1782, 3731], [1782, 3580]], [[1824, 3580], [1824, 3731], [1824, 3731], [1824, 3580]], [[1881, 3580], [1881, 3731], [1881, 3731], [1881, 3580]], [[1909, 3580], [1909, 3731], [1923, 3731], [1923, 3580]], [[1951, 3580], [1951, 3731], [1951, 3731], [1951, 3580]], [[2007, 3580], [2007, 3731], [2007, 3731], [2007, 3580]], [[2035, 3580], [2035, 3731], [2049, 3731], [2049, 3580]], [[2063, 3580], [2063, 3731], [2063, 3731], [2063, 3580]], [[2077, 3580], [2077, 3731], [2091, 3731], [2091, 3580]], [[2232, 3580], [2232, 3731], [2232, 3731], [2232, 3580]], [[2316, 3580], [2316, 3731], [2330, 3731], [2330, 3580]], [[2358, 3580], [2358, 3731], [2358, 3731], [2358, 3580]], [[2400, 3580], [2400, 3731], [2400, 3731], [2400, 3580]], [[2457, 3580], [2457, 3731], [2457, 3731], [2457, 3580]], [[2485, 3580], [2485, 3731], [2485, 3731], [2485, 3580]], [[2513, 3580], [2513, 3731], [2527, 3731], [2527, 3580]], [[2569, 3580], [2569, 3731], [2569, 3731], [2569, 3580]], [[2611, 3580], [2611, 3731], [2625, 3731], [2625, 3580]], [[2653, 3580], [2653, 3731], [2653, 3731], [2653, 3580]], [[2695, 3580], [2695, 3731], [2695, 3731], [2695, 3580]], [[2737, 3580], [2737, 3731], [2752, 3731], [2752, 3580]], [[2766, 3580], [2766, 3731], [2780, 3731], [2780, 3580]], [[2822, 3580], [2822, 3731], [2822, 3731], [2822, 3580]], [[2878, 3580], [2878, 3731], [2878, 3731], [2878, 3580]], [[2906, 3580], [2906, 3731], [2920, 3731], [2920, 3580]], [[2948, 3580], [2948, 3731], [2948, 3731], [2948, 3580]], [[2962, 3580], [2962, 3731], [2962, 3731], [2962, 3580]], [[3004, 3580], [3004, 3731], [3004, 3731], [3004, 3580]], [[3032, 3580], [3032, 3731], [3047, 3731], [3047, 3580]], [[3075, 3580], [3075, 3731], [3075, 3731], [3075, 3580]], [[3131, 3580], [3131, 3731], [3131, 3731], [3131, 3580]]], "confidences": [0.998375415802002, 0.7084844708442688, 0.9999047517776489, 0.9999986886978149, 0.9975200295448303, 0.9999372959136963, 0.9996757507324219, 0.9996148347854614, 0.9969292283058167, 0.9986764788627625, 0.9608417749404907, 0.9995263814926147, 0.9976219534873962, 0.9999587535858154, 0.9711738228797913, 0.9985242486000061, 0.8398705720901489, 0.9996516704559326, 0.9961230158805847, 0.9993791580200195, 0.9999798536300659, 0.9998527765274048, 0.9999849796295166, 0.9962425231933594, 0.9955328702926636, 0.9989853501319885, 0.999942421913147, 0.9898174405097961, 0.9999264478683472, 0.999687910079956, 0.9997530579566956, 0.9998797178268433, 0.9882998466491699, 0.9634631872177124, 0.999843955039978, 0.9989407658576965, 0.9994811415672302, 0.9873757362365723, 0.996773898601532, 0.9990653395652771, 0.9880833029747009, 0.9579822421073914, 0.9963617920875549, 0.968079149723053, 0.9967735409736633, 0.9786046743392944, 0.6416178941726685, 0.9990584254264832, 0.99955815076828, 0.9967063069343567, 0.998286783695221, 0.6909951567649841, 0.7128172516822815, 0.9613966941833496, 0.9996551275253296, 0.9983021020889282, 0.999221682548523, 0.9992075562477112, 0.9989655017852783, 0.9996083378791809, 0.9999632835388184, 0.936977207660675, 0.9235370755195618, 0.9997147917747498, 0.667002260684967, 0.9975676536560059, 0.9907772541046143, 0.9969243407249451, 0.9055776000022888, 0.7779210209846497], "line": {"id": "1175db29-19e8-482d-bca2-7212823cb929", "bbox": {"id": "b70a13b9-2e63-4bd6-a567-65320f60f155", "bbox": [[307, 3580], [307, 3731], [3145, 3731], [3145, 3580]]}, "type": "bbox"}}, {"prediction": "\u0644\u0635\u0648 \u0627\u0644\u0641 \u0647\u0631\u064a\u063a \u0649\u0644\u0627 \u0643\u0644\u0630 \u0641\u0644\u0627\u062e \u0627\u0644\u0648 \u0627\u0645\u062b\u0627\u0644\u062b \u0646\u064a\u0628\u0639\u0643\u0644\u0627 \u0649\u0644\u0627 \u0643\u064a\u0644\u062c\u0631 \u0644\u0633\u063a\u062a\u0648\u0627\u0645\u0647\u0646\u0637\u0627\u0628\u0648 \u0643\u064a\u0646\u0630\u0627", "cuts": [[[361, 3744], [361, 3918], [375, 3918], [375, 3744]], [[417, 3744], [417, 3918], [417, 3918], [417, 3744]], [[487, 3744], [487, 3918], [487, 3918], [487, 3744]], [[515, 3744], [515, 3918], [529, 3918], [529, 3744]], [[543, 3744], [543, 3918], [557, 3918], [557, 3744]], [[585, 3744], [585, 3918], [599, 3918], [599, 3744]], [[613, 3744], [613, 3918], [613, 3918], [613, 3744]], [[641, 3744], [641, 3918], [655, 3918], [655, 3744]], [[683, 3744], [683, 3918], [683, 3918], [683, 3744]], [[739, 3744], [739, 3918], [739, 3918], [739, 3744]], [[753, 3744], [753, 3918], [767, 3918], [767, 3744]], [[795, 3744], [795, 3918], [795, 3918], [795, 3744]], [[837, 3744], [837, 3918], [837, 3918], [837, 3744]], [[879, 3744], [879, 3918], [879, 3918], [879, 3744]], [[893, 3744], [893, 3918], [907, 3918], [907, 3744]], [[921, 3744], [921, 3918], [921, 3918], [921, 3744]], [[935, 3744], [935, 3918], [963, 3918], [963, 3744]], [[1019, 3744], [1019, 3918], [1019, 3918], [1019, 3744]], [[1075, 3744], [1075, 3918], [1075, 3918], [1075, 3744]], [[1103, 3744], [1103, 3918], [1103, 3918], [1103, 3744]], [[1131, 3744], [1131, 3918], [1145, 3918], [1145, 3744]], [[1215, 3744], [1215, 3918], [1229, 3918], [1229, 3744]], [[1243, 3744], [1243, 3918], [1257, 3918], [1257, 3744]], [[1271, 3744], [1271, 3918], [1285, 3918], [1285, 3744]], [[1299, 3744], [1299, 3918], [1313, 3918], [1313, 3744]], [[1383, 3744], [1383, 3918], [1397, 3918], [1397, 3744]], [[1411, 3744], [1411, 3918], [1439, 3918], [1439, 3744]], [[1453, 3744], [1453, 3918], [1467, 3918], [1467, 3744]], [[1509, 3744], [1509, 3918], [1509, 3918], [1509, 3744]], [[1537, 3744], [1537, 3918], [1537, 3918], [1537, 3744]], [[1551, 3744], [1551, 3918], [1551, 3918], [1551, 3744]], [[1565, 3744], [1565, 3918], [1565, 3918], [1565, 3744]], [[1579, 3744], [1579, 3918], [1579, 3918], [1579, 3744]], [[1607, 3744], [1607, 3918], [1621, 3918], [1621, 3744]], [[1635, 3744], [1635, 3918], [1649, 3918], [1649, 3744]], [[1663, 3744], [1663, 3918], [1663, 3918], [1663, 3744]], [[1691, 3744], [1691, 3918], [1719, 3918], [1719, 3744]], [[1747, 3744], [1747, 3918], [1761, 3918], [1761, 3744]], [[1775, 3744], [1775, 3918], [1789, 3918], [1789, 3744]], [[1803, 3744], [1803, 3918], [1817, 3918], [1817, 3744]], [[1845, 3744], [1845, 3918], [1845, 3918], [1845, 3744]], [[1873, 3744], [1873, 3918], [1873, 3918], [1873, 3744]], [[1943, 3744], [1943, 3918], [1957, 3918], [1957, 3744]], [[1971, 3744], [1971, 3918], [1971, 3918], [1971, 3744]], [[1985, 3744], [1985, 3918], [1999, 3918], [1999, 3744]], [[2041, 3744], [2041, 3918], [2041, 3918], [2041, 3744]], [[2055, 3744], [2055, 3918], [2055, 3918], [2055, 3744]], [[2083, 3744], [2083, 3918], [2083, 3918], [2083, 3744]], [[2097, 3744], [2097, 3918], [2111, 3918], [2111, 3744]], [[2153, 3744], [2153, 3918], [2153, 3918], [2153, 3744]], [[2209, 3744], [2209, 3918], [2209, 3918], [2209, 3744]], [[2237, 3744], [2237, 3918], [2237, 3918], [2237, 3744]], [[2265, 3744], [2265, 3918], [2279, 3918], [2279, 3744]], [[2335, 3744], [2335, 3918], [2349, 3918], [2349, 3744]], [[2363, 3744], [2363, 3918], [2377, 3918], [2377, 3744]], [[2419, 3744], [2419, 3918], [2433, 3918], [2433, 3744]], [[2475, 3744], [2475, 3918], [2475, 3918], [2475, 3744]], [[2517, 3744], [2517, 3918], [2517, 3918], [2517, 3744]], [[2545, 3744], [2545, 3918], [2545, 3918], [2545, 3744]], [[2601, 3744], [2601, 3918], [2601, 3918], [2601, 3744]], [[2643, 3744], [2643, 3918], [2643, 3918], [2643, 3744]], [[2657, 3744], [2657, 3918], [2671, 3918], [2671, 3744]], [[2699, 3744], [2699, 3918], [2713, 3918], [2713, 3744]], [[2741, 3744], [2741, 3918], [2741, 3918], [2741, 3744]], [[2769, 3744], [2769, 3918], [2769, 3918], [2769, 3744]], [[2811, 3744], [2811, 3918], [2825, 3918], [2825, 3744]], [[2839, 3744], [2839, 3918], [2839, 3918], [2839, 3744]], [[2881, 3744], [2881, 3918], [2895, 3918], [2895, 3744]], [[2923, 3744], [2923, 3918], [2923, 3918], [2923, 3744]], [[2965, 3744], [2965, 3918], [2979, 3918], [2979, 3744]], [[3021, 3744], [3021, 3918], [3035, 3918], [3035, 3744]], [[3049, 3744], [3049, 3918], [3049, 3918], [3049, 3744]], [[3077, 3744], [3077, 3918], [3091, 3918], [3091, 3744]], [[3119, 3744], [3119, 3918], [3133, 3918], [3133, 3744]]], "confidences": [0.9999858140945435, 0.9996674060821533, 0.9778488278388977, 0.999860405921936, 0.9980079531669617, 0.9999980926513672, 0.9997289776802063, 0.9999096393585205, 0.9999042749404907, 0.9987836480140686, 0.999509334564209, 0.8447310328483582, 0.9997413754463196, 0.9830690622329712, 0.9998390674591064, 0.8895362615585327, 0.9999918937683105, 0.9990143775939941, 0.9999878406524658, 0.9991787075996399, 0.9999074935913086, 0.999941349029541, 0.9998675584793091, 0.9998282194137573, 0.99846351146698, 0.9999916553497314, 0.9999760389328003, 0.9508774876594543, 0.9997891783714294, 0.9323558807373047, 0.9861510396003723, 0.9936410784721375, 0.9994382262229919, 0.9971796274185181, 0.9985735416412354, 0.9859241843223572, 0.9663385152816772, 0.9317023158073425, 0.9961659908294678, 0.9577418565750122, 0.9928492307662964, 0.9323335886001587, 0.9990610480308533, 0.9972244501113892, 0.9995347261428833, 0.9952410459518433, 0.9984773993492126, 0.999396800994873, 0.9987291693687439, 0.8243120908737183, 0.9955060482025146, 0.7022139430046082, 0.9865391850471497, 0.9992412328720093, 0.9976450800895691, 0.9810840487480164, 0.9991329312324524, 0.9990897178649902, 0.9992324113845825, 0.998461127281189, 0.9994111061096191, 0.9990944862365723, 0.9991432428359985, 0.9482150077819824, 0.9927623867988586, 0.9994828701019287, 0.953675389289856, 0.9985700845718384, 0.9993718266487122, 0.94927978515625, 0.9973020553588867, 0.9952284097671509, 0.9812739491462708, 0.9950826168060303], "line": {"id": "90f65d12-1cb0-4210-93aa-3910b4171cd0", "bbox": {"id": "89d8c033-1838-4339-a7c5-765c50025016", "bbox": [[333, 3744], [333, 3918], [3133, 3918], [3133, 3744]]}, "type": "bbox"}}, {"prediction": "\u0649\u0627\u0644\u0648\u0645\u0644\u0627\u0642 \u0645\u062b \u0647\u0641\u0627\u0644\u062e \u0649\u0644\u0639 \u0629\u0628\u0627\u0635\u0639\u0644\u0627 \u0639\u0645\u062c\u0654\u0627 \u0627\u0645 \u0647\u064a\u0641 \u0647\u0644 \u0645\u0633\u0631 \u0645 \u0628\u062c\u0639\u062a \u0646\u064a\u0637\u0642\u064a \u0646\u0628 \u0649\u0644\u0639 \u0649\u0644\u0627 \u0628\u0646\u0627\u062a\u0643\u0644\u0627", "cuts": [[[374, 3912], [374, 4081], [374, 4081], [374, 3912]], [[416, 3912], [416, 4081], [430, 4081], [430, 3912]], [[444, 3912], [444, 4081], [458, 4081], [458, 3912]], [[486, 3912], [486, 4081], [500, 4081], [500, 3912]], [[528, 3912], [528, 4081], [528, 4081], [528, 3912]], [[598, 3912], [598, 4081], [598, 4081], [598, 3912]], [[612, 3912], [612, 4081], [612, 4081], [612, 3912]], [[641, 3912], [641, 4081], [641, 4081], [641, 3912]], [[669, 3912], [669, 4081], [683, 4081], [683, 3912]], [[697, 3912], [697, 4081], [711, 4081], [711, 3912]], [[739, 3912], [739, 4081], [739, 4081], [739, 3912]], [[767, 3912], [767, 4081], [767, 4081], [767, 3912]], [[781, 3912], [781, 4081], [795, 4081], [795, 3912]], [[823, 3912], [823, 4081], [823, 4081], [823, 3912]], [[851, 3912], [851, 4081], [865, 4081], [865, 3912]], [[879, 3912], [879, 4081], [893, 4081], [893, 3912]], [[921, 3912], [921, 4081], [935, 4081], [935, 3912]], [[963, 3912], [963, 4081], [977, 4081], [977, 3912]], [[1019, 3912], [1019, 4081], [1019, 4081], [1019, 3912]], [[1033, 3912], [1033, 4081], [1033, 4081], [1033, 3912]], [[1047, 3912], [1047, 4081], [1061, 4081], [1061, 3912]], [[1089, 3912], [1089, 4081], [1103, 4081], [1103, 3912]], [[1117, 3912], [1117, 4081], [1117, 4081], [1117, 3912]], [[1145, 3912], [1145, 4081], [1145, 4081], [1145, 3912]], [[1173, 3912], [1173, 4081], [1187, 4081], [1187, 3912]], [[1229, 3912], [1229, 4081], [1229, 4081], [1229, 3912]], [[1271, 3912], [1271, 4081], [1285, 4081], [1285, 3912]], [[1313, 3912], [1313, 4081], [1313, 4081], [1313, 3912]], [[1327, 3912], [1327, 4081], [1327, 4081], [1327, 3912]], [[1341, 3912], [1341, 4081], [1356, 4081], [1356, 3912]], [[1370, 3912], [1370, 4081], [1384, 4081], [1384, 3912]], [[1412, 3912], [1412, 4081], [1426, 4081], [1426, 3912]], [[1440, 3912], [1440, 4081], [1454, 4081], [1454, 3912]], [[1482, 3912], [1482, 4081], [1482, 4081], [1482, 3912]], [[1496, 3912], [1496, 4081], [1496, 4081], [1496, 3912]], [[1524, 3912], [1524, 4081], [1524, 4081], [1524, 3912]], [[1538, 3912], [1538, 4081], [1538, 4081], [1538, 3912]], [[1594, 3912], [1594, 4081], [1608, 4081], [1608, 3912]], [[1636, 3912], [1636, 4081], [1650, 4081], [1650, 3912]], [[1664, 3912], [1664, 4081], [1664, 4081], [1664, 3912]], [[1692, 3912], [1692, 4081], [1692, 4081], [1692, 3912]], [[1720, 3912], [1720, 4081], [1720, 4081], [1720, 3912]], [[1748, 3912], [1748, 4081], [1762, 4081], [1762, 3912]], [[1776, 3912], [1776, 4081], [1776, 4081], [1776, 3912]], [[1804, 3912], [1804, 4081], [1818, 4081], [1818, 3912]], [[1832, 3912], [1832, 4081], [1832, 4081], [1832, 3912]], [[1846, 3912], [1846, 4081], [1860, 4081], [1860, 3912]], [[1916, 3912], [1916, 4081], [1916, 4081], [1916, 3912]], [[1972, 3912], [1972, 4081], [1972, 4081], [1972, 3912]], [[1986, 3912], [1986, 4081], [2000, 4081], [2000, 3912]], [[2042, 3912], [2042, 4081], [2042, 4081], [2042, 3912]], [[2084, 3912], [2084, 4081], [2099, 4081], [2099, 3912]], [[2141, 3912], [2141, 4081], [2141, 4081], [2141, 3912]], [[2197, 3912], [2197, 4081], [2211, 4081], [2211, 3912]], [[2253, 3912], [2253, 4081], [2253, 4081], [2253, 3912]], [[2281, 3912], [2281, 4081], [2281, 4081], [2281, 3912]], [[2309, 3912], [2309, 4081], [2323, 4081], [2323, 3912]], [[2351, 3912], [2351, 4081], [2365, 4081], [2365, 3912]], [[2379, 3912], [2379, 4081], [2379, 4081], [2379, 3912]], [[2407, 3912], [2407, 4081], [2421, 4081], [2421, 3912]], [[2463, 3912], [2463, 4081], [2477, 4081], [2477, 3912]], [[2491, 3912], [2491, 4081], [2505, 4081], [2505, 3912]], [[2519, 3912], [2519, 4081], [2519, 4081], [2519, 3912]], [[2547, 3912], [2547, 4081], [2561, 4081], [2561, 3912]], [[2575, 3912], [2575, 4081], [2575, 4081], [2575, 3912]], [[2603, 3912], [2603, 4081], [2617, 4081], [2617, 3912]], [[2687, 3912], [2687, 4081], [2687, 4081], [2687, 3912]], [[2701, 3912], [2701, 4081], [2701, 4081], [2701, 3912]], [[2715, 3912], [2715, 4081], [2729, 4081], [2729, 3912]], [[2771, 3912], [2771, 4081], [2771, 4081], [2771, 3912]], [[2813, 3912], [2813, 4081], [2813, 4081], [2813, 3912]], [[2828, 3912], [2828, 4081], [2842, 4081], [2842, 3912]], [[2856, 3912], [2856, 4081], [2856, 4081], [2856, 3912]], [[2870, 3912], [2870, 4081], [2870, 4081], [2870, 3912]], [[2912, 3912], [2912, 4081], [2912, 4081], [2912, 3912]], [[2926, 3912], [2926, 4081], [2926, 4081], [2926, 3912]], [[2954, 3912], [2954, 4081], [2954, 4081], [2954, 3912]], [[2982, 3912], [2982, 4081], [2982, 4081], [2982, 3912]], [[3024, 3912], [3024, 4081], [3024, 4081], [3024, 3912]], [[3108, 3912], [3108, 4081], [3108, 4081], [3108, 3912]], [[3122, 3912], [3122, 4081], [3136, 4081], [3136, 3912]]], "confidences": [0.991776704788208, 0.9966753721237183, 0.9947142004966736, 0.9186890721321106, 0.9635366797447205, 0.9986220598220825, 0.9777283668518066, 0.9586911797523499, 0.9998502731323242, 0.9988514184951782, 0.9999862909317017, 0.46282950043678284, 0.973861575126648, 0.42882752418518066, 0.9648494124412537, 0.9557760953903198, 0.9976227879524231, 0.5231557488441467, 0.7373040914535522, 0.997160792350769, 0.9977774024009705, 0.9998714923858643, 0.9994800686836243, 0.5984565019607544, 0.9999616146087646, 0.953172504901886, 0.9998903274536133, 0.5577518939971924, 0.6261033415794373, 0.9999628067016602, 0.9987449645996094, 0.9979907274246216, 0.9999592304229736, 0.9994562268257141, 0.776331901550293, 0.9890751838684082, 0.9983356595039368, 0.986653745174408, 0.9932841062545776, 0.9770086407661438, 0.8269332051277161, 0.9864249229431152, 0.9786643981933594, 0.9915361404418945, 0.9968346953392029, 0.9864396452903748, 0.9924086332321167, 0.998105525970459, 0.9923842549324036, 0.9835927486419678, 0.9957876801490784, 0.9960395097732544, 0.9908498525619507, 0.9995176792144775, 0.9677330255508423, 0.9952746629714966, 0.9993299245834351, 0.998761773109436, 0.9985032081604004, 0.9977524876594543, 0.9977297186851501, 0.9998428821563721, 0.9996432065963745, 0.9972556233406067, 0.8950211405754089, 0.982606828212738, 0.9643605351448059, 0.9769435524940491, 0.9995658993721008, 0.9992498755455017, 0.7895783185958862, 0.9948700666427612, 0.8788118362426758, 0.9814590215682983, 0.9998113512992859, 0.8790767788887024, 0.7014104723930359, 0.7737085819244385, 0.9973267316818237, 0.9496884942054749, 0.9877499938011169], "line": {"id": "978e85a1-8528-436c-9fc9-647bc9e04dfc", "bbox": {"id": "2f15907b-447d-4609-92ed-05ba4b2b5c27", "bbox": [[304, 3912], [304, 4081], [3136, 4081], [3136, 3912]]}, "type": "bbox"}}, {"prediction": "\u0647\u064a\u0644\u0639 \u0627\u0645 \u0641\u0644\u0627\u062e\u064a\u0648 \u062f\u062d\u0644\u0627 \u0627\u0630\u0647 \u0649\u0644\u0639 \u0647\u0648\u0636\u0648 \u064a\u0641 \u0644\u0645\u0639\u064a \u0646\u0627\u0643\u0641 \u0647\u0631\u0645\u0654\u0627 \u0644\u062b\u062a\u0645 \u0627\u0646\u0654\u0627\u0648 \u0644\u0627\u0642 \u0627\u0645\u0628 \u0645\u0644\u0639\u0654\u0627", "cuts": [[[305, 4078], [305, 4239], [305, 4239], [305, 4078]], [[348, 4078], [348, 4239], [348, 4239], [348, 4078]], [[362, 4078], [362, 4239], [376, 4239], [376, 4078]], [[390, 4078], [390, 4239], [404, 4239], [404, 4078]], [[447, 4078], [447, 4239], [447, 4239], [447, 4078]], [[461, 4078], [461, 4239], [475, 4239], [475, 4078]], [[489, 4078], [489, 4239], [489, 4239], [489, 4078]], [[518, 4078], [518, 4239], [532, 4239], [532, 4078]], [[617, 4078], [617, 4239], [617, 4239], [617, 4078]], [[645, 4078], [645, 4239], [645, 4239], [645, 4078]], [[659, 4078], [659, 4239], [674, 4239], [674, 4078]], [[702, 4078], [702, 4239], [702, 4239], [702, 4078]], [[730, 4078], [730, 4239], [744, 4239], [744, 4078]], [[787, 4078], [787, 4239], [801, 4239], [801, 4078]], [[815, 4078], [815, 4239], [830, 4239], [830, 4078]], [[872, 4078], [872, 4239], [872, 4239], [872, 4078]], [[900, 4078], [900, 4239], [900, 4239], [900, 4078]], [[915, 4078], [915, 4239], [929, 4239], [929, 4078]], [[957, 4078], [957, 4239], [971, 4239], [971, 4078]], [[985, 4078], [985, 4239], [1000, 4239], [1000, 4078]], [[1028, 4078], [1028, 4239], [1042, 4239], [1042, 4078]], [[1085, 4078], [1085, 4239], [1085, 4239], [1085, 4078]], [[1113, 4078], [1113, 4239], [1113, 4239], [1113, 4078]], [[1156, 4078], [1156, 4239], [1170, 4239], [1170, 4078]], [[1198, 4078], [1198, 4239], [1198, 4239], [1198, 4078]], [[1212, 4078], [1212, 4239], [1212, 4239], [1212, 4078]], [[1241, 4078], [1241, 4239], [1241, 4239], [1241, 4078]], [[1283, 4078], [1283, 4239], [1297, 4239], [1297, 4078]], [[1311, 4078], [1311, 4239], [1311, 4239], [1311, 4078]], [[1397, 4078], [1397, 4239], [1397, 4239], [1397, 4078]], [[1439, 4078], [1439, 4239], [1453, 4239], [1453, 4078]], [[1524, 4078], [1524, 4239], [1524, 4239], [1524, 4078]], [[1552, 4078], [1552, 4239], [1552, 4239], [1552, 4078]], [[1595, 4078], [1595, 4239], [1595, 4239], [1595, 4078]], [[1609, 4078], [1609, 4239], [1609, 4239], [1609, 4078]], [[1638, 4078], [1638, 4239], [1652, 4239], [1652, 4078]], [[1694, 4078], [1694, 4239], [1694, 4239], [1694, 4078]], [[1723, 4078], [1723, 4239], [1723, 4239], [1723, 4078]], [[1765, 4078], [1765, 4239], [1765, 4239], [1765, 4078]], [[1793, 4078], [1793, 4239], [1793, 4239], [1793, 4078]], [[1822, 4078], [1822, 4239], [1836, 4239], [1836, 4078]], [[1978, 4078], [1978, 4239], [1978, 4239], [1978, 4078]], [[2049, 4078], [2049, 4239], [2063, 4239], [2063, 4078]], [[2077, 4078], [2077, 4239], [2077, 4239], [2077, 4078]], [[2120, 4078], [2120, 4239], [2120, 4239], [2120, 4078]], [[2148, 4078], [2148, 4239], [2162, 4239], [2162, 4078]], [[2205, 4078], [2205, 4239], [2205, 4239], [2205, 4078]], [[2247, 4078], [2247, 4239], [2261, 4239], [2261, 4078]], [[2290, 4078], [2290, 4239], [2290, 4239], [2290, 4078]], [[2304, 4078], [2304, 4239], [2318, 4239], [2318, 4078]], [[2332, 4078], [2332, 4239], [2332, 4239], [2332, 4078]], [[2346, 4078], [2346, 4239], [2361, 4239], [2361, 4078]], [[2403, 4078], [2403, 4239], [2417, 4239], [2417, 4078]], [[2431, 4078], [2431, 4239], [2446, 4239], [2446, 4078]], [[2474, 4078], [2474, 4239], [2474, 4239], [2474, 4078]], [[2488, 4078], [2488, 4239], [2502, 4239], [2502, 4078]], [[2531, 4078], [2531, 4239], [2531, 4239], [2531, 4078]], [[2545, 4078], [2545, 4239], [2559, 4239], [2559, 4078]], [[2573, 4078], [2573, 4239], [2573, 4239], [2573, 4078]], [[2601, 4078], [2601, 4239], [2601, 4239], [2601, 4078]], [[2616, 4078], [2616, 4239], [2616, 4239], [2616, 4078]], [[2672, 4078], [2672, 4239], [2672, 4239], [2672, 4078]], [[2701, 4078], [2701, 4239], [2701, 4239], [2701, 4078]], [[2757, 4078], [2757, 4239], [2757, 4239], [2757, 4078]], [[2772, 4078], [2772, 4239], [2772, 4239], [2772, 4078]], [[2786, 4078], [2786, 4239], [2786, 4239], [2786, 4078]], [[2814, 4078], [2814, 4239], [2842, 4239], [2842, 4078]], [[2871, 4078], [2871, 4239], [2871, 4239], [2871, 4078]], [[2928, 4078], [2928, 4239], [2942, 4239], [2942, 4078]], [[2970, 4078], [2970, 4239], [2970, 4239], [2970, 4078]], [[2984, 4078], [2984, 4239], [2998, 4239], [2998, 4078]], [[3013, 4078], [3013, 4239], [3027, 4239], [3027, 4078]], [[3041, 4078], [3041, 4239], [3041, 4239], [3041, 4078]], [[3055, 4078], [3055, 4239], [3069, 4239], [3069, 4078]], [[3098, 4078], [3098, 4239], [3098, 4239], [3098, 4078]], [[3112, 4078], [3112, 4239], [3112, 4239], [3112, 4078]]], "confidences": [0.7411984801292419, 0.6705909371376038, 0.9994827508926392, 0.9999536275863647, 0.9637630581855774, 0.990339457988739, 0.9970928430557251, 0.9842832088470459, 0.9999134540557861, 0.8257625699043274, 0.9998522996902466, 0.9860520362854004, 0.8672003149986267, 0.999895453453064, 0.9987590312957764, 0.6380735635757446, 0.9966011047363281, 0.9999879598617554, 0.9917520880699158, 0.9998186230659485, 0.9985596537590027, 0.9999741315841675, 0.9970946311950684, 0.9999897480010986, 0.9996445178985596, 0.9999563694000244, 0.9980692267417908, 0.9994539618492126, 0.5508994460105896, 0.9075180888175964, 0.6853848099708557, 0.8571422696113586, 0.7955424189567566, 0.8907495737075806, 0.9879714846611023, 0.9994396567344666, 0.848770260810852, 0.9981479644775391, 0.909099280834198, 0.9869056344032288, 0.9833765625953674, 0.9991958737373352, 0.9672496318817139, 0.9993138313293457, 0.5518977046012878, 0.8995003700256348, 0.9811897277832031, 0.9994994401931763, 0.9018236994743347, 0.9840490818023682, 0.9967617392539978, 0.9858490228652954, 0.7903525233268738, 0.996831476688385, 0.9998800754547119, 0.9991426467895508, 0.9994601607322693, 0.9994288086891174, 0.9615521430969238, 0.8211493492126465, 0.9944064617156982, 0.9838215112686157, 0.9928451180458069, 0.9992262125015259, 0.9864301681518555, 0.980707585811615, 0.9878180027008057, 0.7048366665840149, 0.9973539113998413, 0.8788666129112244, 0.9915334582328796, 0.9992111921310425, 0.9873242378234863, 0.9851594567298889, 0.5106502771377563, 0.9952054023742676], "line": {"id": "5cea17da-ef09-477a-92d6-b2458f76d625", "bbox": {"id": "09407f8b-b683-4a37-84bc-34b89367b925", "bbox": [[305, 4078], [305, 4239], [3126, 4239], [3126, 4078]]}, "type": "bbox"}}, {"prediction": "\u062f\u064a\u0634\u0631\u0644\u0627 \u0649\u0644\u0627 \u0646\u064a\u0637\u0642\u064a \u0646\u0628 \u0649\u0644\u0639! \u064a\u0645\u0639\u0633\u0648 \u0645\u0627\u0644\u0633\u0644\u0627 \u0647\u064a\u0644\u0639 \u0646\u0633\u062d\u0644\u0627 \u064a\u0628\u0654\u0627 \u0645\u0627\u0644 \u0627\u0644\u0627\u062b\u062a\u0645\u0655\u0627 \u0629\u0640\u0639\u064a\u0634\u0644\u0627 \u0639\u064a\u0645\u062c", "cuts": [[[316, 4249], [316, 4413], [331, 4413], [331, 4249]], [[359, 4249], [359, 4413], [359, 4413], [359, 4249]], [[402, 4249], [402, 4413], [402, 4413], [402, 4249]], [[444, 4249], [444, 4413], [444, 4413], [444, 4249]], [[459, 4249], [459, 4413], [473, 4413], [473, 4249]], [[487, 4249], [487, 4413], [501, 4413], [501, 4249]], [[515, 4249], [515, 4413], [530, 4413], [530, 4249]], [[587, 4249], [587, 4413], [587, 4413], [587, 4249]], [[601, 4249], [601, 4413], [601, 4413], [601, 4249]], [[629, 4249], [629, 4413], [629, 4413], [629, 4249]], [[643, 4249], [643, 4413], [658, 4413], [658, 4249]], [[686, 4249], [686, 4413], [700, 4413], [700, 4249]], [[715, 4249], [715, 4413], [715, 4413], [715, 4249]], [[743, 4249], [743, 4413], [757, 4413], [757, 4249]], [[800, 4249], [800, 4413], [814, 4413], [814, 4249]], [[828, 4249], [828, 4413], [843, 4413], [843, 4249]], [[857, 4249], [857, 4413], [871, 4413], [871, 4249]], [[914, 4249], [914, 4413], [914, 4413], [914, 4249]], [[942, 4249], [942, 4413], [942, 4413], [942, 4249]], [[971, 4249], [971, 4413], [985, 4413], [985, 4249]], [[1070, 4249], [1070, 4413], [1070, 4413], [1070, 4249]], [[1084, 4249], [1084, 4413], [1084, 4413], [1084, 4249]], [[1113, 4249], [1113, 4413], [1113, 4413], [1113, 4249]], [[1141, 4249], [1141, 4413], [1141, 4413], [1141, 4249]], [[1155, 4249], [1155, 4413], [1184, 4413], [1184, 4249]], [[1212, 4249], [1212, 4413], [1226, 4413], [1226, 4249]], [[1255, 4249], [1255, 4413], [1255, 4413], [1255, 4249]], [[1269, 4249], [1269, 4413], [1269, 4413], [1269, 4249]], [[1312, 4249], [1312, 4413], [1326, 4413], [1326, 4249]], [[1369, 4249], [1369, 4413], [1383, 4413], [1383, 4249]], [[1397, 4249], [1397, 4413], [1411, 4413], [1411, 4249]], [[1426, 4249], [1426, 4413], [1440, 4413], [1440, 4249]], [[1482, 4249], [1482, 4413], [1482, 4413], [1482, 4249]], [[1497, 4249], [1497, 4413], [1511, 4413], [1511, 4249]], [[1553, 4249], [1553, 4413], [1553, 4413], [1553, 4249]], [[1582, 4249], [1582, 4413], [1582, 4413], [1582, 4249]], [[1596, 4249], [1596, 4413], [1610, 4413], [1610, 4249]], [[1625, 4249], [1625, 4413], [1639, 4413], [1639, 4249]], [[1653, 4249], [1653, 4413], [1653, 4413], [1653, 4249]], [[1681, 4249], [1681, 4413], [1681, 4413], [1681, 4249]], [[1710, 4249], [1710, 4413], [1710, 4413], [1710, 4249]], [[1738, 4249], [1738, 4413], [1738, 4413], [1738, 4249]], [[1781, 4249], [1781, 4413], [1795, 4413], [1795, 4249]], [[1824, 4249], [1824, 4413], [1824, 4413], [1824, 4249]], [[1866, 4249], [1866, 4413], [1895, 4413], [1895, 4249]], [[1923, 4249], [1923, 4413], [1923, 4413], [1923, 4249]], [[1937, 4249], [1937, 4413], [1952, 4413], [1952, 4249]], [[1980, 4249], [1980, 4413], [1994, 4413], [1994, 4249]], [[2008, 4249], [2008, 4413], [2023, 4413], [2023, 4249]], [[2065, 4249], [2065, 4413], [2065, 4413], [2065, 4249]], [[2080, 4249], [2080, 4413], [2080, 4413], [2080, 4249]], [[2108, 4249], [2108, 4413], [2108, 4413], [2108, 4249]], [[2122, 4249], [2122, 4413], [2122, 4413], [2122, 4249]], [[2136, 4249], [2136, 4413], [2151, 4413], [2151, 4249]], [[2222, 4249], [2222, 4413], [2236, 4413], [2236, 4249]], [[2264, 4249], [2264, 4413], [2279, 4413], [2279, 4249]], [[2293, 4249], [2293, 4413], [2307, 4413], [2307, 4249]], [[2321, 4249], [2321, 4413], [2336, 4413], [2336, 4249]], [[2378, 4249], [2378, 4413], [2378, 4413], [2378, 4249]], [[2407, 4249], [2407, 4413], [2407, 4413], [2407, 4249]], [[2435, 4249], [2435, 4413], [2435, 4413], [2435, 4249]], [[2463, 4249], [2463, 4413], [2463, 4413], [2463, 4249]], [[2492, 4249], [2492, 4413], [2492, 4413], [2492, 4249]], [[2520, 4249], [2520, 4413], [2520, 4413], [2520, 4249]], [[2549, 4249], [2549, 4413], [2549, 4413], [2549, 4249]], [[2563, 4249], [2563, 4413], [2563, 4413], [2563, 4249]], [[2577, 4249], [2577, 4413], [2591, 4413], [2591, 4249]], [[2606, 4249], [2606, 4413], [2606, 4413], [2606, 4249]], [[2663, 4249], [2663, 4413], [2677, 4413], [2677, 4249]], [[2762, 4249], [2762, 4413], [2762, 4413], [2762, 4249]], [[2805, 4249], [2805, 4413], [2805, 4413], [2805, 4249]], [[2847, 4249], [2847, 4413], [2847, 4413], [2847, 4249]], [[2876, 4249], [2876, 4413], [2876, 4413], [2876, 4249]], [[2890, 4249], [2890, 4413], [2904, 4413], [2904, 4249]], [[2919, 4249], [2919, 4413], [2933, 4413], [2933, 4249]], [[2975, 4249], [2975, 4413], [2990, 4413], [2990, 4249]], [[3061, 4249], [3061, 4413], [3061, 4413], [3061, 4249]], [[3089, 4249], [3089, 4413], [3089, 4413], [3089, 4249]], [[3103, 4249], [3103, 4413], [3103, 4413], [3103, 4249]]], "confidences": [0.9999558925628662, 0.9997685551643372, 0.9997368454933167, 0.9977124929428101, 0.999941349029541, 0.9980744123458862, 0.9950352907180786, 0.9224417805671692, 0.8580173254013062, 0.9524855613708496, 0.9989435076713562, 0.9999842643737793, 0.9986186027526855, 0.9998856782913208, 0.9995737671852112, 0.9912926554679871, 0.9999467134475708, 0.9999945163726807, 0.9995242357254028, 0.9999788999557495, 0.9609777331352234, 0.9153129458427429, 0.9045202136039734, 0.9997207522392273, 0.9395636916160583, 0.9894552826881409, 0.9998144507408142, 0.9997990727424622, 0.9999853372573853, 0.9999799728393555, 1.0, 0.9706733822822571, 0.9758889675140381, 0.9932337403297424, 0.8476508259773254, 0.9795082807540894, 0.9796251058578491, 0.9970026612281799, 0.4895252287387848, 0.5919338464736938, 0.9867957830429077, 0.7614318132400513, 0.9896360039710999, 0.9846451878547668, 0.9974541068077087, 0.5357677340507507, 0.9974640607833862, 0.9990847110748291, 0.9961497783660889, 0.9180766940116882, 0.8193199634552002, 0.9990463852882385, 0.9899054169654846, 0.9957371950149536, 0.9989354014396667, 0.9109645485877991, 0.9999076128005981, 0.999586284160614, 0.7132347226142883, 0.9970788955688477, 0.9983876943588257, 0.6264116764068604, 0.9988951086997986, 0.9250513315200806, 0.8208836913108826, 0.8228710293769836, 0.9901149272918701, 0.997815728187561, 0.9998468160629272, 0.9990491271018982, 0.9956784844398499, 0.7115011811256409, 0.9978455305099487, 0.9982227683067322, 0.9991169571876526, 0.9916975498199463, 0.9556155204772949, 0.9978026747703552, 0.9827321171760559], "line": {"id": "4e63864f-8754-4d94-9e03-4cd62d5618d0", "bbox": {"id": "02dd3ee9-dcdf-4a5d-be3b-4513160c22a2", "bbox": [[302, 4249], [302, 4413], [3132, 4413], [3132, 4249]]}, "type": "bbox"}}, {"prediction": ".\u0649\u0644\u0639 \u064a\u0641 \u0644\u0648\u0642\u0644\u0627 \u0649\u062f\u0646\u0639 \u0631\u062b\u0643 \u062f\u0642 \u0647\u062a\u0635\u0627\u062e \u0636\u0639\u0628\u0627 \u062f\u064a\u0634\u0631\u0644\u0627 \u0644\u0627\u0642\u0641 \u0643\u0644 \u0641\u0627\u0627\u062e\u0645 \u064a\u0636\u0641\u0627\u0631 \u0647\u0627 \u0647\u0644 \u0644\u064a\u0642\u0648", "cuts": [[[297, 4427], [297, 4585], [297, 4585], [297, 4427]], [[350, 4427], [350, 4585], [350, 4585], [350, 4427]], [[363, 4427], [363, 4585], [376, 4585], [376, 4427]], [[389, 4427], [389, 4585], [389, 4585], [389, 4427]], [[429, 4427], [429, 4585], [442, 4585], [442, 4427]], [[468, 4427], [468, 4585], [468, 4585], [468, 4427]], [[481, 4427], [481, 4585], [481, 4585], [481, 4427]], [[508, 4427], [508, 4585], [534, 4585], [534, 4427]], [[587, 4427], [587, 4585], [600, 4585], [600, 4427]], [[626, 4427], [626, 4585], [639, 4585], [639, 4427]], [[679, 4427], [679, 4585], [679, 4585], [679, 4427]], [[705, 4427], [705, 4585], [705, 4585], [705, 4427]], [[718, 4427], [718, 4585], [718, 4585], [718, 4427]], [[732, 4427], [732, 4585], [745, 4585], [745, 4427]], [[797, 4427], [797, 4585], [797, 4585], [797, 4427]], [[850, 4427], [850, 4585], [863, 4585], [863, 4427]], [[890, 4427], [890, 4585], [890, 4585], [890, 4427]], [[916, 4427], [916, 4585], [916, 4585], [916, 4427]], [[955, 4427], [955, 4585], [969, 4585], [969, 4427]], [[995, 4427], [995, 4585], [1008, 4585], [1008, 4427]], [[1021, 4427], [1021, 4585], [1021, 4585], [1021, 4427]], [[1048, 4427], [1048, 4585], [1061, 4585], [1061, 4427]], [[1219, 4427], [1219, 4585], [1219, 4585], [1219, 4427]], [[1245, 4427], [1245, 4585], [1258, 4585], [1258, 4427]], [[1285, 4427], [1285, 4585], [1285, 4585], [1285, 4427]], [[1311, 4427], [1311, 4585], [1324, 4585], [1324, 4427]], [[1337, 4427], [1337, 4585], [1337, 4585], [1337, 4427]], [[1364, 4427], [1364, 4585], [1364, 4585], [1364, 4427]], [[1416, 4427], [1416, 4585], [1429, 4585], [1429, 4427]], [[1456, 4427], [1456, 4585], [1469, 4585], [1469, 4427]], [[1495, 4427], [1495, 4585], [1495, 4585], [1495, 4427]], [[1535, 4427], [1535, 4585], [1548, 4585], [1548, 4427]], [[1614, 4427], [1614, 4585], [1614, 4585], [1614, 4427]], [[1666, 4427], [1666, 4585], [1666, 4585], [1666, 4427]], [[1693, 4427], [1693, 4585], [1706, 4585], [1706, 4427]], [[1732, 4427], [1732, 4585], [1732, 4585], [1732, 4427]], [[1745, 4427], [1745, 4585], [1759, 4585], [1759, 4427]], [[1798, 4427], [1798, 4585], [1798, 4585], [1798, 4427]], [[1824, 4427], [1824, 4585], [1824, 4585], [1824, 4427]], [[1877, 4427], [1877, 4585], [1877, 4585], [1877, 4427]], [[1917, 4427], [1917, 4585], [1930, 4585], [1930, 4427]], [[1943, 4427], [1943, 4585], [1943, 4585], [1943, 4427]], [[1969, 4427], [1969, 4585], [1969, 4585], [1969, 4427]], [[1982, 4427], [1982, 4585], [1996, 4585], [1996, 4427]], [[2048, 4427], [2048, 4585], [2048, 4585], [2048, 4427]], [[2075, 4427], [2075, 4585], [2075, 4585], [2075, 4427]], [[2101, 4427], [2101, 4585], [2114, 4585], [2114, 4427]], [[2140, 4427], [2140, 4585], [2140, 4585], [2140, 4427]], [[2154, 4427], [2154, 4585], [2167, 4585], [2167, 4427]], [[2206, 4427], [2206, 4585], [2219, 4585], [2219, 4427]], [[2272, 4427], [2272, 4585], [2272, 4585], [2272, 4427]], [[2285, 4427], [2285, 4585], [2298, 4585], [2298, 4427]], [[2364, 4427], [2364, 4585], [2377, 4585], [2377, 4427]], [[2404, 4427], [2404, 4585], [2404, 4585], [2404, 4427]], [[2430, 4427], [2430, 4585], [2430, 4585], [2430, 4427]], [[2456, 4427], [2456, 4585], [2456, 4585], [2456, 4427]], [[2470, 4427], [2470, 4585], [2483, 4585], [2483, 4427]], [[2509, 4427], [2509, 4585], [2522, 4585], [2522, 4427]], [[2562, 4427], [2562, 4585], [2562, 4585], [2562, 4427]], [[2601, 4427], [2601, 4585], [2601, 4585], [2601, 4427]], [[2654, 4427], [2654, 4585], [2654, 4585], [2654, 4427]], [[2680, 4427], [2680, 4585], [2680, 4585], [2680, 4427]], [[2720, 4427], [2720, 4585], [2733, 4585], [2733, 4427]], [[2759, 4427], [2759, 4585], [2759, 4585], [2759, 4427]], [[2786, 4427], [2786, 4585], [2786, 4585], [2786, 4427]], [[2838, 4427], [2838, 4585], [2851, 4585], [2851, 4427]], [[2865, 4427], [2865, 4585], [2865, 4585], [2865, 4427]], [[2891, 4427], [2891, 4585], [2891, 4585], [2891, 4427]], [[2917, 4427], [2917, 4585], [2930, 4585], [2930, 4427]], [[2944, 4427], [2944, 4585], [2957, 4585], [2957, 4427]], [[2996, 4427], [2996, 4585], [2996, 4585], [2996, 4427]], [[3023, 4427], [3023, 4585], [3023, 4585], [3023, 4427]], [[3049, 4427], [3049, 4585], [3049, 4585], [3049, 4427]], [[3102, 4427], [3102, 4585], [3102, 4585], [3102, 4427]]], "confidences": [0.8309170007705688, 0.9710848927497864, 0.9978118538856506, 0.9952423572540283, 0.9998619556427002, 0.9853734970092773, 0.9041633605957031, 0.9999642372131348, 0.9992184638977051, 0.987206757068634, 0.9999744892120361, 0.9998753070831299, 0.9998530149459839, 0.9993923902511597, 0.9804210662841797, 0.9999669790267944, 0.9681344628334045, 0.7007457613945007, 0.9999014139175415, 0.9938947558403015, 0.9991243481636047, 0.9994795918464661, 0.9906601309776306, 0.9998043179512024, 0.9999531507492065, 0.9996731281280518, 0.9904140830039978, 0.8484129309654236, 0.9998654127120972, 0.9998722076416016, 0.884601891040802, 0.9997289776802063, 0.5172210335731506, 0.9777674078941345, 0.9999169111251831, 0.49916383624076843, 0.9641693830490112, 0.9937703013420105, 0.7306916117668152, 0.9957754015922546, 0.9984592199325562, 0.943457841873169, 0.6241721510887146, 0.9954598546028137, 0.9971427321434021, 0.8888192176818848, 0.9960846900939941, 0.9893342852592468, 0.9664679169654846, 0.9914203882217407, 0.9974316954612732, 0.9991137385368347, 0.9997954964637756, 0.9750413298606873, 0.9770325422286987, 0.9967040419578552, 0.9981144666671753, 0.9363402724266052, 0.9950194358825684, 0.8760650753974915, 0.9999531507492065, 0.9947760105133057, 0.99937504529953, 0.9894686341285706, 0.9319698810577393, 0.9841182231903076, 0.998865008354187, 0.9998772144317627, 0.9999456405639648, 0.9940637946128845, 0.8951064348220825, 0.9981452226638794, 0.9989315867424011, 0.9663097262382507], "line": {"id": "c54cd0bb-3f6d-4b93-9d2b-977fe67ebc5f", "bbox": {"id": "47a85dad-e82c-41b6-9a41-f6a85da7f60b", "bbox": [[297, 4427], [297, 4585], [3128, 4585], [3128, 4427]]}, "type": "bbox"}}] \ No newline at end of file diff --git a/tests/test_align.py b/tests/test_align.py new file mode 100644 index 000000000..81080ed8a --- /dev/null +++ b/tests/test_align.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- + +import unittest +import json + +import kraken +import dataclasses + +from pathlib import Path + +from kraken.lib import xml +from kraken.align import forced_align + +thisfile = Path(__file__).resolve().parent +resources = thisfile / 'resources' + +class TestKrakenAlign(unittest.TestCase): + """ + Tests for the forced alignment module + """ + def setUp(self): + self.doc = resources / '170025120000003,0074.xml' + self.bls = xml.XMLPage(self.doc).to_container() + + def test_forced_align_simple(self): + """ + Simple alignment test. + """ + pass diff --git a/tests/test_serialization.py b/tests/test_serialization.py index 244fc8b1e..4f8e0d3a7 100644 --- a/tests/test_serialization.py +++ b/tests/test_serialization.py @@ -10,7 +10,7 @@ from hocr_spec import HocrValidator from collections import Counter -from kraken import rpred, serialization +from kraken import containers, serialization from kraken.lib import xml thisfile = Path(__file__).resolve().parent @@ -24,10 +24,10 @@ def roundtrip(self, records, fp): with tempfile.NamedTemporaryFile() as out: fp.seek(0) out.write(fp.getvalue().encode('utf-8')) - doc = xml.parse_xml(out.name)['lines'] + doc = xml.XMLPage(out.name).to_container().lines for orig_line, parsed_line in zip(records, doc): self.assertSequenceEqual(np.array(orig_line.baseline).tolist(), - np.array(parsed_line['baseline']).tolist(), + np.array(parsed_line.baseline).tolist(), msg='Baselines differ after serialization.') def validate_hocr(self, fp): @@ -73,13 +73,40 @@ class TestSerializations(unittest.TestCase): """ def setUp(self): with open(resources /'records.json', 'r') as fp: - self.box_records = [rpred.BBoxOCRRecord(**x) for x in json.load(fp)] + self.box_records = [containers.BBoxOCRRecord(**x) for x in json.load(fp)] with open(resources / 'bl_records.json', 'r') as fp: recs = json.load(fp) - self.bl_records = [rpred.BaselineOCRRecord(**bl) for bl in recs['lines']] + self.bl_records = [containers.BaselineOCRRecord(**bl) for bl in recs['lines']] self.bl_regions = recs['regions'] + self.box_segmentation = containers.Segmentation(type='bbox', + imagename='foo.png', + text_direction='horizontal-lr', + lines=self.box_records, + script_detection=True, + regions={}) + + self.bl_segmentation = containers.Segmentation(type='baselines', + imagename='foo.png', + text_direction='horizontal-lr', + lines=self.bl_records, + script_detection=True, + regions={}) + + self.bl_segmentation_regs = containers.Segmentation(type='baselines', + imagename='foo.png', + text_direction='horizontal-lr', + lines=self.box_records, + script_detection=True, + regions=self.bl_regions) + self.bl_seg_nolines_regs = containers.Segmentation(type='baselines', + imagename='foo.png', + text_direction='horizontal-lr', + script_detection=False, + lines=[], + regions=self.bl_regions) + self.metadata_steps = [{'category': 'preprocessing', 'description': 'PDF image extraction', 'settings': {}}, {'category': 'processing', 'description': 'Baseline and region segmentation', @@ -98,7 +125,7 @@ def test_box_vertical_hocr_serialization(self): """ fp = StringIO() - fp.write(serialization.serialize(self.box_records, image_name='foo.png', writing_mode='vertical-lr', template='hocr')) + fp.write(serialization.serialize(self.box_segmentation, writing_mode='vertical-lr', template='hocr')) validate_hocr(self, fp) def test_box_hocr_serialization(self): @@ -107,7 +134,7 @@ def test_box_hocr_serialization(self): """ fp = StringIO() - fp.write(serialization.serialize(self.box_records, image_name='foo.png', template='hocr')) + fp.write(serialization.serialize(self.box_segmentation, template='hocr')) validate_hocr(self, fp) def test_box_alto_serialization_validation(self): @@ -116,7 +143,7 @@ def test_box_alto_serialization_validation(self): """ fp = StringIO() - fp.write(serialization.serialize(self.box_records, image_name='foo.png', template='alto')) + fp.write(serialization.serialize(self.box_segmentation, template='alto')) validate_alto(self, fp) def test_box_abbyyxml_serialization_validation(self): @@ -125,7 +152,7 @@ def test_box_abbyyxml_serialization_validation(self): """ fp = StringIO() - fp.write(serialization.serialize(self.box_records, image_name='foo.png', template='abbyyxml')) + fp.write(serialization.serialize(self.box_segmentation, template='abbyyxml')) doc = etree.fromstring(fp.getvalue().encode('utf-8')) with open(resources / 'FineReader10-schema-v1.xml') as schema_fp: abbyy_schema = etree.XMLSchema(etree.parse(schema_fp)) @@ -137,7 +164,7 @@ def test_box_pagexml_serialization_validation(self): """ fp = StringIO() - fp.write(serialization.serialize(self.box_records, image_name='foo.png', template='pagexml')) + fp.write(serialization.serialize(self.box_segmentation, template='pagexml')) validate_page(self, fp) def test_bl_alto_serialization_validation(self): @@ -146,7 +173,7 @@ def test_bl_alto_serialization_validation(self): """ fp = StringIO() - fp.write(serialization.serialize(self.bl_records, image_name='foo.png', template='alto')) + fp.write(serialization.serialize(self.bl_segmentation, template='alto')) validate_alto(self, fp) roundtrip(self, self.bl_records, fp) @@ -156,7 +183,7 @@ def test_bl_abbyyxml_serialization_validation(self): """ fp = StringIO() - fp.write(serialization.serialize(self.bl_records, image_name='foo.png', template='abbyyxml')) + fp.write(serialization.serialize(self.bl_segmentation, template='abbyyxml')) doc = etree.fromstring(fp.getvalue().encode('utf-8')) with open(resources / 'FineReader10-schema-v1.xml') as schema_fp: abbyy_schema = etree.XMLSchema(etree.parse(schema_fp)) @@ -168,7 +195,7 @@ def test_bl_pagexml_serialization_validation(self): """ fp = StringIO() - fp.write(serialization.serialize(self.bl_records, image_name='foo.png', template='pagexml')) + fp.write(serialization.serialize(self.bl_segmentation, template='pagexml')) validate_page(self, fp) roundtrip(self, self.bl_records, fp) @@ -178,7 +205,7 @@ def test_bl_region_alto_serialization_validation(self): """ fp = StringIO() - fp.write(serialization.serialize(self.bl_records, image_name='foo.png', template='alto', regions=self.bl_regions)) + fp.write(serialization.serialize(self.bl_segmentation_regs, template='alto')) validate_alto(self, fp) roundtrip(self, self.bl_records, fp) @@ -188,7 +215,7 @@ def test_bl_region_abbyyxml_serialization_validation(self): """ fp = StringIO() - fp.write(serialization.serialize(self.bl_records, image_name='foo.png', template='abbyyxml', regions=self.bl_regions)) + fp.write(serialization.serialize(self.bl_segmentation_regs, template='abbyyxml')) doc = etree.fromstring(fp.getvalue().encode('utf-8')) with open(resources / 'FineReader10-schema-v1.xml') as schema_fp: abbyy_schema = etree.XMLSchema(etree.parse(schema_fp)) @@ -200,7 +227,7 @@ def test_bl_region_pagexml_serialization_validation(self): """ fp = StringIO() - fp.write(serialization.serialize(self.bl_records, image_name='foo.png', template='pagexml', regions=self.bl_regions)) + fp.write(serialization.serialize(self.bl_segmentation_regs, template='pagexml')) validate_page(self, fp) roundtrip(self, self.bl_records, fp) @@ -210,7 +237,7 @@ def test_region_only_alto_serialization_validation(self): """ fp = StringIO() - fp.write(serialization.serialize([], image_name='foo.png', template='alto', regions=self.bl_regions)) + fp.write(serialization.serialize(self.bl_seg_nolines_regs, template='alto')) validate_alto(self, fp) def test_region_only_abbyyxml_serialization_validation(self): @@ -219,7 +246,7 @@ def test_region_only_abbyyxml_serialization_validation(self): """ fp = StringIO() - fp.write(serialization.serialize([], image_name='foo.png', template='abbyyxml', regions=self.bl_regions)) + fp.write(serialization.serialize(self.bl_seg_nolines_regs, template='abbyyxml')) doc = etree.fromstring(fp.getvalue().encode('utf-8')) with open(resources / 'FineReader10-schema-v1.xml') as schema_fp: abbyy_schema = etree.XMLSchema(etree.parse(schema_fp)) @@ -231,7 +258,7 @@ def test_region_only_pagexml_serialization_validation(self): """ fp = StringIO() - fp.write(serialization.serialize([], image_name='foo.png', template='pagexml', regions=self.bl_regions)) + fp.write(serialization.serialize(self.bl_seg_nolines_regs, template='pagexml')) validate_page(self, fp) def test_serialize_segmentation_alto(self): diff --git a/tests/test_xml.py b/tests/test_xml.py index 611959d2e..dbb876012 100644 --- a/tests/test_xml.py +++ b/tests/test_xml.py @@ -25,7 +25,7 @@ def test_page_parsing(self): Test parsing of PAGE XML files with reading order. """ doc = xml.XMLPage(self.page_doc, filetype='page') - self.assertEqual(len(doc.baselines), 97) + self.assertEqual(len(doc.get_sorted_lines()), 97) self.assertEqual(len([item for x in doc.regions.values() for item in x]), 4) def test_alto_parsing(self):