Lordecyhper commited on
Commit
d6469b6
·
verified ·
1 Parent(s): 2548fb3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +49 -32
app.py CHANGED
@@ -2,60 +2,77 @@ import gradio as gr
2
  import numpy as np
3
  from PIL import Image
4
 
5
- def create_ghost_image(cover, reveal):
6
  if cover is None or reveal is None:
7
  return None
8
 
9
- # 1. Convert to Grayscale ('L')
10
- img1_raw = cover.convert('L')
11
- img2_raw = reveal.convert('L')
12
 
13
- # 2. Resize the reveal image to match the cover image size precisely
14
  if img1_raw.size != img2_raw.size:
15
  img2_raw = img2_raw.resize(img1_raw.size, Image.LANCZOS)
16
 
17
- # 3. Math Processing (Numpy)
18
- g1 = np.array(img1_raw, dtype=float) / 255.0
19
- g2 = np.array(img2_raw, dtype=float) / 255.0
 
20
 
21
- # Squash Image 1 (Cover) into the darker half (0.0 - 0.5)
22
- g1 = g1 * 0.5
 
 
23
 
24
- # Squash Image 2 (Reveal) into the brighter half (0.5 - 1.0)
25
- g2 = 0.5 + (g2 * 0.5)
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
- # Calculate Alpha
28
- out_alpha = 1.0 - g2 + g1
 
 
 
 
 
 
29
 
30
- # Calculate Grayscale
31
- # Handle division by zero safely
32
- out_gray = np.divide(g1, out_alpha, out=np.zeros_like(g1), where=out_alpha > 0.001)
33
 
34
- # 4. Convert back to integer pixel data
35
- final_alpha_int = (out_alpha * 255).clip(0, 255).astype(np.uint8)
36
- final_gray_int = (out_gray * 255).clip(0, 255).astype(np.uint8)
37
 
38
- # 5. Stack channels (R, G, B, A)
39
- rgba_data = np.dstack((final_gray_int, final_gray_int, final_gray_int, final_alpha_int))
40
-
41
  return Image.fromarray(rgba_data, 'RGBA')
42
 
43
- # --- Gradio UI Setup ---
44
- with gr.Blocks(title="Ghost Image Generator") as demo:
45
- gr.Markdown("# 👻 Ghost Image Generator")
46
- gr.Markdown("Create an image that looks different in Light Mode vs Dark Mode (Transparency Trick).")
47
 
48
  with gr.Row():
49
  with gr.Column():
50
  img_dark = gr.Image(type="pil", label="Image seen in DARK Mode (Cover)")
51
  img_light = gr.Image(type="pil", label="Image seen in LIGHT Mode (Reveal)")
52
- btn = gr.Button("Generate Ghost Image", variant="primary")
53
 
54
  with gr.Column():
55
- # FIXED LINE BELOW: Removed 'show_download_button=True'
56
- output_img = gr.Image(label="Result (Download and test in Discord/Twitter)", type="pil")
57
 
58
- btn.click(fn=create_ghost_image, inputs=[img_dark, img_light], outputs=output_img)
59
 
60
- # Launch the app
61
  demo.launch()
 
2
  import numpy as np
3
  from PIL import Image
4
 
5
+ def create_color_ghost(cover, reveal):
6
  if cover is None or reveal is None:
7
  return None
8
 
9
+ # 1. Ensure images are RGB
10
+ img1_raw = cover.convert('RGB')
11
+ img2_raw = reveal.convert('RGB')
12
 
13
+ # 2. Resize reveal to match cover
14
  if img1_raw.size != img2_raw.size:
15
  img2_raw = img2_raw.resize(img1_raw.size, Image.LANCZOS)
16
 
17
+ # 3. Convert to Float Arrays (0.0 - 1.0)
18
+ # This time we keep the 3 color channels (Height, Width, 3)
19
+ rgb1 = np.array(img1_raw, dtype=float) / 255.0
20
+ rgb2 = np.array(img2_raw, dtype=float) / 255.0
21
 
22
+ # 4. Create Grayscale copies just to calculate the Alpha Channel
23
+ # We use the luminance of the pixels to decide transparency
24
+ gray1 = np.mean(rgb1, axis=2)
25
+ gray2 = np.mean(rgb2, axis=2)
26
 
27
+ # 5. Squash the contrast (Math Trick)
28
+ # We squash the target values so they don't clip when we combine them
29
+ rgb1 = rgb1 * 0.5
30
+ rgb2 = 0.5 + (rgb2 * 0.5)
31
+
32
+ # We also squash the grayscale versions for the Alpha calculation
33
+ gray1 = gray1 * 0.5
34
+ gray2 = 0.5 + (gray2 * 0.5)
35
+
36
+ # 6. Calculate the Alpha Channel based on Luminance
37
+ # Formula: Alpha = 1 - Light_Image + Dark_Image
38
+ alpha = 1.0 - gray2 + gray1
39
+
40
+ # Clip alpha to avoid errors
41
+ alpha = np.clip(alpha, 0.001, 0.999)
42
 
43
+ # 7. Reconstruct the Color Output
44
+ # We define the pixel color based on the "Dark Background" version (rgb1)
45
+ # P = Target / Alpha
46
+
47
+ # We have to reshape alpha to (H, W, 1) so we can divide the RGB array (H, W, 3) by it
48
+ alpha_3d = alpha[:, :, np.newaxis]
49
+
50
+ out_rgb = np.divide(rgb1, alpha_3d, out=np.zeros_like(rgb1), where=alpha_3d > 0.001)
51
 
52
+ # 8. Convert back to 0-255 Integers
53
+ final_rgb_int = (out_rgb * 255).clip(0, 255).astype(np.uint8)
54
+ final_alpha_int = (alpha * 255).clip(0, 255).astype(np.uint8)
55
 
56
+ # 9. Stack them into an RGBA Image
57
+ # Concatenate the Color channels with the Alpha channel
58
+ rgba_data = np.dstack((final_rgb_int, final_alpha_int))
59
 
 
 
 
60
  return Image.fromarray(rgba_data, 'RGBA')
61
 
62
+ # --- Gradio UI ---
63
+ with gr.Blocks(title="Color Ghost Generator") as demo:
64
+ gr.Markdown("# 👻 Color Ghost Image Generator")
65
+ gr.Markdown("Create a magic image that changes when clicked (Supports Color).")
66
 
67
  with gr.Row():
68
  with gr.Column():
69
  img_dark = gr.Image(type="pil", label="Image seen in DARK Mode (Cover)")
70
  img_light = gr.Image(type="pil", label="Image seen in LIGHT Mode (Reveal)")
71
+ btn = gr.Button("Generate Ghost", variant="primary")
72
 
73
  with gr.Column():
74
+ output_img = gr.Image(label="Result (Download to test)", type="pil")
 
75
 
76
+ btn.click(fn=create_color_ghost, inputs=[img_dark, img_light], outputs=output_img)
77
 
 
78
  demo.launch()