arittrabag commited on
Commit
98e3b33
·
verified ·
1 Parent(s): b27b6da

perf: O(pieces*H*W) -> Theta(H*W) piece extraction via find_objects (103x at 190 pieces)

Browse files
Files changed (1) hide show
  1. src/tearing.py +13 -8
src/tearing.py CHANGED
@@ -19,6 +19,7 @@ from __future__ import annotations
19
  from dataclasses import dataclass
20
 
21
  import numpy as np
 
22
  from scipy.spatial import cKDTree
23
 
24
  from .noise import value_noise
@@ -111,19 +112,23 @@ def tear_page(
111
  _, flat_labels = tree.query(query, k=1, workers=-1)
112
  labels = flat_labels.reshape(H, W).astype(np.int32)
113
 
 
 
 
 
 
 
114
  pieces: list[Piece] = []
115
- for lbl in np.unique(labels):
116
- mask = labels == lbl
117
- ys_idx, xs_idx = np.nonzero(mask)
118
- if ys_idx.size == 0:
119
  continue
120
- y0, y1 = int(ys_idx.min()), int(ys_idx.max()) + 1
121
- x0, x1 = int(xs_idx.min()), int(xs_idx.max()) + 1
122
- sub_mask = mask[y0:y1, x0:x1]
123
  rgb = np.zeros((y1 - y0, x1 - x0, 3), dtype=np.uint8) # black background
124
  rgb[sub_mask] = page_rgb[y0:y1, x0:x1][sub_mask]
125
  pieces.append(
126
- Piece(label=int(lbl), x=x0, y=y0, rgb=rgb, mask=sub_mask)
127
  )
128
 
129
  # Piece-index <-> raw-label map from the pieces we actually emitted, so
 
19
  from dataclasses import dataclass
20
 
21
  import numpy as np
22
+ from scipy.ndimage import find_objects
23
  from scipy.spatial import cKDTree
24
 
25
  from .noise import value_noise
 
112
  _, flat_labels = tree.query(query, k=1, workers=-1)
113
  labels = flat_labels.reshape(H, W).astype(np.int32)
114
 
115
+ # Bounding boxes for every label in ONE pass (Theta(H*W)) instead of a
116
+ # full-array `labels == lbl` scan per piece (O(pieces*H*W) -> the old hot
117
+ # spot at high DPI / many pieces). find_objects indexes by label value, so
118
+ # shift +1 (0 is its "background" sentinel; our labels are 0-based).
119
+ slices = find_objects(labels + 1)
120
+
121
  pieces: list[Piece] = []
122
+ for lbl, sl in enumerate(slices):
123
+ if sl is None: # label value absent from the map
 
 
124
  continue
125
+ y0, y1 = sl[0].start, sl[0].stop
126
+ x0, x1 = sl[1].start, sl[1].stop
127
+ sub_mask = labels[y0:y1, x0:x1] == lbl # mask only over the bbox
128
  rgb = np.zeros((y1 - y0, x1 - x0, 3), dtype=np.uint8) # black background
129
  rgb[sub_mask] = page_rgb[y0:y1, x0:x1][sub_mask]
130
  pieces.append(
131
+ Piece(label=int(lbl), x=int(x0), y=int(y0), rgb=rgb, mask=sub_mask)
132
  )
133
 
134
  # Piece-index <-> raw-label map from the pieces we actually emitted, so