iimmortall commited on
Commit
860c112
·
verified ·
1 Parent(s): 9b7cecc

Deploy InstantRetouch IP2P-BILA ZeroGPU Space

Browse files
.gitattributes CHANGED
@@ -33,3 +33,7 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ assets/examples/4920_O_0_5_input.png filter=lfs diff=lfs merge=lfs -text
37
+ assets/examples/4933_O_0_21_input.png filter=lfs diff=lfs merge=lfs -text
38
+ assets/examples/expert116_input.png filter=lfs diff=lfs merge=lfs -text
39
+ assets/examples/expert48_input.png filter=lfs diff=lfs merge=lfs -text
README.md CHANGED
@@ -8,25 +8,20 @@ pinned: false
8
  license: other
9
  ---
10
 
11
- # InstantRetouch / BILA Space Demo
12
 
13
- This Space is an isolated Hugging Face ZeroGPU/Gradio demo for direct image editing with two BILA backends:
14
 
15
- - `ip2p_bila_score1_8_104`: InstructPix2Pix base plus BILA checkpoint.
16
- - `flux_bila_score1_8_022`: Flux2 Klein base, task LoRA, and BILA checkpoint.
17
 
18
- The demo does not import or depend on the research repo's `agent/` path. It uses the validation-style direct flow:
19
-
20
- 1. Load the base model.
21
- 2. Load the selected checkpoint's `state_dict`.
22
- 3. Generate `bila_output`.
23
- 4. Apply the UI strength as `input + strength * (bila_output - input)`.
24
 
25
  ## Required Space Variables
26
 
27
  Set one of these in the Space environment:
28
 
29
- - `BILA_WEIGHTS_REPO`: Hugging Face model repo containing the weight layout below.
30
  - `BILA_MODEL_ROOT`: local path with the same layout, useful only for staging/debugging.
31
 
32
  Optional:
@@ -41,21 +36,14 @@ Do not commit weights into this Space repo. Put them in a separate HF model repo
41
  ```text
42
  ip2p/
43
  base/
 
 
 
 
44
  checkpoints/
45
- epoch_5_bila_score1_8_104.pth
46
- flux/
47
- base/
48
- task_lora/
49
- pytorch_lora_weights.safetensors
50
- checkpoints/
51
- epoch_8_bila_score1_8_022.pth
52
  metrics/
53
- ip2p_bila_score1_8_104.json
54
- flux_bila_score1_8_022.json
55
  ```
56
 
57
- The app lazily downloads only the selected model's allow-listed files, so it does not pull both large bases during cold start unless both models are used.
58
-
59
- ## ZeroGPU Notes
60
-
61
- ZeroGPU requires the Gradio SDK and the `@spaces.GPU` decorator; this Space is configured that way. A `Dockerfile` is kept only as a fallback for standard paid GPU Spaces.
 
8
  license: other
9
  ---
10
 
11
+ # InstantRetouch / IP2P-BiLA Demo
12
 
13
+ Public Hugging Face ZeroGPU demo for instruction-guided image retouching with the validation-selected IP2P/BiLA checkpoint.
14
 
15
+ - Model: IP2P/BiLA
16
+ - UI: image upload, optional instruction, seed, max side, strength, and selectable examples
17
 
18
+ This Space is isolated from the research repository. It does not import `agent/`, training scripts, or local experiment paths at runtime. Weights live in a separate Hugging Face model repo and are downloaded lazily through `BILA_WEIGHTS_REPO`.
 
 
 
 
 
19
 
20
  ## Required Space Variables
21
 
22
  Set one of these in the Space environment:
23
 
24
+ - `BILA_WEIGHTS_REPO`: Hugging Face model repo containing the IP2P weight layout below.
25
  - `BILA_MODEL_ROOT`: local path with the same layout, useful only for staging/debugging.
26
 
27
  Optional:
 
36
  ```text
37
  ip2p/
38
  base/
39
+ tokenizer/
40
+ text_encoder/
41
+ vae/
42
+ unet/
43
  checkpoints/
44
+ <bila-checkpoint>.pth
 
 
 
 
 
 
45
  metrics/
46
+ <metric-summary>.json
 
47
  ```
48
 
49
+ The app follows the validation-style direct flow: load the IP2P base model, load the BiLA checkpoint named in `model_manifest.json`, generate `bila_output`, then apply strength as `input + strength * (bila_output - input)`.
 
 
 
 
app.py CHANGED
@@ -52,50 +52,65 @@ from demo_runtime.manager import DemoManager
52
 
53
 
54
  manager = DemoManager()
 
 
 
 
 
 
 
 
55
 
56
 
57
  @spaces.GPU(duration=300, size="xlarge")
58
- def run_demo(image, instruction, model_key, seed, max_side, strength):
59
  try:
60
- edited, diff, input_image, status = manager.generate(
61
  image=image,
62
  instruction=instruction,
63
- model_key=model_key,
64
  seed=int(seed),
65
  max_side=int(max_side),
66
  strength=float(strength),
67
  )
