ming0531 commited on
Commit
53f11f5
Β·
verified Β·
1 Parent(s): 7499a86

Clearer layout: header with logo and links, a first-try tip, numbered steps, status callout

Browse files
Files changed (3) hide show
  1. .gitignore +3 -0
  2. app.py +53 -32
  3. flashrefocus/rendering/depth_edits.py +4 -3
.gitignore ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ weights/
2
+ __pycache__/
3
+ *.pyc
app.py CHANGED
@@ -15,7 +15,7 @@ from PIL import Image, ImageDraw, ImageFont
15
 
16
  import engine
17
 
18
- SINGLE, DUAL, TILT = "One focal plane", "Two focal planes", "Tilt-shift"
19
  EXAMPLES = ["examples/portrait_city.jpg", "examples/portrait_aquarium.jpg", "examples/flower.jpg",
20
  "examples/dock.jpg", "examples/sculpture.jpg"]
21
  FONT = "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf"
@@ -193,55 +193,76 @@ def on_reset(state):
193
  return controls + [aif_view(state, r["mode"]), out, depth_view, state, note + msg]
194
 
195
 
196
- HEADER = """
197
- <div style="text-align:center">
198
- <h1 style="margin-bottom:0.2em">FlashReFocus: Interactive Image Refocusing in Seconds</h1>
199
- <p><a href="https://ming053l.github.io/FlashReFocus/">Project page</a> &nbsp;Β·&nbsp;
200
- <a href="https://ming053l.github.io/FlashReFocus/assets/FlashReFocus.pdf">Paper</a> &nbsp;Β·&nbsp;
201
- <a href="https://github.com/ming053l/FlashReFocus">Code</a> &nbsp;Β·&nbsp;
202
- <a href="https://youtu.be/g032M7moWQ4">Video</a></p>
 
 
 
 
 
 
 
 
203
  </div>
 
204
 
205
- **How to use.** (1) Upload a photograph, or pick an example, and press **Restore**: one-step diffusion restores the
206
- all-in-focus image once. (2) **Click** the restored image or the depth map to focus; each click runs only the
207
- 1.3M-parameter renderer. (3) Turn the **aperture**, reshape the **depth curve**, or upload your **own depth map**;
208
- switch to **Two focal planes** (click two points) or **Tilt-shift** (click the row to keep sharp).
 
 
 
 
 
 
 
 
 
 
209
  """
210
 
211
- with gr.Blocks(title="FlashReFocus", theme=gr.themes.Soft()) as demo:
212
- gr.Markdown(HEADER)
213
  state = gr.State(None)
214
  with gr.Row():
215
  with gr.Column():
216
- inp = gr.Image(label="1. Input photograph", type="pil", sources=["upload", "clipboard"], height=340)
217
- with gr.Row():
 
 
218
  long_side = gr.Slider(512, 1536, value=1024, step=64, label="Long side (pixels)")
219
  omega = gr.Slider(0.0, 1.5, value=1.25, step=0.25, label="Detail weight Ο‰",
220
  info="0: pixel LoRA only (most faithful); higher restores more detail.")
221
- btn = gr.Button("Restore", variant="primary")
222
- gr.Examples(EXAMPLES, inputs=inp, label="Examples (Unsplash portraits and our own photographs)")
223
  depth = gr.Image(label="Depth used for rendering (near is bright): click to focus", type="pil",
224
- interactive=False, height=300)
225
- with gr.Accordion("Depth map controls", open=True):
226
  gamma = gr.Slider(0.4, 2.5, value=1.0, step=0.05, label="Depth curve Ξ³ (D β†’ D^Ξ³)",
227
  info="< 1 spreads out the near range, > 1 the far range; blur follows the new spacing.")
228
  band = gr.Slider(0.02, 0.3, value=0.06, step=0.01, label="Tilt-shift: half-height of the sharp band")
229
  user_depth = gr.Image(label="Your own depth map (optional; near is bright, any size)", type="numpy",
230
- sources=["upload", "clipboard"], height=160)
231
  with gr.Column():
232
- aif = gr.Image(label="2. Restored all-in-focus image: click to focus", type="pil", interactive=False,
233
- height=340)
234
- with gr.Row():
235
- mode = gr.Radio([SINGLE, DUAL, TILT], value=SINGLE, label="Focus mode",
236
- info="Two planes: the first click sets A, the second B. Tilt-shift: click a row.")
237
- strength = gr.Slider(0.0, 1.5, value=0.5, step=0.05, label="Aperture (blur strength K)",
238
  info="0: everything sharp; 1: the widest aperture seen in training; above 1 extrapolates.")
239
  with gr.Row():
240
- show_marker = gr.Checkbox(value=False, label="Show focal markers on the result")
241
- clear = gr.Button("Clear focal points")
242
- reset = gr.Button("Reset to recommended settings")
243
- out = gr.Image(label="3. Refocused", type="pil", interactive=False, height=420, format="png")
244
- status = gr.Markdown("")
245
  controls = [state, strength, mode, gamma, band, show_marker]
