|
24 | 24 | )
|
25 | 25 |
|
26 | 26 | from .. import get_ocrd_tool
|
27 |
| -from .ocrolib import midrange |
| 27 | +from .ocrolib import midrange, morph |
28 | 28 | from .common import (
|
29 | 29 | pil2array,
|
| 30 | + odd, |
30 | 31 | # DSAVE,
|
31 | 32 | # binarize,
|
32 | 33 | check_page,
|
@@ -165,6 +166,7 @@ def _process_segment(self, parent, parent_image, parent_coords, page_id, zoom, l
|
165 | 166 | LOG = getLogger('processor.OcropyResegment')
|
166 | 167 | threshold = self.parameter['min_fraction']
|
167 | 168 | margin = self.parameter['extend_margins']
|
| 169 | + method = self.parameter['method'] |
168 | 170 | # prepare line segmentation
|
169 | 171 | parent_array = pil2array(parent_image)
|
170 | 172 | #parent_array, _ = common.binarize(parent_array, maxskew=0) # just in case still raw
|
@@ -216,6 +218,28 @@ def _process_segment(self, parent, parent_image, parent_coords, page_id, zoom, l
|
216 | 218 | ignore_bin[draw.polygon(segment_polygon[:, 1],
|
217 | 219 | segment_polygon[:, 0],
|
218 | 220 | parent_bin.shape)] = True
|
| 221 | + if method != 'lineest': |
| 222 | + LOG.debug('calculating connected component and distance transforms for "%s"', parent.id) |
| 223 | + bin = parent_bin & ~ ignore_bin |
| 224 | + components, _ = morph.label(bin) |
| 225 | + labels = np.insert(line_labels, 0, ignore_bin, axis=0) |
| 226 | + distances = np.zeros_like(labels, np.uint8) |
| 227 | + for i, label in enumerate(labels): |
| 228 | + distances[i] = morph.dist_labels(label.astype(np.uint8)) |
| 229 | + # normalize the distances of all lines so larger ones do not displace smaller ones |
| 230 | + distances[i] = distances[i] / distances[i].max() * 255 |
| 231 | + # estimate glyph scale (roughly) |
| 232 | + _, counts = np.unique(components, return_counts=True) |
| 233 | + if counts.shape[0] > 1: |
| 234 | + counts = np.sqrt(3 * counts) |
| 235 | + scale = int(np.median(counts[(5/zoom < counts) & (counts < 100/zoom)])) |
| 236 | + components *= (counts > 15/zoom)[components] |
| 237 | + LOG.debug("estimated scale: %d", scale) |
| 238 | + else: |
| 239 | + scale = 43 |
| 240 | + spread_dist(lines, line_labels, distances, parent_bin, components, parent_coords, |
| 241 | + scale=scale, loc=parent.id, threshold=threshold) |
| 242 | + return |
219 | 243 | try:
|
220 | 244 | new_line_labels, _, _, _, _, scale = compute_segmentation(
|
221 | 245 | parent_bin, seps=ignore_bin, zoom=zoom, fullpage=fullpage,
|
@@ -351,6 +375,61 @@ def _process_segment(self, parent, parent_image, parent_coords, page_id, zoom, l
|
351 | 375 | continue
|
352 | 376 | otherline.get_Coords().set_points(points_from_polygon(other_polygon))
|
353 | 377 |
|
| 378 | +def spread_dist(lines, labels, distances, binarized, components, coords, |
| 379 | + scale=43, loc='', threshold=0.9): |
| 380 | + """redefine line coordinates by contourizing spread of connected components with max-distance of existing labels |
| 381 | + """ |
| 382 | + LOG = getLogger('processor.OcropyResegment') |
| 383 | + # use depth to flatten overlapping lines as seed labels |
| 384 | + max_labels = np.argmax(distances, axis=0) |
| 385 | + # allocate to connected components consistently (by majority, |
| 386 | + # ignoring smallest components like punctuation) |
| 387 | + #max_ccomps = morph.propagate_labels_majority(binarized, max_labels) |
| 388 | + max_ccomps = morph.propagate_labels_majority(components > 0, max_labels) |
| 389 | + # dilate/grow labels from connected components against each other and bg |
| 390 | + max_ccomps = morph.spread_labels(max_ccomps, maxdist=scale/2) |
| 391 | + # find polygon hull and modify line coords |
| 392 | + for i, line in enumerate(lines): |
| 393 | + mask = max_ccomps == i + 1 |
| 394 | + label = labels[i] |
| 395 | + count = np.count_nonzero(label) |
| 396 | + if not count: |
| 397 | + LOG.warning("skipping zero-area line '%s'", line.id) |
| 398 | + continue |
| 399 | + covers = np.count_nonzero(mask) / count |
| 400 | + if covers < threshold / 3: |
| 401 | + LOG.debug("new line for '%s' only covers %.1f%% bg", |
| 402 | + line.id, covers * 100) |
| 403 | + continue |
| 404 | + count = np.count_nonzero(label * binarized) |
| 405 | + if not count: |
| 406 | + LOG.warning("skipping binarizy-empty line '%s'", line.id) |
| 407 | + continue |
| 408 | + covers = np.count_nonzero(mask * binarized) / count |
| 409 | + if covers < threshold: |
| 410 | + LOG.debug("new line for '%s' only covers %.1f%% fg", |
| 411 | + line.id, covers * 100) |
| 412 | + continue |
| 413 | + LOG.debug('Black pixels before/after resegment of line "%s": %d/%d', |
| 414 | + line.id, count, covers * count) |
| 415 | + contours = [contour[:,::-1] # get x,y order again |
| 416 | + for contour, area in morph.find_contours(mask)] |
| 417 | + #LOG.debug("joining %d subsegments for %s", len(contours), line.id) |
| 418 | + if len(contours) == 0: |
| 419 | + LOG.warning("no contours for %s - keeping", line.id) |
| 420 | + continue |
| 421 | + else: |
| 422 | + # get alpha shape |
| 423 | + poly = join_polygons([make_valid(Polygon(contour)) |
| 424 | + for contour in contours], loc=loc) |
| 425 | + poly = poly.exterior.coords[:-1] |
| 426 | + polygon = coordinates_for_segment(poly, None, coords) |
| 427 | + polygon = polygon_for_parent(polygon, line.parent_object_) |
| 428 | + if polygon is None: |
| 429 | + LOG.warning("Ignoring extant line for %s", line.id) |
| 430 | + continue |
| 431 | + line.get_Coords().set_points(points_from_polygon(polygon)) |
| 432 | + |
354 | 433 | def diff_polygons(poly1, poly2):
|
355 | 434 | poly = poly1.difference(poly2)
|
356 | 435 | if poly.type == 'MultiPolygon':
|
|
0 commit comments