coderuday21 commited on
Commit
f29f921
·
1 Parent(s): 216e4eb

Save full-res before image so the before/after slider works from history View

Browse files
Files changed (4) hide show
  1. app/main.py +11 -2
  2. app/models.py +1 -0
  3. static/js/app.js +9 -27
  4. templates/index.html +1 -1
app/main.py CHANGED
@@ -35,6 +35,7 @@ try:
35
  for col, col_type in [
36
  ("zone", "VARCHAR(128) DEFAULT ''"),
37
  ("village", "VARCHAR(128) DEFAULT ''"),
 
38
  ("before_thumb_path", "VARCHAR(512) DEFAULT ''"),
39
  ("after_thumb_path", "VARCHAR(512) DEFAULT ''"),
40
  ]:
@@ -236,7 +237,12 @@ async def detect(
236
  Image.fromarray(result_image).save(overlay_path)
237
  relative_overlay = f"overlays/{overlay_filename}"
238
 
239
- # Save before/after thumbnails for history table (efficient small images)
 
 
 
 
 
240
  before_thumb_file = OVERLAYS_DIR / f"{base_name}_before_thumb.png"
241
  after_thumb_file = OVERLAYS_DIR / f"{base_name}_after_thumb.png"
242
  before_thumb_pil = before_pil.copy()
@@ -280,6 +286,7 @@ async def detect(
280
  change_percentage=change_pct,
281
  regions_count=len(change_regions),
282
  overlay_path=relative_overlay,
 
283
  before_thumb_path=relative_before_thumb,
284
  after_thumb_path=relative_after_thumb,
285
  regions_json=json.dumps(regions_serializable),
@@ -323,6 +330,7 @@ async def detect(
323
  "regions": regions_serializable,
324
  "overlayBase64Png": overlay_b64,
325
  "overlayUrl": f"/api/overlay/{relative_overlay}",
 
326
  "beforeThumbUrl": f"/api/overlay/{relative_before_thumb}",
327
  "afterThumbUrl": f"/api/overlay/{relative_after_thumb}",
328
  "notificationSent": notification_sent,
@@ -400,6 +408,7 @@ def get_run(
400
  },
401
  "regions": regions,
402
  "overlayUrl": f"/api/overlay/{run.overlay_path}" if run.overlay_path else None,
 
403
  "beforeThumbUrl": f"/api/overlay/{run.before_thumb_path}" if (getattr(run, "before_thumb_path", None) or "").strip() else None,
404
  "afterThumbUrl": f"/api/overlay/{run.after_thumb_path}" if (getattr(run, "after_thumb_path", None) or "").strip() else None,
405
  "createdAt": run.created_at.isoformat(),
@@ -419,7 +428,7 @@ def delete_run(
419
  if not run:
420
  raise HTTPException(status_code=404, detail="Run not found")
421
  # Delete overlay and thumbnail files if they exist
422
- for path_attr in ("overlay_path", "before_thumb_path", "after_thumb_path"):
423
  path_val = getattr(run, path_attr, None)
424
  if path_val:
425
  f = OVERLAYS_DIR.parent / path_val
 
35
  for col, col_type in [
36
  ("zone", "VARCHAR(128) DEFAULT ''"),
37
  ("village", "VARCHAR(128) DEFAULT ''"),
38
+ ("before_full_path", "VARCHAR(512) DEFAULT ''"),
39
  ("before_thumb_path", "VARCHAR(512) DEFAULT ''"),
40
  ("after_thumb_path", "VARCHAR(512) DEFAULT ''"),
41
  ]:
 
237
  Image.fromarray(result_image).save(overlay_path)
238
  relative_overlay = f"overlays/{overlay_filename}"
239
 
240
+ # Save full-resolution before image (used by the before/after slider from history)
241
+ before_full_file = OVERLAYS_DIR / f"{base_name}_before.png"
242
+ before_pil.save(before_full_file)
243
+ relative_before_full = f"overlays/{base_name}_before.png"
244
+
245
+ # Save small thumbnails for the history table rows
246
  before_thumb_file = OVERLAYS_DIR / f"{base_name}_before_thumb.png"
247
  after_thumb_file = OVERLAYS_DIR / f"{base_name}_after_thumb.png"
248
  before_thumb_pil = before_pil.copy()
 
286
  change_percentage=change_pct,
287
  regions_count=len(change_regions),
288
  overlay_path=relative_overlay,
289
+ before_full_path=relative_before_full,
290
  before_thumb_path=relative_before_thumb,
291
  after_thumb_path=relative_after_thumb,
292
  regions_json=json.dumps(regions_serializable),
 
330
  "regions": regions_serializable,
331
  "overlayBase64Png": overlay_b64,
332
  "overlayUrl": f"/api/overlay/{relative_overlay}",
333
+ "beforeFullUrl": f"/api/overlay/{relative_before_full}",
334
  "beforeThumbUrl": f"/api/overlay/{relative_before_thumb}",
335
  "afterThumbUrl": f"/api/overlay/{relative_after_thumb}",
336
  "notificationSent": notification_sent,
 
408
  },
409
  "regions": regions,
410
  "overlayUrl": f"/api/overlay/{run.overlay_path}" if run.overlay_path else None,
411
+ "beforeFullUrl": f"/api/overlay/{run.before_full_path}" if (getattr(run, "before_full_path", None) or "").strip() else None,
412
  "beforeThumbUrl": f"/api/overlay/{run.before_thumb_path}" if (getattr(run, "before_thumb_path", None) or "").strip() else None,
413
  "afterThumbUrl": f"/api/overlay/{run.after_thumb_path}" if (getattr(run, "after_thumb_path", None) or "").strip() else None,
414
  "createdAt": run.created_at.isoformat(),
 
428
  if not run:
429
  raise HTTPException(status_code=404, detail="Run not found")
430
  # Delete overlay and thumbnail files if they exist
431
+ for path_attr in ("overlay_path", "before_full_path", "before_thumb_path", "after_thumb_path"):
432
  path_val = getattr(run, path_attr, None)
433
  if path_val:
434
  f = OVERLAYS_DIR.parent / path_val
app/models.py CHANGED
@@ -29,6 +29,7 @@ class DetectionRun(Base):
29
  change_percentage = Column(Float, nullable=False)
30
  regions_count = Column(Integer, default=0)
31
  overlay_path = Column(String(512), default="")
 
32
  before_thumb_path = Column(String(512), default="")
33
  after_thumb_path = Column(String(512), default="")
34
  zone = Column(String(128), default="")
 
29
  change_percentage = Column(Float, nullable=False)
30
  regions_count = Column(Integer, default=0)
31
  overlay_path = Column(String(512), default="")
32
+ before_full_path = Column(String(512), default="")
33
  before_thumb_path = Column(String(512), default="")
34
  after_thumb_path = Column(String(512), default="")
35
  zone = Column(String(128), default="")
static/js/app.js CHANGED
@@ -398,48 +398,30 @@ function showResult(data) {
398
  const beforeImg = document.getElementById('compare-before-img');
399
  const afterImg = document.getElementById('compare-after-img');
400
 
401
- // "Before" side = original before image, "After" side = result overlay
402
- const sliderSection = document.querySelector('.zoom-slider-section');
403
- let hasBefore = false;
404
-
405
  if (data.overlayBase64Png) {
406
- // Fresh detection: result image is base64, before image from file picker
407
  afterImg.src = 'data:image/png;base64,' + data.overlayBase64Png;
408
  const beforeFile = document.getElementById('file-before').files?.[0];
409
  if (beforeFile) {
410
- hasBefore = true;
411
  readFileAsDataURL(beforeFile).then((url) => { beforeImg.src = url; });
412
- } else if (data.beforeThumbUrl) {
413
- hasBefore = true;
414
- beforeImg.src = data.beforeThumbUrl;
415
  }
416
  } else {
417
- // Loading from history: use stored URLs
418
  afterImg.src = data.overlayUrl || '';
419
- if (data.beforeThumbUrl) {
420
- hasBefore = true;
421
- beforeImg.src = data.beforeThumbUrl;
422
- }
423
  }
424
 
425
- // If no before image available, still show the overlay but hide the slider handle
426
- if (!hasBefore) {
427
- beforeImg.src = afterImg.src;
428
- }
429
-
430
- // Wait for images to load before positioning the slider, then reset
431
  let loaded = 0;
432
  const onReady = () => {
433
- loaded++;
434
- if (loaded >= 2) {
435
- resetCompareSlider();
436
- resetZoom();
437
- }
438
  };
439
  afterImg.onload = onReady;
440
  beforeImg.onload = onReady;
441
- // Fallback for cached images (onload won't fire if already loaded)
442
- setTimeout(() => { resetCompareSlider(); resetZoom(); }, 400);
443
 
444
  tbody.innerHTML = '';
445
  const regions = (data.regions || []).slice(0, 50);
 
398
  const beforeImg = document.getElementById('compare-before-img');
399
  const afterImg = document.getElementById('compare-after-img');
400
 
401
+ // "Before" side = original before image (full-res), "After" side = result overlay
 
 
 
402
  if (data.overlayBase64Png) {
403
+ // Fresh detection: overlay is base64, before from file picker or saved full-res
404
  afterImg.src = 'data:image/png;base64,' + data.overlayBase64Png;
405
  const beforeFile = document.getElementById('file-before').files?.[0];
406
  if (beforeFile) {
 
407
  readFileAsDataURL(beforeFile).then((url) => { beforeImg.src = url; });
408
+ } else {
409
+ beforeImg.src = data.beforeFullUrl || data.beforeThumbUrl || '';
 
410
  }
411
  } else {
412
+ // Loading from history: use full-res before image for slider, overlay for changes
413
  afterImg.src = data.overlayUrl || '';
414
+ beforeImg.src = data.beforeFullUrl || data.beforeThumbUrl || '';
 
 
 
415
  }
416
 
417
+ // Wait for both images to fully load before positioning the slider handle
 
 
 
 
 
418
  let loaded = 0;
419
  const onReady = () => {
420
+ if (++loaded >= 2) { resetCompareSlider(); resetZoom(); }
 
 
 
 
421
  };
422
  afterImg.onload = onReady;
423
  beforeImg.onload = onReady;
424
+ setTimeout(() => { resetCompareSlider(); resetZoom(); }, 500);
 
425
 
426
  tbody.innerHTML = '';
427
  const regions = (data.regions || []).slice(0, 50);
templates/index.html CHANGED
@@ -345,6 +345,6 @@
345
  </div>
346
  </div>
347
 
348
- <script src="/static/js/app.js?v=18"></script>
349
  </body>
350
  </html>
 
345
  </div>
346
  </div>
347
 
348
+ <script src="/static/js/app.js?v=19"></script>
349
  </body>
350
  </html>