ysharma HF Staff hadisalman commited on
Commit
82adb40
·
0 Parent(s):

Duplicate from hadisalman/photoguard

Browse files

Co-authored-by: Hadi Salman <[email protected]>

.gitattributes ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tflite filter=lfs diff=lfs merge=lfs -text
29
+ *.tgz filter=lfs diff=lfs merge=lfs -text
30
+ *.wasm filter=lfs diff=lfs merge=lfs -text
31
+ *.xz filter=lfs diff=lfs merge=lfs -text
32
+ *.zip filter=lfs diff=lfs merge=lfs -text
33
+ *.zst filter=lfs diff=lfs merge=lfs -text
34
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
35
+ *jpg filter=lfs diff=lfs merge=lfs -text
36
+ *.jpg filter=lfs diff=lfs merge=lfs -text
README.md ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Photoguard
3
+ emoji: 🛡
4
+ colorFrom: gray
5
+ colorTo: green
6
+ sdk: gradio
7
+ sdk_version: 3.19.1
8
+ app_file: app.py
9
+ pinned: false
10
+ license: apache-2.0
11
+ duplicated_from: hadisalman/photoguard
12
+ ---
13
+
14
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
app.py ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from io import BytesIO
2
+ import requests
3
+ import gradio as gr
4
+ import requests
5
+ import torch
6
+ from tqdm import tqdm
7
+ from PIL import Image, ImageOps
8
+ from diffusers import StableDiffusionInpaintPipeline
9
+ from torchvision.transforms import ToPILImage
10
+ from utils import preprocess, prepare_mask_and_masked_image, recover_image, resize_and_crop
11
+
12
+ gr.close_all()
13
+ topil = ToPILImage()
14
+
15
+ pipe_inpaint = StableDiffusionInpaintPipeline.from_pretrained(
16
+ "runwayml/stable-diffusion-inpainting",
17
+ revision="fp16",
18
+ torch_dtype=torch.float16,
19
+ safety_checker=None,
20
+ )
21
+ pipe_inpaint = pipe_inpaint.to("cuda")
22
+
23
+ ## Good params for editing that we used all over the paper --> decent quality and speed
24
+ GUIDANCE_SCALE = 7.5
25
+ NUM_INFERENCE_STEPS = 100
26
+ DEFAULT_SEED = 1234
27
+
28
+ def pgd(X, targets, model, criterion, eps=0.1, step_size=0.015, iters=40, clamp_min=0, clamp_max=1, mask=None):
29
+ X_adv = X.clone().detach() + (torch.rand(*X.shape)*2*eps-eps).cuda()
30
+ pbar = tqdm(range(iters))
31
+ for i in pbar:
32
+ actual_step_size = step_size - (step_size - step_size / 100) / iters * i
33
+ X_adv.requires_grad_(True)
34
+
35
+ loss = (model(X_adv).latent_dist.mean - targets).norm()
36
+ pbar.set_description(f"Loss {loss.item():.5f} | step size: {actual_step_size:.4}")
37
+
38
+ grad, = torch.autograd.grad(loss, [X_adv])
39
+
40
+ X_adv = X_adv - grad.detach().sign() * actual_step_size
41
+ X_adv = torch.minimum(torch.maximum(X_adv, X - eps), X + eps)
42
+ X_adv.data = torch.clamp(X_adv, min=clamp_min, max=clamp_max)
43
+ X_adv.grad = None
44
+
45
+ if mask is not None:
46
+ X_adv.data *= mask
47
+
48
+ return X_adv
49
+
50
+ def get_target():
51
+ target_url = 'https://www.rtings.com/images/test-materials/2015/204_Gray_Uniformity.png'
52
+ response = requests.get(target_url)
53
+ target_image = Image.open(BytesIO(response.content)).convert("RGB")
54
+ target_image = target_image.resize((512, 512))
55
+ return target_image
56
+
57
+ def immunize_fn(init_image, mask_image):
58
+ with torch.autocast('cuda'):
59
+ mask, X = prepare_mask_and_masked_image(init_image, mask_image)
60
+ X = X.half().cuda()
61
+ mask = mask.half().cuda()
62
+
63
+ targets = pipe_inpaint.vae.encode(preprocess(get_target()).half().cuda()).latent_dist.mean
64
+
65
+ adv_X = pgd(X,
66
+ targets = targets,
67
+ model=pipe_inpaint.vae.encode,
68
+ criterion=torch.nn.MSELoss(),
69
+ clamp_min=-1,
70
+ clamp_max=1,
71
+ eps=0.12,
72
+ step_size=0.01,
73
+ iters=200,
74
+ mask=1-mask
75
+ )
76
+
77
+ adv_X = (adv_X / 2 + 0.5).clamp(0, 1)
78
+
79
+ adv_image = topil(adv_X[0]).convert("RGB")
80
+ adv_image = recover_image(adv_image, init_image, mask_image, background=True)
81
+ return adv_image
82
+
83
+ def run(image, prompt, seed, immunize=False):
84
+ if seed == '':
85
+ seed = DEFAULT_SEED
86
+ else:
87
+ seed = int(seed)
88
+ torch.manual_seed(seed)
89
+
90
+ init_image = Image.fromarray(image['image'])
91
+ init_image = resize_and_crop(init_image, (512,512))
92
+ mask_image = ImageOps.invert(Image.fromarray(image['mask']).convert('RGB'))
93
+ mask_image = resize_and_crop(mask_image, init_image.size)
94
+
95
+ if immunize:
96
+ immunized_image = immunize_fn(init_image, mask_image)
97
+
98
+ image_edited = pipe_inpaint(prompt=prompt,
99
+ image=init_image if not immunize else immunized_image,
100
+ mask_image=mask_image,
101
+ height = init_image.size[0],
102
+ width = init_image.size[1],
103
+ eta=1,
104
+ guidance_scale=GUIDANCE_SCALE,
105
+ num_inference_steps=NUM_INFERENCE_STEPS,
106
+ ).images[0]
107
+
108
+ image_edited = recover_image(image_edited, init_image, mask_image)
109
+
110
+ if immunize:
111
+ return [(immunized_image, 'Immunized Image'), (image_edited, 'Edited After Immunization')]
112
+ else:
113
+ return [(image_edited, 'Edited Image')]
114
+
115
+
116
+ demo = gr.Interface(fn=run,
117
+ inputs=[
118
+ gr.ImageMask(label='Drawing tool to mask regions you want to keep, e.g. faces'),
119
+ gr.Textbox(label='Prompt', placeholder='A photo of a man in a wedding'),
120
+ gr.Textbox(label='Seed (Change to get different edits!)', placeholder=str(DEFAULT_SEED), visible=True),
121
+ gr.Checkbox(label='Immunize', value=False),
122
+ ],
123
+ cache_examples=False,
124
+ outputs=[gr.Gallery(
125
+ label="Generated images",
126
+ show_label=False,
127
+ elem_id="gallery").style(grid=[1,2], height="auto")],
128
+ examples=[
129
+ ['./images/hadi_and_trevor.jpg', 'man attending a wedding', '329357'],
130
+ ['./images/trevor_2.jpg', 'two men in prison', '329357'],
131
+ ['./images/elon_2.jpg', 'man in a metro station', '214213'],
132
+ ],
133
+ examples_per_page=20,
134
+ allow_flagging='never',
135
+ title="Interactive Demo: Immunize your Photos Against AI-powered Malicious Manipulation",
136
+ description='''<u>Official</u> demo of our paper: <br>
137
+ **Raising the Cost of Malicious AI-Powered Image Editing** <br>
138
+ *[Hadi Salman](https://twitter.com/hadisalmanX)\*, [Alaa Khaddaj](https://twitter.com/Alaa_Khaddaj)\*, [Guillaume Leclerc](https://twitter.com/gpoleclerc)\*, [Andrew Ilyas](https://twitter.com/andrew_ilyas), [Aleksander Madry](https://twitter.com/aleks_madry)* <br>
139
+ MIT &nbsp;&nbsp;[Paper](https://arxiv.org/abs/2302.06588)
140
+ &nbsp;&nbsp;[Blog post](https://gradientscience.org/photoguard/)
141
+ &nbsp;&nbsp;[![](https://badgen.net/badge/icon/GitHub?icon=github&label)](https://github.com/MadryLab/photoguard)
142
+ <br />
143
+ Below you can test our (encoder attack) immunization method for making images resistant to manipulation by Stable Diffusion. This immunization process forces the model to perform unrealistic edits.
144
+ <br />
145
+ **This is a research project and is not production-ready. See Section 5 in our paper for discussion on its limitations.**
146
+ <details closed>
147
+ <summary>Click for demo steps:</summary>
148
+
149
+ + Upload an image (or select from the below examples!)
150
+ + Mask (using the drawing tool) the parts of the image you want to maintain unedited (e.g., faces of people)
151
+ + Add a prompt to edit the image accordingly (see examples below)
152
+ + Play with the seed and click submit until you get a realistic edit that you are happy with (or use default seeds below)
153
+
154
+ Now let's immunize your image and try again!
155
+ + Click on the "immunize" button, then submit.
156
+ + You will get the immunized image (which looks identical to the original one) and the edited image, which is now hopefully unrealistic!
157
+ </details>
158
+ ''',
159
+ )
160
+
161
+ # demo.launch()
162
+ demo.launch(server_name='0.0.0.0', share=False, server_port=7860, inline=False)
images/elon_1.jpg ADDED