68
- comparison = [(input_image, "Input"), (diff, "Base output"), (edited, "BILA output")]
69
- return edited, comparison, status
70
  except Exception as exc:
71
  raise gr.Error(str(exc))
72
 
73
 
74
  with gr.Blocks(title="InstantRetouch") as demo:
75
- gr.Markdown("# InstantRetouch")
 
 
 
 
 
 
 
76
  with gr.Row():
77
  with gr.Column(scale=1):
78
  image = gr.Image(type="pil", label="Input image")
79
- instruction = gr.Textbox(label="Instruction", lines=3)
80
- model_key = gr.Dropdown(
81
- choices=manager.model_choices,
82
- value=manager.default_model,
83
- label="Model",
84
- )
85
  with gr.Row():
86
  seed = gr.Number(value=42, precision=0, label="Seed")
87
  max_side = gr.Slider(512, 2048, value=1024, step=64, label="Max side")
88
  strength = gr.Slider(0.0, 2.0, value=1.0, step=0.05, label="Strength")
89
  button = gr.Button("Run", variant="primary")
90
  with gr.Column(scale=1):
91
- edited = gr.Image(type="pil", label="Edited")
92
  status = gr.Textbox(label="Status", interactive=False)
93
- comparison = gr.Gallery(label="Comparison", columns=3, height="auto")
 
 
 
 
 
 
94
 
95
  button.click(
96
  fn=run_demo,
97
- inputs=[image, instruction, model_key, seed, max_side, strength],
98
- outputs=[edited, comparison, status],
99
  )
100
 
101
 
 
52
 
53
 
54
  manager = DemoManager()
55
+ DEFAULT_MODEL = manager.default_model
56
+ EXAMPLE_DIR = ROOT / "assets" / "examples"
57
+ EXAMPLES = [
58
+ [str(EXAMPLE_DIR / "4920_O_0_5_input.png"), "Make the image feel more serene and add a subtle blue hue.", 42, 1024, 1.0],
59
+ [str(EXAMPLE_DIR / "4933_O_0_21_input.png"), "Improve the exposure and make the colors richer while keeping a natural photo look.", 7, 1024, 1.0],
60
+ [str(EXAMPLE_DIR / "expert48_input.png"), "Brighten the image and enhance clarity with balanced contrast.", 123, 1024, 0.9],
61
+ [str(EXAMPLE_DIR / "expert116_input.png"), "", 314, 1024, 1.0],
62
+ ]
63
 
64
 
65
  @spaces.GPU(duration=300, size="xlarge")
66
+ def run_demo(image, instruction, seed, max_side, strength):
67
  try:
68
+ edited, _diff, _input_image, status = manager.generate(
69
  image=image,
70
  instruction=instruction,
71
+ model_key=DEFAULT_MODEL,
72
  seed=int(seed),
73
  max_side=int(max_side),
74
  strength=float(strength),
75
  )
76
+ return edited, status
 
77
  except Exception as exc:
78
  raise gr.Error(str(exc))
79
 
80
 
81
  with gr.Blocks(title="InstantRetouch") as demo:
82
+ gr.Markdown(
83
+ """
84
+ # InstantRetouch
85
+ Instruction-guided photo retouching with the selected IP2P/BiLA checkpoint. Upload an image, enter an optional instruction, or click one of the examples below.
86
+
87
+ This public demo uses the validation-selected IP2P/BiLA model only. The strength slider blends the model output with the input for gentler or stronger edits.
88
+ """
89
+ )
90
  with gr.Row():
91
  with gr.Column(scale=1):
92
  image = gr.Image(type="pil", label="Input image")
93
+ instruction = gr.Textbox(label="Instruction", lines=3, placeholder="Optional. Leave empty for prompt=\"\".")
 
 
 
 
 
94
  with gr.Row():
95
  seed = gr.Number(value=42, precision=0, label="Seed")
96
  max_side = gr.Slider(512, 2048, value=1024, step=64, label="Max side")
97
  strength = gr.Slider(0.0, 2.0, value=1.0, step=0.05, label="Strength")
98
  button = gr.Button("Run", variant="primary")
99
  with gr.Column(scale=1):
100
+ edited = gr.Image(type="pil", label="Edited image")
101
  status = gr.Textbox(label="Status", interactive=False)
102
+
103
+ gr.Examples(
104
+ examples=EXAMPLES,
105
+ inputs=[image, instruction, seed, max_side, strength],
106
+ examples_per_page=4,
107
+ cache_examples=False,
108
+ )
109
 
110
  button.click(
111
  fn=run_demo,
112
+ inputs=[image, instruction, seed, max_side, strength],
113
+ outputs=[edited, status],
114
  )
115
 
116
 
assets/examples/4920_O_0_5_input.png ADDED

Git LFS Details

  • SHA256: aa968c1ab81530849bd33b72c2b8678febf97ea4fc47b0d2a6a34d1d50409ff0
  • Pointer size: 131 Bytes
  • Size of remote file: 321 kB