246
  outputs = [aif, out, depth, state, status]
247
  btn.click(restore, [inp, long_side, omega, gamma], [aif, depth, out, state, status])
 
15
 
16
  import engine
17
 
18
+ SINGLE, DUAL, TILT = "πŸ“ One focal plane", "πŸ‘₯ Two focal planes", "πŸ™οΈ Tilt-shift"
19
  EXAMPLES = ["examples/portrait_city.jpg", "examples/portrait_aquarium.jpg", "examples/flower.jpg",
20
  "examples/dock.jpg", "examples/sculpture.jpg"]
21
  FONT = "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf"
 
193
  return controls + [aif_view(state, r["mode"]), out, depth_view, state, note + msg]
194
 
195
 
196
+ LOGO = """<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64" height="64"><circle cx="32" cy="32" r="30" fill="#FFFFFF" stroke="#2E7D32" stroke-width="3"/><g fill="#48A43F" stroke="#FFFFFF" stroke-width="1.6" stroke-linejoin="round"><path d="M32 4 A28 28 0 0 1 56.2 18 L40.6 27 Z" transform="rotate(0 32 32)"/><path d="M32 4 A28 28 0 0 1 56.2 18 L40.6 27 Z" transform="rotate(60 32 32)"/><path d="M32 4 A28 28 0 0 1 56.2 18 L40.6 27 Z" transform="rotate(120 32 32)"/><path d="M32 4 A28 28 0 0 1 56.2 18 L40.6 27 Z" transform="rotate(180 32 32)"/><path d="M32 4 A28 28 0 0 1 56.2 18 L40.6 27 Z" transform="rotate(240 32 32)"/><path d="M32 4 A28 28 0 0 1 56.2 18 L40.6 27 Z" transform="rotate(300 32 32)"/></g><path d="M35.5 19 L25.5 34 H31.5 L28.5 45 L39 29 H33 Z" fill="#FFD21E" stroke="#363636" stroke-width="1.4" stroke-linejoin="round"/></svg>
197
+ """
198
+
199
+ HEADER = f"""
200
+ <div id="header">
201
+ <div class="title">{LOGO}<div><h1>FlashReFocus</h1><p>Interactive image refocusing in seconds: restore a photo once,
202
+ then click to refocus, open the aperture, or keep two subjects sharp.</p></div></div>
203
+ <div class="links">
204
+ <a href="https://ming053l.github.io/FlashReFocus/" target="_blank">🌐 Project page</a>
205
+ <a href="https://ming053l.github.io/FlashReFocus/assets/FlashReFocus.pdf" target="_blank">πŸ“„ Paper</a>
206
+ <a href="https://github.com/ming053l/FlashReFocus" target="_blank">πŸ’» Code</a>
207
+ <a href="https://youtu.be/g032M7moWQ4" target="_blank">🎬 Video</a>
208
+ </div>
209
+ <div class="tip">πŸ‘‰ <b>Try this first:</b> pick the aquarium example β†’ <b>Restore</b> β†’ choose <b>πŸ‘₯ Two focal planes</b>
210
+ β†’ click the face, then the shark. Every click after Restore runs only the 1.3M-parameter renderer.</div>
211
  </div>
212
+ """
213
 
214
+ CSS = """
215
+ #header {margin-bottom: 4px}
216
+ #header .title {display: flex; align-items: center; justify-content: center; gap: 16px}
217
+ #header h1 {margin: 0; font-size: 2.1em; color: #2E7D32}
218
+ #header .title p {margin: 2px 0 0; color: var(--body-text-color-subdued)}
219
+ #header .links {display: flex; flex-wrap: wrap; justify-content: center; gap: 8px; margin: 12px 0}
220
+ #header .links a {padding: 5px 14px; border-radius: 999px; border: 1px solid #48A43F; color: #2E7D32;
221
+ text-decoration: none; font-weight: 600}
222
+ #header .links a:hover {background: #48A43F; color: white}
223
+ #header .tip {background: #f1f8ef; border-left: 4px solid #48A43F; padding: 10px 14px; border-radius: 8px;
224
+ color: #1f3d1f}
225
+ .step h3 {margin: 6px 0 2px; color: #2E7D32}
226
+ #status {background: #f1f8ef; border-left: 4px solid #48A43F; padding: 8px 14px; border-radius: 8px; min-height: 2.4em}
227
+ #status p {margin: 0; color: #1f3d1f}
228
  """
229
 
230
+ with gr.Blocks(title="FlashReFocus", theme=gr.themes.Soft(primary_hue="green"), css=CSS) as demo:
231
+ gr.HTML(HEADER)
232
  state = gr.State(None)
233
  with gr.Row():
