File size: 9,582 Bytes
d101062
7bdaae1
547723f
7bdaae1
 
 
d101062
 
7bdaae1
 
8bd5dc7
46d2fcf
e3f032e
 
327dc47
7bdaae1
 
 
e3f032e
 
e05e986
7bdaae1
 
 
 
bd1ec8b
e3f032e
 
 
 
 
 
 
 
 
 
 
 
7bdaae1
 
 
 
 
e3f032e
 
 
 
03ea29b
7bdaae1
 
e3f032e
 
03ea29b
e3f032e
 
 
 
 
 
46d2fcf
7bdaae1
 
 
 
e3f032e
 
 
03ea29b
7bdaae1
 
e3f032e
 
 
 
e05e986
ce193d2
46d2fcf
 
 
 
d101062
547723f
8bd5dc7
d101062
8bd5dc7
7bdaae1
d101062
 
 
 
 
 
 
 
 
 
 
 
e05e986
 
2a3ac81
7bdaae1
46d2fcf
e3f032e
e05e986
 
7bdaae1
e05e986
 
46d2fcf
e05e986
 
7bdaae1
547723f
 
 
 
 
 
 
 
 
 
 
7bdaae1
e05e986
 
7bdaae1
e05e986
 
 
 
7bdaae1
e05e986
7bdaae1
 
e3f032e
7bdaae1
e3f032e
7bdaae1
e3f032e
 
 
 
 
 
 
 
d101062
8bd5dc7
 
e05e986
 
8bd5dc7
547723f
327dc47
 
 
 
 
9c5d561
 
 
d101062
 
 
 
7bdaae1
d101062
e05e986
d101062
 
 
 
 
e05e986
d101062
 
 
9c5d561
e3f032e
 
 
 
 
 
46d2fcf
e3f032e
46d2fcf
d101062
e3f032e
 
7bdaae1
e05e986
d101062
 
327dc47
46d2fcf
547723f
 
 
 
 
 
 
 
46d2fcf
 
547723f
 
 
 
 
 
46d2fcf
 
 
547723f
 
 
 
 
 
 
 
 
e3f032e
46d2fcf
547723f
 
 
 
 
 
 
 
 
 
 
 
e3f032e
 
46d2fcf
e3f032e
d101062
46d2fcf
e3f032e
 
 
 
46d2fcf
 
e05e986
7bdaae1
e3f032e
7bdaae1
e3f032e
7bdaae1
 
e3f032e
 
 
547723f
 
e3f032e
7bdaae1
547723f
7bdaae1
e3f032e
547723f
 
 
7bdaae1
547723f
7bdaae1
547723f
 
e3f032e
 
 
 
 
 
 
 
7bdaae1
e3f032e
 
 
 
9c5d561
46d2fcf
9c5d561
547723f
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
import os
import io
import json
import base64
import re
from PIL import Image
import numpy as np
import torch
import gradio as gr
import spaces
from diffusers import FluxImg2ImgPipeline
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC

# Retrieve the encryption key from the environment (set in Hugging Face Secrets Manager)
ENCRYPTION_KEY = os.environ.get("key", "FAKEFALLBACKKEY_FOR_LOCAL_TESTING")

dtype = torch.bfloat16
device = "cuda" if torch.cuda.is_available() else "cpu"

pipe = FluxImg2ImgPipeline.from_pretrained(
    "black-forest-labs/FLUX.1-schnell", 
    torch_dtype=torch.bfloat16
).to(device)

def generate_key(password, salt=None):
    if salt is None:
        salt = os.urandom(16)
    kdf = PBKDF2HMAC(
        algorithm=hashes.SHA256(),
        length=32,
        salt=salt,
        iterations=100000,
    )
    key = base64.urlsafe_b64encode(kdf.derive(password.encode()))
    return key, salt