Git LFS Details

  • SHA256: b461ad0d0c64453a72083c90ac50fa30026e8e3ac1defc74ff584965159ec17f
  • Pointer size: 131 Bytes
  • Size of remote file: 395 kB
images/elon_2.jpg ADDED

Git LFS Details

  • SHA256: e084ef96bdd5fdec4577a8463bf10650d037c7a91babcc95367bb1577ad4d551
  • Pointer size: 132 Bytes
  • Size of remote file: 1.3 MB
images/hadi_and_trevor.jpg ADDED

Git LFS Details

  • SHA256: d4141c2ca76c57a4dc9c436ff0d9cc881a3de1c4cdbc66311a5518c40419cbdb
  • Pointer size: 131 Bytes
  • Size of remote file: 146 kB
images/trevor_2.jpg ADDED

Git LFS Details

  • SHA256: 26e07361a25183e4674c5fbf9470e2a50bd1a3c3e23e95b61a2023be826be2f0
  • Pointer size: 131 Bytes
  • Size of remote file: 461 kB
images/trevor_3.jpg ADDED

Git LFS Details

  • SHA256: 27600d7a1eaf217c4005105ff34b8547a36ae684283bd4ebe952e7920334dae7
  • Pointer size: 132 Bytes
  • Size of remote file: 1.13 MB
