prithivMLmods commited on
Commit
e7bdc71
·
verified ·
1 Parent(s): a4fa726

update app

Browse files
Files changed (1) hide show
  1. app.py +36 -41
app.py CHANGED
@@ -104,8 +104,8 @@ except Exception as e:
104
 
105
  ADAPTER_SPECS = {
106
  "Object-Remover": {
107
- "repo": "prithivMLmods/QIE-2511-Object-Remover-v2",
108
- "weights": "Qwen-Image-Edit-2511-Object-Remover-v2-9200.safetensors",
109
  "adapter_name": "object-remover",
110
  },
111
  }
@@ -115,6 +115,7 @@ DEFAULT_PROMPT = "Remove the red highlighted object from the scene"
115
 
116
 
117
  def burn_boxes_onto_image(pil_image: Image.Image, boxes_json_str: str) -> Image.Image:
 
118
  import json
119
  if not pil_image:
120
  return pil_image
@@ -125,10 +126,9 @@ def burn_boxes_onto_image(pil_image: Image.Image, boxes_json_str: str) -> Image.
125
  if not boxes:
126
  return pil_image
127
 
128
- img = pil_image.copy().convert("RGBA")
129
  w, h = img.size
130
- overlay = Image.new("RGBA", img.size, (0, 0, 0, 0))
131
- draw = ImageDraw.Draw(overlay)
132
  bw = max(3, w // 250)
133
 
134
  for b in boxes:
@@ -138,10 +138,10 @@ def burn_boxes_onto_image(pil_image: Image.Image, boxes_json_str: str) -> Image.
138
  y2 = int(b["y2"] * h)
139
  lx, rx = min(x1, x2), max(x1, x2)
140
  ty, by_ = min(y1, y2), max(y1, y2)
141
- draw.rectangle([lx, ty, rx, by_], fill=(255, 0, 0, 90))
142
- draw.rectangle([lx, ty, rx, by_], outline=(255, 0, 0, 255), width=bw)
143
 
144
- return Image.alpha_composite(img, overlay).convert("RGB")
145
 
146
 
147
  @spaces.GPU
@@ -341,7 +341,6 @@ label{font-weight:600!important;color:#333!important}
341
  }
342
  .dark #bbox-debug-count{color:#C084FC;background:rgba(168,85,247,.12)}
343
 
344
- /* ===== FIX: hide the sync textbox visually but keep it in the DOM ===== */
345
  #boxes-json-input{
346
  max-height:0!important;
347
  overflow:hidden!important;
@@ -372,7 +371,7 @@ bbox_drawer_js = r"""
372
  const btnClear = document.getElementById('tb-clear');
373
 
374
  if (!canvas || !wrap || !debugCount || !btnDraw) {
375
- console.log('[BBox] waiting for DOM');
376
  setTimeout(initCanvasBbox, 250);
377
  return;
378
  }
@@ -394,8 +393,10 @@ bbox_drawer_js = r"""
394
  let dragStart = {x:0, y:0};
395
  let dragOrig = null;
396
  const HANDLE = 7;
 
 
 
397
 
398
- /* ── helpers ── */
399
  function n2px(b) {
400
  return {x1:b.x1*dispW, y1:b.y1*dispH, x2:b.x2*dispW, y2:b.y2*dispH};
401
  }
@@ -422,12 +423,10 @@ bbox_drawer_js = r"""
422
  y: Math.max(0,Math.min(dispH, cy-r.top))};
423
  }
424
 
425
- /* ── FIX: always update debug + badge, then try textbox ── */
426
  function syncToGradio() {
427
  window.__bboxBoxes = boxes;
428
  const jsonStr = JSON.stringify(boxes);
429
 
430
- /* 1) update on-screen debug no matter what */
431
  if (debugCount) {
432
  debugCount.textContent = boxes.length > 0
433
  ? '\u2705 ' + boxes.length + ' box' + (boxes.length > 1 ? 'es' : '') +
@@ -436,10 +435,9 @@ bbox_drawer_js = r"""
436
  : '\u2B1C No boxes drawn yet';
437
  }