def encrypt_image(image, password=None):
    # Use the secure key if no override is provided
    if password is None:
        password = ENCRYPTION_KEY

    # Convert PIL Image to bytes
    img_byte_arr = io.BytesIO()
    image.save(img_byte_arr, format='PNG')
    img_byte_arr = img_byte_arr.getvalue()

    # Generate key for encryption using the secure password
    key, salt = generate_key(password)
    cipher = Fernet(key)
    encrypted_data = cipher.encrypt(img_byte_arr)

    return {
        'encrypted_data': base64.b64encode(encrypted_data).decode('utf-8'),
        'salt': base64.b64encode(salt).decode('utf-8'),
        'original_width': image.width,
        'original_height': image.height
    }

def decrypt_image(encrypted_data_dict, password=None):
    if password is None:
        password = ENCRYPTION_KEY

    # Extract the encrypted data and salt
    encrypted_data = base64.b64decode(encrypted_data_dict['encrypted_data'])
    salt = base64.b64decode(encrypted_data_dict['salt'])

    # Regenerate the key using the secure password and salt
    key, _ = generate_key(password, salt)
    cipher = Fernet(key)
    decrypted_data = cipher.decrypt(encrypted_data)
    image = Image.open(io.BytesIO(decrypted_data))
    return image

def sanitize_prompt(prompt):
    # Allow only alphanumeric characters, spaces, and basic punctuation
    allowed_chars = re.compile(r"[^a-zA-Z0-9\s.,!?-]")
    sanitized_prompt = allowed_chars.sub("", prompt)
    return sanitized_prompt

def convert_to_fit_size(original_width_and_height, maximum_size=2048):
    width, height = original_width_and_height
    if width <= maximum_size and height <= maximum_size:
        return width, height

    if width > height:
        scaling_factor = maximum_size / width
    else:
        scaling_factor = maximum_size / height

    new_width = int(width * scaling_factor)
    new_height = int(height * scaling_factor)
    return new_width, new_height

def adjust_to_multiple_of_32(width: int, height: int):
    width = width - (width % 32)
    height = height - (height % 32)
    return width, height

@spaces.GPU(duration=40)
def process_images(image, prompt="a girl", strength=0.75, seed=0, inference_step=4, progress=gr.Progress(track_tqdm=True)):
    progress(0, desc="Starting")

    def process_img2img(image, prompt="a person", strength=0.75, seed=0, num_inference_steps=4):
        if image is None:
            print("Empty input image returned")
            return None
        generator = torch.Generator(device).manual_seed(seed)
        fit_width, fit_height = convert_to_fit_size(image.size)
        width, height = adjust_to_multiple_of_32(fit_width, fit_height)
        image = image.resize((width, height), Image.LANCZOS)

        output = pipe(
            prompt=prompt,
            image=image,
            generator=generator,
            strength=strength,
            width=width,
            height=height,
            guidance_scale=0,
            num_inference_steps=num_inference_steps,
            max_sequence_length=256
        )

        pil_image = output.images[0]
        new_width, new_height = pil_image.size

        if (new_width != fit_width) or (new_height != fit_height):
            resized_image = pil_image.resize((fit_width, fit_height), Image.LANCZOS)
            return resized_image
        return pil_image

    output = process_img2img(image, prompt, strength, seed, inference_step)

    # Encrypt the output image using the secure key
    if output is not None:
        encrypted_output = encrypt_image(output)
        return {
            "display_image": output,
            "encrypted_data": encrypted_output
        }
    return None

def save_encrypted_image(encrypted_data, filename="encrypted_image.enc"):
    with open(filename, 'w') as f:
        json.dump(encrypted_data, f)
    return f"Encrypted image saved as {filename}"

def read_file(path: str) -> str:
    with open(path, 'r', encoding='utf-8') as f:
        content = f.read()
    return content

