Skip to content

Quickstart

This guide will get you started with polars-cv in 5 minutes.

Your First Pipeline

A polars-cv pipeline has three parts:

  1. Source: How to interpret input data
  2. Operations: Transformations to apply
  3. Sink: Output format
import polars as pl
from polars_cv import Pipeline

# 1. Define a pipeline
pipe = (
    Pipeline()
    .source("image_bytes")  # Decode image bytes (PNG/JPEG/TIFF)
    .resize(height=224, width=224)
)

# 2. Apply to a DataFrame column and specify sink
df = pl.DataFrame({"image": [png_bytes]})
result = df.with_columns(
    resized=pl.col("image").cv.pipe(pipe).sink("numpy")
)

Naming the source format is optional: source() defaults to "auto", which infers the decode path from the column's dtype (see Sources). The examples below spell it out to show which kind of input they expect.

Common Operations

Image Processing

# Grayscale conversion
Pipeline().source("image_bytes").grayscale()

# Blur with sigma=3
Pipeline().source("image_bytes").blur(sigma=3.0)

# Threshold to binary
Pipeline().source("image_bytes").grayscale().threshold(128)

# Crop region
Pipeline().source("image_bytes").crop(top=10, left=10, height=100, width=100)

# Flip horizontally
Pipeline().source("image_bytes").flip_h()

Normalization

# MinMax normalization [0, 1]
Pipeline().source("image_bytes").normalize(method="minmax")

# ZScore normalization (mean=0, std=1)
Pipeline().source("image_bytes").normalize(method="zscore")

Dynamic Parameters

A parameter can be a Polars expression, resolved per row at execution time, whenever it does not change the output rank or dtype at planning time (a size may be per-row, and is then unknown until the row runs) — which covers the numeric parameters, the individual elements of list-valued ones, and the non-structural enums and flags:

# Resize each image to different dimensions
df = pl.DataFrame({
    "image": [img1, img2, img3],
    "target_h": [64, 128, 256],
    "target_w": [64, 128, 256],
})

pipe = (
    Pipeline()
    .source("image_bytes")
    .resize(height=pl.col("target_h"), width=pl.col("target_w"))
)

result = df.with_columns(
    resized=pl.col("image").cv.pipe(pipe).sink("numpy")
)

Structural parameters — the ones that fix the output rank or dtype, such as cast(dtype=) or a reduction axis — stay literal-only and raise TypeError at build time if given an expression. See Parameters & Errors for the full rule.

A null in a parameter column fails the query by default; .on_null_param("null") nulls just the rows that depend on it instead — see Null parameter values.

Output Formats (Sinks)

Format Description Use Case
numpy NumPy-compatible struct NumPy, OpenCV, Scikit-image
torch PyTorch-compatible struct PyTorch DataLoaders
ndarray The numpy struct, tagged polars_cv.ndarray Columns that should say what they are (see Extension Types)
png Re-encode as PNG bytes Storage, display
jpeg Re-encode as JPEG bytes Web usage
webp Re-encode as WebP bytes Web usage (smaller files)
tiff Re-encode as TIFF bytes Scientific imaging
blob Self-describing VIEW binary Round-tripping buffers between pipelines
list Polars nested List Internal Polars analysis
array Fixed-size Polars Array Fixed-shape tensors
native Python primitive Scalars (Area, Mean)

The numpy and torch sinks accept dtype="f16" to downcast the output tensor to half precision at the encode boundary — halving the output bytes and host→device transfer. (The engine has no native f16 dtype; only the sink container is f16.)

# Half-precision output tensor
result = df.with_columns(
    processed=pl.col("image").cv.pipe(pipe).sink("torch", dtype="f16")
)

Tip

Every call already spreads its rows over every core. For data larger than memory, use a LazyFrame and the streaming engine — see Streaming & Scaling.

Reading from Files

# Read from local paths or URLs
df = pl.DataFrame({"path": ["/path/to/image.png", "https://example.com/img.jpg"]})
pipe = Pipeline().source("file_path").resize(height=224, width=224)

result = df.with_columns(
    processed=pl.col("path").cv.pipe(pipe).sink("numpy")
)

Auto DType and Planning-Time Requirements

image_bytes and file_path decode dtype at runtime, so pipelines from these sources begin with dtype auto unless explicitly constrained.

  • Common decodes: PNG/JPEG -> u8, 16-bit PNG -> u16
  • TIFF may produce u8, u16, f32, or f64

For sink("numpy"), sink("png"), sink("jpeg"), etc., this is usually fine. For sink("list") or sink("array"), dtype must be known during planning.

# Option 1: pin dtype at source
pipe = Pipeline().source("image_bytes", dtype="f32").resize(height=224, width=224)

# Option 2: resolve dtype in the pipeline
pipe = Pipeline().source("file_path").resize(height=224, width=224).cast("f32")

result = df.with_columns(values=pl.col("image").cv.pipe(pipe).sink("list"))

Next Steps