Skip to content

Streaming & Scaling

The .cv.pipe(...) expression is an ordinary elementwise Polars plugin, so the parallelism comes from the Polars engine, not from inside the plugin. This has one important consequence for large workloads.

Why eager runs single-threaded

On a plain DataFrame.with_columns(...) / .select(...) call (the eager, in-memory engine), the whole column is processed on a single thread:

# Eager: single-threaded over the whole column
result = df.with_columns(
    processed=pl.col("image").cv.pipe(pipe).sink("numpy")
)

This is fine for small or interactive use. For anything larger, polars-cv emits a one-time warning pointing you here (see Silencing the warning).

Use the streaming engine for scale

Run through the lazy streaming engine instead. Polars splits the column into morsels and processes them across its worker pool, and can spill intermediate state to disk when memory is tight:

result = (
    df.lazy()
    .with_columns(processed=pl.col("image").cv.pipe(pipe).sink("blob"))
    .collect(engine="streaming")
)

This is the recommended path for anything beyond small/interactive use. The plugin's per-morsel graph is compiled once and cached process-wide, so per-morsel overhead is just a hash lookup — you get Polars' multi-threaded, larger-than-memory execution with no extra plumbing.

Note

The detection-metrics APIs already collect with engine="streaming" internally, so you don't need to opt in when using them.

Silencing the warning

A large batch run under the eager engine prints a one-time notice. Control it with environment variables:

Variable Effect
POLARS_CV_SILENCE_ENGINE_WARNING=1 Suppress the warning entirely
POLARS_CV_ENGINE_WARN_ROWS=<n> Row-count threshold above which the warning fires

Cheaper decoding for curation passes

When scaling over large image sets, you often only need a cheap signal (a perceptual hash, a mean, a quality score) to filter before doing full-resolution work. Decode a downscaled thumbnail first with thumbnail(max_size) (JPEG IDCT-scaled decode), compute the signal, filter, then full-decode only the survivors in a second pass.

Next Steps