438
 
439
- /* 2) try to push into the Gradio Textbox (best-effort) */
440
  const container = document.getElementById('boxes-json-input');
441
  if (!container) {
442
- console.warn('[BBox] #boxes-json-input not in DOM (hidden?)');
443
  return;
444
  }
445
  const targets = [
@@ -461,7 +459,6 @@ bbox_drawer_js = r"""
461
  });
462
  }
463
 
464
- /* ── draw ── */
465
  function placeholder() {
466
  ctx.fillStyle='#2a2a2a'; ctx.fillRect(0,0,dispW,dispH);
467
  ctx.strokeStyle='#444'; ctx.lineWidth=2; ctx.setLineDash([8,4]);
@@ -482,19 +479,20 @@ bbox_drawer_js = r"""
482
  const p = n2px(b);
483
  const lx=p.x1, ty=p.y1, w=p.x2-p.x1, h=p.y2-p.y1;
484
 
485
- ctx.fillStyle = 'rgba(255,0,0,0.25)';
486
- ctx.fillRect(lx,ty,w,h);
487
-
488
  if (i === selectedIdx) {
489
- ctx.strokeStyle = 'rgba(0,120,255,0.95)';
490
- ctx.lineWidth = 2.5; ctx.setLineDash([6,3]);
 
491
  } else {
492
- ctx.strokeStyle = 'rgba(255,0,0,0.9)';
493
- ctx.lineWidth = 2.5; ctx.setLineDash([]);
 
494
  }
495
- ctx.strokeRect(lx,ty,w,h);
496
  ctx.setLineDash([]);
497
 
 
498
  ctx.fillStyle = i===selectedIdx ? 'rgba(0,120,255,0.85)' : 'rgba(255,0,0,0.85)';
499
  ctx.font = 'bold 11px IBM Plex Mono,monospace';
500
  ctx.textAlign = 'left'; ctx.textBaseline = 'top';
@@ -507,16 +505,17 @@ bbox_drawer_js = r"""
507
  if (i === selectedIdx) drawHandles(p);
508
  });
509
 
 
510
  if (tempRect) {
511
  const rx = Math.min(tempRect.x1,tempRect.x2);
512
  const ry = Math.min(tempRect.y1,tempRect.y2);
513
  const rw = Math.abs(tempRect.x2-tempRect.x1);
514
  const rh = Math.abs(tempRect.y2-tempRect.y1);
515
- ctx.fillStyle='rgba(255,0,0,0.12)';
516
- ctx.fillRect(rx,ry,rw,rh);
517
- ctx.strokeStyle='rgba(255,0,0,0.7)';
518
- ctx.lineWidth=2; ctx.setLineDash([6,3]);
519
- ctx.strokeRect(rx,ry,rw,rh); ctx.setLineDash([]);
520
  }
521
  updateBadge();
522
  }
@@ -582,7 +581,6 @@ bbox_drawer_js = r"""
582
  }
583
  function hideStatus() { status.style.display = 'none'; }
584
 
585
- /* ── pointer events ── */
586
  function onDown(e) {
587
  if (!baseImg) return;
588
  e.preventDefault();
@@ -711,7 +709,6 @@ bbox_drawer_js = r"""
711
  canvas.addEventListener('touchend', onUp, {passive:false});
712
  canvas.addEventListener('touchcancel',(e)=>{e.preventDefault();dragging=false;redraw();},{passive:false});
713
 
714
- /* ── toolbar ── */
715
  btnDraw.addEventListener('click', ()=>setMode('draw'));
716
  btnSelect.addEventListener('click', ()=>setMode('select'));
717
 
@@ -739,16 +736,14 @@ bbox_drawer_js = r"""
739
  });
740
 
