Jatin-tec commited on
Commit
4c4f437
·
1 Parent(s): aa30915

crop footer

Browse files
Files changed (1) hide show
  1. trufor_runner.py +130 -3
trufor_runner.py CHANGED
@@ -7,7 +7,7 @@ import subprocess
7
  import tempfile
8
  from dataclasses import dataclass
9
  from pathlib import Path
10
- from typing import Dict, Optional
11
 
12
  import numpy as np
13
  from PIL import Image
@@ -136,10 +136,17 @@ class TruForEngine:
136
  if image is None:
137
  raise TruForUnavailableError("No image supplied to TruFor inference.")
138
 
 
 
 
 
 
 
 
139
  if self.backend == "docker":
140
- return self._infer_docker(image)
141
  if self.backend == "native":
142
- return self._infer_native(image)
143
 
144
  raise TruForUnavailableError("TruFor backend not configured.")
145
 
@@ -267,3 +274,123 @@ class TruForEngine:
267
 
268
  heat_img = Image.fromarray(heat, mode="RGB").resize(base_rgb.size, Image.BILINEAR)
269
  return Image.blend(base_rgb, heat_img, alpha)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  import tempfile
8
  from dataclasses import dataclass
9
  from pathlib import Path
10
+ from typing import Dict, Optional, Tuple
11
 
12
  import numpy as np
13
  from PIL import Image
 
136
  if image is None:
137
  raise TruForUnavailableError("No image supplied to TruFor inference.")
138
 
139
+ prepared_image, cropped = self._strip_gps_overlay(image)
140
+ if cropped:
141
+ LOGGER.debug(
142
+ "Cropping %d px GPS overlay before TruFor inference.",
143
+ image.height - prepared_image.height,
144
+ )
145
+
146
  if self.backend == "docker":
147
+ return self._infer_docker(prepared_image)
148
  if self.backend == "native":
149
+ return self._infer_native(prepared_image)
150
 
151
  raise TruForUnavailableError("TruFor backend not configured.")
152
 
 
274
 
275
  heat_img = Image.fromarray(heat, mode="RGB").resize(base_rgb.size, Image.BILINEAR)
276
  return Image.blend(base_rgb, heat_img, alpha)
