Spaces:
Sleeping
Sleeping
redo the space to spice things up.
Browse files- app.py +139 -143
- image_utils.py +26 -0
- metrics_utils.py +48 -0
- report_utils.py +47 -0
app.py
CHANGED
@@ -1,93 +1,41 @@
|
|
1 |
import importlib
|
2 |
-
from functools import partial
|
3 |
from typing import List
|
4 |
|
5 |
import gradio as gr
|
6 |
import numpy as np
|
7 |
import torch
|
8 |
from diffusers import StableDiffusionPipeline
|
9 |
-
from
|
10 |
-
|
11 |
-
from
|
|
|
|
|
12 |
|
13 |
SEED = 0
|
14 |
WEIGHT_DTYPE = torch.float16
|
15 |
|
16 |
TITLE = "Evaluate Schedulers with StableDiffusionPipeline 🧨"
|
17 |
-
|
18 |
This Space allows you to quantitatively compare [different noise schedulers](https://huggingface.co/docs/diffusers/using-diffusers/schedulers) with a [`StableDiffusionPipeline`](https://huggingface.co/docs/diffusers/api/pipelines/stable_diffusion/overview).
|
19 |
|
20 |
One of the applications of this Space could be to evaluate different schedulers for a certain Stable Diffusion checkpoint for a fixed number of inference steps.
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
* The evaluator first sets a seed and then generates the initial noise which is passed as the initial latent to start the image generation process. It is done to ensure fair comparison.
|
25 |
* This initial latent is used every time the pipeline is run (with different schedulers).
|
26 |
* To quantify the quality of the generated images we use:
|
27 |
* [Inception Score](https://en.wikipedia.org/wiki/Inception_score)
|
28 |
* [Clip Score](https://arxiv.org/abs/2104.08718)
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
* The default scheduler associated with the provided checkpoint is always used for reporting the scores.
|
33 |
* Increasing both the number of images per prompt and the number of inference steps could quickly build up the inference queue and thus
|
34 |
resulting in slowdowns.
|
35 |
"""
|
36 |
|
37 |
-
|
38 |
-
|
39 |
-
torch.manual_seed(SEED)
|
40 |
-
clip_score_fn = partial(clip_score, model_name_or_path="openai/clip-vit-base-patch16")
|
41 |
-
|
42 |
-
|
43 |
-
def make_grid(images, rows, cols):
|
44 |
-
w, h = images[0].size
|
45 |
-
grid = Image.new("RGB", size=(cols * w, rows * h))
|
46 |
-
for i, image in enumerate(images):
|
47 |
-
grid.paste(image, box=(i % cols * w, i // cols * h))
|
48 |
-
return grid
|
49 |
-
|
50 |
-
|
51 |
-
# Copied from https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/pipeline_utils.py#L814
|
52 |
-
def numpy_to_pil(images):
|
53 |
-
"""
|
54 |
-
Convert a numpy image or a batch of images to a PIL image.
|
55 |
-
"""
|
56 |
-
if images.ndim == 3:
|
57 |
-
images = images[None, ...]
|
58 |
-
images = (images * 255).round().astype("uint8")
|
59 |
-
if images.shape[-1] == 1:
|
60 |
-
# special case for grayscale (single channel) images
|
61 |
-
pil_images = [Image.fromarray(image.squeeze(), mode="L") for image in images]
|
62 |
-
else:
|
63 |
-
pil_images = [Image.fromarray(image) for image in images]
|
64 |
-
|
65 |
-
return pil_images
|
66 |
-
|
67 |
-
|
68 |
-
def prepare_report(scheduler_name: str, results: dict):
|
69 |
-
image_grid = results["images"]
|
70 |
-
scores = results["scores"]
|
71 |
-
img_str = ""
|
72 |
-
|
73 |
-
image_name = f"{scheduler_name}_images.png"
|
74 |
-
image_grid.save(image_name)
|
75 |
-
img_str = img_str = f"![img_grid_{scheduler_name}](/file=./{image_name})\n"
|
76 |
-
|
77 |
-
report_str = f"""
|
78 |
-
\n\n## {scheduler_name}
|
79 |
-
|
80 |
-
### Sample images
|
81 |
-
|
82 |
-
{img_str}
|
83 |
-
|
84 |
-
### Scores
|
85 |
-
|
86 |
-
{scores}
|
87 |
-
\n\n
|
88 |
-
"""
|
89 |
-
|
90 |
-
return report_str
|
91 |
|
92 |
|
93 |
def initialize_pipeline(checkpoint: str):
|
@@ -115,63 +63,51 @@ def get_latents(num_images_per_prompt: int, seed=SEED):
|
|
115 |
return latents
|
116 |
|
117 |
|
118 |
-
def compute_metrics(images: np.ndarray, prompts: List[str]):
|
119 |
-
inception_score_fn.update(torch.from_numpy(images).permute(0, 3, 1, 2))
|
120 |
-
inception_score = inception_score_fn.compute()
|
121 |
-
|
122 |
-
images_int = (images * 255).astype("uint8")
|
123 |
-
clip_score = clip_score_fn(
|
124 |
-
torch.from_numpy(images_int).permute(0, 3, 1, 2), prompts
|
125 |
-
).detach()
|
126 |
-
return {
|
127 |
-
"inception_score (⬆️)": {
|
128 |
-
"mean": round(float(inception_score[0]), 4),
|
129 |
-
"std": round(float(inception_score[1]), 4),
|
130 |
-
},
|
131 |
-
"clip_score (⬆️)": round(float(clip_score), 4),
|
132 |
-
}
|
133 |
-
|
134 |
-
|
135 |
def run(
|
136 |
prompt: str,
|
137 |
num_images_per_prompt: int,
|
138 |
num_inference_steps: int,
|
139 |
checkpoint: str,
|
140 |
-
|
|
|
|
|
|
|
|
|
141 |
):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
142 |
all_images = {}
|
|
|
143 |
|
|
|
144 |
sd_pipeline, original_scheduler_config = initialize_pipeline(checkpoint)
|
|
|
|
|
|
|
145 |
latents = get_latents(num_images_per_prompt)
|
146 |
prompts = [prompt] * num_images_per_prompt
|
147 |
|
148 |
-
images = sd_pipeline(
|
149 |
-
prompts,
|
150 |
-
latents=latents,
|
151 |
-
num_inference_steps=num_inference_steps,
|
152 |
-
output_type="numpy",
|
153 |
-
).images
|
154 |
original_scheduler_name = original_scheduler_config._class_name
|
|
|
155 |
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
}
|
163 |
-
)
|
164 |
-
# print("First scheduler complete.")
|
165 |
-
|
166 |
-
for scheduler_name in schedulers_to_test:
|
167 |
-
if scheduler_name == original_scheduler_name:
|
168 |
-
continue
|
169 |
-
scheduler_cls = get_scheduler(scheduler_name)
|
170 |
-
current_scheduler = scheduler_cls.from_config(original_scheduler_config)
|
171 |
-
sd_pipeline.scheduler = current_scheduler
|
172 |
|
173 |
cur_scheduler_images = sd_pipeline(
|
174 |
-
prompts,
|
|
|
|
|
|
|
175 |
).images
|
176 |
all_images.update(
|
177 |
{
|
@@ -179,51 +115,111 @@ def run(
|
|
179 |
"images": make_grid(
|
180 |
numpy_to_pil(cur_scheduler_images), 1, num_images_per_prompt
|
181 |
),
|
182 |
-
"scores":
|
183 |
}
|
184 |
}
|
185 |
)
|
186 |
-
|
|
|
187 |
|
|
|
188 |
output_str = ""
|
189 |
for scheduler_name in all_images:
|
190 |
-
# print(f"scheduler_name: {scheduler_name}")
|
191 |
output_str += prepare_report(scheduler_name, all_images[scheduler_name])
|
192 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
193 |
return output_str
|
194 |
|
195 |
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
gr.
|
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 |
-
description=DESCRIPTION,
|
227 |
-
allow_flagging=False,
|
228 |
-
)
|
229 |
-
demo.launch()
|
|
|
1 |
import importlib
|
|
|
2 |
from typing import List
|
3 |
|
4 |
import gradio as gr
|
5 |
import numpy as np
|
6 |
import torch
|
7 |
from diffusers import StableDiffusionPipeline
|
8 |
+
from torchmetrics import PeakSignalNoiseRatio, StructuralSimilarityIndexMeasure
|
9 |
+
|
10 |
+
from image_utils import make_grid, numpy_to_pil
|
11 |
+
from metrics_utils import compute_main_metrics, compute_psnr_or_ssim
|
12 |
+
from report_utils import add_psnr_ssim_to_report, prepare_report
|
13 |
|
14 |
SEED = 0
|
15 |
WEIGHT_DTYPE = torch.float16
|
16 |
|
17 |
TITLE = "Evaluate Schedulers with StableDiffusionPipeline 🧨"
|
18 |
+
ABSTRACT = """
|
19 |
This Space allows you to quantitatively compare [different noise schedulers](https://huggingface.co/docs/diffusers/using-diffusers/schedulers) with a [`StableDiffusionPipeline`](https://huggingface.co/docs/diffusers/api/pipelines/stable_diffusion/overview).
|
20 |
|
21 |
One of the applications of this Space could be to evaluate different schedulers for a certain Stable Diffusion checkpoint for a fixed number of inference steps.
|
22 |
+
"""
|
23 |
+
DESCRIPTION = """
|
24 |
+
#### Hoes does it work?
|
25 |
* The evaluator first sets a seed and then generates the initial noise which is passed as the initial latent to start the image generation process. It is done to ensure fair comparison.
|
26 |
* This initial latent is used every time the pipeline is run (with different schedulers).
|
27 |
* To quantify the quality of the generated images we use:
|
28 |
* [Inception Score](https://en.wikipedia.org/wiki/Inception_score)
|
29 |
* [Clip Score](https://arxiv.org/abs/2104.08718)
|
30 |
+
#### Notes
|
31 |
+
* When selecting a model checkpoint, if you select "Other" you will have the option to provide a custom Stable Diffusion checkpoint.
|
|
|
32 |
* The default scheduler associated with the provided checkpoint is always used for reporting the scores.
|
33 |
* Increasing both the number of images per prompt and the number of inference steps could quickly build up the inference queue and thus
|
34 |
resulting in slowdowns.
|
35 |
"""
|
36 |
|
37 |
+
psnr_fn = PeakSignalNoiseRatio()
|
38 |
+
ssim_fn = StructuralSimilarityIndexMeasure()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
39 |
|
40 |
|
41 |
def initialize_pipeline(checkpoint: str):
|
|
|
63 |
return latents
|
64 |
|
65 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
66 |
def run(
|
67 |
prompt: str,
|
68 |
num_images_per_prompt: int,
|
69 |
num_inference_steps: int,
|
70 |
checkpoint: str,
|
71 |
+
other_finedtuned_checkpoints: str = None,
|
72 |
+
schedulers_to_test: List[str] = None,
|
73 |
+
ssim: bool = False,
|
74 |
+
psnr: bool = False,
|
75 |
+
progress=gr.Progress(),
|
76 |
):
|
77 |
+
progress(0, desc="Starting...")
|
78 |
+
|
79 |
+
if checkpoint == "Other" and other_finedtuned_checkpoints == "":
|
80 |
+
return "❌ No legit checkpoint provided ❌"
|
81 |
+
|
82 |
+
elif checkpoint == "Other":
|
83 |
+
checkpoint = other_finedtuned_checkpoints
|
84 |
+
|
85 |
all_images = {}
|
86 |
+
scheduler_images = {}
|
87 |
|
88 |
+
# Set up the pipeline
|
89 |
sd_pipeline, original_scheduler_config = initialize_pipeline(checkpoint)
|
90 |
+
sd_pipeline.set_progress_bar_config(disable=True)
|
91 |
+
|
92 |
+
# Prepare latents to start generation and the prompts.
|
93 |
latents = get_latents(num_images_per_prompt)
|
94 |
prompts = [prompt] * num_images_per_prompt
|
95 |
|
|
|
|
|
|
|
|
|
|
|
|
|
96 |
original_scheduler_name = original_scheduler_config._class_name
|
97 |
+
schedulers_to_test.append(original_scheduler_name)
|
98 |
|
99 |
+
# Start generating the images and computing their scores.
|
100 |
+
for scheduler_name in progress.tqdm(schedulers_to_test):
|
101 |
+
if scheduler_name != original_scheduler_name:
|
102 |
+
scheduler_cls = get_scheduler(scheduler_name)
|
103 |
+
current_scheduler = scheduler_cls.from_config(original_scheduler_config)
|
104 |
+
sd_pipeline.scheduler = current_scheduler
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
105 |
|
106 |
cur_scheduler_images = sd_pipeline(
|
107 |
+
prompts,
|
108 |
+
latents=latents,
|
109 |
+
num_inference_steps=num_inference_steps,
|
110 |
+
output_type="numpy",
|
111 |
).images
|
112 |
all_images.update(
|
113 |
{
|
|
|
115 |
"images": make_grid(
|
116 |
numpy_to_pil(cur_scheduler_images), 1, num_images_per_prompt
|
117 |
),
|
118 |
+
"scores": compute_main_metrics(cur_scheduler_images, prompts),
|
119 |
}
|
120 |
}
|
121 |
)
|
122 |
+
scheduler_images.update({scheduler_name: cur_scheduler_images})
|
123 |
+
torch.cuda.empty_cache()
|
124 |
|
125 |
+
# Prepare output report.
|
126 |
output_str = ""
|
127 |
for scheduler_name in all_images:
|
|
|
128 |
output_str += prepare_report(scheduler_name, all_images[scheduler_name])
|
129 |
+
|
130 |
+
# Append PSNR or SSIM if needed.
|
131 |
+
if len(schedulers_to_test) > 1:
|
132 |
+
ssim_scores = psnr_scores = None
|
133 |
+
if ssim:
|
134 |
+
ssim_scores = compute_psnr_or_ssim(
|
135 |
+
ssim_fn, scheduler_images, original_scheduler_name
|
136 |
+
)
|
137 |
+
if psnr:
|
138 |
+
psnr_scores = compute_psnr_or_ssim(
|
139 |
+
psnr_fn, scheduler_images, original_scheduler_name
|
140 |
+
)
|
141 |
+
|
142 |
+
if len(schedulers_to_test) > 1:
|
143 |
+
ssim_psnr_str = add_psnr_ssim_to_report(
|
144 |
+
original_scheduler_name, ssim_scores, psnr_scores
|
145 |
+
)
|
146 |
+
if ssim_psnr_str != "":
|
147 |
+
output_str += ssim_psnr_str
|
148 |
+
|
149 |
return output_str
|
150 |
|
151 |
|
152 |
+
with gr.Blocks(title="Scheduler Evaluation") as demo:
|
153 |
+
gr.Markdown(f"## {TITLE}\n\n\n\n{ABSTRACT}")
|
154 |
+
|
155 |
+
with gr.Row():
|
156 |
+
with gr.Column():
|
157 |
+
prompt = gr.Text(
|
158 |
+
max_lines=1, placeholder="a painting of a dog", label="prompt"
|
159 |
+
)
|
160 |
+
num_images_per_prompt = gr.Slider(
|
161 |
+
3, 10, value=3, step=1, label="num_images_per_prompt"
|
162 |
+
)
|
163 |
+
num_inference_steps = gr.Slider(
|
164 |
+
10, 100, value=50, step=1, label="num_inference_steps"
|
165 |
+
)
|
166 |
+
model_ckpt = gr.Dropdown(
|
167 |
+
[
|
168 |
+
"CompVis/stable-diffusion-v1-4",
|
169 |
+
"runwayml/stable-diffusion-v1-5",
|
170 |
+
"stabilityai/stable-diffusion-2-base",
|
171 |
+
"Other",
|
172 |
+
],
|
173 |
+
value="CompVis/stable-diffusion-v1-4",
|
174 |
+
multiselect=False,
|
175 |
+
interactive=True,
|
176 |
+
label="model_ckpt",
|
177 |
+
)
|
178 |
+
other_finedtuned_checkpoints = gr.Textbox(
|
179 |
+
visible=False,
|
180 |
+
interactive=True,
|
181 |
+
placeholder="valhalla/sd-pokemon-model",
|
182 |
+
label="custom_checkpoint",
|
183 |
+
)
|
184 |
+
model_ckpt.change(
|
185 |
+
lambda x: gr.Dropdown.update(visible=x == "Other"),
|
186 |
+
model_ckpt,
|
187 |
+
other_finedtuned_checkpoints,
|
188 |
+
)
|
189 |
+
schedulers_to_test = gr.Dropdown(
|
190 |
+
[
|
191 |
+
"EulerDiscreteScheduler",
|
192 |
+
"PNDMScheduler",
|
193 |
+
"LMSDiscreteScheduler",
|
194 |
+
"DPMSolverMultistepScheduler",
|
195 |
+
"DDIMScheduler",
|
196 |
+
],
|
197 |
+
value=["LMSDiscreteScheduler"],
|
198 |
+
multiselect=True,
|
199 |
+
label="schedulers_to_test",
|
200 |
+
)
|
201 |
+
ssim = gr.Checkbox(label="Compute SSIM")
|
202 |
+
psnr = gr.Checkbox(label="Compute PSNR")
|
203 |
+
evaluation_button = gr.Button(value="Submit")
|
204 |
+
|
205 |
+
with gr.Column():
|
206 |
+
report = gr.Markdown(label="Evaluation Report").style()
|
207 |
+
|
208 |
+
evaluation_button.click(
|
209 |
+
run,
|
210 |
+
inputs=[
|
211 |
+
prompt,
|
212 |
+
num_images_per_prompt,
|
213 |
+
num_inference_steps,
|
214 |
+
model_ckpt,
|
215 |
+
other_finedtuned_checkpoints,
|
216 |
+
schedulers_to_test,
|
217 |
+
ssim,
|
218 |
+
psnr,
|
219 |
],
|
220 |
+
outputs=report,
|
221 |
+
)
|
222 |
+
|
223 |
+
gr.Markdown(f"{DESCRIPTION}")
|
224 |
+
|
225 |
+
demo.queue().launch(debug=True)
|
|
|
|
|
|
|
|
image_utils.py
ADDED
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from PIL import Image
|
2 |
+
|
3 |
+
|
4 |
+
def make_grid(images, rows, cols):
|
5 |
+
w, h = images[0].size
|
6 |
+
grid = Image.new("RGB", size=(cols * w, rows * h))
|
7 |
+
for i, image in enumerate(images):
|
8 |
+
grid.paste(image, box=(i % cols * w, i // cols * h))
|
9 |
+
return grid
|
10 |
+
|
11 |
+
|
12 |
+
# Copied from https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/pipeline_utils.py#L814
|
13 |
+
def numpy_to_pil(images):
|
14 |
+
"""
|
15 |
+
Convert a numpy image or a batch of images to a PIL image.
|
16 |
+
"""
|
17 |
+
if images.ndim == 3:
|
18 |
+
images = images[None, ...]
|
19 |
+
images = (images * 255).round().astype("uint8")
|
20 |
+
if images.shape[-1] == 1:
|
21 |
+
# special case for grayscale (single channel) images
|
22 |
+
pil_images = [Image.fromarray(image.squeeze(), mode="L") for image in images]
|
23 |
+
else:
|
24 |
+
pil_images = [Image.fromarray(image) for image in images]
|
25 |
+
|
26 |
+
return pil_images
|
metrics_utils.py
ADDED
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from functools import partial
|
2 |
+
from typing import Callable, Dict, List
|
3 |
+
|
4 |
+
import numpy as np
|
5 |
+
import torch
|
6 |
+
from torchmetrics.functional.multimodal import clip_score
|
7 |
+
from torchmetrics.image.inception import InceptionScore
|
8 |
+
|
9 |
+
SEED = 0
|
10 |
+
|
11 |
+
inception_score_fn = InceptionScore(normalize=True)
|
12 |
+
torch.manual_seed(SEED)
|
13 |
+
clip_score_fn = partial(clip_score, model_name_or_path="openai/clip-vit-base-patch16")
|
14 |
+
|
15 |
+
|
16 |
+
def compute_main_metrics(images: np.ndarray, prompts: List[str]) -> Dict:
|
17 |
+
inception_score_fn.update(torch.from_numpy(images).permute(0, 3, 1, 2))
|
18 |
+
inception_score = inception_score_fn.compute()
|
19 |
+
|
20 |
+
images_int = (images * 255).astype("uint8")
|
21 |
+
clip_score = clip_score_fn(
|
22 |
+
torch.from_numpy(images_int).permute(0, 3, 1, 2), prompts
|
23 |
+
).detach()
|
24 |
+
return {
|
25 |
+
"inception_score (⬆️)": {
|
26 |
+
"mean": round(float(inception_score[0]), 4),
|
27 |
+
"std": round(float(inception_score[1]), 4),
|
28 |
+
},
|
29 |
+
"clip_score (⬆️)": round(float(clip_score), 4),
|
30 |
+
}
|
31 |
+
|
32 |
+
|
33 |
+
def compute_psnr_or_ssim(
|
34 |
+
fn: Callable, images_dict: Dict, original_scheduler_name: str
|
35 |
+
) -> Dict:
|
36 |
+
result_dict = {}
|
37 |
+
original_scheduler_images = images_dict[original_scheduler_name]
|
38 |
+
original_scheduler_images = torch.from_numpy(original_scheduler_images).permute(
|
39 |
+
0, 3, 1, 2
|
40 |
+
)
|
41 |
+
for k in images_dict:
|
42 |
+
if k != original_scheduler_name:
|
43 |
+
current_scheduler_images = torch.from_numpy(images_dict[k]).permute(
|
44 |
+
0, 3, 1, 2
|
45 |
+
)
|
46 |
+
current_value = fn(current_scheduler_images, original_scheduler_images)
|
47 |
+
result_dict.update({k: round(float(current_value), 4)})
|
48 |
+
return result_dict
|
report_utils.py
ADDED
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import json
|
2 |
+
|
3 |
+
|
4 |
+
def prepare_report(scheduler_name: str, results: dict):
|
5 |
+
image_grid = results["images"]
|
6 |
+
scores = results["scores"]
|
7 |
+
img_str = ""
|
8 |
+
|
9 |
+
image_name = f"{scheduler_name}_images.png"
|
10 |
+
image_grid.save(image_name)
|
11 |
+
img_str = img_str = f"![img_grid_{scheduler_name}](/file=./{image_name})\n"
|
12 |
+
|
13 |
+
report_str = f"""
|
14 |
+
\n\n## {scheduler_name}
|
15 |
+
|
16 |
+
### Sample images
|
17 |
+
|
18 |
+
{img_str}
|
19 |
+
|
20 |
+
### Scores
|
21 |
+
|
22 |
+
{scores}
|
23 |
+
\n\n
|
24 |
+
"""
|
25 |
+
|
26 |
+
return report_str
|
27 |
+
|
28 |
+
|
29 |
+
def add_psnr_ssim_to_report(
|
30 |
+
original_scheduler_name: str, ssim_scores: Dict = None, psnr_scores: Dict = None
|
31 |
+
) -> str:
|
32 |
+
current_str = ""
|
33 |
+
if ssim_scores is not None:
|
34 |
+
current_str += f"""
|
35 |
+
\n\n
|
36 |
+
## SSIM
|
37 |
+
|
38 |
+
SSIM computed w.r.t the images generated with {original_scheduler_name}:\n\n {json.dumps(ssim_scores, indent=6)}
|
39 |
+
"""
|
40 |
+
if psnr_scores is not None:
|
41 |
+
current_str += f"""
|
42 |
+
\n\n
|
43 |
+
## PSNR
|
44 |
+
|
45 |
+
PSNR computed w.r.t the images generated with {original_scheduler_name}:\n\n {json.dumps(psnr_scores, indent=6)}
|
46 |
+
"""
|
47 |
+
return current_str
|