741
  btnClear.addEventListener('click', () => {
742
- boxes.length = 0; /* clear in-place */
743
  window.__bboxBoxes = boxes;
744
  selectedIdx = -1;
745
  syncToGradio(); redraw(); hideStatus();
746
  });
747
 
748
- /* ── FIX: robust image polling with multiple selectors ── */
749
  let lastSrc = null;
750
  setInterval(() => {
751
- /* try several selectors Gradio might use */
752
  const imgs = document.querySelectorAll('#source-image-component img');
753
  let el = null;
754
  for (const img of imgs) {
@@ -794,7 +789,6 @@ bbox_drawer_js = r"""
794
  }
795
  }, 300);
796
 
797
- /* periodic re-sync every 500 ms */
798
  setInterval(() => { syncToGradio(); }, 500);
799
 
800
  new ResizeObserver(() => {
@@ -811,10 +805,11 @@ bbox_drawer_js = r"""
811
  """
812
 
813
 
814
- with gr.Blocks() as demo:
815
  gr.Markdown("# **QIE-Object-Remover-Bbox**", elem_id="main-title")
816
  gr.Markdown(
817
- "Perform diverse image edits using a specialized [LoRA](https://huggingface.co/prithivMLmods/QIE-2511-Object-Remover-v2). Upload an image, draw red bounding boxes over the objects you want to remove, and click Remove Object."
 
818
  "Multiple boxes supported. Select, move, resize or delete individual boxes.",
819
  elem_id="subtitle",
820
  )
@@ -832,7 +827,7 @@ with gr.Blocks() as demo:
832
  gr.HTML(
833
  '<div class="bbox-hint">'
834
  "<b>Draw mode:</b> Click & drag to create red rectangles. "
835
- "<b>Select mode:</b> Click a box to select it drag to <b>move</b>, "
836
  "drag handles to <b>resize</b>. Use <b>Delete Selected</b> to remove one box."
837
  "</div>"
838
  )
@@ -862,7 +857,7 @@ with gr.Blocks() as demo:
862
  """
863
  )
864
 
865
- gr.HTML('<div id="bbox-debug-count"> No boxes drawn yet</div>')
866
 
867
  boxes_json = gr.Textbox(
868
  value="[]",
@@ -879,7 +874,7 @@ with gr.Blocks() as demo:
879
  info="Edit the prompt if needed",
880
  )
881
 
882
- run_btn = gr.Button("🗑️ Remove Object", variant="primary", size="lg")
883
 
884
  with gr.Column(scale=1):
885
  result = gr.Image(label="Output Image", height=449)
@@ -901,7 +896,7 @@ with gr.Blocks() as demo:
901
  "[prithivMLmods](https://huggingface.co/prithivMLmods). "
902
  "Adapter: [QIE-2511-Object-Remover-v2]"
903
  "(https://huggingface.co/prithivMLmods/QIE-2511-Object-Remover-v2). "
904
- "More adapters [Qwen-Image-Edit-LoRAs]"
905
  "(https://huggingface.co/models?other=base_model:adapter:Qwen/Qwen-Image-Edit-2509)."
906
  )
907
 
 
104
 
105
  ADAPTER_SPECS = {
106
  "Object-Remover": {
107
+ "repo": "prithivMLmods/QIE-2509-Object-Remover-Bbox",
108
+ "weights": "QIE-2509-Object-Remover-Bbox-5000.safetensors",
109
  "adapter_name": "object-remover",
110
  },
111
  }
 
115
 
116
 
117
  def burn_boxes_onto_image(pil_image: Image.Image, boxes_json_str: str) -> Image.Image:
118
+ """Burn red outline-only rectangles onto the image (no fill)."""
119
  import json
120
  if not pil_image:
121
  return pil_image
 
126
  if not boxes:
127
  return pil_image
128
 
129
+ img = pil_image.copy().convert("RGB")
130
  w, h = img.size
131
+ draw = ImageDraw.Draw(img)
 
132
  bw = max(3, w // 250)
133
 
134
  for b in boxes:
 
138
  y2 = int(b["y2"] * h)
139
  lx, rx = min(x1, x2), max(x1, x2)
140
  ty, by_ = min(y1, y2), max(y1, y2)
141
+ # Red outline only no fill
142
+ draw.rectangle([lx, ty, rx, by_], outline=(255, 0, 0), width=bw)
143
 
144
+ return img
145
 
146
 
147
  @spaces.GPU
 
341
  }
342
  .dark #bbox-debug-count{color:#C084FC;background:rgba(168,85,247,.12)}
343
 
 
344
  #boxes-json-input{
345
  max-height:0!important;
346
  overflow:hidden!important;
 
371
  const btnClear = document.getElementById('tb-clear');
372
 
373
  if (!canvas || !wrap || !debugCount || !btnDraw) {
374
+ console.log('[BBox] waiting for DOM...');
375
  setTimeout(initCanvasBbox, 250);
376
  return;
377
  }
 
393
  let dragStart = {x:0, y:0};
394
  let dragOrig = null;
395
  const HANDLE = 7;
396
+ const RED_STROKE = 'rgba(255,0,0,0.95)';
397
+ const RED_STROKE_WIDTH = 3;
398
+ const SEL_STROKE = 'rgba(0,120,255,0.95)';
399
 
 
400
  function n2px(b) {
401
  return {x1:b.x1*dispW, y1:b.y1*dispH, x2:b.x2*dispW, y2:b.y2*dispH};
402
  }
 
423
  y: Math.max(0,Math.min(dispH, cy-r.top))};
