File size: 3,706 Bytes
dd96d2f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
#!/usr/bin/env python3
"""
Upscale l'image cover (lobby) via Vertex AI Imagen 4.0 upscale.

Prérequis : GCP_PROJECT_ID et GCP_LOCATION dans .env, et `gcloud auth application-default login`
(ou GOOGLE_APPLICATION_CREDENTIALS).

Usage :
  cd backend && python -m scripts.upscale_cover
  cd backend && python -m scripts.upscale_cover --factor x2 --input ../frontend/src/cover.jpg --output ../frontend/src/cover.jpg
"""
from __future__ import annotations

import argparse
import base64
import subprocess
import sys
from pathlib import Path

_backend = Path(__file__).resolve().parent.parent
if str(_backend) not in sys.path:
    sys.path.insert(0, str(_backend))

from config import GCP_PROJECT_ID, GCP_LOCATION


def get_access_token() -> str:
    """Token via gcloud (ADC)."""
    out = subprocess.run(
        ["gcloud", "auth", "print-access-token"],
        capture_output=True,
        text=True,
        check=False,
    )
    if out.returncode != 0:
        raise RuntimeError(
            "Échec gcloud auth. Lancez: gcloud auth application-default login"
        )
    return out.stdout.strip()


def upscale_image(
    image_path: Path,
    out_path: Path,
    factor: str = "x4",
    region: str | None = None,
    project_id: str | None = None,
) -> None:
    region = region or GCP_LOCATION
    project_id = project_id or GCP_PROJECT_ID
    if not project_id:
        raise SystemExit("Définir GCP_PROJECT_ID dans .env")

    data = image_path.read_bytes()
    if len(data) > 10 * 1024 * 1024:
        raise SystemExit("Image trop lourde (max 10 Mo)")

    b64 = base64.standard_b64encode(data).decode("ascii")
    token = get_access_token()
    url = (
        f"https://{region}-aiplatform.googleapis.com/v1/projects/{project_id}"
        f"/locations/{region}/publishers/google/models/imagen-4.0-upscale-preview:predict"
    )
    import httpx

    payload = {
        "instances": [
            {"prompt": "Upscale the image", "image": {"bytesBase64Encoded": b64}}
        ],
        "parameters": {
            "mode": "upscale",
            "upscaleConfig": {"upscaleFactor": factor},
            "outputOptions": {"mimeType": "image/png"},
        },
    }
    resp = httpx.post(
        url,
        json=payload,
        headers={
            "Authorization": f"Bearer {token}",
            "Content-Type": "application/json",
        },
        timeout=120.0,
    )
    resp.raise_for_status()
    body = resp.json()
    preds = body.get("predictions") or []
    if not preds or "bytesBase64Encoded" not in preds[0]:
        raise SystemExit("Réponse Vertex sans image: " + str(body)[:500])
    out_b64 = preds[0]["bytesBase64Encoded"]
    out_path.parent.mkdir(parents=True, exist_ok=True)
    out_path.write_bytes(base64.standard_b64decode(out_b64))
    print(f"Upscale {factor} OK → {out_path} ({out_path.stat().st_size / 1024:.1f} Ko)")


def main() -> None:
    parser = argparse.ArgumentParser(description="Upscale cover image via Vertex AI Imagen")
    parser.add_argument(
        "--input",
        type=Path,
        default=_backend.parent / "frontend" / "src" / "cover.jpg",
        help="Image source",
    )
    parser.add_argument(
        "--output",
        type=Path,
        default=None,
        help="Image de sortie (défaut: --input)",
    )
    parser.add_argument(
        "--factor",
        choices=("x2", "x3", "x4"),
        default="x4",
        help="Facteur d'upscale (défaut: x4)",
    )
    args = parser.parse_args()
    out = args.output or args.input
    if not args.input.is_file():
        raise SystemExit(f"Fichier introuvable: {args.input}")
    upscale_image(args.input, out, factor=args.factor)


if __name__ == "__main__":
    main()