images/trevor_4.jpg ADDED

Git LFS Details

  • SHA256: 4c9e1b9bd859d4e005bb50c8898eb6665b7d9f14b05f2c573f975c7de3b0c923
  • Pointer size: 131 Bytes
  • Size of remote file: 892 kB
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ diffusers==0.10.2
2
+ transformers
3
+ scipy
4
+ accelerate
5
+ torchvision
6
+ --extra-index-url https://download.pytorch.org/whl/cu113
utils.py ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import numpy as np
3
+ import torch
4
+ from PIL import Image, ImageOps
5
+ from torchvision.transforms import ToPILImage, ToTensor
6
+ totensor = ToTensor()
7
+ topil = ToPILImage()
8
+
9
+
10
+
11
+ def resize_and_crop(img, size, crop_type="center"):
12
+ '''Resize and crop the image to the given size.'''
13
+ if crop_type == "top":
14
+ center = (0, 0)
15
+ elif crop_type == "center":
16
+ center = (0.5, 0.5)
17
+ else:
18
+ raise ValueError
19
+
20
+ resize = list(size)
21
+ if size[0] is None:
22
+ resize[0] = img.size[0]
23
+ if size[1] is None:
24
+ resize[1] = img.size[1]
25
+ return ImageOps.fit(img, resize, centering=center)
26
+
27
+
28
+ def recover_image(image, init_image, mask, background=False):
29
+ image = totensor(image)
30
+ mask = totensor(mask)[0]
31
+ init_image = totensor(init_image)
32
+
33
+ if background:
34
+ result = mask * init_image + (1 - mask) * image
35
+ else:
36
+ result = mask * image + (1 - mask) * init_image
37
+ return topil(result)
38
+
39
+
40
+ def preprocess(image):
41
+ w, h = image.size
42
+ w, h = map(lambda x: x - x % 32, (w, h)) # resize to integer multiple of 32
43
+ image = image.resize((w, h), resample=Image.LANCZOS)
44
+ image = np.array(image).astype(np.float32) / 255.0
45
+ image = image[None].transpose(0, 3, 1, 2)
46
+ image = torch.from_numpy(image)
47
+ return 2.0 * image - 1.0
48
+
49
+
50
+ def prepare_mask_and_masked_image(image, mask):
51
+
52
+ image = np.array(image.convert("RGB"))
53
+ image = image[None].transpose(0, 3, 1, 2)
54
+ image = torch.from_numpy(image).to(dtype=torch.float32) / 127.5 - 1.0
55
+
56
+ mask = np.array(mask.convert("L"))
57
+ mask = mask.astype(np.float32) / 255.0
58
+ mask = mask[None, None]
59
+ mask[mask < 0.5] = 0
60
+ mask[mask >= 0.5] = 1
61
+ mask = torch.from_numpy(mask)
62
+
63
+ masked_image = image * (mask < 0.5)
64
+
65
+ return mask, masked_image