424
  }
425
 
 
426
  function syncToGradio() {
427
  window.__bboxBoxes = boxes;
428
  const jsonStr = JSON.stringify(boxes);
429
 
 
430
  if (debugCount) {
431
  debugCount.textContent = boxes.length > 0
432
  ? '\u2705 ' + boxes.length + ' box' + (boxes.length > 1 ? 'es' : '') +
 
435
  : '\u2B1C No boxes drawn yet';
436
  }
437
 
 
438
  const container = document.getElementById('boxes-json-input');
439
  if (!container) {
440
+ console.warn('[BBox] #boxes-json-input not in DOM');
441
  return;
442
  }
443
  const targets = [
 
459
  });
460
  }
461
 
 
462
  function placeholder() {
463
  ctx.fillStyle='#2a2a2a'; ctx.fillRect(0,0,dispW,dispH);
464
  ctx.strokeStyle='#444'; ctx.lineWidth=2; ctx.setLineDash([8,4]);
 
479
  const p = n2px(b);
480
  const lx=p.x1, ty=p.y1, w=p.x2-p.x1, h=p.y2-p.y1;
481
 
482
+ /* RED OUTLINE ONLY — no fill */
 
 
483
  if (i === selectedIdx) {
484
+ ctx.strokeStyle = SEL_STROKE;
485
+ ctx.lineWidth = RED_STROKE_WIDTH + 1;
486
+ ctx.setLineDash([6,3]);
487
  } else {
488
+ ctx.strokeStyle = RED_STROKE;
489
+ ctx.lineWidth = RED_STROKE_WIDTH;
490
+ ctx.setLineDash([]);
491
  }
492
+ ctx.strokeRect(lx, ty, w, h);
493
  ctx.setLineDash([]);
494
 
495
+ /* label tag */
496
  ctx.fillStyle = i===selectedIdx ? 'rgba(0,120,255,0.85)' : 'rgba(255,0,0,0.85)';
497
  ctx.font = 'bold 11px IBM Plex Mono,monospace';
498
  ctx.textAlign = 'left'; ctx.textBaseline = 'top';
 
505
  if (i === selectedIdx) drawHandles(p);
506
  });
507
 
