marginalized blobs with marginalized tiles
Browse files- inference_tab/helpers.py +1 -13
- inference_tab/inference_logic.py +37 -18
- inference_tab/inference_setup.py +15 -0
inference_tab/helpers.py
CHANGED
|
@@ -235,15 +235,12 @@ def processYOLOBoxes(iou):
|
|
| 235 |
boxes_full = [b["bbox"] for b in box_data] # Format: [x1, y1, x2, y2]
|
| 236 |
return boxes_full
|
| 237 |
|
| 238 |
-
def prepare_tiles(image_path, boxes_full, tile_size=1024, overlap=
|
| 239 |
"""
|
| 240 |
Tiles the image and prepares per-tile metadata including filtered boxes and point prompts.
|
| 241 |
Returns full image size H, W.
|
| 242 |
"""
|
| 243 |
|
| 244 |
-
full_img = cv2.imread(image_path)
|
| 245 |
-
H_full, W_full, _ = full_img.shape
|
| 246 |
-
|
| 247 |
tiles, (H, W, _) = tile_image_with_overlap(image_path, tile_size, overlap)
|
| 248 |
os.makedirs("tmp/tiles", exist_ok=True)
|
| 249 |
meta = []
|
|
@@ -253,14 +250,6 @@ def prepare_tiles(image_path, boxes_full, tile_size=1024, overlap=50, iou=0.5, c
|
|
| 253 |
tile_array = cv2.cvtColor(tile_array, cv2.COLOR_BGR2RGB)
|
| 254 |
Image.fromarray(tile_array).save(tile_path)
|
| 255 |
|
| 256 |
-
my1 = max(0, y_offset - tile_margin)
|
| 257 |
-
my2 = min(H_full, y_offset + tile_size + tile_margin)
|
| 258 |
-
mx1 = max(0, x_offset - tile_margin)
|
| 259 |
-
mx2 = min(W_full, x_offset + tile_size + tile_margin)
|
| 260 |
-
tile_with_margin = full_img[my1:my2, mx1:mx2]
|
| 261 |
-
cv2.imwrite(f"tiles/tile_{idx}_margin.png", tile_with_margin)
|
| 262 |
-
|
| 263 |
-
|
| 264 |
tile_h, tile_w, _ = tile_array.shape
|
| 265 |
|
| 266 |
# Select boxes overlapping this tile
|
|
@@ -342,7 +331,6 @@ def prepare_tiles(image_path, boxes_full, tile_size=1024, overlap=50, iou=0.5, c
|
|
| 342 |
|
| 343 |
|
| 344 |
|
| 345 |
-
|
| 346 |
def merge_tile_masks(H, W):
|
| 347 |
"""
|
| 348 |
Merge predicted tile masks into a full-size image.
|
|
|
|
| 235 |
boxes_full = [b["bbox"] for b in box_data] # Format: [x1, y1, x2, y2]
|
| 236 |
return boxes_full
|
| 237 |
|
| 238 |
+
def prepare_tiles(image_path, boxes_full, tile_size=1024, overlap=256, iou=0.5, c_th=0.75, edge_margin=10):
|
| 239 |
"""
|
| 240 |
Tiles the image and prepares per-tile metadata including filtered boxes and point prompts.
|
| 241 |
Returns full image size H, W.
|
| 242 |
"""
|
| 243 |
|
|
|
|
|
|
|
|
|
|
| 244 |
tiles, (H, W, _) = tile_image_with_overlap(image_path, tile_size, overlap)
|
| 245 |
os.makedirs("tmp/tiles", exist_ok=True)
|
| 246 |
meta = []
|
|
|
|
| 250 |
tile_array = cv2.cvtColor(tile_array, cv2.COLOR_BGR2RGB)
|
| 251 |
Image.fromarray(tile_array).save(tile_path)
|
| 252 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 253 |
tile_h, tile_w, _ = tile_array.shape
|
| 254 |
|
| 255 |
# Select boxes overlapping this tile
|
|
|
|
| 331 |
|
| 332 |
|
| 333 |
|
|
|
|
| 334 |
def merge_tile_masks(H, W):
|
| 335 |
"""
|
| 336 |
Merge predicted tile masks into a full-size image.
|
inference_tab/inference_logic.py
CHANGED
|
@@ -37,11 +37,12 @@ _trocr_device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
|
| 37 |
|
| 38 |
|
| 39 |
|
|
|
|
| 40 |
def run_inference(tile_dict, gcp_path,user_crs, city_name, score_th, hist_th, hist_dic):
|
| 41 |
IMAGE_FOLDER = os.path.join(OUTPUT_DIR, "blobs")
|
| 42 |
CSV_FILE = os.path.join(OUTPUT_DIR, "annotations.csv")
|
| 43 |
MASK_FILE = os.path.join(OUTPUT_DIR, "mask.tif")
|
| 44 |
-
|
| 45 |
|
| 46 |
if os.path.exists(IMAGE_FOLDER):
|
| 47 |
shutil.rmtree(IMAGE_FOLDER)
|
|
@@ -89,7 +90,7 @@ def run_inference(tile_dict, gcp_path,user_crs, city_name, score_th, hist_th, hi
|
|
| 89 |
if stop_pipeline:
|
| 90 |
yield log + "Pipeline stopped: no text segments found.\n", None
|
| 91 |
return
|
| 92 |
-
|
| 93 |
for msg in extractSegments(image_path):
|
| 94 |
log += msg + "\n"
|
| 95 |
yield log, None
|
|
@@ -275,7 +276,7 @@ def getSegments(image_path,iou=0.5,c_th=0.75,edge_margin=10):
|
|
| 275 |
with open(BOXES_PATH, "r") as f:
|
| 276 |
box_data = json.load(f)
|
| 277 |
boxes = [b["bbox"] for b in box_data]
|
| 278 |
-
yield "Prepare tiles..."
|
| 279 |
H,W = prepare_tiles(image_path, boxes, tile_size=1024, overlap=50, iou=iou, c_th=c_th, edge_margin=edge_margin)
|
| 280 |
yield "Run inference on tiles..."
|
| 281 |
for msg in run_tile_inference():
|
|
@@ -283,23 +284,16 @@ def getSegments(image_path,iou=0.5,c_th=0.75,edge_margin=10):
|
|
| 283 |
if "Stopping inference" in msg:
|
| 284 |
yield "No labels detected – halting getSegments."
|
| 285 |
return
|
| 286 |
-
yield "
|
| 287 |
merge_tile_masks(H,W)
|
| 288 |
|
| 289 |
MASK_PATH = os.path.join(OUTPUT_DIR,"mask.tif")
|
| 290 |
yield f"{MASK_PATH}"
|
| 291 |
-
|
| 292 |
|
| 293 |
|
| 294 |
def extractSegments(image_path, min_size=500, margin=150):
|
| 295 |
image = cv2.imread(image_path)
|
| 296 |
|
| 297 |
-
margin_path = image_path.replace(".png", "_margin.png")
|
| 298 |
-
image_margin_tile = cv2.imread(margin_path)
|
| 299 |
-
if image_margin_tile is None:
|
| 300 |
-
yield f"Error: Could not load margin tile at {margin_path}"
|
| 301 |
-
|
| 302 |
-
|
| 303 |
MASK_PATH = os.path.join(OUTPUT_DIR, "mask.tif")
|
| 304 |
mask = cv2.imread(MASK_PATH, cv2.IMREAD_UNCHANGED)
|
| 305 |
|
|
@@ -337,22 +331,47 @@ def extractSegments(image_path, min_size=500, margin=150):
|
|
| 337 |
y_max_m = min(height, y_max + margin)
|
| 338 |
|
| 339 |
cropped_image_margin = image[y_min_m:y_max_m, x_min_m:x_max_m]
|
| 340 |
-
cropped_mask_margin = blob_mask[y_min_m:y_max_m, x_min_m:x_max_m]
|
| 341 |
-
|
| 342 |
-
cropped_image_margin = image_margin_tile[y_min : y_max + 2*margin,
|
| 343 |
-
x_min : x_max + 2*margin]
|
| 344 |
-
cropped_mask_margin = np.zeros(cropped_image_margin.shape[:2], dtype=np.uint8)
|
| 345 |
-
cropped_mask_margin[margin : margin + (y_max-y_min),
|
| 346 |
-
margin : margin + (x_max-x_min)] = blob_mask[y_min:y_max, x_min:x_max]
|
| 347 |
|
| 348 |
shaded_margin = cropped_image_margin.copy()
|
| 349 |
overlay_margin = cropped_image_margin.copy()
|
| 350 |
overlay_margin[cropped_mask_margin == 1] = (255, 200, 100)
|
| 351 |
shaded_margin = cv2.addWeighted(overlay_margin, 0.35, shaded_margin, 0.65, 0)
|
| 352 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 353 |
BLOB_PATH_MARGIN = os.path.join(OUTPUT_DIR, "blobs", f"{blob_id}_margin.png")
|
| 354 |
cv2.imwrite(BLOB_PATH_MARGIN, shaded_margin)
|
| 355 |
|
|
|
|
| 356 |
yield f"Done."
|
| 357 |
|
| 358 |
|
|
|
|
| 37 |
|
| 38 |
|
| 39 |
|
| 40 |
+
|
| 41 |
def run_inference(tile_dict, gcp_path,user_crs, city_name, score_th, hist_th, hist_dic):
|
| 42 |
IMAGE_FOLDER = os.path.join(OUTPUT_DIR, "blobs")
|
| 43 |
CSV_FILE = os.path.join(OUTPUT_DIR, "annotations.csv")
|
| 44 |
MASK_FILE = os.path.join(OUTPUT_DIR, "mask.tif")
|
| 45 |
+
|
| 46 |
|
| 47 |
if os.path.exists(IMAGE_FOLDER):
|
| 48 |
shutil.rmtree(IMAGE_FOLDER)
|
|
|
|
| 90 |
if stop_pipeline:
|
| 91 |
yield log + "Pipeline stopped: no text segments found.\n", None
|
| 92 |
return
|
| 93 |
+
|
| 94 |
for msg in extractSegments(image_path):
|
| 95 |
log += msg + "\n"
|
| 96 |
yield log, None
|
|
|
|
| 276 |
with open(BOXES_PATH, "r") as f:
|
| 277 |
box_data = json.load(f)
|
| 278 |
boxes = [b["bbox"] for b in box_data]
|
| 279 |
+
yield f"Prepare tiles for {image_path}..."
|
| 280 |
H,W = prepare_tiles(image_path, boxes, tile_size=1024, overlap=50, iou=iou, c_th=c_th, edge_margin=edge_margin)
|
| 281 |
yield "Run inference on tiles..."
|
| 282 |
for msg in run_tile_inference():
|
|
|
|
| 284 |
if "Stopping inference" in msg:
|
| 285 |
yield "No labels detected – halting getSegments."
|
| 286 |
return
|
| 287 |
+
yield "Merge predicted masks into image..."
|
| 288 |
merge_tile_masks(H,W)
|
| 289 |
|
| 290 |
MASK_PATH = os.path.join(OUTPUT_DIR,"mask.tif")
|
| 291 |
yield f"{MASK_PATH}"
|
|
|
|
| 292 |
|
| 293 |
|
| 294 |
def extractSegments(image_path, min_size=500, margin=150):
|
| 295 |
image = cv2.imread(image_path)
|
| 296 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 297 |
MASK_PATH = os.path.join(OUTPUT_DIR, "mask.tif")
|
| 298 |
mask = cv2.imread(MASK_PATH, cv2.IMREAD_UNCHANGED)
|
| 299 |
|
|
|
|
| 331 |
y_max_m = min(height, y_max + margin)
|
| 332 |
|
| 333 |
cropped_image_margin = image[y_min_m:y_max_m, x_min_m:x_max_m]
|
| 334 |
+
cropped_mask_margin = blob_mask[y_min_m:y_max_m, x_min_m:x_max_m]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 335 |
|
| 336 |
shaded_margin = cropped_image_margin.copy()
|
| 337 |
overlay_margin = cropped_image_margin.copy()
|
| 338 |
overlay_margin[cropped_mask_margin == 1] = (255, 200, 100)
|
| 339 |
shaded_margin = cv2.addWeighted(overlay_margin, 0.35, shaded_margin, 0.65, 0)
|
| 340 |
|
| 341 |
+
BLOB_PATH_MARGIN = os.path.join(OUTPUT_DIR, "blobs", f"{blob_id}_margin.png")
|
| 342 |
+
cv2.imwrite(BLOB_PATH_MARGIN, shaded_margin)'''
|
| 343 |
+
|
| 344 |
+
# ---- MARGINALIZED NEW (with mask/shading) ----
|
| 345 |
+
base, ext = os.path.splitext(image_path)
|
| 346 |
+
margin_tile_path = f"{base}_margin{ext}"
|
| 347 |
+
margin_img = cv2.imread(margin_tile_path)
|
| 348 |
+
h_m, w_m = margin_img.shape[:2]
|
| 349 |
+
|
| 350 |
+
x1_m = max(0, x_min)
|
| 351 |
+
y1_m = max(0, y_min)
|
| 352 |
+
x2_m = min(w_m, x_max + (2 * margin))
|
| 353 |
+
y2_m = min(h_m, y_max + (2 * margin))
|
| 354 |
+
cropped = margin_img[y1_m:y2_m, x1_m:x2_m]
|
| 355 |
+
|
| 356 |
+
padded_blob_mask = cv2.copyMakeBorder(
|
| 357 |
+
blob_mask,
|
| 358 |
+
margin, margin, margin, margin,
|
| 359 |
+
borderType=cv2.BORDER_CONSTANT,
|
| 360 |
+
value=0
|
| 361 |
+
)
|
| 362 |
+
|
| 363 |
+
cropped_mask = padded_blob_mask[y1_m:y2_m, x1_m:x2_m]
|
| 364 |
+
|
| 365 |
+
shaded_margin = cropped.copy()
|
| 366 |
+
overlay_margin = cropped.copy()
|
| 367 |
+
|
| 368 |
+
overlay_margin[cropped_mask == 1] = (255, 200, 100)
|
| 369 |
+
shaded_margin = cv2.addWeighted(overlay_margin, 0.35, shaded_margin, 0.65, 0)
|
| 370 |
+
|
| 371 |
BLOB_PATH_MARGIN = os.path.join(OUTPUT_DIR, "blobs", f"{blob_id}_margin.png")
|
| 372 |
cv2.imwrite(BLOB_PATH_MARGIN, shaded_margin)
|
| 373 |
|
| 374 |
+
|
| 375 |
yield f"Done."
|
| 376 |
|
| 377 |
|
inference_tab/inference_setup.py
CHANGED
|
@@ -5,6 +5,7 @@ from PIL import Image
|
|
| 5 |
import os
|
| 6 |
|
| 7 |
TILE_SIZE = 1024
|
|
|
|
| 8 |
TILE_FOLDER = "tiles"
|
| 9 |
os.makedirs(TILE_FOLDER, exist_ok=True)
|
| 10 |
tiles_cache = {"tiles": [], "selected_tile": None}
|
|
@@ -29,13 +30,27 @@ def make_tiles(image, tile_size=TILE_SIZE):
|
|
| 29 |
def create_tiles(image_file):
|
| 30 |
img = Image.open(image_file.name).convert("RGB")
|
| 31 |
img = np.array(img)
|
|
|
|
| 32 |
|
| 33 |
annotated, tiles = make_tiles(img, TILE_SIZE)
|
| 34 |
tiles_cache["tiles"] = []
|
| 35 |
|
| 36 |
for idx, (coords, tile) in enumerate(tiles):
|
|
|
|
|
|
|
| 37 |
tile_path = os.path.join(TILE_FOLDER, f"tile_{idx}.png")
|
| 38 |
Image.fromarray(tile).save(tile_path)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
tiles_cache["tiles"].append((coords, tile_path)) # store path instead of array
|
| 40 |
|
| 41 |
tiles_cache["selected_tile"] = None
|
|
|
|
| 5 |
import os
|
| 6 |
|
| 7 |
TILE_SIZE = 1024
|
| 8 |
+
MARGIN_TILE = 150
|
| 9 |
TILE_FOLDER = "tiles"
|
| 10 |
os.makedirs(TILE_FOLDER, exist_ok=True)
|
| 11 |
tiles_cache = {"tiles": [], "selected_tile": None}
|
|
|
|
| 30 |
def create_tiles(image_file):
|
| 31 |
img = Image.open(image_file.name).convert("RGB")
|
| 32 |
img = np.array(img)
|
| 33 |
+
H, W, _ = img.shape
|
| 34 |
|
| 35 |
annotated, tiles = make_tiles(img, TILE_SIZE)
|
| 36 |
tiles_cache["tiles"] = []
|
| 37 |
|
| 38 |
for idx, (coords, tile) in enumerate(tiles):
|
| 39 |
+
x, y, x_end, y_end = coords
|
| 40 |
+
|
| 41 |
tile_path = os.path.join(TILE_FOLDER, f"tile_{idx}.png")
|
| 42 |
Image.fromarray(tile).save(tile_path)
|
| 43 |
+
|
| 44 |
+
# Marginalized tile
|
| 45 |
+
x_m = max(0, x - MARGIN_TILE)
|
| 46 |
+
y_m = max(0, y - MARGIN_TILE)
|
| 47 |
+
x_end_m = min(W, x_end + MARGIN_TILE)
|
| 48 |
+
y_end_m = min(H, y_end + MARGIN_TILE)
|
| 49 |
+
tile_margin = img[y_m:y_end_m, x_m:x_end_m]
|
| 50 |
+
tile_margin_path = os.path.join(TILE_FOLDER, f"tile_{idx}_margin.png")
|
| 51 |
+
Image.fromarray(tile_margin).save(tile_margin_path)
|
| 52 |
+
|
| 53 |
+
|
| 54 |
tiles_cache["tiles"].append((coords, tile_path)) # store path instead of array
|
| 55 |
|
| 56 |
tiles_cache["selected_tile"] = None
|