AlbeRota commited on
Commit
296993d
·
1 Parent(s): a45ed0e
.gitignore ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ .venv/
2
+ sample_images/
README.md CHANGED
@@ -1,11 +1,11 @@
1
  ---
2
- title: UnReflectAnything Demo
3
- emoji: 👀
4
  colorFrom: yellow
5
  colorTo: blue
6
  sdk: gradio
7
- sdk_version: 6.16.0
8
- python_version: '3.12'
9
  app_file: app.py
10
  pinned: false
11
  license: mit
 
1
  ---
2
+ title: UnReflectAnything
3
+ emoji: 💻
4
  colorFrom: yellow
5
  colorTo: blue
6
  sdk: gradio
7
+ sdk_version: 6.5.1
8
+ python_version: 3.12
9
  app_file: app.py
10
  pinned: false
11
  license: mit
app.py ADDED
@@ -0,0 +1,479 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Gradio demo for UnReflectAnything: remove specular reflections from images."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import shutil
6
+ import sys
7
+ from pathlib import Path
8
+ from typing import NamedTuple
9
+
10
+ # Allow importing unreflectanything when run from gradio_space (e.g. HF Space with root dir)
11
+ _REPO_ROOT = Path(__file__).resolve().parent.parent
12
+ if _REPO_ROOT not in sys.path:
13
+ sys.path.insert(0, str(_REPO_ROOT))
14
+
15
+ # Guard against missing '__main__' in worker threads (wandb/pydantic compat)
16
+ if "__main__" not in sys.modules:
17
+ import types
18
+ sys.modules["__main__"] = types.ModuleType("__main__")
19
+
20
+ _GRADIO_DIR = Path(__file__).resolve().parent
21
+ try:
22
+ import spaces
23
+ except ModuleNotFoundError:
24
+ spaces = None
25
+ import gradio as gr
26
+ import numpy as np
27
+ import torch
28
+
29
+ from huggingface_hub import hf_hub_download, snapshot_download
30
+
31
+ HF_REPO = "AlbeRota/UnReflectAnything"
32
+ IMAGE_EXTENSIONS = (".png", ".jpg", ".jpeg", ".bmp", ".tif", ".tiff", ".webp")
33
+
34
+
35
+ class HFAssets(NamedTuple):
36
+ """Paths to assets downloaded from the Hugging Face repo."""
37
+
38
+ weights_path: str
39
+ config_path: str
40
+ logo_path: str
41
+ sample_images_dir: Path
42
+
43
+
44
+ def _download_from_hf() -> HFAssets:
45
+ """Download weights, config, logo, and sample images from the HF repo. Returns paths to all assets."""
46
+ weights_path = hf_hub_download(
47
+ repo_id=HF_REPO,
48
+ revision="a0d3c7bff5ddb2c430e74b5f8ee67be7cc28fcbe",
49
+ filename="weights/full_model_weights.pt",
50
+ )
51
+ print("Weights path: ", weights_path)
52
+ # config_path = hf_hub_download(
53
+ # repo_id=HF_REPO,
54
+ # filename="configs/pretrained_config.yaml",
55
+ # )
56
+ logo_path = hf_hub_download(
57
+ repo_id=HF_REPO,
58
+ filename="assets/logo.png",
59
+ )
60
+ sample_images_root = Path(
61
+ snapshot_download(
62
+ repo_id=HF_REPO,
63
+ allow_patterns=["sample_images/*"],
64
+ )
65
+ )
66
+ sample_images_dir = sample_images_root / "sample_images"
67
+ return HFAssets(
68
+ weights_path=weights_path,
69
+ config_path=Path(__file__).parent / "pretrained_config.yaml",
70
+ logo_path=logo_path,
71
+ sample_images_dir=sample_images_dir,
72
+ )
73
+
74
+
75
+ _cached_assets: HFAssets | None = None
76
+
77
+
78
+ def _get_assets() -> HFAssets:
79
+ """Return HF assets, downloading once and caching."""
80
+ global _cached_assets
81
+ if _cached_assets is None:
82
+ _cached_assets = _download_from_hf()
83
+ return _cached_assets
84
+
85
+
86
+ # Local copy of sample images under cwd so Gradio never needs allowed_paths for examples
87
+ _SAMPLE_IMAGES_COPY_DIR: Path | None = None
88
+
89
+
90
+ def _get_sample_image_paths() -> list[str]:
91
+ """Return paths of sample images under cwd (copied from HF cache) so Gradio can use them without allowed_paths."""
92
+ global _SAMPLE_IMAGES_COPY_DIR
93
+ assets = _get_assets()
94
+ src = assets.sample_images_dir
95
+ if not src.is_dir():
96
+ return []
97
+ dest = _GRADIO_DIR / "sample_images"
98
+ dest.mkdir(parents=True, exist_ok=True)
99
+ paths = []
100
+ for p in sorted(src.iterdir()):
101
+ if not p.is_file() or p.suffix.lower() not in IMAGE_EXTENSIONS:
102
+ continue
103
+ dst_file = dest / p.name
104
+ if not dst_file.exists() or dst_file.stat().st_mtime < p.stat().st_mtime:
105
+ shutil.copy2(p, dst_file)
106
+ paths.append(str(dst_file.resolve()))
107
+ _SAMPLE_IMAGES_COPY_DIR = dest
108
+ return paths
109
+
110
+
111
+ def _get_sample_image_arrays() -> list[np.ndarray]:
112
+ """Load sample images as numpy arrays (H, W, 3) uint8 for gr.Examples so the input Image shows a preview."""
113
+ from PIL import Image
114
+
115
+ paths = _get_sample_image_paths()
116
+ arrays = []
117
+ for p in paths:
118
+ try:
119
+ img = Image.open(p).convert("RGB")
120
+ arrays.append(np.array(img))
121
+ except Exception:
122
+ continue
123
+ return arrays
124
+
125
+
126
+ # Single model instance; loaded in background at app start or on first inference.
127
+ _cached_ura_model = None
128
+ _cached_device = None
129
+
130
+
131
+ def _get_model(device: str):
132
+ """Return the pretrained model, loading it once and moving to the requested device."""
133
+ global _cached_ura_model, _cached_device
134
+ assets = _get_assets()
135
+
136
+ from unreflectanything import model
137
+
138
+ # If the model isn't loaded yet, initialize it
139
+ if _cached_ura_model is None:
140
+ print(f"Loading model initially on {device}...")
141
+ _cached_ura_model = model(
142
+ pretrained=True,
143
+ weights_path=assets.weights_path,
144
+ # config_path=assets.config_path,
145
+ device=device,
146
+ verbose=False,
147
+ )
148
+ _cached_device = device
149
+
150
+ # If the model is loaded but on the wrong device, move it
151
+ if _cached_device != device:
152
+ print(f"Moving model from {_cached_device} to {device}...")
153
+ _cached_ura_model.to(device)
154
+ _cached_device = device
155
+
156
+ return _cached_ura_model
157
+
158
+ def build_ui():
159
+ _get_assets()
160
+ # PREVENT: _get_model("cuda") here. It will crash ZeroGPU during startup.
161
+ print("UI building... Model will initialize on first inference.")
162
+
163
+ # Note: Use the decorator directly on the function that does the heavy lifting
164
+ def _extract_tokens_nc(tokens) -> torch.Tensor:
165
+ """Extract [N, C] from list (last layer) or tensor [B, N, C] (first sample)."""
166
+ t = tokens[-1] if isinstance(tokens, list) else tokens
167
+ t = t[0].cpu().float() if t.dim() == 3 else t.cpu().float()
168
+ return t.squeeze(0) if t.dim() == 3 else t # [N, C]
169
+
170
+ def _tokens_pair_to_rgb(
171
+ tokens_completed,
172
+ tokens_input,
173
+ h: int,
174
+ w: int,
175
+ ) -> tuple[np.ndarray, np.ndarray]:
176
+ """PCA fit once on completed tokens; apply same mean and V to both; joint min/max norm."""
177
+ from PIL import Image as PILImage
178
+
179
+ t_comp = _extract_tokens_nc(tokens_completed) # [N, C]
180
+ t_inp = _extract_tokens_nc(tokens_input) # [N, C]
181
+ mean = t_comp.mean(dim=0, keepdim=True) # [1, C] – fit on completed only
182
+ centered_comp = t_comp - mean # [N, C]
183
+ U, S, V = torch.svd_lowrank(centered_comp, q=3) # V: [C, 3]
184
+ # Project both with same parameters (same mean, same V)
185
+ proj_comp = (t_comp - mean) @ V # [N, 3]
186
+ proj_inp = (t_inp - mean) @ V # [N, 3]
187
+ # Joint min/max so both images share the same color scale
188
+ lo = min(proj_comp.min().item(), proj_inp.min().item())
189
+ hi = max(proj_comp.max().item(), proj_inp.max().item())
190
+ eps = 1e-8
191
+ proj_comp = (proj_comp - lo) / (hi - lo + eps)
192
+ proj_inp = (proj_inp - lo) / (hi - lo + eps)
193
+ grid = int(t_comp.shape[0] ** 0.5)
194
+ def to_img(proj: torch.Tensor) -> np.ndarray:
195
+ arr = (proj.reshape(grid, grid, 3).numpy() * 255).clip(0, 255).astype(np.uint8)
196
+ return np.array(PILImage.fromarray(arr).resize((w, h), PILImage.BILINEAR))
197
+ return to_img(proj_comp), to_img(proj_inp)
198
+
199
+ def _gray_to_rgb(tensor_1c: torch.Tensor, h: int, w: int) -> np.ndarray:
200
+ """Convert [B, 1, H_model, W_model] to resized [H, W, 3] uint8 grayscale-as-RGB."""
201
+ from torchvision.transforms import functional as TF
202
+
203
+ resized = TF.resize(tensor_1c.cpu(), [h, w], antialias=True) # [B, 1, H, W]
204
+ gray = (resized[0, 0].numpy().clip(0.0, 1.0) * 255).astype(np.uint8) # [H, W]
205
+ return np.stack([gray] * 3, axis=-1) # [H, W, 3]
206
+
207
+ @spaces.GPU if spaces else lambda x: x
208
+ def run_inference(
209
+ image: np.ndarray | None,
210
+ threshold: float = 0.3,
211
+ dilation: int = 40,
212
+ ) -> dict[str, np.ndarray] | None:
213
+ """Run reflection removal; return all visualisable outputs as numpy arrays."""
214
+ if image is None:
215
+ return None
216
+
217
+ from torchvision.transforms import functional as TF
218
+ import time
219
+
220
+ device = "cuda" if torch.cuda.is_available() else "cpu"
221
+ ura_model = _get_model(device)
222
+
223
+ target_side = ura_model.image_size
224
+ h, w = image.shape[:2]
225
+
226
+ tensor = TF.to_tensor(image).unsqueeze(0) # [1, 3, H, W]
227
+ tensor = TF.resize(tensor, [target_side, target_side], antialias=True)
228
+ tensor = tensor.to(device, dtype=torch.float32)
229
+
230
+ with torch.no_grad():
231
+ start_time = time.time()
232
+ out = ura_model(
233
+ images=tensor,
234
+ threshold=threshold,
235
+ dilation=int(dilation),
236
+ return_dict=True,
237
+ )
238
+ end_time = time.time()
239
+
240
+ inference_time_ms = (end_time - start_time) * 1000
241
+ gr.Info(f"Inference complete in {inference_time_ms:.1f} ms")
242
+
243
+ results: dict[str, np.ndarray] = {}
244
+
245
+ # Diffuse: [1, 3, H, W] -> [H, W, 3] uint8
246
+ diffuse = TF.resize(out["diffuse"].cpu(), [h, w], antialias=True)
247
+ results["diffuse"] = (diffuse[0].numpy().transpose(1, 2, 0).clip(0.0, 1.0) * 255).astype(np.uint8)
248
+
249
+ # Detected highlight: RGBA overlay superimposed on darkened input
250
+ hl_data = out.get("highlight")
251
+ if hl_data is not None:
252
+ m = TF.resize(hl_data.cpu(), [h, w], antialias=True)[0, 0].numpy().clip(0.0, 1.0) # [H, W]
253
+ image_dark = (image.astype(np.float32) * 0.5).clip(0, 255) # [H, W, 3] base
254
+ overlay_rgb = np.array([255, 200, 0], dtype=np.float32) # amber
255
+ alpha = (0.5 * m)[:, :, np.newaxis] # [H, W, 1]
256
+ comp_rgb = (1 - alpha) * image_dark + alpha * overlay_rgb # [H, W, 3]
257
+ comp_uint8 = np.clip(comp_rgb, 0, 255).astype(np.uint8)
258
+ results["highlight_overlay"] = np.concatenate(
259
+ [comp_uint8, np.full((h, w, 1), 255, dtype=np.uint8)], axis=-1
260
+ ) # [H, W, 4] RGBA
261
+ results["highlight_gray"] = _gray_to_rgb(hl_data, h, w)
262
+
263
+ # Highlight mask (binary/dilated)
264
+ mask_data = out.get("highlight_mask")
265
+ if mask_data is not None:
266
+ results["highlight_mask"] = _gray_to_rgb(mask_data, h, w)
267
+
268
+ # DINOv3 tokens (PCA visualization) – same PCA fit for both, joint color scale
269
+ tokens_completed_data = out.get("tokens_completed")
270
+ tokens_input_data = out.get("tokens_input")
271
+ if tokens_completed_data is not None and tokens_input_data is not None:
272
+ img_comp, img_inp = _tokens_pair_to_rgb(tokens_completed_data, tokens_input_data, h, w)
273
+ results["tokens_completed"] = img_comp
274
+ results["tokens_input"] = img_inp
275
+ elif tokens_completed_data is not None:
276
+ t = _extract_tokens_nc(tokens_completed_data)
277
+ mean = t.mean(dim=0, keepdim=True)
278
+ V = torch.svd_lowrank(t - mean, q=3)[2]
279
+ proj = (t - mean) @ V
280
+ lo, hi = proj.min().item(), proj.max().item()
281
+ proj = (proj - lo) / (hi - lo + 1e-8)
282
+ grid = int(t.shape[0] ** 0.5)
283
+ from PIL import Image as PILImage
284
+ arr = (proj.reshape(grid, grid, 3).numpy() * 255).clip(0, 255).astype(np.uint8)
285
+ results["tokens_completed"] = np.array(PILImage.fromarray(arr).resize((w, h), PILImage.BILINEAR))
286
+ results["tokens_input"] = results["tokens_completed"]
287
+ elif tokens_input_data is not None:
288
+ t = _extract_tokens_nc(tokens_input_data)
289
+ mean = t.mean(dim=0, keepdim=True)
290
+ V = torch.svd_lowrank(t - mean, q=3)[2]
291
+ proj = (t - mean) @ V
292
+ lo, hi = proj.min().item(), proj.max().item()
293
+ proj = (proj - lo) / (hi - lo + 1e-8)
294
+ grid = int(t.shape[0] ** 0.5)
295
+ from PIL import Image as PILImage
296
+ arr = (proj.reshape(grid, grid, 3).numpy() * 255).clip(0, 255).astype(np.uint8)
297
+ results["tokens_input"] = np.array(PILImage.fromarray(arr).resize((w, h), PILImage.BILINEAR))
298
+ results["tokens_completed"] = results["tokens_input"]
299
+
300
+ return results
301
+
302
+ VIEW_MODES = ["Diffuse", "Highlight", "Inpaint mask", "DINOv3 space"]
303
+
304
+ def run_inference_slider(
305
+ image: np.ndarray | None,
306
+ threshold: float,
307
+ dilation: int,
308
+ ) -> tuple:
309
+ """Return 4 slider tuples: (left, right) for each view mode."""
310
+ results = run_inference(image, threshold, dilation)
311
+ if results is None:
312
+ return (None,) * 4
313
+
314
+ diffuse = results["diffuse"]
315
+ # Darken input for slider so highlights are more visible; Gradio expects uint8 [0,255]
316
+ image_dark = (image.astype(np.float32) * 0.5).clip(0, 255).astype(np.uint8)
317
+ hl_overlay = results.get("highlight_overlay", diffuse)
318
+ hl_gray = results.get("highlight_gray", diffuse)
319
+ hl_mask = results.get("highlight_mask", diffuse)
320
+ tok_comp = results.get("tokens_completed", diffuse)
321
+ tok_inp = results.get("tokens_input", diffuse)
322
+
323
+ return (
324
+ (image, diffuse), # Diffuse
325
+ (image_dark, hl_overlay), # Detected highlight
326
+ (hl_gray, hl_mask), # Highlight mask
327
+ (tok_inp, tok_comp), # DINOv3 tokens
328
+ )
329
+
330
+ assets = _get_assets()
331
+ with gr.Blocks(title="UnReflectAnything") as demo:
332
+ with gr.Row(elem_classes="mobile-stack"):
333
+ with gr.Column(scale=0, min_width=100):
334
+ if Path(assets.logo_path).is_file():
335
+ gr.Image(
336
+ value=assets.logo_path,
337
+ show_label=False,
338
+ interactive=False,
339
+ height=100,
340
+ container=False,
341
+ buttons=[],
342
+ )
343
+ with gr.Column(scale=1):
344
+ gr.Markdown(
345
+ """
346
+ # UnReflectAnything
347
+ UnReflectAnything inputs any RGB image and **removes specular highlights**,
348
+ returning a clean diffuse-only outputs. We trained UnReflectAnything by synthetizing
349
+ specularities and supervising in DINOv3 feature space.
350
+ UnReflectAnything works on both natural indoor and **surgical/endoscopic** domain data.
351
+ Visit the [Project Page](https://alberto-rota.github.io/UnReflectAnything/)!
352
+ """
353
+ )
354
+ slider_labels = [
355
+ "Diffuse",
356
+ "Highlight",
357
+ "Inpaint",
358
+ "DINOv3 Space",
359
+ ]
360
+ with gr.Row(elem_classes="mobile-stack"):
361
+ inp = gr.Image(
362
+ type="numpy",
363
+ label="Input",
364
+ height=600,
365
+ )
366
+ sliders = []
367
+ for i, lbl in enumerate(slider_labels):
368
+ sliders.append(
369
+ gr.ImageSlider(
370
+ label=lbl,
371
+ type="numpy",
372
+ height=600,
373
+ show_label=True,
374
+ visible=(i == 0),
375
+ )
376
+ )
377
+ with gr.Row(elem_classes="mobile-stack"):
378
+ threshold_slider = gr.Slider(
379
+ minimum=0.0, maximum=1.0, value=0.2, step=0.01,
380
+ label="Highlight Threshold",
381
+ info="Brightness threshold for detecting specular highlights",
382
+ )
383
+ dilation_slider = gr.Slider(
384
+ minimum=0, maximum=100, value=40, step=1,
385
+ label="Mask Dilation",
386
+ info="Dilation (px) applied to the detected highlight mask",
387
+ )
388
+ view_radio = gr.Radio(
389
+ choices=VIEW_MODES,
390
+ value=VIEW_MODES[0],
391
+ label="Output view",
392
+ )
393
+ run_btn = gr.Button("Run UnReflectAnything", variant="primary")
394
+ run_btn.click(
395
+ fn=run_inference_slider,
396
+ inputs=[inp, threshold_slider, dilation_slider],
397
+ outputs=sliders,
398
+ )
399
+ view_radio.change(
400
+ fn=lambda mode: [gr.update(visible=(m == mode)) for m in VIEW_MODES],
401
+ inputs=view_radio,
402
+ outputs=sliders,
403
+ )
404
+ sample_arrays = _get_sample_image_arrays()
405
+ if sample_arrays:
406
+ gr.Examples(
407
+ examples=[[arr] for arr in sample_arrays],
408
+ inputs=inp,
409
+ label="Pre-loaded examples",
410
+ examples_per_page=20,
411
+ )
412
+ gr.HTML("""<hr>""")
413
+ gr.Markdown("""
414
+ [Project Page](https://alberto-rota.github.io/UnReflectAnything/) ⋅
415
+ [GitHub](https://github.com/alberto-rota/UnReflectAnything) ⋅
416
+ [Model Card](https://huggingface.co/AlbeRota/UnReflectAnything) ⋅
417
+ [Paper](https://arxiv.org/abs/2512.09583) ⋅
418
+ [Contact](mailto:alberto1.rota@polimi.it)
419
+ """)
420
+ return demo
421
+
422
+
423
+ demo = build_ui()
424
+
425
+
426
+ def _launch_allowed_paths():
427
+ """Paths Gradio is allowed to serve (e.g. for gr.Examples from HF cache)."""
428
+ paths = [str(_GRADIO_DIR)]
429
+ try:
430
+ assets = _get_assets()
431
+ sample_dir = assets.sample_images_dir
432
+ if sample_dir.is_dir():
433
+ paths.append(str(sample_dir.resolve()))
434
+ # Also allow parent (snapshot root) in case Gradio resolves paths from repo root
435
+ parent = sample_dir.parent
436
+ if parent.is_dir():
437
+ paths.append(str(parent.resolve()))
438
+ except Exception as e:
439
+ print(f"Warning: could not add HF sample_images to allowed_paths: {e}")
440
+ return paths
441
+
442
+
443
+ _MOBILE_CSS = """
444
+ @media (max-width: 768px) {
445
+ .mobile-stack { flex-direction: column !important; }
446
+ .mobile-stack > .gr-column,
447
+ .mobile-stack > div { min-width: 100% !important; }
448
+ }
449
+ """
450
+
451
+
452
+ def _launch_kwargs():
453
+ """Default kwargs for launch() so allowed_paths are always set (e.g. when HF Spaces runs demo.launch())."""
454
+ return {
455
+ "allowed_paths": _launch_allowed_paths(),
456
+ "theme": gr.themes.Soft(primary_hue="orange", secondary_hue="blue"),
457
+ "css": _MOBILE_CSS,
458
+ }
459
+
460
+
461
+ # Ensure launch() always receives allowed_paths (e.g. when HF Spaces runner calls demo.launch() without args)
462
+ _original_launch = demo.launch
463
+
464
+
465
+ def _launch_with_allowed_paths(*args, **kwargs):
466
+ for key, value in _launch_kwargs().items():
467
+ if key not in kwargs:
468
+ kwargs[key] = value
469
+ return _original_launch(*args, **kwargs)
470
+
471
+
472
+ demo.launch = _launch_with_allowed_paths
473
+
474
+
475
+ # Replace your existing launch logic at the very bottom of the file with this:
476
+ if __name__ == "__main__":
477
+ demo.launch(ssr_mode=True, server_name="0.0.0.0", server_port=7860)
478
+ else:
479
+ demo.launch(ssr_mode=True, server_name="0.0.0.0", server_port=7860)
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ gradio
2
+ huggingface-hub
3
+ unreflectanything @ git+https://github.com/alberto-rota/UnReflectAnything.git@49fc52a351863257c0137f7c15397d6bda91b8ef
4
+ dotmap
tmp/config.log ADDED
File without changes
tmp/engine_initializers.log ADDED
File without changes
tmp/models.log ADDED
File without changes
tmp/optimization.log ADDED
File without changes