mirror of
https://github.com/qurator-spk/eynollah.git
synced 2026-07-26 05:29:16 +02:00
layout/writer: simplify and refactor using dataclasses for segments…
- `utils`:
- add dataclasses `Region` (including contour, skew, conf,
centers and area – the latter computed during init, but only once
instead of over and over) and `TextRegion` (ditto, but with a list
of lines) to encapsulate intermediate results more succinctly
- drop `check_any_text_region_in_model_one_is_main_or_header`
- `run_single()`:
- instantiate `Region` and `TextRegion` lists for separator lines,
drop-caps, marginals, paragraphs, headings, images, tables instead
of multiple sets of identifiers for these segment types, name them
more consistently
- apply contour "dilation" (really Shapely polygon instantiation,
repair/join and buffering) only once in the writer after rescaling
to original/final image size
- `filter_contours_inside_a_bigger_one()`: separate and rename
- → `filter_small_regions()`: taking lists of `Region` (original and
deskewed) for paragraphs and marginals, filtering small regions
that are contained in large regions or marginals in-place
- → `filter_small_textlines()`: taking a list of `TextRegion` and
filtering lines that are contained in larger lines (of possibly
other regions) in-place
- `filter_contours_without_textlines_inside()`: simplify and rename
→ `filter_textregions_without_textlines()`
- rename `separate_marginals_to_left_and_right_and_order_from_top_to_down`
→ `separate_marginals_and_order()` and simplify
- `get_slopes_and_deskew*`: work on list of `Region` in-place
(modifying their `.skew` and `.lines`), extract line confidence, too
- writer:
- expect `Region` and `TextRegion` instances for various segment types
instead of wild list of identifiers
- simplify (a lot)
- merge `build_pagexml_no_full_layout` and build_pagexml_full_layout`
→ `build_pagexml()` (w/ empty lists as default)
- also annotate textline confidence
- enlarge/dilate/buffer coordinates differently for various segment types:
6px for text and tables, 2px for images and seplines
- add more type hints
This commit is contained in:
parent
5e3fde31d9
commit
beb5545899
5 changed files with 547 additions and 808 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -1,10 +1,11 @@
|
||||||
|
from typing import Optional
|
||||||
|
import os.path
|
||||||
try:
|
try:
|
||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
import matplotlib.patches as mpatches
|
import matplotlib.patches as mpatches
|
||||||
except ImportError:
|
except ImportError:
|
||||||
plt = mpatches = None
|
plt = mpatches = None
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import os.path
|
|
||||||
import cv2
|
import cv2
|
||||||
from scipy.ndimage import gaussian_filter1d
|
from scipy.ndimage import gaussian_filter1d
|
||||||
|
|
||||||
|
|
@ -21,11 +22,11 @@ class EynollahPlotter:
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
dir_out,
|
dir_out,
|
||||||
dir_of_all,
|
dir_of_all: Optional[str] = None,
|
||||||
dir_save_page,
|
dir_save_page: Optional[str] = None,
|
||||||
dir_of_deskewed,
|
dir_of_deskewed: Optional[str] = None,
|
||||||
dir_of_layout,
|
dir_of_layout: Optional[str] = None,
|
||||||
dir_of_cropped_images,
|
dir_of_cropped_images: Optional[str] = None,
|
||||||
):
|
):
|
||||||
self.dir_out = dir_out
|
self.dir_out = dir_out
|
||||||
self.dir_of_all = dir_of_all
|
self.dir_of_all = dir_of_all
|
||||||
|
|
@ -172,6 +173,8 @@ class EynollahPlotter:
|
||||||
x, y, w, h = cv2.boundingRect(cont_ind)
|
x, y, w, h = cv2.boundingRect(cont_ind)
|
||||||
box = [x, y, w, h]
|
box = [x, y, w, h]
|
||||||
image, _ = crop_image_inside_box(box, image_page)
|
image, _ = crop_image_inside_box(box, image_page)
|
||||||
|
if not image.size:
|
||||||
|
continue
|
||||||
image = resize_image(image,
|
image = resize_image(image,
|
||||||
int(image.shape[0] / scale_y),
|
int(image.shape[0] / scale_y),
|
||||||
int(image.shape[1] / scale_x))
|
int(image.shape[1] / scale_x))
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@ from typing import Iterable, List, Tuple
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
import time
|
import time
|
||||||
import math
|
import math
|
||||||
from itertools import islice
|
from dataclasses import dataclass, field
|
||||||
|
from itertools import islice, compress
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
|
|
@ -10,16 +11,18 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
plt = None
|
plt = None
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from shapely import geometry
|
from shapely import geometry, prepare, ops, plotting
|
||||||
import cv2
|
import cv2
|
||||||
from scipy.signal import find_peaks
|
from scipy.signal import find_peaks
|
||||||
from scipy.ndimage import gaussian_filter1d
|
from scipy.ndimage import gaussian_filter1d
|
||||||
from skimage import morphology
|
from skimage import morphology
|
||||||
|
|
||||||
from .is_nan import isNaN
|
from .is_nan import isNaN
|
||||||
from .contour import (contours_in_same_horizon,
|
from .contour import (contour2polygon,
|
||||||
|
contours_in_same_horizon,
|
||||||
find_center_of_contours,
|
find_center_of_contours,
|
||||||
find_new_features_of_contours,
|
find_new_features_of_contours,
|
||||||
|
polygon2contour,
|
||||||
return_contours_of_image,
|
return_contours_of_image,
|
||||||
return_parent_contours)
|
return_parent_contours)
|
||||||
|
|
||||||
|
|
@ -39,6 +42,32 @@ def batched(iterable, n):
|
||||||
while batch := tuple(islice(iterator, n)):
|
while batch := tuple(islice(iterator, n)):
|
||||||
yield batch
|
yield batch
|
||||||
|
|
||||||
|
def itemgetter(seq):
|
||||||
|
# replacement for operator.itemgetter
|
||||||
|
# (which is ambiguous about its return type:
|
||||||
|
# single-item if 1 argument, tuple otherwise)
|
||||||
|
def fun(obj):
|
||||||
|
return list(obj[idx] for idx in seq)
|
||||||
|
return fun
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Region:
|
||||||
|
contour: np.ndarray
|
||||||
|
area: int = 0
|
||||||
|
cx: float = 0
|
||||||
|
cy: float = 0
|
||||||
|
skew: float = 0
|
||||||
|
conf: float = 0
|
||||||
|
def __post_init__(self):
|
||||||
|
self.area = cv2.contourArea(self.contour)
|
||||||
|
moments = cv2.moments(self.contour)
|
||||||
|
self.cx = moments['m10'] / (moments['m00'] or 1e-32)
|
||||||
|
self.cy = moments['m01'] / (moments['m00'] or 1e-32)
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class TextRegion(Region):
|
||||||
|
lines: List[Region] = field(default_factory=list)
|
||||||
|
|
||||||
def return_multicol_separators_x_start_end(
|
def return_multicol_separators_x_start_end(
|
||||||
regions_without_separators, peak_points, top, bot,
|
regions_without_separators, peak_points, top, bot,
|
||||||
x_min_hor_some, x_max_hor_some, cy_hor_some, y_min_hor_some, y_max_hor_some):
|
x_min_hor_some, x_max_hor_some, cy_hor_some, y_min_hor_some, y_max_hor_some):
|
||||||
|
|
@ -800,93 +829,16 @@ def fill_bb_of_drop_capitals(
|
||||||
|
|
||||||
return full_prediction == label_drop_fl_model
|
return full_prediction == label_drop_fl_model
|
||||||
|
|
||||||
def check_any_text_region_in_model_one_is_main_or_header(
|
|
||||||
regions_model_1, regions_model_full,
|
|
||||||
contours_only_text_parent,
|
|
||||||
all_box_coord, all_found_textline_polygons,
|
|
||||||
slopes,
|
|
||||||
contours_only_text_parent_d_ordered, conf_contours):
|
|
||||||
|
|
||||||
cx_main, cy_main, x_min_main, x_max_main, y_min_main, y_max_main, y_corr_x_min_from_argmin = \
|
|
||||||
find_new_features_of_contours(contours_only_text_parent)
|
|
||||||
|
|
||||||
length_con=x_max_main-x_min_main
|
|
||||||
height_con=y_max_main-y_min_main
|
|
||||||
|
|
||||||
all_found_textline_polygons_main=[]
|
|
||||||
all_found_textline_polygons_head=[]
|
|
||||||
|
|
||||||
all_box_coord_main=[]
|
|
||||||
all_box_coord_head=[]
|
|
||||||
|
|
||||||
slopes_main=[]
|
|
||||||
slopes_head=[]
|
|
||||||
|
|
||||||
contours_only_text_parent_main=[]
|
|
||||||
contours_only_text_parent_head=[]
|
|
||||||
|
|
||||||
conf_contours_main=[]
|
|
||||||
conf_contours_head=[]
|
|
||||||
|
|
||||||
contours_only_text_parent_main_d=[]
|
|
||||||
contours_only_text_parent_head_d=[]
|
|
||||||
|
|
||||||
for ii, con in enumerate(contours_only_text_parent):
|
|
||||||
img = np.zeros(regions_model_1.shape[:2])
|
|
||||||
img = cv2.fillPoly(img, pts=[con], color=255)
|
|
||||||
|
|
||||||
all_pixels=((img == 255)*1).sum()
|
|
||||||
pixels_header=( ( (img == 255) & (regions_model_full[:,:,0]==2) )*1 ).sum()
|
|
||||||
pixels_main=all_pixels-pixels_header
|
|
||||||
|
|
||||||
if (pixels_header>=pixels_main) and ( (length_con[ii]/float(height_con[ii]) )>=1.3 ):
|
|
||||||
regions_model_1[:,:][(regions_model_1[:,:]==1) & (img == 255) ]=2
|
|
||||||
contours_only_text_parent_head.append(con)
|
|
||||||
if len(contours_only_text_parent_d_ordered):
|
|
||||||
contours_only_text_parent_head_d.append(contours_only_text_parent_d_ordered[ii])
|
|
||||||
all_box_coord_head.append(all_box_coord[ii])
|
|
||||||
slopes_head.append(slopes[ii])
|
|
||||||
all_found_textline_polygons_head.append(all_found_textline_polygons[ii])
|
|
||||||
conf_contours_head.append(None)
|
|
||||||
else:
|
|
||||||
regions_model_1[:,:][(regions_model_1[:,:]==1) & (img == 255) ]=1
|
|
||||||
contours_only_text_parent_main.append(con)
|
|
||||||
conf_contours_main.append(conf_contours[ii])
|
|
||||||
if len(contours_only_text_parent_d_ordered):
|
|
||||||
contours_only_text_parent_main_d.append(contours_only_text_parent_d_ordered[ii])
|
|
||||||
all_box_coord_main.append(all_box_coord[ii])
|
|
||||||
slopes_main.append(slopes[ii])
|
|
||||||
all_found_textline_polygons_main.append(all_found_textline_polygons[ii])
|
|
||||||
|
|
||||||
#print(all_pixels,pixels_main,pixels_header)
|
|
||||||
|
|
||||||
return (regions_model_1,
|
|
||||||
contours_only_text_parent_main,
|
|
||||||
contours_only_text_parent_head,
|
|
||||||
all_box_coord_main,
|
|
||||||
all_box_coord_head,
|
|
||||||
all_found_textline_polygons_main,
|
|
||||||
all_found_textline_polygons_head,
|
|
||||||
slopes_main,
|
|
||||||
slopes_head,
|
|
||||||
contours_only_text_parent_main_d,
|
|
||||||
contours_only_text_parent_head_d,
|
|
||||||
conf_contours_main,
|
|
||||||
conf_contours_head)
|
|
||||||
|
|
||||||
def split_textregion_main_vs_head(
|
def split_textregion_main_vs_head(
|
||||||
regions_model_1,
|
regions_model_1: np.ndarray,
|
||||||
regions_model_full,
|
regions_model_full: np.ndarray,
|
||||||
polygons_of_textregions,
|
textregions: List[Region],
|
||||||
polygons_of_textregions_d,
|
textregions_d: List[Region],
|
||||||
all_found_textline_polygons,
|
|
||||||
slopes,
|
|
||||||
conf_textregions,
|
|
||||||
label_text=1,
|
label_text=1,
|
||||||
label_head_full=2,
|
label_head_full=2,
|
||||||
label_head_final=2,
|
label_head_final=2,
|
||||||
label_main_final=1,
|
label_main_final=1,
|
||||||
):
|
) -> Tuple[np.ndarray, List[Region], List[Region], List[Region], List[Region]]:
|
||||||
|
|
||||||
### to make it faster
|
### to make it faster
|
||||||
h_o = regions_model_1.shape[0]
|
h_o = regions_model_1.shape[0]
|
||||||
|
|
@ -900,19 +852,12 @@ def split_textregion_main_vs_head(
|
||||||
(regions_model_full.shape[1] // zoom,
|
(regions_model_full.shape[1] // zoom,
|
||||||
regions_model_full.shape[0] // zoom),
|
regions_model_full.shape[0] // zoom),
|
||||||
interpolation=cv2.INTER_NEAREST)
|
interpolation=cv2.INTER_NEAREST)
|
||||||
contours_z = [contour // zoom
|
contours_z = [textregion.contour // zoom
|
||||||
for contour in polygons_of_textregions]
|
for textregion in textregions]
|
||||||
|
|
||||||
###
|
main = np.ones(len(contours_z), dtype=bool)
|
||||||
_, _, x_min_main, x_max_main, y_min_main, y_max_main, _ = \
|
|
||||||
find_new_features_of_contours(contours_z)
|
|
||||||
|
|
||||||
length_con=x_max_main-x_min_main
|
|
||||||
height_con=y_max_main-y_min_main
|
|
||||||
|
|
||||||
main = []
|
|
||||||
head = []
|
|
||||||
for ii, con in enumerate(contours_z):
|
for ii, con in enumerate(contours_z):
|
||||||
|
width, height = cv2.boundingRect(con)[2:]
|
||||||
parent = np.zeros_like(regions_model_1)
|
parent = np.zeros_like(regions_model_1)
|
||||||
parent = cv2.fillPoly(parent, pts=[con], color=1)
|
parent = cv2.fillPoly(parent, pts=[con], color=1)
|
||||||
|
|
||||||
|
|
@ -920,16 +865,14 @@ def split_textregion_main_vs_head(
|
||||||
pixels_main = parent.sum() - pixels_head
|
pixels_main = parent.sum() - pixels_head
|
||||||
|
|
||||||
if (( pixels_head >= 0.6 * pixels_main and
|
if (( pixels_head >= 0.6 * pixels_main and
|
||||||
length_con[ii] >= 1.3 * height_con[ii] and
|
width >= 1.3 * height and
|
||||||
length_con[ii] <= 3 * height_con[ii] ) or
|
width <= 3 * height ) or
|
||||||
( pixels_head >= 0.3 * pixels_main and
|
( pixels_head >= 0.3 * pixels_main and
|
||||||
length_con[ii] >= 3 * height_con[ii] )):
|
width >= 3 * height )):
|
||||||
|
|
||||||
head.append(ii)
|
main[ii] = False
|
||||||
label = label_head_final
|
label = label_head_final
|
||||||
|
|
||||||
else:
|
else:
|
||||||
main.append(ii)
|
|
||||||
label = label_main_final
|
label = label_main_final
|
||||||
|
|
||||||
regions_model_1[(regions_model_1 == label_text) & (parent > 0)] = label
|
regions_model_1[(regions_model_1 == label_text) & (parent > 0)] = label
|
||||||
|
|
@ -941,35 +884,26 @@ def split_textregion_main_vs_head(
|
||||||
# interpolation=cv2.INTER_NEAREST)
|
# interpolation=cv2.INTER_NEAREST)
|
||||||
###
|
###
|
||||||
|
|
||||||
def select(lis, indexes):
|
|
||||||
if not len(lis):
|
|
||||||
return []
|
|
||||||
return [lis[ind] for ind in indexes]
|
|
||||||
|
|
||||||
return (regions_model_1,
|
return (regions_model_1,
|
||||||
select(polygons_of_textregions, main),
|
list(compress(textregions, main)),
|
||||||
select(polygons_of_textregions, head),
|
list(compress(textregions, ~main)),
|
||||||
select(polygons_of_textregions_d, main),
|
list(compress(textregions_d, main)),
|
||||||
select(polygons_of_textregions_d, head),
|
list(compress(textregions_d, ~main)),
|
||||||
select(all_found_textline_polygons, main),
|
|
||||||
select(all_found_textline_polygons, head),
|
|
||||||
select(slopes, main),
|
|
||||||
select(slopes, head),
|
|
||||||
select(conf_textregions, main),
|
|
||||||
select(conf_textregions, head),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def small_textlines_to_parent_adherence2(textlines_con, textline_mask, num_col):
|
def small_textlines_to_parent_adherence2(
|
||||||
|
textregions: List[TextRegion],
|
||||||
|
area_factor: float,
|
||||||
|
num_col: int,
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
for each region, split up textlines into small and large areas;
|
for each region, split up textlines into small and large;
|
||||||
keep only the ones with large area, but expanded (by merging
|
keep only the ones with large area, but expanded (by merging
|
||||||
contours) by all intersecting lines with small area
|
contours) by all intersecting lines with small area
|
||||||
"""
|
"""
|
||||||
textlines_con_new = []
|
textlines_con_new = []
|
||||||
for region in textlines_con:
|
for textregion in textregions:
|
||||||
areas_cnt_text = np.array(list(map(cv2.contourArea, region)))
|
areas = np.array([line.area for line in textregion.lines]) * area_factor
|
||||||
areas_cnt_text = areas_cnt_text / float(textline_mask.size)
|
|
||||||
indexes_textlines = np.arange(len(region))
|
|
||||||
|
|
||||||
if num_col == 0:
|
if num_col == 0:
|
||||||
min_area = 0.0004
|
min_area = 0.0004
|
||||||
|
|
@ -977,52 +911,43 @@ def small_textlines_to_parent_adherence2(textlines_con, textline_mask, num_col):
|
||||||
min_area = 0.0003
|
min_area = 0.0003
|
||||||
else:
|
else:
|
||||||
min_area = 0.0001
|
min_area = 0.0001
|
||||||
indexes_textlines_small = indexes_textlines[areas_cnt_text < min_area]
|
textlines_small = list(compress(textregion.lines, areas < min_area))
|
||||||
indexes_textlines_large = indexes_textlines[areas_cnt_text >= min_area]
|
textlines_large = list(compress(textregion.lines, areas >= min_area))
|
||||||
|
textlines_small_poly = [contour2polygon(line.contour) for line in textlines_small]
|
||||||
|
textlines_large_poly = [contour2polygon(line.contour) for line in textlines_large]
|
||||||
|
textregion.lines = textlines_large
|
||||||
|
|
||||||
textlines_small = [region[i] for i in indexes_textlines_small]
|
if geometry.MultiPolygon(textlines_small_poly).intersects(
|
||||||
textlines_large = [region[i] for i in indexes_textlines_large]
|
geometry.MultiPolygon(textlines_large_poly)):
|
||||||
|
# FIXME: also consider confidence (less certain lines replaced by better ones)...
|
||||||
img_small = np.zeros_like(textline_mask)
|
textlines_large_prep = [prepare(poly) for poly in textlines_large_poly]
|
||||||
img_small = cv2.fillPoly(img_small, pts=textlines_small, color=1)
|
textlines_small_indexes_interlarge = []
|
||||||
img_large = np.zeros_like(textline_mask)
|
for small_poly in textlines_small_poly:
|
||||||
img_large = cv2.fillPoly(img_large, pts=textlines_large, color=1)
|
intersections = [small_poly.intersection(prep).area
|
||||||
img_inter = img_small + img_large == 2
|
if prep.intersects(small_poly) else 0
|
||||||
if np.any(img_inter):
|
for prep in textlines_large_prep]
|
||||||
indexes_textlines_inter = []
|
|
||||||
for contour_small in textlines_small:
|
|
||||||
intersections = []
|
|
||||||
for contour_large in textlines_large:
|
|
||||||
img0_small = np.zeros_like(textline_mask)
|
|
||||||
img0_small = cv2.fillPoly(img0_small, pts=[contour_small], color=1)
|
|
||||||
img0_large = np.zeros_like(textline_mask)
|
|
||||||
img0_large = cv2.fillPoly(img0_large, pts=[contour_large], color=1)
|
|
||||||
img0_inter = img0_small + img0_large == 2
|
|
||||||
intersections.append(np.count_nonzero(img0_inter))
|
|
||||||
idx_large = np.argmax(intersections)
|
idx_large = np.argmax(intersections)
|
||||||
if intersections[idx_large] <= 0:
|
if intersections[idx_large] == 0:
|
||||||
idx_large = -1
|
idx_large = -1
|
||||||
indexes_textlines_inter.append(idx_large)
|
textlines_small_indexes_interlarge.append(idx_large)
|
||||||
|
textlines_small_indexes_interlarge = np.array(textlines_small_indexes_interlarge)
|
||||||
indexes_textlines_inter = np.array(indexes_textlines_inter)
|
for idx_large in set(textlines_small_indexes_interlarge):
|
||||||
for idx_large in set(indexes_textlines_inter):
|
|
||||||
if idx_large < 0:
|
if idx_large < 0:
|
||||||
continue
|
continue
|
||||||
img0_union = np.zeros_like(textline_mask)
|
large_poly = textlines_large_poly[idx_large]
|
||||||
img0_union = cv2.fillPoly(img0_union, pts=[textlines_large[idx_large]], color=255)
|
indexes_small = np.flatnonzero(textlines_small_indexes_interlarge == idx_large)
|
||||||
indexes_inter_small = np.flatnonzero(indexes_textlines_inter == idx_large)
|
for idx_small in indexes_small:
|
||||||
for idx_small in indexes_inter_small:
|
large_poly = large_poly.union(textlines_small_poly[idx_small])
|
||||||
img0_union = cv2.fillPoly(img0_union, pts=[textlines_small[idx_small]], color=255)
|
if large_poly.geom_type == 'GeometryCollection':
|
||||||
|
# hetergeneous: filter lines and points
|
||||||
_, thresh = cv2.threshold(img0_union, 0, 255, 0)
|
large_poly = ops.unary_union([geom for geom in large_poly.geoms
|
||||||
contours_union, _ = cv2.findContours(thresh.astype(np.uint8),
|
if geom.area > 0])
|
||||||
cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
if large_poly.geom_type == 'MultiPolygon':
|
||||||
areas_union = np.array(list(map(cv2.contourArea, contours_union)))
|
# disjoint: pick largest part
|
||||||
contour_union = contours_union[np.argmax(areas_union)] #contours_union[0]
|
idx_large = np.argmax([geom.area for geom in large_poly.geoms])
|
||||||
textlines_large[idx_large] = contour_union
|
large_poly = large_poly.geoms[idx_large]
|
||||||
|
# replace original
|
||||||
textlines_con_new.append(textlines_large)
|
textregion.lines[idx_large].contour = polygon2contour(large_poly)
|
||||||
return textlines_con_new
|
|
||||||
|
|
||||||
def order_of_regions(textline_mask, contours_main, contours_head, contours_drop, y_ref, x_ref):
|
def order_of_regions(textline_mask, contours_main, contours_head, contours_drop, y_ref, x_ref):
|
||||||
"""
|
"""
|
||||||
|
|
@ -1051,7 +976,7 @@ def order_of_regions(textline_mask, contours_main, contours_head, contours_drop,
|
||||||
total = len(contours_main) + len(contours_head) + len(contours_drop)
|
total = len(contours_main) + len(contours_head) + len(contours_drop)
|
||||||
assert total == 0 or np.any(textline_mask)
|
assert total == 0 or np.any(textline_mask)
|
||||||
|
|
||||||
# ax1 = plt.subplot(2, 1, 1, title="order_of_regions textline_mask")
|
# ax1 = plt.subplot(1, 2, 1, title="order_of_regions textline_mask")
|
||||||
# plt.imshow(textline_mask, aspect='auto')
|
# plt.imshow(textline_mask, aspect='auto')
|
||||||
y = textline_mask.sum(axis=1) # horizontal projection profile
|
y = textline_mask.sum(axis=1) # horizontal projection profile
|
||||||
y_padded = np.zeros(len(y) + 40)
|
y_padded = np.zeros(len(y) + 40)
|
||||||
|
|
@ -1061,15 +986,21 @@ def order_of_regions(textline_mask, contours_main, contours_head, contours_drop,
|
||||||
#z = gaussian_filter1d(y_padded, sigma_gaus)
|
#z = gaussian_filter1d(y_padded, sigma_gaus)
|
||||||
#peaks, _ = find_peaks(z, height=0)
|
#peaks, _ = find_peaks(z, height=0)
|
||||||
#peaks = peaks - 20
|
#peaks = peaks - 20
|
||||||
# ax2 = plt.subplot(2, 1, 2, title="smoothed horizontal projection", sharex=ax1)
|
# ax2 = plt.subplot(1, 2, 2, title="smoothed horizontal projection", sharey=ax1)
|
||||||
# plt.plot(y)
|
# ax2plot = plt.plot(y)[0]
|
||||||
|
# xdata = ax2plot.get_xdata()
|
||||||
|
# ydata = ax2plot.get_ydata()
|
||||||
|
# ax2plot.set_xdata(ydata)
|
||||||
|
# ax2plot.set_ydata(xdata)
|
||||||
|
# ax2.set_xlim(*ax1.get_xlim())
|
||||||
|
# ax2.set_ylim(*ax1.get_ylim())
|
||||||
zneg_rev = np.max(y_padded) - y_padded
|
zneg_rev = np.max(y_padded) - y_padded
|
||||||
zneg = np.zeros(len(zneg_rev) + 40)
|
zneg = np.zeros(len(zneg_rev) + 40)
|
||||||
zneg[20 : len(zneg_rev) + 20] = zneg_rev
|
zneg[20 : len(zneg_rev) + 20] = zneg_rev
|
||||||
zneg = gaussian_filter1d(zneg, sigma_gaus)
|
zneg = gaussian_filter1d(zneg, sigma_gaus)
|
||||||
|
|
||||||
peaks_neg, _ = find_peaks(zneg, height=0)
|
peaks_neg, _ = find_peaks(zneg, height=0)
|
||||||
# plt.vlines(peaks_neg - 40, 0, None, label="peaks")
|
# plt.hlines(peaks_neg - 40, 0, textline_mask.shape[1], label="peaks")
|
||||||
# plt.show()
|
# plt.show()
|
||||||
peaks_neg = peaks_neg - 20 - 20
|
peaks_neg = peaks_neg - 20 - 20
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
from typing import Sequence, Union
|
from typing import List, Sequence, Union, Tuple
|
||||||
from numbers import Number
|
from numbers import Number
|
||||||
from functools import partial
|
from functools import partial
|
||||||
import itertools
|
import itertools
|
||||||
|
|
@ -167,26 +167,34 @@ def dilate_textregion_contours(all_found_textregion_polygons):
|
||||||
[polygon2contour(contour2polygon(contour, dilate=6))
|
[polygon2contour(contour2polygon(contour, dilate=6))
|
||||||
for contour in all_found_textregion_polygons])
|
for contour in all_found_textregion_polygons])
|
||||||
|
|
||||||
def match_deskewed_contours(slope_deskew, contours_o, contours_d, shape_o, shape_d):
|
def match_deskewed_contours(
|
||||||
|
slope_deskew: float,
|
||||||
|
regions_o, #: List[Region], # (cyclic import)
|
||||||
|
regions_d, #: List[Region], # (cyclic import)
|
||||||
|
shape_o: Tuple[int, int],
|
||||||
|
shape_d: Tuple[int, int],
|
||||||
|
) -> np.ndarray:
|
||||||
from . import ensure_array
|
from . import ensure_array
|
||||||
|
|
||||||
cntareas_o = np.array([cv2.contourArea(contour) for contour in contours_o])
|
centers_o = np.array([[region.cx, region.cy] for region in regions_o]) # [N, 2]
|
||||||
cntareas_d = np.array([cv2.contourArea(contour) for contour in contours_d])
|
centers_d = np.array([[region.cx, region.cy] for region in regions_d]) # [N, 2]
|
||||||
|
cntareas_o = np.array([region.area for region in regions_o])
|
||||||
|
cntareas_d = np.array([region.area for region in regions_d])
|
||||||
cntareas_o = cntareas_o / float(np.prod(shape_o[:2]))
|
cntareas_o = cntareas_o / float(np.prod(shape_o[:2]))
|
||||||
cntareas_d = cntareas_d / float(np.prod(shape_d[:2]))
|
cntareas_d = cntareas_d / float(np.prod(shape_d[:2]))
|
||||||
|
|
||||||
contours_o = ensure_array(contours_o)
|
contours_o = ensure_array([region.contour for region in regions_o])
|
||||||
contours_d = ensure_array(contours_d)
|
contours_d = ensure_array([region.contour for region in regions_d])
|
||||||
|
|
||||||
sort_o = np.argsort(cntareas_o)
|
sort_o = np.argsort(cntareas_o)
|
||||||
sort_d = np.argsort(cntareas_d)
|
sort_d = np.argsort(cntareas_d)
|
||||||
|
centers_o = centers_o[sort_o].T # [2, N]
|
||||||
|
centers_d = centers_d[sort_d].T # [2, N]
|
||||||
contours_o = contours_o[sort_o]
|
contours_o = contours_o[sort_o]
|
||||||
contours_d = contours_d[sort_d]
|
contours_d = contours_d[sort_d]
|
||||||
cntareas_o = cntareas_o[sort_o]
|
cntareas_o = cntareas_o[sort_o]
|
||||||
cntareas_d = cntareas_d[sort_d]
|
cntareas_d = cntareas_d[sort_d]
|
||||||
|
|
||||||
centers_o = np.stack(find_center_of_contours(contours_o)) # [2, N]
|
|
||||||
centers_d = np.stack(find_center_of_contours(contours_d)) # [2, N]
|
|
||||||
center0_o = centers_o[:, -1:] # [2, 1]
|
center0_o = centers_o[:, -1:] # [2, 1]
|
||||||
center0_d = centers_d[:, -1:] # [2, 1]
|
center0_d = centers_d[:, -1:] # [2, 1]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,36 +3,44 @@
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import os.path
|
import os.path
|
||||||
import logging
|
import logging
|
||||||
from typing import Optional
|
from typing import Optional, List, Tuple
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from shapely import affinity, clip_by_rect
|
from shapely import affinity, clip_by_rect
|
||||||
|
|
||||||
from ocrd_utils import points_from_polygon
|
from ocrd_utils import points_from_polygon
|
||||||
from ocrd_models.ocrd_page import (
|
from ocrd_models.ocrd_page import (
|
||||||
BorderType,
|
BorderType,
|
||||||
CoordsType,
|
CoordsType,
|
||||||
TextLineType,
|
TextLineType,
|
||||||
TextEquivType,
|
TextEquivType,
|
||||||
TextRegionType,
|
TextRegionType,
|
||||||
ImageRegionType,
|
ImageRegionType,
|
||||||
TableRegionType,
|
TableRegionType,
|
||||||
SeparatorRegionType,
|
SeparatorRegionType,
|
||||||
to_xml
|
PcGtsType,
|
||||||
)
|
to_xml
|
||||||
|
)
|
||||||
|
|
||||||
|
from .utils import Region, TextRegion
|
||||||
from .utils.xml import create_page_xml, xml_reading_order
|
from .utils.xml import create_page_xml, xml_reading_order
|
||||||
from .utils.counter import EynollahIdCounter
|
from .utils.counter import EynollahIdCounter
|
||||||
from .utils.contour import contour2polygon, make_valid
|
from .utils.contour import contour2polygon, make_valid
|
||||||
|
|
||||||
class EynollahXmlWriter:
|
class EynollahXmlWriter:
|
||||||
|
|
||||||
def __init__(self, *, dir_out, image_filename, image_width, image_height, curved_line, pcgts=None):
|
def __init__(
|
||||||
|
self, *,
|
||||||
|
dir_out: Optional[str],
|
||||||
|
image_filename: str,
|
||||||
|
image_width: int,
|
||||||
|
image_height: int,
|
||||||
|
pcgts: Optional[PcGtsType] = None,
|
||||||
|
):
|
||||||
self.logger = logging.getLogger('eynollah.writer')
|
self.logger = logging.getLogger('eynollah.writer')
|
||||||
self.counter = EynollahIdCounter()
|
self.counter = EynollahIdCounter()
|
||||||
self.dir_out = dir_out
|
self.dir_out = dir_out
|
||||||
self.image_filename = image_filename
|
self.image_filename = image_filename
|
||||||
self.output_filename = os.path.join(self.dir_out or "", self.image_filename_stem) + ".xml"
|
self.output_filename = os.path.join(self.dir_out or "", self.image_filename_stem) + ".xml"
|
||||||
self.curved_line = curved_line
|
|
||||||
self.pcgts = pcgts
|
self.pcgts = pcgts
|
||||||
self.image_height = image_height
|
self.image_height = image_height
|
||||||
self.image_width = image_width
|
self.image_width = image_width
|
||||||
|
|
@ -40,132 +48,56 @@ class EynollahXmlWriter:
|
||||||
self.scale_y = 1.0
|
self.scale_y = 1.0
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def image_filename_stem(self):
|
def image_filename_stem(self) -> str:
|
||||||
return Path(Path(self.image_filename).name).stem
|
return Path(Path(self.image_filename).name).stem
|
||||||
|
|
||||||
def calculate_points(self, contour, offset=None):
|
def calculate_points(
|
||||||
poly = contour2polygon(contour)
|
self, contour: np.ndarray,
|
||||||
|
offset: Optional[List[int]] = None,
|
||||||
|
dilate: int = 0,
|
||||||
|
) -> str:
|
||||||
|
poly = contour2polygon(contour, dilate=dilate)
|
||||||
if offset is not None:
|
if offset is not None:
|
||||||
poly = affinity.translate(poly, *offset)
|
poly = affinity.translate(poly, *offset)
|
||||||
poly = affinity.scale(poly, xfact=1 / self.scale_x, yfact=1 / self.scale_y, origin=(0, 0))
|
poly = affinity.scale(poly, xfact=1 / self.scale_x, yfact=1 / self.scale_y, origin=(0, 0))
|
||||||
poly = make_valid(clip_by_rect(poly, 0, 0, self.image_width, self.image_height))
|
poly = make_valid(clip_by_rect(poly, 0, 0, self.image_width, self.image_height))
|
||||||
return points_from_polygon(poly.exterior.coords[:-1])
|
return points_from_polygon(poly.exterior.coords[:-1])
|
||||||
|
|
||||||
def serialize_lines_in_region(self, text_region, all_found_textline_polygons, region_idx, page_coord, slopes, counter, ocr_all_textlines_textregion):
|
def serialize_lines_in_region(
|
||||||
for j, polygon_textline in enumerate(all_found_textline_polygons[region_idx]):
|
self, text_region: TextRegionType,
|
||||||
coords = CoordsType()
|
offset: List[int],
|
||||||
textline = TextLineType(id=counter.next_line_id, Coords=coords)
|
counter: EynollahIdCounter,
|
||||||
if ocr_all_textlines_textregion:
|
lines: List[Region],
|
||||||
# FIXME: add OCR confidence
|
) -> None:
|
||||||
textline.set_TextEquiv([TextEquivType(Unicode=ocr_all_textlines_textregion[j])])
|
for line in lines:
|
||||||
|
textline = TextLineType(
|
||||||
|
id=counter.next_line_id,
|
||||||
|
Coords=CoordsType(points=self.calculate_points(line.contour, offset, 5),
|
||||||
|
conf=line.conf)
|
||||||
|
)
|
||||||
text_region.add_TextLine(textline)
|
text_region.add_TextLine(textline)
|
||||||
text_region.set_orientation(-slopes[region_idx])
|
|
||||||
offset = [page_coord[2], page_coord[0]]
|
|
||||||
coords.set_points(self.calculate_points(polygon_textline, offset))
|
|
||||||
|
|
||||||
def write_pagexml(self, pcgts):
|
def write_pagexml(self, pcgts):
|
||||||
self.logger.info("output filename: '%s'", self.output_filename)
|
self.logger.info("output filename: '%s'", self.output_filename)
|
||||||
with open(self.output_filename, 'w') as f:
|
with open(self.output_filename, 'w') as f:
|
||||||
f.write(to_xml(pcgts))
|
f.write(to_xml(pcgts))
|
||||||
|
|
||||||
def build_pagexml_no_full_layout(
|
def build_pagexml(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
num_col,
|
num_col=1,
|
||||||
found_polygons_text_region,
|
page_coord: Optional[List[int]] = None,
|
||||||
page_coord,
|
page_contour: Optional[np.ndarray] = None,
|
||||||
page_slope,
|
page_skew: float = 0.,
|
||||||
order_of_texts,
|
order_of_texts: List[int] = [],
|
||||||
all_found_textline_polygons,
|
textregions: List[TextRegion] = [],
|
||||||
found_polygons_images,
|
textregions_h: List[TextRegion] = [],
|
||||||
found_polygons_tables,
|
images: List[Region] = [],
|
||||||
found_polygons_marginals_left,
|
tables: List[Region] = [],
|
||||||
found_polygons_marginals_right,
|
drop_caps: List[Region] = [],
|
||||||
all_found_textline_polygons_marginals_left,
|
marginals_left: List[TextRegion] = [],
|
||||||
all_found_textline_polygons_marginals_right,
|
marginals_right: List[TextRegion] = [],
|
||||||
slopes,
|
seplines: List[Region] = [],
|
||||||
slopes_marginals_left,
|
|
||||||
slopes_marginals_right,
|
|
||||||
cont_page,
|
|
||||||
polygons_seplines,
|
|
||||||
ocr_all_textlines=None,
|
|
||||||
ocr_all_textlines_marginals_left=None,
|
|
||||||
ocr_all_textlines_marginals_right=None,
|
|
||||||
ocr_all_textlines_drop=None,
|
|
||||||
conf_textregions=None,
|
|
||||||
conf_marginals_left=None,
|
|
||||||
conf_marginals_right=None,
|
|
||||||
conf_images=None,
|
|
||||||
conf_tables=None,
|
|
||||||
):
|
|
||||||
return self.build_pagexml_full_layout(
|
|
||||||
num_col=num_col,
|
|
||||||
found_polygons_text_region=found_polygons_text_region,
|
|
||||||
found_polygons_text_region_h=[],
|
|
||||||
page_coord=page_coord,
|
|
||||||
page_slope=page_slope,
|
|
||||||
order_of_texts=order_of_texts,
|
|
||||||
all_found_textline_polygons=all_found_textline_polygons,
|
|
||||||
all_found_textline_polygons_h=[],
|
|
||||||
found_polygons_images=found_polygons_images,
|
|
||||||
found_polygons_tables=found_polygons_tables,
|
|
||||||
found_polygons_drop_capitals=[],
|
|
||||||
found_polygons_marginals_left=found_polygons_marginals_left,
|
|
||||||
found_polygons_marginals_right=found_polygons_marginals_right,
|
|
||||||
all_found_textline_polygons_marginals_left=all_found_textline_polygons_marginals_left,
|
|
||||||
all_found_textline_polygons_marginals_right=all_found_textline_polygons_marginals_right,
|
|
||||||
slopes=slopes,
|
|
||||||
slopes_h=[],
|
|
||||||
slopes_marginals_left=slopes_marginals_left,
|
|
||||||
slopes_marginals_right=slopes_marginals_right,
|
|
||||||
cont_page=cont_page,
|
|
||||||
polygons_seplines=polygons_seplines,
|
|
||||||
ocr_all_textlines=ocr_all_textlines,
|
|
||||||
ocr_all_textlines_marginals_left=ocr_all_textlines_marginals_left,
|
|
||||||
ocr_all_textlines_marginals_right=ocr_all_textlines_marginals_right,
|
|
||||||
conf_textregions=conf_textregions,
|
|
||||||
conf_marginals_left=conf_marginals_left,
|
|
||||||
conf_marginals_right=conf_marginals_right,
|
|
||||||
conf_images=conf_images,
|
|
||||||
conf_tables=conf_tables,
|
|
||||||
)
|
|
||||||
|
|
||||||
def build_pagexml_full_layout(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
num_col,
|
|
||||||
found_polygons_text_region,
|
|
||||||
found_polygons_text_region_h,
|
|
||||||
page_coord,
|
|
||||||
page_slope,
|
|
||||||
order_of_texts,
|
|
||||||
all_found_textline_polygons,
|
|
||||||
all_found_textline_polygons_h,
|
|
||||||
found_polygons_images,
|
|
||||||
found_polygons_tables,
|
|
||||||
found_polygons_drop_capitals,
|
|
||||||
found_polygons_marginals_left,
|
|
||||||
found_polygons_marginals_right,
|
|
||||||
all_found_textline_polygons_marginals_left,
|
|
||||||
all_found_textline_polygons_marginals_right,
|
|
||||||
slopes,
|
|
||||||
slopes_h,
|
|
||||||
slopes_marginals_left,
|
|
||||||
slopes_marginals_right,
|
|
||||||
cont_page,
|
|
||||||
polygons_seplines,
|
|
||||||
ocr_all_textlines=None,
|
|
||||||
ocr_all_textlines_h=None,
|
|
||||||
ocr_all_textlines_marginals_left=None,
|
|
||||||
ocr_all_textlines_marginals_right=None,
|
|
||||||
ocr_all_textlines_drop=None,
|
|
||||||
conf_textregions=None,
|
|
||||||
conf_textregions_h=None,
|
|
||||||
conf_marginals_left=None,
|
|
||||||
conf_marginals_right=None,
|
|
||||||
conf_images=None,
|
|
||||||
conf_tables=None,
|
|
||||||
conf_drops=None,
|
|
||||||
):
|
):
|
||||||
self.logger.debug('enter build_pagexml')
|
self.logger.debug('enter build_pagexml')
|
||||||
|
|
||||||
|
|
@ -175,118 +107,92 @@ class EynollahXmlWriter:
|
||||||
page = pcgts.get_Page()
|
page = pcgts.get_Page()
|
||||||
pcgts.Metadata.Comments = "num_col %d" % num_col
|
pcgts.Metadata.Comments = "num_col %d" % num_col
|
||||||
page.set_custom('layout {num_col:%d;} ' % num_col)
|
page.set_custom('layout {num_col:%d;} ' % num_col)
|
||||||
page.set_orientation(-page_slope)
|
page.set_orientation(-page_skew)
|
||||||
if len(cont_page):
|
if page_contour is not None:
|
||||||
page.set_Border(BorderType(Coords=CoordsType(points=self.calculate_points(cont_page[0]))))
|
page.set_Border(BorderType(Coords=CoordsType(points=self.calculate_points(page_contour))))
|
||||||
|
if page_coord is None:
|
||||||
offset = [page_coord[2], page_coord[0]]
|
offset = [0, 0]
|
||||||
|
else:
|
||||||
|
offset = [page_coord[2], page_coord[0]]
|
||||||
counter = EynollahIdCounter()
|
counter = EynollahIdCounter()
|
||||||
if len(order_of_texts):
|
if len(order_of_texts):
|
||||||
_counter_marginals = EynollahIdCounter(region_idx=len(order_of_texts))
|
_counter_marginals = EynollahIdCounter(region_idx=len(order_of_texts))
|
||||||
id_of_marginalia_left = [_counter_marginals.next_region_id
|
id_of_marginalia_left = [_counter_marginals.next_region_id
|
||||||
for _ in found_polygons_marginals_left]
|
for _ in marginals_left]
|
||||||
id_of_marginalia_right = [_counter_marginals.next_region_id
|
id_of_marginalia_right = [_counter_marginals.next_region_id
|
||||||
for _ in found_polygons_marginals_right]
|
for _ in marginals_right]
|
||||||
xml_reading_order(page, order_of_texts, id_of_marginalia_left, id_of_marginalia_right)
|
xml_reading_order(page, order_of_texts, id_of_marginalia_left, id_of_marginalia_right)
|
||||||
|
|
||||||
for mm, region_contour in enumerate(found_polygons_text_region):
|
for region in textregions:
|
||||||
textregion = TextRegionType(
|
textregion = TextRegionType(
|
||||||
id=counter.next_region_id, type_='paragraph',
|
id=counter.next_region_id, type_='paragraph',
|
||||||
Coords=CoordsType(points=self.calculate_points(region_contour, offset))
|
Coords=CoordsType(points=self.calculate_points(region.contour, offset, 6),
|
||||||
|
conf=region.conf),
|
||||||
|
orientation=-region.skew
|
||||||
)
|
)
|
||||||
assert textregion.Coords
|
self.serialize_lines_in_region(textregion, offset, counter, region.lines)
|
||||||
if conf_textregions:
|
|
||||||
textregion.Coords.set_conf(conf_textregions[mm])
|
|
||||||
page.add_TextRegion(textregion)
|
page.add_TextRegion(textregion)
|
||||||
if ocr_all_textlines:
|
|
||||||
ocr_textlines = ocr_all_textlines[mm]
|
|
||||||
else:
|
|
||||||
ocr_textlines = None
|
|
||||||
self.serialize_lines_in_region(textregion, all_found_textline_polygons, mm, page_coord,
|
|
||||||
slopes, counter, ocr_textlines)
|
|
||||||
|
|
||||||
self.logger.debug('len(found_polygons_text_region_h) %s', len(found_polygons_text_region_h))
|
self.logger.debug('len(textregions_h) %s', len(textregions_h))
|
||||||
for mm, region_contour in enumerate(found_polygons_text_region_h):
|
for region in textregions_h:
|
||||||
textregion = TextRegionType(
|
textregion = TextRegionType(
|
||||||
id=counter.next_region_id, type_='heading',
|
id=counter.next_region_id, type_='heading',
|
||||||
Coords=CoordsType(points=self.calculate_points(region_contour, offset))
|
Coords=CoordsType(points=self.calculate_points(region.contour, offset, 6),
|
||||||
|
conf=region.conf),
|
||||||
|
orientation=-region.skew
|
||||||
)
|
)
|
||||||
assert textregion.Coords
|
self.serialize_lines_in_region(textregion, offset, counter, region.lines)
|
||||||
if conf_textregions_h:
|
|
||||||
textregion.Coords.set_conf(conf_textregions_h[mm])
|
|
||||||
page.add_TextRegion(textregion)
|
page.add_TextRegion(textregion)
|
||||||
if ocr_all_textlines_h:
|
|
||||||
ocr_textlines = ocr_all_textlines_h[mm]
|
|
||||||
else:
|
|
||||||
ocr_textlines = None
|
|
||||||
self.serialize_lines_in_region(textregion, all_found_textline_polygons_h, mm, page_coord,
|
|
||||||
slopes_h, counter, ocr_textlines)
|
|
||||||
|
|
||||||
for mm, region_contour in enumerate(found_polygons_drop_capitals):
|
for region in drop_caps:
|
||||||
dropcapital = TextRegionType(
|
textregion = TextRegionType(
|
||||||
id=counter.next_region_id, type_='drop-capital',
|
id=counter.next_region_id, type_='drop-capital',
|
||||||
Coords=CoordsType(points=self.calculate_points(region_contour, offset))
|
Coords=CoordsType(points=self.calculate_points(region.contour, offset, 6),
|
||||||
|
conf=region.conf),
|
||||||
|
orientation=-region.skew
|
||||||
)
|
)
|
||||||
if conf_drops:
|
self.serialize_lines_in_region(textregion, offset, counter, [region])
|
||||||
dropcapital.Coords.set_conf(conf_drops[mm])
|
page.add_TextRegion(textregion)
|
||||||
page.add_TextRegion(dropcapital)
|
|
||||||
slopes_drop = [0]
|
|
||||||
if ocr_all_textlines_drop:
|
|
||||||
ocr_textlines = ocr_all_textlines_drop[mm]
|
|
||||||
else:
|
|
||||||
ocr_textlines = None
|
|
||||||
self.serialize_lines_in_region(dropcapital, [[found_polygons_drop_capitals[mm]]], 0, page_coord,
|
|
||||||
slopes_drop, counter, ocr_textlines)
|
|
||||||
|
|
||||||
for mm, region_contour in enumerate(found_polygons_marginals_left):
|
for region in marginals_left:
|
||||||
marginal = TextRegionType(
|
textregion = TextRegionType(
|
||||||
id=counter.next_region_id, type_='marginalia',
|
id=counter.next_region_id, type_='marginalia',
|
||||||
Coords=CoordsType(points=self.calculate_points(region_contour, offset))
|
Coords=CoordsType(points=self.calculate_points(region.contour, offset, 6),
|
||||||
|
conf=region.conf),
|
||||||
|
orientation=-region.skew
|
||||||
)
|
)
|
||||||
if conf_marginals_left:
|
self.serialize_lines_in_region(textregion, offset, counter, region.lines)
|
||||||
marginal.Coords.set_conf(conf_marginals_left[mm])
|
page.add_TextRegion(textregion)
|
||||||
page.add_TextRegion(marginal)
|
|
||||||
if ocr_all_textlines_marginals_left:
|
|
||||||
ocr_textlines = ocr_all_textlines_marginals_left[mm]
|
|
||||||
else:
|
|
||||||
ocr_textlines = None
|
|
||||||
self.serialize_lines_in_region(marginal, all_found_textline_polygons_marginals_left, mm, page_coord,
|
|
||||||
slopes_marginals_left, counter, ocr_textlines)
|
|
||||||
|
|
||||||
for mm, region_contour in enumerate(found_polygons_marginals_right):
|
for region in marginals_right:
|
||||||
marginal = TextRegionType(
|
textregion = TextRegionType(
|
||||||
id=counter.next_region_id, type_='marginalia',
|
id=counter.next_region_id, type_='marginalia',
|
||||||
Coords=CoordsType(points=self.calculate_points(region_contour, offset))
|
Coords=CoordsType(points=self.calculate_points(region.contour, offset, 6),
|
||||||
|
conf=region.conf),
|
||||||
|
orientation=-region.skew
|
||||||
)
|
)
|
||||||
if conf_marginals_right:
|
self.serialize_lines_in_region(textregion, offset, counter, region.lines)
|
||||||
marginal.Coords.set_conf(conf_marginals_right[mm])
|
page.add_TextRegion(textregion)
|
||||||
page.add_TextRegion(marginal)
|
|
||||||
if ocr_all_textlines_marginals_right:
|
|
||||||
ocr_textlines = ocr_all_textlines_marginals_right[mm]
|
|
||||||
else:
|
|
||||||
ocr_textlines = None
|
|
||||||
self.serialize_lines_in_region(marginal, all_found_textline_polygons_marginals_right, mm, page_coord,
|
|
||||||
slopes_marginals_right, counter, ocr_textlines)
|
|
||||||
|
|
||||||
for mm, region_contour in enumerate(found_polygons_images):
|
for region in images:
|
||||||
image = ImageRegionType(
|
image = ImageRegionType(
|
||||||
id=counter.next_region_id,
|
id=counter.next_region_id,
|
||||||
Coords=CoordsType(points=self.calculate_points(region_contour, offset)))
|
Coords=CoordsType(points=self.calculate_points(region.contour, offset, 2),
|
||||||
if conf_images:
|
conf=region.conf))
|
||||||
image.Coords.set_conf(conf_images[mm])
|
|
||||||
page.add_ImageRegion(image)
|
page.add_ImageRegion(image)
|
||||||
|
|
||||||
for region_contour in polygons_seplines:
|
for region in seplines:
|
||||||
page.add_SeparatorRegion(
|
page.add_SeparatorRegion(
|
||||||
SeparatorRegionType(id=counter.next_region_id,
|
SeparatorRegionType(
|
||||||
Coords=CoordsType(points=self.calculate_points(region_contour, offset))))
|
id=counter.next_region_id,
|
||||||
|
Coords=CoordsType(points=self.calculate_points(region.contour, offset, 2),
|
||||||
|
conf=region.conf)))
|
||||||
|
|
||||||
for mm, region_contour in enumerate(found_polygons_tables):
|
for region in tables:
|
||||||
table = TableRegionType(
|
table = TableRegionType(
|
||||||
id=counter.next_region_id,
|
id=counter.next_region_id,
|
||||||
Coords=CoordsType(points=self.calculate_points(region_contour, offset)))
|
Coords=CoordsType(points=self.calculate_points(region.contour, offset, 6),
|
||||||
if conf_tables:
|
conf=region.conf))
|
||||||
table.Coords.set_conf(conf_tables[mm])
|
|
||||||
page.add_TableRegion(table)
|
page.add_TableRegion(table)
|
||||||
|
|
||||||
return pcgts
|
return pcgts
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue