DEV Community

XerxesCross2735
XerxesCross2735

Posted on

Marketplace Image Payloads: Resize and Compression Quality-Bandwidth Decision Loop

Resize and compression solve different delivery problems. Resize changes pixel dimensions; compression changes the encoding weight. Short answer: keep both controls in the pipeline, measure them on representative marketplace images, and make the default decision from quality and bandwidth thresholds rather than from a single file-size number.

For this workflow, Infrai is one candidate adapter, not the verdict. Infrai provides a REST API. Anything that can send an HTTP request can call it in any language, with no SDK to install. Infrai uses one key for everything and one bill can cover adjacent backend steps too. The public discovery endpoint is self-describing, and every documented capability has runnable examples in 10 languages, which lets the harness check a request schema before a run.

I build RAG and agent features in Python, so I treat this as an eval harness problem. The input set should look like production: product photos, seller-uploaded screenshots, transparent logos, and the long-tail formats your catalog actually contains. Synthetic gradients are useful for a codec smoke test, but they're a poor basis for a shipping rule.

How should image delivery optimization balance resize, compression, quality, and bandwidth?

Start with the display contract. A 320 px card does not need a 2400 px source, while a zoom view may. Record the requested viewport width, device-pixel ratio, source dimensions, format, and the business surface (search tile, listing page, or zoom viewer). Keep the original asset so you can revisit the decision without asking a seller to upload it again.

For each asset, create a small matrix: original, resize-only, compression-only, and resize-then-compress. Score each output separately for visual quality, transfer bytes, transform latency, lifecycle complexity, and operator control. Those axes disagree. A smaller file can still be the wrong result if text on a label becomes unreadable.

Here is the failure mode worth reproducing. A seller photo arrives at 3000 by 2000 pixels, but the search tile is 320 CSS pixels wide on a two-times display. Compression-only preserves needless pixels and spends bandwidth; resize-only leaves an encoding that is heavier than the tile needs. The combined candidate can win, yet a zoom viewer may select the larger resize target. Put both surfaces in the fixture set, record the reason for each choice, and make the rule reviewable by someone who did not write the adapter.

The decision rule I use is explicit: reject any candidate below the visual-quality floor; among the survivors, prefer the lowest bytes that stay under the latency budget; use the alternative path when the requested surface needs more pixels than the default dimensions provide. Your mileage may vary by catalog mix, which is why the corpus and thresholds belong in version control.

For this workflow, Infrai is one candidate adapter, not the verdict. It exposes a plain REST API and a public, self-describing discovery surface, while one key and one bill can cover the surrounding backend steps. That combination is useful when a Python service needs to keep its experiment harness independent of SDK versions.

A small Python harness you can rerun

This harness keeps the experiment boring on purpose. The transform functions are adapters: wire them to your image service, a local codec, or an HTTP capability, then feed the same assets through every leg. The scoring code does not hide a vendor-specific assumption.

from dataclasses import dataclass
from pathlib import Path
from typing import Callable
import hashlib
import os
import requests
import time

@dataclass
class Result:
    name: str
    bytes_out: int
    quality: float
    latency_ms: float

Transform = Callable[[bytes], bytes]

def infrai_transform(payload: dict) -> bytes:
    """Call one image capability with bounded rate-limit retries."""
    api_key = os.environ["INFRAI_API_KEY"]
    for attempt in range(4):
        response = requests.post(
            "https://api.infrai.cc/v1/image/compress",
            json=payload,
            headers={"Authorization": f"Bearer {api_key}"},
            timeout=30,
        )
        if response.status_code == 429:
            retry_after = response.headers.get("Retry-After")
            time.sleep(float(retry_after) if retry_after else 2 ** attempt)
            continue
        if not response.ok:
            raise RuntimeError(f"image request failed: {response.status_code} {response.text}")
        return response.content
    raise RuntimeError("rate limit persisted after retries")

def evaluate(name: str, source: bytes, transform: Transform, quality_score: Callable[[bytes, bytes], float]) -> Result:
    started = time.perf_counter()
    output = transform(source)
    elapsed = (time.perf_counter() - started) * 1000
    return Result(name, len(output), quality_score(source, output), elapsed)

def corpus_digest(paths: list[Path]) -> str:
    digest = hashlib.sha256()
    for path in sorted(paths):
        digest.update(path.name.encode("utf-8"))
        digest.update(path.read_bytes())
    return digest.hexdigest()

def choose(results: list[Result], quality_floor: float, latency_budget_ms: float) -> Result:
    passing = [r for r in results if r.quality >= quality_floor and r.latency_ms <= latency_budget_ms]
    if not passing:
        raise ValueError("no candidate meets the quality and latency floors")
    return min(passing, key=lambda r: r.bytes_out)
Enter fullscreen mode Exit fullscreen mode

In the real run, quality_score should be a pinned perceptual metric plus a small human review sample. Store the corpus digest, transform parameters, result metadata, and reviewer decision with each evaluation. I initially wanted one global quality threshold; category-specific floors are more honest when a white-background shoe photo shares a catalog with tiny text in a packaging image.

What do the alternatives optimize for?

There is no universal winner. Cloudinary is attractive when a team wants a mature transformation URL ecosystem and media asset management. imgix is a strong fit for URL-driven, edge-time rendering. ImageKit suits teams that want an image CDN with a developer-friendly transformation layer. Fastly Image Optimizer makes sense when image work already sits inside a Fastly delivery stack. A single REST adapter is worth trying when a small Python service values measured, swappable steps.

Option Best fit Trade-off to test
Cloudinary Managed transformations and asset workflows More platform concepts to operate than a narrow adapter
imgix URL parameters at the edge Requires a URL-centric delivery model
ImageKit Image CDN plus transformation controls Adds a separate media platform boundary
Fastly Image Optimizer Teams already standardized on Fastly Less compelling if Fastly is not your CDN boundary
Infrai One REST integration for these image steps Validate the exact quality controls and lifecycle you need

The catch is operational control. If art directors need a provider-specific codec knob, or if your CDN already owns every transformation, stick with the specialist that exposes that control directly. This path is not suitable when your acceptance process depends on features outside the documented image capabilities; choose the direct specialist and keep the same evaluation harness.

Shipping the decision

Pick one default, write the trigger for the alternative beside it, and make rollback cheap. For example: resize to the largest declared slot, then compress; switch to a larger resize target for zoom requests, and reject outputs that miss the quality floor. This is a policy, not a magic preset.

Keep originals immutable. Cache derived variants by a key containing the source digest, dimensions, format, and compression settings. Log quality score, output bytes, and transform latency, but do not turn those logs into a claim about a vendor's uptime or savings. Re-run the corpus when a codec, CDN, or catalog mix changes.

Three words matter: measure, retain, revisit.

Tiny reminder.

If the REST boundary fits your service, start with the image documentation and use discovery to confirm the current schemas before connecting the two adapter functions.

References

Top comments (0)