277
+
278
+ @staticmethod
279
+ def _strip_gps_overlay(image: Image.Image) -> Tuple[Image.Image, bool]:
280
+ gray = np.asarray(image.convert("L"), dtype=np.uint8)
281
+ hsv = np.asarray(image.convert("HSV"), dtype=np.uint8)
282
+ hue = hsv[..., 0] / 255.0
283
+ sat = hsv[..., 1] / 255.0
284
+ val = hsv[..., 2] / 255.0
285
+ height, width = gray.shape
286
+ min_overlay = max(int(height * 0.08), 40)
287
+ max_overlay = max(int(height * 0.45), min_overlay + 1)
288
+ if height <= min_overlay:
289
+ return image, False
290
+ start_row = height - min_overlay
291
+ stop_row = max(height - max_overlay, 1)
292
+
293
+ row_means = gray.mean(axis=1)
294
+ sat_means = sat.mean(axis=1)
295
+ blue_mask = (hue >= 0.5) & (hue <= 0.75) & (sat >= 0.25) & (val <= 0.95)
296
+ yellow_mask = (hue >= 0.08) & (hue <= 0.18) & (sat >= 0.35) & (val >= 0.45)
297
+ white_mask = (val >= 0.87) & (sat <= 0.28)
298
+ dark_mask = val <= 0.28
299
+
300
+ overlay_mask = blue_mask | yellow_mask | white_mask | dark_mask
301
+ overlay_ratio_rows = overlay_mask.mean(axis=1)
302
+
303
+ boundary = None
304
+ best_score = 0.0
305
+
306
+ # First, try to detect a long contiguous overlay band using hysteresis on coverage.
307
+ high_ratio = 0.52
308
+ low_ratio = 0.36
309
+ run_len = 0
310
+ run_top = height
311
+ for row in range(height - 1, stop_row - 1, -1):
312
+ ratio = overlay_ratio_rows[row]
313
+ if ratio >= high_ratio or (run_len > 0 and ratio >= low_ratio):
314
+ run_len += 1
315
+ run_top = row
316
+ if run_len >= max_overlay:
317
+ break
318
+ elif run_len > 0:
319
+ if run_len >= min_overlay:
320
+ break
321
+ run_len = 0
322
+ run_top = height
323
+ elif height - row >= max_overlay:
324
+ break
325
+
326
+ if run_len >= min_overlay:
327
+ boundary_candidate = run_top
328
+ overlay_consistency = overlay_ratio_rows[boundary_candidate:height].mean()
329
+ boundary_strength = abs(row_means[boundary_candidate - 1] - row_means[boundary_candidate]) if boundary_candidate > 0 else abs(row_means[boundary_candidate] - row_means[min(boundary_candidate + 1, height - 1)])
330
+
331
+ if overlay_consistency >= 0.45 and boundary_strength >= 4.0:
332
+ overlay_height = height - boundary_candidate
333
+ margin = min(max(int(overlay_height * 0.25), 18), boundary_candidate)
334
+ crop_row = max(boundary_candidate - margin, 0)
335
+ cropped_image = image.crop((0, 0, width, crop_row))
336
+ return cropped_image, True
337
+
338
+ # Detect GPS Map Camera overlay at the bottom and crop it out if present.
339
+ for row in range(start_row, stop_row - 1, -1):
340
+ overlay_height = height - row
341
+ if overlay_height < min_overlay:
342
+ continue
343
+ if overlay_height > max_overlay:
344
+ break
345
+
346
+ overlay_hue = hue[row:height, :]
347
+ overlay_sat = sat[row:height, :]
348
+ overlay_val = val[row:height, :]
349
+
350
+ high_sat_ratio = float((overlay_sat > 0.3).mean())
351
+ dark_ratio = float((overlay_val < 0.3).mean())
352
+ bright_ratio = float((overlay_val > 0.88).mean())
353
+ colored_band_ratio = float(((overlay_sat > 0.32) & (overlay_val > 0.25) & (overlay_val < 0.85)).mean())
354
+ blue_ratio = float(((overlay_hue > 0.48) & (overlay_hue < 0.74) & (overlay_sat > 0.28)).mean())
355
+ yellow_ratio = float(((overlay_hue > 0.07) & (overlay_hue < 0.2) & (overlay_sat > 0.35) & (overlay_val > 0.45)).mean())
356
+
357
+ prev_mean = row_means[row - 1] if row > 0 else row_means[row]
358
+ boundary_strength = abs(prev_mean - row_means[row])
359
+ saturation_jump = sat_means[row - 1] - sat_means[row] if row > 0 else 0.0
360
+
361
+ score = 0.0
362
+ if high_sat_ratio > 0.38:
363
+ score += 0.8
364
+ if colored_band_ratio > 0.35:
365
+ score += 0.7
366
+ if blue_ratio > 0.22:
367
+ score += 0.8
368
+ if yellow_ratio > 0.12:
369
+ score += 0.5
370
+ if dark_ratio > 0.32:
371
+ score += 0.6
372
+ if bright_ratio > 0.07:
373
+ score += 0.5
374
+ if boundary_strength > 5.5:
375
+ score += 0.6
376
+ if saturation_jump < -0.05:
377
+ score += 0.4
378
+
379
+ edge_ratio = boundary_strength / max(row_means[row], 1.0)
380
+ if edge_ratio > 0.11:
381
+ score += 0.3
382
+ if overlay_ratio_rows[row:height].mean() > 0.42:
383
+ score += 0.3
384
+
385
+ if score > best_score:
386
+ best_score = score
387
+ boundary = row
388
+
389
+ if boundary is None or best_score < 1.6:
390
+ return image, False
391
+
392
+ overlay_height = height - boundary
393
+ margin = min(max(int(overlay_height * 0.25), 18), boundary)
394
+ crop_row = max(boundary - margin, 0)
395
+ cropped_image = image.crop((0, 0, width, crop_row))
396
+ return cropped_image, True