hysts HF staff commited on
Commit
b8bfb45
1 Parent(s): 77c13d7
Files changed (7) hide show
  1. .pre-commit-config.yaml +59 -36
  2. .style.yapf +0 -5
  3. .vscode/settings.json +30 -0
  4. app.py +14 -13
  5. app_generated_image.py +51 -85
  6. app_real_image.py +53 -75
  7. style.css +8 -0
.pre-commit-config.yaml CHANGED
@@ -1,37 +1,60 @@
1
- exclude: patch
2
  repos:
3
- - repo: https://github.com/pre-commit/pre-commit-hooks
4
- rev: v4.2.0
5
- hooks:
6
- - id: check-executables-have-shebangs
7
- - id: check-json
8
- - id: check-merge-conflict
9
- - id: check-shebang-scripts-are-executable
10
- - id: check-toml
11
- - id: check-yaml
12
- - id: double-quote-string-fixer
13
- - id: end-of-file-fixer
14
- - id: mixed-line-ending
15
- args: ['--fix=lf']
16
- - id: requirements-txt-fixer
17
- - id: trailing-whitespace
18
- - repo: https://github.com/myint/docformatter
19
- rev: v1.4
20
- hooks:
21
- - id: docformatter
22
- args: ['--in-place']
23
- - repo: https://github.com/pycqa/isort
24
- rev: 5.12.0
25
- hooks:
26
- - id: isort
27
- - repo: https://github.com/pre-commit/mirrors-mypy
28
- rev: v0.991
29
- hooks:
30
- - id: mypy
31
- args: ['--ignore-missing-imports']
32
- additional_dependencies: ['types-python-slugify']
33
- - repo: https://github.com/google/yapf
34
- rev: v0.32.0
35
- hooks:
36
- - id: yapf
37
- args: ['--parallel', '--in-place']
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  repos:
2
+ - repo: https://github.com/pre-commit/pre-commit-hooks
3
+ rev: v4.6.0
4
+ hooks:
5
+ - id: check-executables-have-shebangs
6
+ - id: check-json
7
+ - id: check-merge-conflict
8
+ - id: check-shebang-scripts-are-executable
9
+ - id: check-toml
10
+ - id: check-yaml
11
+ - id: end-of-file-fixer
12
+ - id: mixed-line-ending
13
+ args: ["--fix=lf"]
14
+ - id: requirements-txt-fixer
15
+ - id: trailing-whitespace
16
+ - repo: https://github.com/myint/docformatter
17
+ rev: v1.7.5
18
+ hooks:
19
+ - id: docformatter
20
+ args: ["--in-place"]
21
+ - repo: https://github.com/pycqa/isort
22
+ rev: 5.13.2
23
+ hooks:
24
+ - id: isort
25
+ args: ["--profile", "black"]
26
+ - repo: https://github.com/pre-commit/mirrors-mypy
27
+ rev: v1.10.0
28
+ hooks:
29
+ - id: mypy
30
+ args: ["--ignore-missing-imports"]
31
+ additional_dependencies:
32
+ [
33
+ "types-python-slugify",
34
+ "types-requests",
35
+ "types-PyYAML",
36
+ "types-pytz",
37
+ ]
38
+ - repo: https://github.com/psf/black
39
+ rev: 24.4.2
40
+ hooks:
41
+ - id: black
42
+ language_version: python3.10
43
+ args: ["--line-length", "119"]
44
+ - repo: https://github.com/kynan/nbstripout
45
+ rev: 0.7.1
46
+ hooks:
47
+ - id: nbstripout
48
+ args:
49
+ [
50
+ "--extra-keys",
51
+ "metadata.interpreter metadata.kernelspec cell.metadata.pycharm",
52
+ ]
53
+ - repo: https://github.com/nbQA-dev/nbQA
54
+ rev: 1.8.5
55
+ hooks:
56
+ - id: nbqa-black
57
+ - id: nbqa-pyupgrade
58
+ args: ["--py37-plus"]
59
+ - id: nbqa-isort
60
+ args: ["--float-to-top"]
.style.yapf DELETED
@@ -1,5 +0,0 @@
1
- [style]
2
- based_on_style = pep8
3
- blank_line_before_nested_class_or_def = false
4
- spaces_before_comment = 2
5
- split_before_logical_operator = true
 
 
 
 
 
 
.vscode/settings.json ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "editor.formatOnSave": true,
3
+ "files.insertFinalNewline": false,
4
+ "[python]": {
5
+ "editor.defaultFormatter": "ms-python.black-formatter",
6
+ "editor.formatOnType": true,
7
+ "editor.codeActionsOnSave": {
8
+ "source.organizeImports": "explicit"
9
+ }
10
+ },
11
+ "[jupyter]": {
12
+ "files.insertFinalNewline": false
13
+ },
14
+ "black-formatter.args": [
15
+ "--line-length=119"
16
+ ],
17
+ "isort.args": ["--profile", "black"],
18
+ "flake8.args": [
19
+ "--max-line-length=119"
20
+ ],
21
+ "ruff.lint.args": [
22
+ "--line-length=119"
23
+ ],
24
+ "notebook.output.scrolling": true,
25
+ "notebook.formatOnCellExecution": true,
26
+ "notebook.formatOnSave.enabled": true,
27
+ "notebook.codeActionsOnSave": {
28
+ "source.organizeImports": "explicit"
29
+ }
30
+ }
app.py CHANGED
@@ -13,29 +13,30 @@ import torch
13
  from app_generated_image import create_prompt_demo
