File size: 3,724 Bytes
ffe929e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
#!/usr/bin/env python3
"""Standalone post-process: take an existing HiDream PNG and smooth the
32-pixel patch grid seams. NO model load — just numpy + PIL.

Usage:
  postprocess.py <input.png> <output.png> [--radius N] [--strength F]

Strategy:
  For each seam line (x, y multiples of PATCH_SIZE), apply a 1D gaussian
  blur perpendicular to the seam, blended with the original by --strength.
  The blur kernel is symmetric, so flat regions get more smoothing than
  sharp edges (which the gaussian's centre weight preserves).

  --radius   blur radius in pixels (default 3)
  --strength blend weight 0-1 (default 0.7 = 70% blurred + 30% original)
"""
from __future__ import annotations

import argparse
import sys
from pathlib import Path

import numpy as np
from PIL import Image

PATCH_SIZE = 32


def gaussian_kernel_1d(radius: int) -> np.ndarray:
    """Build a normalised 1D gaussian kernel with sigma=radius/2."""
    sigma = radius / 2.0
    x = np.arange(-radius, radius + 1, dtype=np.float32)
    k = np.exp(-0.5 * (x / sigma) ** 2)
    return k / k.sum()


def smooth_seams(rgb: np.ndarray, radius: int = 3, strength: float = 0.7) -> np.ndarray:
    """Smooth horizontal+vertical patch seams via local gaussian blur.

    The blur is applied to the SEAM rows/cols only, then alpha-blended back
    by `strength`. Non-seam pixels are untouched.
    """
    out = rgb.astype(np.float32).copy()
    H, W, C = rgb.shape
    kernel = gaussian_kernel_1d(radius)  # length 2*radius+1

    # --- Horizontal seams (rows at y in {patch, 2*patch, ...}) ---
    # We smooth the 2 rows on each side of each seam (4 rows total per seam).
    for y in range(PATCH_SIZE, H, PATCH_SIZE):
        for offset in (-2, -1, 0, 1):
            yy = y + offset
            if not (0 <= yy < H):
                continue
            lo = max(0, yy - radius)
            hi = min(H, yy + radius + 1)
            k_lo = radius - (yy - lo)
            k_hi = radius + (hi - yy)
            k = kernel[k_lo:k_hi]
            k = k / k.sum()
            band = out[lo:hi]                       # [n, W, C]
            blurred = (band * k[:, None, None]).sum(axis=0)
            out[yy] = (1 - strength) * out[yy] + strength * blurred

    # --- Vertical seams (cols at x in {patch, 2*patch, ...}) ---
    for x in range(PATCH_SIZE, W, PATCH_SIZE):
        for offset in (-2, -1, 0, 1):
            xx = x + offset
            if not (0 <= xx < W):
                continue
            lo = max(0, xx - radius)
            hi = min(W, xx + radius + 1)
            k_lo = radius - (xx - lo)
            k_hi = radius + (hi - xx)
            k = kernel[k_lo:k_hi]
            k = k / k.sum()
            band = out[:, lo:hi]                    # [H, n, C]
            blurred = (band * k[None, :, None]).sum(axis=1)
            out[:, xx] = (1 - strength) * out[:, xx] + strength * blurred

    return np.clip(out, 0, 255).astype(np.uint8)


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("input")
    ap.add_argument("output")
    ap.add_argument("--radius", type=int, default=3)
    ap.add_argument("--strength", type=float, default=0.7)
    args = ap.parse_args()

    inp = Path(args.input)
    if not inp.exists():
        sys.exit(f"input not found: {inp}")

    rgb = np.array(Image.open(inp).convert("RGB"))
    H, W = rgb.shape[:2]
    print(f"{inp.name}: {W}x{H}, {(W // PATCH_SIZE) - 1} vertical + {(H // PATCH_SIZE) - 1} horizontal seams")
    print(f"smoothing with radius={args.radius}, strength={args.strength}...")

    out = smooth_seams(rgb, radius=args.radius, strength=args.strength)
    Image.fromarray(out).save(args.output)
    print(f"saved -> {args.output}")


if __name__ == "__main__":
    main()