EgeEken commited on
Commit
ef3a5e4
·
1 Parent(s): 66df96a

overhead investigation + optimization

Browse files
Files changed (2) hide show
  1. server.py +21 -4
  2. static/app.js +19 -2
server.py CHANGED
@@ -215,6 +215,13 @@ def multlist(bit_count: int = 2, min: int = -10, max: int = 20, mode: str = "Sta
215
 
216
  @app.post("/api/compress")
217
  async def compress(request: Request):
 
 
 
 
 
 
 
218
  form = await request.form()
219
  upload = form.get("image")
220
  if upload is None:
@@ -225,6 +232,7 @@ async def compress(request: Request):
225
  except Exception as exc:
226
  return JSONResponse({"error": f"Could not read image: {exc}"}, status_code=400)
227
  w, h = img.size
 
228
 
229
  mode = form.get("mode", "Auto")
230
  if mode == "Auto":
@@ -242,24 +250,33 @@ async def compress(request: Request):
242
  except (ValueError, TypeError):
243
  pass
244
  config = PBC3Config(**kwargs)
 
245
 
246
  try:
247
  result = PBC3.compress(img, config=config)
248
  except Exception as exc:
249
  return JSONResponse({"error": f"Compression failed: {exc}"}, status_code=400)
250
  reconstructed = result.image.convert("RGB")
 
251
 
252
  a = np.asarray(img, dtype=np.float32)
253
  b = np.asarray(reconstructed.resize(img.size), dtype=np.float32)
254
  mse = mse_metric(a, b)
 
 
 
 
 
 
 
255
 
256
  original_raw = w * h * 3
257
  compressed = len(result.data)
 
258
 
259
  return JSONResponse({
260
- "original_image": _png_b64(img),
261
- "reconstructed_image": _png_b64(reconstructed),
262
- "pbc_base64": base64.b64encode(result.data).decode(),
263
  "width": w,
264
  "height": h,
265
  "original_raw_kb": round(original_raw / 1024, 2),
@@ -269,10 +286,10 @@ async def compress(request: Request):
269
  "compression_percent": round(compressed / original_raw * 100, 2) if original_raw else 0,
270
  "mse": round(mse, 2),
271
  "time_seconds": round(result.encode_seconds, 2),
 
272
  "params": {"mode": mode, **{k: (list(v) if isinstance(v, tuple) else v) for k, v in kwargs.items()}},
273
  })
274
 
275
-
276
  @app.post("/api/stream_compress")
277
  async def stream_compress(image: UploadFile = File(...), downsample_initialize: str = Form("false")):
278
  raw = await image.read()
 
215
 
216
  @app.post("/api/compress")
217
  async def compress(request: Request):
218
+ stages = {}
219
+ t = time.perf_counter()
220
+ def mark(name):
221
+ nonlocal t
222
+ stages[name] = round(time.perf_counter() - t, 3)
223
+ t = time.perf_counter()
224
+
225
  form = await request.form()
226
  upload = form.get("image")
227
  if upload is None:
 
232
  except Exception as exc:
233
  return JSONResponse({"error": f"Could not read image: {exc}"}, status_code=400)
234
  w, h = img.size
235
+ mark("read_decode")
236
 
237
  mode = form.get("mode", "Auto")
238
  if mode == "Auto":
 
250
  except (ValueError, TypeError):
251
  pass
252
  config = PBC3Config(**kwargs)
253
+ mark("build_config")
254
 
255
  try:
256
  result = PBC3.compress(img, config=config)
257
  except Exception as exc:
258
  return JSONResponse({"error": f"Compression failed: {exc}"}, status_code=400)
259
  reconstructed = result.image.convert("RGB")
260
+ mark("compress")
261
 
262
  a = np.asarray(img, dtype=np.float32)
263
  b = np.asarray(reconstructed.resize(img.size), dtype=np.float32)
264
  mse = mse_metric(a, b)
265
+ mark("mse")
266
+
267
+ recon_b64 = _png_b64(reconstructed) # original is now sent by the client (it has the bytes)
268
+ mark("png_reconstructed")
269
+
270
+ pbc_b64 = base64.b64encode(result.data).decode()
271
+ mark("encode_payload")
272
 
273
  original_raw = w * h * 3
274
  compressed = len(result.data)
275
+ print(f"[compress] encode={result.encode_seconds:.3f}s stages={stages}", flush=True)
276
 
277
  return JSONResponse({
278
+ "reconstructed_image": recon_b64,
279
+ "pbc_base64": pbc_b64,
 
280
  "width": w,
281
  "height": h,
282
  "original_raw_kb": round(original_raw / 1024, 2),
 
286
  "compression_percent": round(compressed / original_raw * 100, 2) if original_raw else 0,
287
  "mse": round(mse, 2),
288
  "time_seconds": round(result.encode_seconds, 2),
289
+ "stage_timings": stages,
290
  "params": {"mode": mode, **{k: (list(v) if isinstance(v, tuple) else v) for k, v in kwargs.items()}},
291
  })
292
 
 
293
  @app.post("/api/stream_compress")
294
  async def stream_compress(image: UploadFile = File(...), downsample_initialize: str = Form("false")):
295
  raw = await image.read()
static/app.js CHANGED
@@ -604,7 +604,16 @@ function renderJob(job) {
604
  function updateJobMeta(job) {
605
  const m = job.el.querySelector(".job-meta");
606
  if (job.status === "sent") m.textContent = `${((performance.now() - job.tSent) / 1000).toFixed(1)}s`;
607
- else if (job.status === "done") m.textContent = `total ${job.total.toFixed(1)}s · encode ${job.encode.toFixed(2)}s · overhead ${Math.max(0, job.total - job.encode).toFixed(1)}s`;
 
 
 
 
 
 
 
 
 
608
  else if (job.status === "error") m.textContent = "failed — see console";
609
  else m.textContent = "waiting…";
610
  }
@@ -628,7 +637,7 @@ function scheduleJobRemoval(job) {
628
  }
629
 
630
  function enqueueJob(file) {
631
- const job = { name: file.name, fd: buildCompressForm(file), status: "queued" };
632
  job.el = renderJob(job);
633
  jobQueueEl.appendChild(job.el);
634
  jobQueue.push(job);
@@ -657,6 +666,14 @@ async function runJob(job) {
657
  const data = await res.json();
658
  job.total = (performance.now() - job.tSent) / 1000;
659
  job.encode = data.time_seconds;
 
 
 
 
 
 
 
 
660
  setJobState(job, "done");
661
  addToRegistry(data);
662
  showLastResult(data);
 
604
  function updateJobMeta(job) {
605
  const m = job.el.querySelector(".job-meta");
606
  if (job.status === "sent") m.textContent = `${((performance.now() - job.tSent) / 1000).toFixed(1)}s`;
607
+ else if (job.status === "done") {
608
+ let s = `total ${job.total.toFixed(1)}s · encode ${job.encode.toFixed(2)}s · overhead ${Math.max(0, job.total - job.encode).toFixed(1)}s`;
609
+ if (job.stages) {
610
+ const top = Object.entries(job.stages).sort((a, b) => b[1] - a[1]).slice(0, 3)
611
+ .map(([k, v]) => `${k} ${v}s`).join(" · ");
612
+ s += `<br><span style="opacity:.65">server: ${top}</span>`;
613
+ }
614
+ job.el.querySelector(".job-meta").innerHTML = s;
615
+ return;
616
+ }
617
  else if (job.status === "error") m.textContent = "failed — see console";
618
  else m.textContent = "waiting…";
619
  }
 
637
  }
638
 
639
  function enqueueJob(file) {
640
+ const job = { name: file.name, file, fd: buildCompressForm(file), status: "queued" };
641
  job.el = renderJob(job);
642
  jobQueueEl.appendChild(job.el);
643
  jobQueue.push(job);
 
666
  const data = await res.json();
667
  job.total = (performance.now() - job.tSent) / 1000;
668
  job.encode = data.time_seconds;
669
+ job.stages = data.stage_timings;
670
+ if (job.file && !data.decoded && !data.original_image) {
671
+ data.original_image = await new Promise(r => {
672
+ const fr = new FileReader();
673
+ fr.onload = () => r(fr.result);
674
+ fr.readAsDataURL(job.file);
675
+ });
676
+ }
677
  setJobState(job, "done");
678
  addToRegistry(data);
679
  showLastResult(data);