14
  from app_real_image import create_real_image_demo
15
 
16
- DESCRIPTION = '# [Plug-and-Play diffusion features](https://github.com/MichalGeyer/plug-and-play)'
17
 
18
- if (SPACE_ID := os.getenv('SPACE_ID')) is not None:
19
  DESCRIPTION += f'\n<p>For faster inference without waiting in queue, you may duplicate the space and upgrade to GPU in settings. <a href="https://huggingface.co/spaces/{SPACE_ID}?duplicate=true"><img style="display: inline; margin-top: 0em; margin-bottom: 0em" src="https://bit.ly/3gLdBN6" alt="Duplicate Space" /></a></p>'
20
  if not torch.cuda.is_available():
21
- DESCRIPTION += '\n<p>Running on CPU 🥶 This demo does not work on CPU.</p>'
22
 
23
  if torch.cuda.is_available():
24
- weight_dir = pathlib.Path('plug-and-play/models/ldm/stable-diffusion-v1')
25
  if not weight_dir.exists():
 
26
  subprocess.run(
27
  shlex.split(
28
- 'mkdir -p plug-and-play/models/ldm/stable-diffusion-v1/'))
29
- subprocess.run(
30
- shlex.split(
31
- 'wget https://huggingface.co/CompVis/stable-diffusion-v-1-4-original/resolve/main/sd-v1-4.ckpt -O plug-and-play/models/ldm/stable-diffusion-v1/model.ckpt'
32
- ))
33
 
34
- with gr.Blocks(css='style.css') as demo:
35
  gr.Markdown(DESCRIPTION)
36
  with gr.Tabs():
37
- with gr.TabItem('Use real image as input'):
38
  create_real_image_demo()
39
- with gr.TabItem('Use prompt as input'):
40
  create_prompt_demo()
41
- demo.queue(max_size=10).launch()
 
 
 
13
  from app_generated_image import create_prompt_demo
14
  from app_real_image import create_real_image_demo
15
 
16
+ DESCRIPTION = "# [Plug-and-Play diffusion features](https://github.com/MichalGeyer/plug-and-play)"
17
 
18
+ if (SPACE_ID := os.getenv("SPACE_ID")) is not None:
19
  DESCRIPTION += f'\n<p>For faster inference without waiting in queue, you may duplicate the space and upgrade to GPU in settings. <a href="https://huggingface.co/spaces/{SPACE_ID}?duplicate=true"><img style="display: inline; margin-top: 0em; margin-bottom: 0em" src="https://bit.ly/3gLdBN6" alt="Duplicate Space" /></a></p>'
20
  if not torch.cuda.is_available():
21
+ DESCRIPTION += "\n<p>Running on CPU 🥶 This demo does not work on CPU.</p>"
22
 
23
  if torch.cuda.is_available():
24
+ weight_dir = pathlib.Path("plug-and-play/models/ldm/stable-diffusion-v1")
25
  if not weight_dir.exists():
26
+ subprocess.run(shlex.split("mkdir -p plug-and-play/models/ldm/stable-diffusion-v1/"))
27
  subprocess.run(
28
  shlex.split(
29
+ "wget https://huggingface.co/CompVis/stable-diffusion-v-1-4-original/resolve/main/sd-v1-4.ckpt -O plug-and-play/models/ldm/stable-diffusion-v1/model.ckpt"
30
+ )
31
+ )
 
 
32
 
33
+ with gr.Blocks(css="style.css") as demo:
34
  gr.Markdown(DESCRIPTION)
35
  with gr.Tabs():
36
+ with gr.TabItem("Use real image as input"):
37
  create_real_image_demo()
38
+ with gr.TabItem("Use prompt as input"):
39
  create_prompt_demo()