css = """
#col-left {
    margin: 0 auto;
    max-width: 640px;
}
#col-right {
    margin: 0 auto;
    max-width: 640px;
}
.grid-container {
  display: flex;
  align-items: center;
  justify-content: center;
  gap:10px;
}

.image {
  width: 128px; 
  height: 128px; 
  object-fit: cover; 
}

.text {
  font-size: 16px;
}

.encryption-notice {
  background-color: #f0f0f0;
  padding: 15px;
  border-radius: 5px;
  margin-top: 10px;
  text-align: center;
}
"""

with gr.Blocks(css=css, elem_id="demo-container") as demo:
    # Store encrypted data in a state variable
    encrypted_output_state = gr.State(None)

    with gr.Column():
        gr.HTML(read_file("demo_header.html"))
        gr.HTML(read_file("demo_tools.html"))
    with gr.Row():
        with gr.Column():
            image = gr.Image(
                height=800,
                sources=['upload', 'clipboard'],
                image_mode='RGB',
                elem_id="image_upload",
                type="pil",
                label="Upload"
            )
            with gr.Row(elem_id="prompt-container", equal_height=False):
                with gr.Row():
                    prompt = gr.Textbox(
                        label="Prompt",
                        value="a women",
                        placeholder="Your prompt (what you want in place of what is erased)",
                        elem_id="prompt"
                    )
            btn = gr.Button("Img2Img", elem_id="run_button", variant="primary")
            with gr.Accordion(label="Advanced Settings", open=False):
                with gr.Row(equal_height=True):
                    strength = gr.Number(
                        value=0.75, minimum=0, maximum=0.75, step=0.01, label="Strength"
                    )
                    seed = gr.Number(
                        value=100, minimum=0, step=1, label="Seed"
                    )
                    inference_step = gr.Number(
                        value=4, minimum=1, step=4, label="Inference Steps"
                    )
                id_input = gr.Text(label="Name", visible=False)
        with gr.Column():
            image_out = gr.Image(
                height=800,
                sources=[],
                label="Output (Encrypted)",
                elem_id="output-img",
                format="jpg"
            )
            encryption_notice = gr.HTML(
                '<div class="encryption-notice">'
                'The output image is encrypted. Use the Save button to download the encrypted file.'
                '</div>'
            )
            save_btn = gr.Button("Save Encrypted Image")
            save_result = gr.Text(label="Save Result")

    # Examples section
    gr.Examples(
        examples=[
            ["examples/draw_input.jpg", "examples/draw_output.jpg", "a women, eyes closed, mouth opened"],
            ["examples/draw-gimp_input.jpg", "examples/draw-gimp_output.jpg", "a women, eyes closed, mouth opened"],
            ["examples/gimp_input.jpg", "examples/gimp_output.jpg", "a women, hand on neck"],
            ["examples/inpaint_input.jpg", "examples/inpaint_output.jpg", "a women, hand on neck"]
        ],
        inputs=[image, image_out, prompt],
    )

    gr.HTML(read_file("demo_footer.html"))

    # Process images and encrypt outputs
    def handle_image_generation(image, prompt, strength, seed, inference_step):
        result = process_images(image, prompt, strength, seed, inference_step)
        if result:
            return result["display_image"], result["encrypted_data"]
        return None, None

    btn.click(
        fn=handle_image_generation,
        inputs=[image, prompt, strength, seed, inference_step],
        outputs=[image_out, encrypted_output_state],
        api_name="/process_images"
    )

    prompt.submit(
        fn=handle_image_generation,
        inputs=[image, prompt, strength, seed, inference_step],
        outputs=[image_out, encrypted_output_state],
        api_name="/process_images"
    )

    def handle_save_encrypted(encrypted_data):
        if encrypted_data:
            import tempfile
            fd, path = tempfile.mkstemp(suffix='.encimg')
            with os.fdopen(fd, 'w') as f:
                json.dump(encrypted_data, f)
            return f"Encrypted image saved to {path}"
        return "No encrypted image to save"

    save_btn.click(
        fn=handle_save_encrypted,
        inputs=[encrypted_output_state],
        outputs=[save_result]
    )

if __name__ == "__main__":
    demo.launch(share=True, show_error=True)