508
+ /* temp drawing rect — outline only */
509
  if (tempRect) {
510
  const rx = Math.min(tempRect.x1,tempRect.x2);
511
  const ry = Math.min(tempRect.y1,tempRect.y2);
512
  const rw = Math.abs(tempRect.x2-tempRect.x1);
513
  const rh = Math.abs(tempRect.y2-tempRect.y1);
514
+ ctx.strokeStyle = RED_STROKE;
515
+ ctx.lineWidth = RED_STROKE_WIDTH;
516
+ ctx.setLineDash([6,3]);
517
+ ctx.strokeRect(rx, ry, rw, rh);
518
+ ctx.setLineDash([]);
519
  }
520
  updateBadge();
521
  }
 
581
  }
582
  function hideStatus() { status.style.display = 'none'; }
583
 
 
584
  function onDown(e) {
585
  if (!baseImg) return;
586
  e.preventDefault();
 
709
  canvas.addEventListener('touchend', onUp, {passive:false});
710
  canvas.addEventListener('touchcancel',(e)=>{e.preventDefault();dragging=false;redraw();},{passive:false});
711
 
 
712
  btnDraw.addEventListener('click', ()=>setMode('draw'));
713
  btnSelect.addEventListener('click', ()=>setMode('select'));
714
 
 
736
  });
737
 
738
  btnClear.addEventListener('click', () => {
739
+ boxes.length = 0;
740
  window.__bboxBoxes = boxes;
741
  selectedIdx = -1;
742
  syncToGradio(); redraw(); hideStatus();
743
  });
744
 
 
745
  let lastSrc = null;
746
  setInterval(() => {
 
747
  const imgs = document.querySelectorAll('#source-image-component img');
748
  let el = null;
749
  for (const img of imgs) {
 
789
  }
790
  }, 300);
791
 
 
792
  setInterval(() => { syncToGradio(); }, 500);
793
 
794
  new ResizeObserver(() => {
 
805
  """
806
 
807
 
808
+ with gr.Blocks(css=css, theme=purple_theme) as demo:
809
  gr.Markdown("# **QIE-Object-Remover-Bbox**", elem_id="main-title")
810
  gr.Markdown(
811
+ "Perform diverse image edits using a specialized [LoRA](https://huggingface.co/prithivMLmods/QIE-2511-Object-Remover-v2). "
812
+ "Upload an image, draw red bounding boxes over the objects you want to remove, and click Remove Object. "
813
  "Multiple boxes supported. Select, move, resize or delete individual boxes.",
814
  elem_id="subtitle",
815
  )
 
827
  gr.HTML(
828
  '<div class="bbox-hint">'
829
  "<b>Draw mode:</b> Click & drag to create red rectangles. "
830
+ "<b>Select mode:</b> Click a box to select it \u2192 drag to <b>move</b>, "
831
  "drag handles to <b>resize</b>. Use <b>Delete Selected</b> to remove one box."
832
  "</div>"
833
  )
 
857
  """
858
  )
859
 
860
+ gr.HTML('<div id="bbox-debug-count">\u2B1C No boxes drawn yet</div>')
861
 
862
  boxes_json = gr.Textbox(
863
  value="[]",
 
874
  info="Edit the prompt if needed",
875
  )
876
 
877
+ run_btn = gr.Button("\U0001F5D1\uFE0F Remove Object", variant="primary", size="lg")
878
 
879
  with gr.Column(scale=1):
880
  result = gr.Image(label="Output Image", height=449)
 
896
  "[prithivMLmods](https://huggingface.co/prithivMLmods). "
897
  "Adapter: [QIE-2511-Object-Remover-v2]"
898
  "(https://huggingface.co/prithivMLmods/QIE-2511-Object-Remover-v2). "
899
+ "More adapters \u2192 [Qwen-Image-Edit-LoRAs]"
900
  "(https://huggingface.co/models?other=base_model:adapter:Qwen/Qwen-Image-Edit-2509)."
901
  )
902