40
+
41
+ if __name__ == "__main__":
42
+ demo.queue(max_size=10).launch()
app_generated_image.py CHANGED
@@ -19,15 +19,14 @@ def gen_feature_extraction_config(
19
  guidance_scale: float,
20
  ddim_steps: int,
21
  ) -> str:
22
- config = OmegaConf.load(
23
- 'plug-and-play/configs/pnp/feature-extraction-generated.yaml')
24
  config.config.experiment_name = exp_name
25
  config.config.prompt = prompt
26
  config.config.seed = seed
27
  config.config.scale = guidance_scale
28
  config.config.ddim_steps = ddim_steps
29
- temp_file = tempfile.NamedTemporaryFile(suffix='.yaml', delete=False)
30
- with open(temp_file.name, 'w') as f:
31
  f.write(OmegaConf.to_yaml(config))
32
  return temp_file.name
33
 
@@ -39,7 +38,7 @@ def run_feature_extraction_command(
39
  ddim_steps: int,
40
  ) -> tuple[str, str]:
41
  exp_name = f'{prompt.replace(" ", "_")}_{seed}_{guidance_scale:.1f}_{ddim_steps}'
42
- if not pathlib.Path(f'plug-and-play/experiments/{exp_name}').exists():
43
  config_path = gen_feature_extraction_config(
44
  exp_name,
45
  prompt,
@@ -47,10 +46,8 @@ def run_feature_extraction_command(
47
  guidance_scale,
48
  ddim_steps,
49
  )
50
- subprocess.run(shlex.split(
51
- f'python run_features_extraction.py --config {config_path}'),
52
- cwd='plug-and-play')
53
- return f'plug-and-play/experiments/{exp_name}/samples/0.png', exp_name
54
 
55
 
56
  def gen_pnp_config(
@@ -63,7 +60,7 @@ def gen_pnp_config(
63
  negative_prompt_alpha: float,
64
  negative_prompt_schedule: str,
65
  ) -> str:
66
- config = OmegaConf.load('plug-and-play/configs/pnp/pnp-generated.yaml')
67
  config.source_experiment_name = exp_name
68
  config.prompts = [prompt]
69
  config.scale = guidance_scale
@@ -72,8 +69,8 @@ def gen_pnp_config(
72
  config.negative_prompt = negative_prompt
73
  config.negative_prompt_alpha = negative_prompt_alpha
74
  config.negative_prompt_schedule = negative_prompt_schedule
75
- temp_file = tempfile.NamedTemporaryFile(suffix='.yaml', delete=False)
76
- with open(temp_file.name, 'w') as f:
77
  f.write(OmegaConf.to_yaml(config))
78
  return temp_file.name
79
 
@@ -98,107 +95,76 @@ def run_pnp_command(
98
  negative_prompt_alpha,
99
  negative_prompt_schedule,
100
  )
101
- subprocess.run(shlex.split(f'python run_pnp.py --config {config_path}'),
102
- cwd='plug-and-play')
103
 
104
  out_dir = pathlib.Path(
105
  f'plug-and-play/experiments/{exp_name}/translations/{guidance_scale}_{prompt.replace(" ", "_")}'
106
  )
107
  out_label = f'INJECTION_T_{feature_injection_threshold}_STEPS_{ddim_steps}_NP-ALPHA_{negative_prompt_alpha}_SCHEDULE_{negative_prompt_schedule}_NP_{negative_prompt.replace(" ", "_")}'
108
- out_path = out_dir / f'{out_label}_sample_0.png'
109
  return out_path.as_posix()
110
 
111
 
112
- def process_example(source_prompt: str, seed: int,
113
- translation_prompt: str) -> tuple[str, str, str]:
114
- generated_image, exp_name = run_feature_extraction_command(
115
- source_prompt, seed, guidance_scale=5, ddim_steps=50)
116
- result = run_pnp_command(exp_name,
117
- translation_prompt,
118
- negative_prompt='',
119
- guidance_scale=7.5,
120
- ddim_steps=50,
121
- feature_injection_threshold=40,
122
- negative_prompt_alpha=0.75,
123
- negative_prompt_schedule='linear')
124
  return generated_image, exp_name, result
125
 
126
 
127
  def create_prompt_demo() -> gr.Blocks:
128
  with gr.Blocks() as demo:
129
  with gr.Box():
130
- gr.Markdown(
131
- 'Step 1 (This step will take about 1.5 minutes on A10G.)')
132
  with gr.Row():
133
  with gr.Column():
134
- source_prompt = gr.Text(label='Source prompt')
135
- seed = gr.Slider(label='Seed',
136
- minimum=0,
137
- maximum=100000,
138
- step=1,
139
- value=0)
140
- with gr.Accordion(label='Advanced settings', open=False):
141
  source_guidance_scale = gr.Slider(
142
- label='Guidance scale',
143
- minimum=0,
144
- maximum=50,
145
- step=0.1,
146
- value=5)
147
- source_ddim_steps = gr.Slider(label='DDIM steps',
148
- minimum=1,
149
- maximum=100,
150
- step=1,
151
- value=50)
152
- extract_feature_button = gr.Button(
153
- 'Generate and extract features')
154
  with gr.Column():
155
- generated_image = gr.Image(label='Generated image',
156
- type='filepath')
157
  exp_name = gr.Text(visible=False)
158
  with gr.Box():
159
- gr.Markdown(
160
- 'Step 2 (This step will take about 1.5 minutes on A10G.)')
161
  with gr.Row():
162
  with gr.Column():
163
- translation_prompt = gr.Text(
164
- label='Prompt for translation')
165
- negative_prompt = gr.Text(label='Negative prompt')
166
- with gr.Accordion(label='Advanced settings', open=False):
167
- guidance_scale = gr.Slider(label='Guidance scale',
168
- minimum=0,
169
- maximum=50,
170
- step=0.1,
171
- value=7.5)
172
  ddim_steps = gr.Slider(
173
- label='Number of inference steps',
174
- minimum=1,
175
- maximum=100,
176
- step=1,
177
- value=50)
178
  feature_injection_threshold = gr.Slider(
179
- label='Feature injection threshold',
180
- minimum=0,
181
- maximum=100,
182
- step=1,
183
- value=40)
184
  negative_prompt_alpha = gr.Slider(
185
- label='Negative prompt alpha',
186
- minimum=0,
187
- maximum=1,
188
- step=0.01,
189
- value=0.75)
190
  negative_prompt_schedule = gr.Dropdown(
191
- label='Negative prompt schedule',
192
- choices=['linear', 'constant', 'exp'],
193
- value='linear')
194
- generate_button = gr.Button('Generate')
195
  with gr.Column():
196
- result = gr.Image(label='Result', type='filepath')
197
  with gr.Row():
198
  gr.Examples(
199
  examples=[
200
- ['horse in mud', 50, 'a photo of a zebra in the snow'],
201
- ['horse in mud', 50, 'a photo of a husky in the grass'],
202
  ],
203
  inputs=[
204
  source_prompt,
@@ -211,7 +177,7 @@ def create_prompt_demo() -> gr.Blocks:
211
  result,
212
  ],
213
  fn=process_example,
214
- cache_examples=os.getenv('CACHE_EXAMPLES'),
215
  )
216
 
217
  extract_feature_button.click(
@@ -244,6 +210,6 @@ def create_prompt_demo() -> gr.Blocks:
244
  return demo
245
 
246
 
247
- if __name__ == '__main__':
248
  demo = create_prompt_demo()
249
  demo.queue().launch()
 
19
  guidance_scale: float,
20
  ddim_steps: int,
21
  ) -> str:
22
+ config = OmegaConf.load("plug-and-play/configs/pnp/feature-extraction-generated.yaml")
 
23
  config.config.experiment_name = exp_name
24
  config.config.prompt = prompt
25
  config.config.seed = seed
26
  config.config.scale = guidance_scale
27
  config.config.ddim_steps = ddim_steps
28
+ temp_file = tempfile.NamedTemporaryFile(suffix=".yaml", delete=False)
29
+ with open(temp_file.name, "w") as f:
30
  f.write(OmegaConf.to_yaml(config))
31
  return temp_file.name
32
 
 
38
  ddim_steps: int,
39
  ) -> tuple[str, str]:
40
  exp_name = f'{prompt.replace(" ", "_")}_{seed}_{guidance_scale:.1f}_{ddim_steps}'
41
+ if not pathlib.Path(f"plug-and-play/experiments/{exp_name}").exists():
42
  config_path = gen_feature_extraction_config(
43
  exp_name,
44
  prompt,
 
46
  guidance_scale,
47
  ddim_steps,
48
  )
49
+ subprocess.run(shlex.split(f"python run_features_extraction.py --config {config_path}"), cwd="plug-and-play")
50
+ return f"plug-and-play/experiments/{exp_name}/samples/0.png", exp_name
 
 
51
 
52
 
53
  def gen_pnp_config(
 
60
  negative_prompt_alpha: float,
61
  negative_prompt_schedule: str,
62
  ) -> str:
63
+ config = OmegaConf.load("plug-and-play/configs/pnp/pnp-generated.yaml")
64
  config.source_experiment_name = exp_name
65
  config.prompts = [prompt]
66
  config.scale = guidance_scale
 
69
  config.negative_prompt = negative_prompt
70
  config.negative_prompt_alpha = negative_prompt_alpha
71
  config.negative_prompt_schedule = negative_prompt_schedule
72
+ temp_file = tempfile.NamedTemporaryFile(suffix=".yaml", delete=False)
73
+ with open(temp_file.name, "w") as f:
74
  f.write(OmegaConf.to_yaml(config))
75
  return temp_file.name
76
 
 
95
  negative_prompt_alpha,
96
  negative_prompt_schedule,
97
  )
98
+ subprocess.run(shlex.split(f"python run_pnp.py --config {config_path}"), cwd="plug-and-play")
 
99
 
100
  out_dir = pathlib.Path(
101
  f'plug-and-play/experiments/{exp_name}/translations/{guidance_scale}_{prompt.replace(" ", "_")}'
102
  )
103
  out_label = f'INJECTION_T_{feature_injection_threshold}_STEPS_{ddim_steps}_NP-ALPHA_{negative_prompt_alpha}_SCHEDULE_{negative_prompt_schedule}_NP_{negative_prompt.replace(" ", "_")}'
104
+ out_path = out_dir / f"{out_label}_sample_0.png"
105
  return out_path.as_posix()
106
 
107
 
108
+ def process_example(source_prompt: str, seed: int, translation_prompt: str) -> tuple[str, str, str]:
109
+ generated_image, exp_name = run_feature_extraction_command(source_prompt, seed, guidance_scale=5, ddim_steps=50)
110
+ result = run_pnp_command(
111
+ exp_name,
112
+ translation_prompt,
113
+ negative_prompt="",
114
+ guidance_scale=7.5,
115
+ ddim_steps=50,
116
+ feature_injection_threshold=40,
117
+ negative_prompt_alpha=0.75,
118
+ negative_prompt_schedule="linear",
119
+ )
120
  return generated_image, exp_name, result
121
 
122
 
123
  def create_prompt_demo() -> gr.Blocks:
124
  with gr.Blocks() as demo:
125
  with gr.Box():
126
+ gr.Markdown("Step 1 (This step will take about 1.5 minutes on A10G.)")
 
127
  with gr.Row():
128
  with gr.Column():
129
+ source_prompt = gr.Text(label="Source prompt")
130
+ seed = gr.Slider(label="Seed", minimum=0, maximum=100000, step=1, value=0)
131
+ with gr.Accordion(label="Advanced settings", open=False):
 
 
 
 
132
  source_guidance_scale = gr.Slider(
133
+ label="Guidance scale", minimum=0, maximum=50, step=0.1, value=5
134
+ )
135
+ source_ddim_steps = gr.Slider(label="DDIM steps", minimum=1, maximum=100, step=1, value=50)
136
+ extract_feature_button = gr.Button("Generate and extract features")
 
 
 
 
 
 
 
 
137
  with gr.Column():
138
+ generated_image = gr.Image(label="Generated image", type="filepath")
 
139
  exp_name = gr.Text(visible=False)
140
  with gr.Box():
141
+ gr.Markdown("Step 2 (This step will take about 1.5 minutes on A10G.)")
 
142
  with gr.Row():
143
  with gr.Column():
144
+ translation_prompt = gr.Text(label="Prompt for translation")
145
+ negative_prompt = gr.Text(label="Negative prompt")
146
+ with gr.Accordion(label="Advanced settings", open=False):
147
+ guidance_scale = gr.Slider(label="Guidance scale", minimum=0, maximum=50, step=0.1, value=7.5)
 
 
 
 
 
148
  ddim_steps = gr.Slider(
149
+ label="Number of inference steps", minimum=1, maximum=100, step=1, value=50
150
+ )
 
 
 
151
  feature_injection_threshold = gr.Slider(
152
+ label="Feature injection threshold", minimum=0, maximum=100, step=1, value=40
153
+ )
 
 
 
154
  negative_prompt_alpha = gr.Slider(
155
+ label="Negative prompt alpha", minimum=0, maximum=1, step=0.01, value=0.75
156
+ )
 
 
 
157
  negative_prompt_schedule = gr.Dropdown(
158
+ label="Negative prompt schedule", choices=["linear", "constant", "exp"], value="linear"
159
+ )
160
+ generate_button = gr.Button("Generate")
 
161
  with gr.Column():
162
+ result = gr.Image(label="Result", type="filepath")
163
  with gr.Row():
164
  gr.Examples(
165
  examples=[
166
+ ["horse in mud", 50, "a photo of a zebra in the snow"],
167
+ ["horse in mud", 50, "a photo of a husky in the grass"],
168
  ],
169
  inputs=[
170
  source_prompt,
 
177
  result,
178
  ],
179
  fn=process_example,
180
+ cache_examples=os.getenv("CACHE_EXAMPLES"),
181
  )
182
 
183
  extract_feature_button.click(
 
210
  return demo
211
 
212
 
213
+ if __name__ == "__main__":
214
  demo = create_prompt_demo()
215
  demo.queue().launch()
app_real_image.py CHANGED
@@ -14,30 +14,27 @@ from omegaconf import OmegaConf
14
 
15
 
16
  def get_exp_name(path: str) -> str:
17
- with open(path, 'rb') as f:
18
  res = hashlib.md5(f.read()).hexdigest()
19
  return res
20
 
21
 
22
  def gen_feature_extraction_config(exp_name: str, init_image_path: str) -> str:
23
- config = OmegaConf.load(
24
- 'plug-and-play/configs/pnp/feature-extraction-real.yaml')
25
  config.config.experiment_name = exp_name
26
  config.config.init_img = init_image_path
27
- temp_file = tempfile.NamedTemporaryFile(suffix='.yaml', delete=False)
28
- with open(temp_file.name, 'w') as f:
29
  f.write(OmegaConf.to_yaml(config))
30
  return temp_file.name
31
 
32
 
33
  def run_feature_extraction_command(init_image_path: str) -> tuple[str, str]:
34
  exp_name = get_exp_name(init_image_path)
35
- if not pathlib.Path(f'plug-and-play/experiments/{exp_name}').exists():
36
  config_path = gen_feature_extraction_config(exp_name, init_image_path)
37
- subprocess.run(shlex.split(
38
- f'python run_features_extraction.py --config {config_path}'),
39
- cwd='plug-and-play')
40
- return f'plug-and-play/experiments/{exp_name}/samples/0.png', exp_name
41
 
42
 
43
  def gen_pnp_config(
@@ -50,7 +47,7 @@ def gen_pnp_config(
50
  negative_prompt_alpha: float,
51
  negative_prompt_schedule: str,
52
  ) -> str:
53
- config = OmegaConf.load('plug-and-play/configs/pnp/pnp-real.yaml')
54
  config.source_experiment_name = exp_name
55
  config.prompts = [prompt]
56
  config.scale = guidance_scale
@@ -59,8 +56,8 @@ def gen_pnp_config(
59
  config.negative_prompt = negative_prompt
60
  config.negative_prompt_alpha = negative_prompt_alpha
61
  config.negative_prompt_schedule = negative_prompt_schedule
62
- temp_file = tempfile.NamedTemporaryFile(suffix='.yaml', delete=False)
63
- with open(temp_file.name, 'w') as f:
64
  f.write(OmegaConf.to_yaml(config))
65
  return temp_file.name
66
 
@@ -85,102 +82,83 @@ def run_pnp_command(
85
  negative_prompt_alpha,
86
  negative_prompt_schedule,
87
  )
88
- subprocess.run(shlex.split(f'python run_pnp.py --config {config_path}'),
89
- cwd='plug-and-play')
90
 
91
  out_dir = pathlib.Path(
92
  f'plug-and-play/experiments/{exp_name}/translations/{guidance_scale}_{prompt.replace(" ", "_")}'
93
  )
94
  out_label = f'INJECTION_T_{feature_injection_threshold}_STEPS_{ddim_steps}_NP-ALPHA_{negative_prompt_alpha}_SCHEDULE_{negative_prompt_schedule}_NP_{negative_prompt.replace(" ", "_")}'
95
- out_path = out_dir / f'{out_label}_sample_0.png'
96
  return out_path.as_posix()
97
 
98
 
99
- def process_example(image: str, translation_prompt: str,
100
- negative_prompt: str) -> tuple[str, str, str]:
101
  reconstructed_image, exp_name = run_feature_extraction_command(image)
102
- result = run_pnp_command(exp_name,
103
- translation_prompt,
104
- negative_prompt,
105
- guidance_scale=10,
106
- ddim_steps=50,
107
- feature_injection_threshold=40,
108
- negative_prompt_alpha=1,
109
- negative_prompt_schedule='linear')
 
 
110
  return reconstructed_image, exp_name, result
111
 
112
 
113
  def create_real_image_demo():
114
  with gr.Blocks() as demo:
115
  with gr.Box():
116
- gr.Markdown(
117
- 'Step 1 (This step will take about 5 minutes on A10G.)')
118
  with gr.Row():
119
  with gr.Column():
120
- image = gr.Image(label='Input image', type='filepath')
121
- extract_feature_button = gr.Button(
122
- 'Reconstruct and extract features')
123
  with gr.Column():
124
- reconstructed_image = gr.Image(label='Reconstructed image',
125
- type='filepath')
126
  exp_name = gr.Text(visible=False)
127
  with gr.Box():
128
- gr.Markdown(
129
- 'Step 2 (This step will take about 1.5 minutes on A10G.)')
130
  with gr.Row():
131
  with gr.Column():
132
- translation_prompt = gr.Text(
133
- label='Prompt for translation')
134
- negative_prompt = gr.Text(label='Negative prompt')
135
- with gr.Accordion(label='Advanced settings', open=False):
136
- guidance_scale = gr.Slider(label='Guidance scale',
137
- minimum=0,
138
- maximum=50,
139
- step=0.1,
140
- value=10)
141
  ddim_steps = gr.Slider(
142
- label='Number of inference steps',
143
- minimum=1,
144
- maximum=100,
145
- step=1,
146
- value=50)
147
  feature_injection_threshold = gr.Slider(
148
- label='Feature injection threshold',
149
- minimum=0,
150
- maximum=100,
151
- step=1,
152
- value=40)
153
  negative_prompt_alpha = gr.Slider(
154
- label='Negative prompt alpha',
155
- minimum=0,
156
- maximum=1,
157
- step=0.01,
158
- value=1)
159
  negative_prompt_scheduler = gr.Dropdown(
160
- label='Negative prompt schedule',
161
- choices=['linear', 'constant', 'exp'],
162
- value='linear')
163
- generate_button = gr.Button('Generate')
164
  with gr.Column():
165
- result = gr.Image(label='Result', type='filepath')
166
 
167
  with gr.Row():
168
  gr.Examples(
169
  examples=[
170
  [
171
- 'plug-and-play/data/horse.png',
172
- 'a photo of a robot horse',
173
- 'a photo of a white horse',
174
  ],
175
  [
176
- 'plug-and-play/data/horse.png',
177
- 'a photo of a bronze horse in a museum',
178
- 'a photo of a white horse',
179
  ],
180
  [
181
- 'plug-and-play/data/horse.png',
182
- 'a photo of a pink horse on the beach',
183
- 'a photo of a white horse',
184
  ],
185
  ],
186
  inputs=[
@@ -194,7 +172,7 @@ def create_real_image_demo():
194
  result,
195
  ],
196
  fn=process_example,
197
- cache_examples=os.getenv('CACHE_EXAMPLES'),
198
  )
199
 
200
  extract_feature_button.click(
@@ -223,6 +201,6 @@ def create_real_image_demo():
223
  return demo
224
 
225
 
226
- if __name__ == '__main__':
227
  demo = create_real_image_demo()
228
  demo.queue().launch()
 
14
 
15
 
16
  def get_exp_name(path: str) -> str:
17
+ with open(path, "rb") as f:
18
  res = hashlib.md5(f.read()).hexdigest()
19
  return res
20
 
21
 
22
  def gen_feature_extraction_config(exp_name: str, init_image_path: str) -> str:
23
+ config = OmegaConf.load("plug-and-play/configs/pnp/feature-extraction-real.yaml")
 
24
  config.config.experiment_name = exp_name
25
  config.config.init_img = init_image_path
26
+ temp_file = tempfile.NamedTemporaryFile(suffix=".yaml", delete=False)
27
+ with open(temp_file.name, "w") as f:
28
  f.write(OmegaConf.to_yaml(config))
29
  return temp_file.name
30
 
31
 
32
  def run_feature_extraction_command(init_image_path: str) -> tuple[str, str]:
33
  exp_name = get_exp_name(init_image_path)
34
+ if not pathlib.Path(f"plug-and-play/experiments/{exp_name}").exists():
35
  config_path = gen_feature_extraction_config(exp_name, init_image_path)
36
+ subprocess.run(shlex.split(f"python run_features_extraction.py --config {config_path}"), cwd="plug-and-play")
37
+ return f"plug-and-play/experiments/{exp_name}/samples/0.png", exp_name
 
 
38
 
39
 
40
  def gen_pnp_config(
 
47
  negative_prompt_alpha: float,
48
  negative_prompt_schedule: str,
49
  ) -> str:
50
+ config = OmegaConf.load("plug-and-play/configs/pnp/pnp-real.yaml")
51
  config.source_experiment_name = exp_name
52
  config.prompts = [prompt]
53
  config.scale = guidance_scale
 
56
  config.negative_prompt = negative_prompt
57
  config.negative_prompt_alpha = negative_prompt_alpha
58
  config.negative_prompt_schedule = negative_prompt_schedule
59
+ temp_file = tempfile.NamedTemporaryFile(suffix=".yaml", delete=False)
60
+ with open(temp_file.name, "w") as f:
61
  f.write(OmegaConf.to_yaml(config))
62
  return temp_file.name
63
 
 
82
  negative_prompt_alpha,
83
  negative_prompt_schedule,
84
  )
85
+ subprocess.run(shlex.split(f"python run_pnp.py --config {config_path}"), cwd="plug-and-play")
 
86
 
87
  out_dir = pathlib.Path(
88
  f'plug-and-play/experiments/{exp_name}/translations/{guidance_scale}_{prompt.replace(" ", "_")}'
89
  )
90
  out_label = f'INJECTION_T_{feature_injection_threshold}_STEPS_{ddim_steps}_NP-ALPHA_{negative_prompt_alpha}_SCHEDULE_{negative_prompt_schedule}_NP_{negative_prompt.replace(" ", "_")}'
91
+ out_path = out_dir / f"{out_label}_sample_0.png"
92
  return out_path.as_posix()
93
 
94
 
95
+ def process_example(image: str, translation_prompt: str, negative_prompt: str) -> tuple[str, str, str]:
 
96
  reconstructed_image, exp_name = run_feature_extraction_command(image)
97
+ result = run_pnp_command(
98
+ exp_name,
99
+ translation_prompt,
100
+ negative_prompt,
101
+ guidance_scale=10,
102
+ ddim_steps=50,
103
+ feature_injection_threshold=40,
104
+ negative_prompt_alpha=1,
105
+ negative_prompt_schedule="linear",
106
+ )
107
  return reconstructed_image, exp_name, result
108
 
109
 
110
  def create_real_image_demo():
111
  with gr.Blocks() as demo:
112
  with gr.Box():
113
+ gr.Markdown("Step 1 (This step will take about 5 minutes on A10G.)")
 
114
  with gr.Row():
115
  with gr.Column():
116
+ image = gr.Image(label="Input image", type="filepath")
117
+ extract_feature_button = gr.Button("Reconstruct and extract features")
 
118
  with gr.Column():
119
+ reconstructed_image = gr.Image(label="Reconstructed image", type="filepath")
 
120
  exp_name = gr.Text(visible=False)
121
  with gr.Box():
122
+ gr.Markdown("Step 2 (This step will take about 1.5 minutes on A10G.)")
 
123
  with gr.Row():
124
  with gr.Column():
125
+ translation_prompt = gr.Text(label="Prompt for translation")
126
+ negative_prompt = gr.Text(label="Negative prompt")
127
+ with gr.Accordion(label="Advanced settings", open=False):
128
+ guidance_scale = gr.Slider(label="Guidance scale", minimum=0, maximum=50, step=0.1, value=10)
 
 
 
 
 
129
  ddim_steps = gr.Slider(
130
+ label="Number of inference steps", minimum=1, maximum=100, step=1, value=50
131
+ )
 
 
 
132
  feature_injection_threshold = gr.Slider(
133
+ label="Feature injection threshold", minimum=0, maximum=100, step=1, value=40
134
+ )
 
 
 
135
  negative_prompt_alpha = gr.Slider(
136
+ label="Negative prompt alpha", minimum=0, maximum=1, step=0.01, value=1
137
+ )
 
 
 
138
  negative_prompt_scheduler = gr.Dropdown(
139
+ label="Negative prompt schedule", choices=["linear", "constant", "exp"], value="linear"
140
+ )
141
+ generate_button = gr.Button("Generate")
 
142
  with gr.Column():
143
+ result = gr.Image(label="Result", type="filepath")
144
 
145
  with gr.Row():
146
  gr.Examples(
147
  examples=[
148
  [
149
+ "plug-and-play/data/horse.png",
150
+ "a photo of a robot horse",
151
+ "a photo of a white horse",
152
  ],
153
  [
154
+ "plug-and-play/data/horse.png",
155
+ "a photo of a bronze horse in a museum",
156
+ "a photo of a white horse",
157
  ],
158
  [
159
+ "plug-and-play/data/horse.png",
160
+ "a photo of a pink horse on the beach",
161
+ "a photo of a white horse",
162
  ],
163
  ],
164
  inputs=[
 
172
  result,
173
  ],
174
  fn=process_example,
175
+ cache_examples=os.getenv("CACHE_EXAMPLES"),
176
  )
177
 
178
  extract_feature_button.click(
 
201
  return demo
202
 
203
 
204
+ if __name__ == "__main__":
205
  demo = create_real_image_demo()
206
  demo.queue().launch()
style.css CHANGED
@@ -1,3 +1,11 @@
1
  h1 {
2
  text-align: center;
 
 
 
 
 
 
 
 
3
  }
 
1
  h1 {
2
  text-align: center;
3
+ display: block;
4
+ }
5
+
6
+ #duplicate-button {
7
+ margin: auto;
8
+ color: #fff;
9
+ background: #1565c0;
10
+ border-radius: 100vh;
11
  }