assets/examples/4933_O_0_21_input.png ADDED

Git LFS Details

  • SHA256: 8cacf8a9ae53a29ba10399e8f6f9c085b2d524c7b35acbf08c11d5e1e45772a8
  • Pointer size: 131 Bytes
  • Size of remote file: 665 kB
assets/examples/expert116_input.png ADDED

Git LFS Details

  • SHA256: 4effdc11f7c86c78b4ad70d7f6a9a881b4ec67dfdbcd03d7475b0c7ccf809ac4
  • Pointer size: 131 Bytes
  • Size of remote file: 121 kB
assets/examples/expert48_input.png ADDED

Git LFS Details

  • SHA256: 693c612097a0c22b67ebf006c08746c277203b875a19898731d8a7887bd9fd73
  • Pointer size: 131 Bytes
  • Size of remote file: 316 kB
demo_runtime/manager.py CHANGED
@@ -109,10 +109,6 @@ class DemoManager:
109
  edited_tensor = blend_strength(prepared.full_tensor, result["bila"], strength)
110
  edited = tensor_to_pil(edited_tensor)
111
  diff = tensor_to_pil(result["diff"])
112
- evidence = model_cfg["evidence"]["scores_avg"]
113
- status = (
114
- f"{model_cfg['label']} | score_1={evidence['score_1']:.3f} | "
115
- f"score_2={evidence['score_2']:.3f} | seed={int(seed)}"
116
- )
117
  return edited, diff, prepared.full_pil, status
118
 
 
109
  edited_tensor = blend_strength(prepared.full_tensor, result["bila"], strength)
110
  edited = tensor_to_pil(edited_tensor)
111
  diff = tensor_to_pil(result["diff"])
112
+ status = f"{model_cfg['label']} | seed={int(seed)}"
 
 
 
 
113
  return edited, diff, prepared.full_pil, status
114
 
model_manifest.json CHANGED
@@ -3,13 +3,16 @@
3
  "default_model": "ip2p_bila_score1_8_104",
4
  "models": {
5
  "ip2p_bila_score1_8_104": {
6
- "label": "IP2P/BiLA (score_1 8.104)",
7
  "kind": "ip2p",
8
  "weights": {
9
  "base": "ip2p/base",
10
  "checkpoint": "ip2p/checkpoints/epoch_5_bila_score1_8_104.pth"
11
  },
12
- "expected_checkpoint_keys": ["state_dict.unet", "state_dict.bila"],
 
 
 
13
  "config": {
14
  "bila_grid_res": 32,
15
  "bila_grid_bins": 8,
@@ -28,47 +31,6 @@
28
  "score_2": 8.984478935698448
29
  }
30
  }
31
- },
32
- "flux_bila_score1_8_022": {
33
- "label": "Flux/BiLA (score_1 8.022)",
34
- "kind": "flux",
35
- "weights": {
36
- "base": "flux/base",
37
- "task_lora": "flux/task_lora/pytorch_lora_weights.safetensors",
38
- "checkpoint": "flux/checkpoints/epoch_8_bila_score1_8_022.pth"
39
- },
40
- "expected_checkpoint_keys": [
41
- "state_dict.transformer_lora",
42
- "state_dict.bila"
43
- ],
44
- "config": {
45
- "bila_grid_res": 16,
46
- "bila_grid_bins": 8,
47
- "bila_use_flux_rgb": false,
48
- "model_size": 512,
49
- "cfg": false,
50
- "use_t2i": false,
51
- "not_scheduler_decode": true,
52
- "mixed_precision": "bf16",
53
- "max_sequence_length": 512,
54
- "distill_strategy": "merge_then_new",
55
- "distill_lora_rank": 32,
56
- "distill_lora_alpha": 32,
57
- "distill_lora_dropout": 0.0,
58
- "task_lora_rank": 32,
59
- "task_lora_alpha": 32
60
- },
61
- "evidence": {
62
- "metric_file": "metrics/flux_bila_score1_8_022.json",
63
- "source_run": "train-2026-03-11--00-29-image-all-1",
64
- "model_filter": "bila",
65
- "num_pairs": 89,
66
- "scores_avg": {
67
- "score_1": 8.02247191011236,
68
- "score_2": 9.426966292134832
69
- }
70
- }
71
  }
72
  }
73
  }
74
-
 
3
  "default_model": "ip2p_bila_score1_8_104",
4
  "models": {
5
  "ip2p_bila_score1_8_104": {
6
+ "label": "IP2P/BiLA",
7
  "kind": "ip2p",
8
  "weights": {
9
  "base": "ip2p/base",
10
  "checkpoint": "ip2p/checkpoints/epoch_5_bila_score1_8_104.pth"
11
  },
12
+ "expected_checkpoint_keys": [
13
+ "state_dict.unet",
14
+ "state_dict.bila"
15
+ ],
16
  "config": {
17
  "bila_grid_res": 32,
18
  "bila_grid_bins": 8,
 
31
  "score_2": 8.984478935698448
32
  }
33
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  }
35
  }
36
  }