File size: 6,854 Bytes
c75b162 | 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 | """
Generate qualitative comparison figures:
1. Full-frame side-by-side: sharp | blur | evssm | vdiff | turtle
2. Zoom-in crops showing detail
3. TURTLE artifact analysis
"""
import argparse
import numpy as np
from pathlib import Path
from PIL import Image, ImageDraw, ImageFont
BASE = Path(__file__).parent.parent
METHODS = [
("sharp", "Sharp GT", BASE / "data/scannet_blur_proto/vddiff/test/sharp"),
("blur", "Blur input", BASE / "data/scannet_blur_proto/vddiff/test/blur"),
("evssm", "EVSSM", BASE / "data/evssm_deblurred"),
("vdiff", "VD-Diff", BASE / "data/vdiff_deblurred"),
("turtle", "TURTLE", BASE / "data/turtle_deblurred"),
]
def label_bar(text, w, h=32, bg=(30,30,30), fg=(255,255,255)):
img = Image.new("RGB", (w, h), bg)
draw = ImageDraw.Draw(img)
# center text
bbox = draw.textbbox((0, 0), text)
tw, th = bbox[2]-bbox[0], bbox[3]-bbox[1]
draw.text(((w-tw)//2, (h-th)//2), text, fill=fg)
return np.array(img)
def psnr(a, b):
mse = np.mean((a.astype(float) - b.astype(float))**2)
return 20*np.log10(255/np.sqrt(mse)) if mse > 1e-10 else 100.0
def load(path):
return np.array(Image.open(path).convert("RGB"))
def add_label(img_arr, text, psnr_val=None):
h, w = img_arr.shape[:2]
label = f"{text}" + (f" PSNR={psnr_val:.2f}" if psnr_val else "")
bar = label_bar(label, w, h=36)
return np.vstack([bar, img_arr])
def make_comparison_row(scene, frame_idx, crop_box=None, scale=0.4):
"""Returns a row of images: [sharp | blur | evssm | vdiff | turtle]"""
frame_name = f"{frame_idx:06d}.png"
sharp_path = BASE / "data/scannet_blur_proto/vddiff/test/sharp" / scene / frame_name
if not sharp_path.exists():
return None
sharp = load(sharp_path)
cols = []
for key, label, root in METHODS:
path = root / scene / frame_name
if not path.exists():
continue
img = load(path)
# resize to same as sharp
if img.shape[:2] != sharp.shape[:2]:
img = np.array(Image.fromarray(img).resize((sharp.shape[1], sharp.shape[0])))
if crop_box:
x1, y1, x2, y2 = crop_box
img = img[y1:y2, x1:x2]
s = sharp[y1:y2, x1:x2]
else:
s = sharp
# downscale for display
if scale < 1.0 and crop_box is None:
nh = int(img.shape[0] * scale)
nw = int(img.shape[1] * scale)
img = np.array(Image.fromarray(img).resize((nw, nh), Image.LANCZOS))
s = np.array(Image.fromarray(s).resize((nw, nh), Image.LANCZOS))
p = psnr(img, s) if key != "sharp" else None
cols.append(add_label(img, label, p))
if not cols:
return None
# ensure same height
max_h = max(c.shape[0] for c in cols)
padded = []
for c in cols:
if c.shape[0] < max_h:
pad = np.zeros((max_h - c.shape[0], c.shape[1], 3), dtype=np.uint8)
c = np.vstack([c, pad])
padded.append(c)
return np.hstack(padded)
def make_scene_grid(scene, frame_idxs, out_path, crop_box=None, scale=0.4):
rows = []
for fi in frame_idxs:
row = make_comparison_row(scene, fi, crop_box=crop_box, scale=scale)
if row is not None:
rows.append(row)
if not rows:
print(f" No rows for {scene}")
return
# add scene label
scene_bar = label_bar(f"Scene: {scene}" + (f" crop={crop_box}" if crop_box else ""),
rows[0].shape[1], h=28, bg=(60,30,30))
grid = np.vstack([scene_bar] + rows)
Image.fromarray(grid).save(out_path, quality=93)
print(f" saved -> {out_path}")
def main():
p = argparse.ArgumentParser()
p.add_argument("--out-dir", default=str(BASE / "outputs/eval/qualitative"))
p.add_argument("--scenes", nargs="*", default=["scene0000_00", "scene0000_01", "scene0000_02"])
p.add_argument("--frames", nargs="*", type=int, default=[0, 15, 30, 45])
p.add_argument("--scale", type=float, default=0.4)
args = p.parse_args()
out_dir = Path(args.out_dir)
out_dir.mkdir(parents=True, exist_ok=True)
# Crop regions for detail analysis (y1,x1,y2,x2 format)
crops = {
"topleft": (0, 0, 320, 480),
"center": (300, 400, 620, 880),
"bottomright": (600, 700, 968, 1296),
}
for scene in args.scenes:
print(f"\n[{scene}]")
# 1. Full-frame overview (downscaled)
make_scene_grid(scene, args.frames,
out_dir / f"{scene}_overview.jpg",
scale=args.scale)
# 2. Zoomed crops
for crop_name, box in crops.items():
x1, y1, x2, y2 = box
make_scene_grid(scene, args.frames[:2],
out_dir / f"{scene}_crop_{crop_name}.jpg",
crop_box=(x1, y1, x2, y2), scale=1.0)
# 3. TURTLE diff map (show where TURTLE changes the image)
frame_name = f"{args.frames[0]:06d}.png"
blur_path = BASE / "data/scannet_blur_proto/vddiff/test/blur" / scene / frame_name
turtle_path = BASE / "data/turtle_deblurred" / scene / frame_name
sharp_path = BASE / "data/scannet_blur_proto/vddiff/test/sharp" / scene / frame_name
if turtle_path.exists() and blur_path.exists():
blur = load(blur_path).astype(float)
turtle = load(turtle_path).astype(float)
sharp = load(sharp_path).astype(float)
# diff maps amplified 3x
diff_turtle_vs_blur = np.clip(np.abs(turtle - blur) * 3, 0, 255).astype(np.uint8)
diff_blur_vs_sharp = np.clip(np.abs(blur - sharp) * 3, 0, 255).astype(np.uint8)
diff_turtle_vs_sharp = np.clip(np.abs(turtle - sharp) * 3, 0, 255).astype(np.uint8)
nh = int(blur.shape[0] * args.scale)
nw = int(blur.shape[1] * args.scale)
def rs(a): return np.array(Image.fromarray(a).resize((nw, nh), Image.LANCZOS))
cols = [
add_label(rs(blur.astype(np.uint8)), "Blur input"),
add_label(rs(turtle.astype(np.uint8)), f"TURTLE PSNR={psnr(turtle, sharp):.2f}"),
add_label(rs(sharp.astype(np.uint8)), "Sharp GT"),
add_label(rs(diff_turtle_vs_blur), "Diff: TURTLE vs Blur (×3)"),
add_label(rs(diff_blur_vs_sharp), "Diff: Blur vs Sharp (×3)"),
add_label(rs(diff_turtle_vs_sharp), "Diff: TURTLE vs Sharp (×3)"),
]
grid = np.hstack(cols)
path = out_dir / f"{scene}_turtle_analysis.jpg"
Image.fromarray(grid).save(path, quality=93)
print(f" turtle analysis -> {path}")
print(f"\nAll figures saved to {out_dir}/")
if __name__ == "__main__":
main()
|