LogicGoInfotechSpaces commited on
Commit
174828e
·
1 Parent(s): ff3e3e4

feat(api): add /inpaint-url endpoint returning public download URL; docs updated

Browse files
Files changed (2) hide show
  1. README.md +3 -2
  2. api/main.py +39 -3
README.md CHANGED
@@ -19,12 +19,13 @@ Endpoints:
19
  - `POST /upload-image` (form-data: image=file) → {"id":"<image_id>","filename":"name.png"}
20
  - `POST /upload-mask` (form-data: mask=file) → {"id":"<mask_id>","filename":"mask.png"}
21
  - `POST /inpaint` (JSON: {image_id, mask_id}) → returns result directly (no download step)
 
22
  - `POST /inpaint-multipart` (form-data: image=file, mask=file) → {"result":"output_xxx.png"}
23
- - `GET /download/{filename}` → image file (optional; not required when using ID-based inpaint)
24
  - `GET /logs` → recent uploads/results
25
 
26
  Note:
27
- - When using the ID-based `POST /inpaint` endpoint, the API responds with the inpainted image directly, so the `GET /download/{filename}` endpoint is not needed.
28
 
29
  Local run:
30
  - Install deps: `python3 -m pip install -r requirements.txt`
 
19
  - `POST /upload-image` (form-data: image=file) → {"id":"<image_id>","filename":"name.png"}
20
  - `POST /upload-mask` (form-data: mask=file) → {"id":"<mask_id>","filename":"mask.png"}
21
  - `POST /inpaint` (JSON: {image_id, mask_id}) → returns result directly (no download step)
22
+ - `POST /inpaint-url` (JSON: {image_id, mask_id}) → {"result":"output_xxx.png","url":"https://.../download/output_xxx.png"}
23
  - `POST /inpaint-multipart` (form-data: image=file, mask=file) → {"result":"output_xxx.png"}
24
+ - `GET /download/{filename}` → image file (public; optional for ID-based inpaint)
25
  - `GET /logs` → recent uploads/results
26
 
27
  Note:
28
+ - `POST /inpaint` returns image bytes directly. Use `POST /inpaint-url` if you need a shareable URL in the response.
29
 
30
  Local run:
31
  - Install deps: `python3 -m pip install -r requirements.txt`
api/main.py CHANGED
@@ -5,7 +5,7 @@ from datetime import datetime
5
  from typing import Dict, List, Optional
6
 
7
  import numpy as np
8
- from fastapi import FastAPI, UploadFile, File, HTTPException, Depends, Header
9
  from fastapi.responses import FileResponse, JSONResponse
10
  from pydantic import BaseModel
11
  from PIL import Image
@@ -148,10 +148,33 @@ def inpaint(req: InpaintRequest, _: None = Depends(bearer_auth)):
148
  return FileResponse(result_path, media_type="image/png", filename=result_name)
149
 
150
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  @app.post("/inpaint-multipart")
152
  def inpaint_multipart(
153
  image: UploadFile = File(...),
154
  mask: UploadFile = File(...),
 
155
  _: None = Depends(bearer_auth),
156
  ) -> Dict[str, str]:
157
  # Load in-memory
@@ -164,8 +187,21 @@ def inpaint_multipart(
164
  result_path = os.path.join(OUTPUT_DIR, result_name)
165
  Image.fromarray(result).save(result_path)
166
 
167
- logs.append({"result": result_name, "timestamp": datetime.utcnow().isoformat()})
168
- return {"result": result_name}
 
 
 
 
 
 
 
 
 
 
 
 
 
169
 
170
 
171
  @app.get("/download/{filename}")
 
5
  from typing import Dict, List, Optional
6
 
7
  import numpy as np
8
+ from fastapi import FastAPI, UploadFile, File, HTTPException, Depends, Header, Request
9
  from fastapi.responses import FileResponse, JSONResponse
10
  from pydantic import BaseModel
11
  from PIL import Image
 
148
  return FileResponse(result_path, media_type="image/png", filename=result_name)
149
 
150
 
151
+ @app.post("/inpaint-url")
152
+ def inpaint_url(req: InpaintRequest, request: Request, _: None = Depends(bearer_auth)) -> Dict[str, str]:
153
+ """Same as /inpaint but returns a JSON with a public download URL instead of image bytes."""
154
+ if req.image_id not in file_store or file_store[req.image_id]["type"] != "image":
155
+ raise HTTPException(status_code=404, detail="image_id not found")
156
+ if req.mask_id not in file_store or file_store[req.mask_id]["type"] != "mask":
157
+ raise HTTPException(status_code=404, detail="mask_id not found")
158
+
159
+ img_rgba = _load_rgba_image(file_store[req.image_id]["path"])
160
+ mask_img = Image.open(file_store[req.mask_id]["path"]) # may be RGB/gray/RGBA
161
+ mask_rgba = _load_rgba_mask_from_image(mask_img)
162
+
163
+ result = process_inpaint(np.array(img_rgba), mask_rgba)
164
+ result_name = f"output_{uuid.uuid4().hex}.png"
165
+ result_path = os.path.join(OUTPUT_DIR, result_name)
166
+ Image.fromarray(result).save(result_path)
167
+
168
+ url = str(request.url_for("download_file", filename=result_name))
169
+ logs.append({"result": result_name, "url": url, "timestamp": datetime.utcnow().isoformat()})
170
+ return {"result": result_name, "url": url}
171
+
172
+
173
  @app.post("/inpaint-multipart")
174
  def inpaint_multipart(
175
  image: UploadFile = File(...),
176
  mask: UploadFile = File(...),
177
+ request: Request = None,
178
  _: None = Depends(bearer_auth),
179
  ) -> Dict[str, str]:
180
  # Load in-memory
 
187
  result_path = os.path.join(OUTPUT_DIR, result_name)
188
  Image.fromarray(result).save(result_path)
189
 
190
+ url: Optional[str] = None
191
+ try:
192
+ if request is not None:
193
+ url = str(request.url_for("download_file", filename=result_name))
194
+ except Exception:
195
+ url = None
196
+
197
+ entry: Dict[str, str] = {"result": result_name, "timestamp": datetime.utcnow().isoformat()}
198
+ if url:
199
+ entry["url"] = url
200
+ logs.append(entry)
201
+ resp: Dict[str, str] = {"result": result_name}
202
+ if url:
203
+ resp["url"] = url
204
+ return resp
205
 
206
 
207
  @app.get("/download/{filename}")