234
  with gr.Column():
235
+ gr.Markdown("### β‘  Upload a photo and restore it once", elem_classes="step")
236
+ inp = gr.Image(label="Input photograph", type="pil", sources=["upload", "clipboard"], height=320)
237
+ gr.Examples(EXAMPLES, inputs=inp, label="Examples (Unsplash portraits and our own photographs)")
238
+ with gr.Accordion("Restoration settings", open=False):
239
  long_side = gr.Slider(512, 1536, value=1024, step=64, label="Long side (pixels)")
240
  omega = gr.Slider(0.0, 1.5, value=1.25, step=0.25, label="Detail weight Ο‰",
241
  info="0: pixel LoRA only (most faithful); higher restores more detail.")
242
+ btn = gr.Button("✨ Restore", variant="primary", size="lg")
243
+ gr.Markdown("### β‘’ Depth map", elem_classes="step")
244
  depth = gr.Image(label="Depth used for rendering (near is bright): click to focus", type="pil",
245
+ interactive=False, height=280)
246
+ with gr.Accordion("Depth controls", open=False):
247
  gamma = gr.Slider(0.4, 2.5, value=1.0, step=0.05, label="Depth curve Ξ³ (D β†’ D^Ξ³)",
248
  info="< 1 spreads out the near range, > 1 the far range; blur follows the new spacing.")
249
  band = gr.Slider(0.02, 0.3, value=0.06, step=0.01, label="Tilt-shift: half-height of the sharp band")
250
  user_depth = gr.Image(label="Your own depth map (optional; near is bright, any size)", type="numpy",
251
+ sources=["upload", "clipboard"], height=150)
252
  with gr.Column():
253
+ gr.Markdown("### β‘‘ Click the restored image to focus", elem_classes="step")
254
+ aif = gr.Image(label="Restored all-in-focus image (click to focus)", type="pil", interactive=False,
255
+ height=320)
256
+ mode = gr.Radio([SINGLE, DUAL, TILT], value=SINGLE, label="Focus mode",
257
+ info="Two planes: the first click sets A, the second B. Tilt-shift: click the row to keep sharp.")
258
+ strength = gr.Slider(0.0, 1.5, value=0.5, step=0.05, label="πŸ”† Aperture (blur strength K)",
259
  info="0: everything sharp; 1: the widest aperture seen in training; above 1 extrapolates.")
260
  with gr.Row():
261
+ show_marker = gr.Checkbox(value=False, label="Show focal markers")
262
+ clear = gr.Button("Clear focal points", size="sm")
263
+ reset = gr.Button("β†Ί Recommended settings", size="sm")
264
+ status = gr.Markdown("Upload a photo or pick an example, then press ✨ Restore.", elem_id="status")
265
+ out = gr.Image(label="Refocused result", type="pil", interactive=False, height=430, format="png")
266
  controls = [state, strength, mode, gamma, band, show_marker]
267
  outputs = [aif, out, depth, state, status]
268
  btn.click(restore, [inp, long_side, omega, gamma], [aif, depth, out, state, status])
flashrefocus/rendering/depth_edits.py CHANGED
@@ -11,11 +11,12 @@ def depth_at(depth, x, y):
11
 
12
  def two_focal_planes(depth, focal_depth_a, focal_depth_b):
13
  """D' = D_A + min(|D - D_A|, |D - D_B|): every pixel is blurred by its distance to the nearer of two focal planes.
14
- Render D' with focal depth D_A. When D_A > 0.5 the distance is subtracted instead, so that D' stays within [0, 1]
15
- (adding it to a far plane would clip everything to 1, i.e. nothing blurred)."""
16
  da, db = float(focal_depth_a), float(focal_depth_b)
17
  distance = torch.minimum((depth - da).abs(), (depth - db).abs())
18
- return (da - distance if da > 0.5 else da + distance).clamp(0, 1)
 
19
 
20
 
21
  def tilt_shift(height, width, row, slope=3.0, band=0.06, device="cpu"):
 
11
 
12
  def two_focal_planes(depth, focal_depth_a, focal_depth_b):
13
  """D' = D_A + min(|D - D_A|, |D - D_B|): every pixel is blurred by its distance to the nearer of two focal planes.
14
+ Render D' with focal depth D_A. If adding the distance would clip more pixels at 1 than subtracting it would clip at
15
+ 0 (a far plane A), it is subtracted instead: adding it to D_A = 1 would make the whole image sharp."""
16
  da, db = float(focal_depth_a), float(focal_depth_b)
17
  distance = torch.minimum((depth - da).abs(), (depth - db).abs())
18
+ subtract = int((da - distance < 0).sum()) < int((da + distance > 1).sum())
19
+ return (da - distance if subtract else da + distance).clamp(0, 1)
20
 
21
 
22
  def tilt_shift(height, width, row, slope=3.0, band=0.06, device="cpu"):