Skip to content

Functions

Utility functions for working with polars-cv outputs.

NumPy Conversion

numpy_from_struct

Convert numpy/torch/ndarray sink output (struct) to NumPy array.

The numpy and torch sinks return a Polars Struct (NUMPY_OUTPUT_SCHEMA): - data: Binary - raw bytes of the array - dtype: String - NumPy dtype name (e.g., "float32") - shape: List[UInt64] - shape of the array - strides: List[Int64] - byte strides, so non-contiguous outputs keep their layout - offset: UInt64 - byte offset into data

The ndarray sink returns the same struct tagged polars_cv.ndarray (NdArrayType); numpy_from_struct reads both.

from polars_cv import numpy_from_struct

# From a Series element (dict)
arr = numpy_from_struct(result["tensor"][0])
print(f"Shape: {arr.shape}, dtype: {arr.dtype}")

# Or pass a Series directly (takes first element)
arr = numpy_from_struct(result["tensor"])

numpy_from_column

Read every row of a numpy/ndarray sink column as a NumPy array, without copying. Each array is a read-only view of the column's own Arrow memory, with the row's shape, dtype and byte strides, and keeps that memory alive after the column is gone; null rows are None. Pass copy=True for owned, writable, C-contiguous arrays.

from polars_cv import numpy_from_column

arrays = numpy_from_column(result["tensor"])
batch = [a for a in arrays if a is not None]

Prefer it to numpy_from_struct over .to_list() rows, which copies every row into Python bytes first.

Extension Types

extension_types

polars-cv's Arrow extension types.

An extension type tags a column with what it is — a point, a contour, a bounding box, an N-D array — on top of the plain struct that stores it. A consumer then identifies the column by its type instead of sniffing field names, and a tagged column keeps that identity through Parquet and IPC.

======================== ============================================ type storage ======================== ============================================ polars_cv.ndarray :data:NUMPY_OUTPUT_SCHEMA (the numpy/torch sink struct) polars_cv.point :data:~polars_cv.geometry.schemas.POINT_SCHEMA polars_cv.contour :data:~polars_cv.geometry.schemas.CONTOUR_SCHEMA polars_cv.bbox :data:~polars_cv.geometry.schemas.BBOX_SCHEMA ======================== ============================================

Producing and consuming them:

  • .sink("ndarray") emits a polars_cv.ndarray column; sink("numpy") keeps emitting the plain struct.
  • Any column with the right storage can be tagged with .ext.to(PointType()) and friends — a zero-copy relabel.
  • Every polars-cv expression accepts a tagged column wherever it accepts the plain struct. Geometry outputs are plain structs.
  • .ext.storage() recovers the plain struct. Polars' struct operations (.struct.field, unnest, casts) need it: they do not see through a tag.

An instance of one of these classes always has canonical storage. polars rebuilds a registered type through :meth:~_PolarsCvType.ext_from_params, and a column that carries one of these names over any other storage (or with metadata) comes back as polars' generic :class:polars.Extension instead — so isinstance(dtype, PointType) is a complete check, never a guess.

Where the facts live. The storage layouts belong to the Rust plugin (geom_schema, output::numpy_output_dtype); ExtType in src/ext_types.rs names the four types there and _lib.extension_types() publishes them. They are restated here only so import polars_cv can register the types without loading the compiled extension (registering late would let a Parquet read decay a tagged column to polars' generic extension). The parity test test_python_types_match_the_rust_declaration holds the two together.

.. warning:: Polars documents its extension-type API as unstable. Everything polars-cv does with it goes through this module and src/ext_types.rs.

EXTENSION_TYPES module-attribute

EXTENSION_TYPES: tuple[type[_PolarsCvType], ...] = (
    NdArrayType,
    PointType,
    ContourType,
    BBoxType,
)

NdArrayType

Bases: _PolarsCvType

polars_cv.ndarray: a strided N-D array, as the numpy/torch sink struct.

Emitted by .sink("ndarray"); read back with :func:polars_cv.numpy_from_struct.

ext_from_params classmethod

ext_from_params(
    name: str, storage: Any, metadata: str | None
) -> pl.datatypes.BaseExtension

Rebuild the type polars read, refusing to vouch for a malformed one.

Returns an instance of this class only for the canonical storage and no metadata. Anything else under this name is returned as the generic pl.Extension: raising here is not an option, because polars turns an exception from this hook into a panic.

PointType

Bases: _PolarsCvType

polars_cv.point: a 2-D {x, y} point.

ext_from_params classmethod

ext_from_params(
    name: str, storage: Any, metadata: str | None
) -> pl.datatypes.BaseExtension

Rebuild the type polars read, refusing to vouch for a malformed one.

Returns an instance of this class only for the canonical storage and no metadata. Anything else under this name is returned as the generic pl.Extension: raising here is not an option, because polars turns an exception from this hook into a panic.

ContourType

Bases: _PolarsCvType

polars_cv.contour: a polygon {exterior, holes, is_closed}.

ext_from_params classmethod

ext_from_params(
    name: str, storage: Any, metadata: str | None
) -> pl.datatypes.BaseExtension

Rebuild the type polars read, refusing to vouch for a malformed one.

Returns an instance of this class only for the canonical storage and no metadata. Anything else under this name is returned as the generic pl.Extension: raising here is not an option, because polars turns an exception from this hook into a panic.

BBoxType

Bases: _PolarsCvType

polars_cv.bbox: an axis-aligned box {x, y, width, height}.

ext_from_params classmethod

ext_from_params(
    name: str, storage: Any, metadata: str | None
) -> pl.datatypes.BaseExtension

Rebuild the type polars read, refusing to vouch for a malformed one.

Returns an instance of this class only for the canonical storage and no metadata. Anything else under this name is returned as the generic pl.Extension: raising here is not an option, because polars turns an exception from this hook into a panic.

register_extension_types

register_extension_types() -> None

Register every polars-cv type with polars. Idempotent.

Called once at import polars_cv. A name already registered to a class that is not ours is refused: registering over it would change how the other code's columns decode, and leaving it would change how ours do.

Mask Metrics

mask_iou

Compute IoU between two mask pipelines.

from polars_cv import mask_iou, Pipeline
import polars as pl

pred_pipe = Pipeline().source("image_bytes").grayscale().threshold(128)
gt_pipe = Pipeline().source("contour").rasterize(width=256, height=256)

result = df.with_columns(
    iou=mask_iou(
        pl.col("prediction").cv.pipe(pred_pipe),
        pl.col("ground_truth").cv.pipe(gt_pipe),
    )
)

mask_dice

Compute Dice coefficient between two mask pipelines.

from polars_cv import mask_dice

result = df.with_columns(
    dice=mask_dice(
        pl.col("prediction").cv.pipe(pred_pipe),
        pl.col("ground_truth").cv.pipe(gt_pipe),
    )
)

Hash Comparison

hamming_distance

Compute Hamming distance between two perceptual hashes.

from polars_cv import hamming_distance, Pipeline
import polars as pl

hash_pipe = Pipeline().source("image_bytes").perceptual_hash()

result = df.with_columns(
    distance=hamming_distance(
        pl.col("image_a").cv.pipe(hash_pipe),
        pl.col("image_b").cv.pipe(hash_pipe),
    )
)

hash_similarity

Compute similarity percentage between two perceptual hashes.

from polars_cv import hash_similarity

result = df.with_columns(
    similarity=hash_similarity(
        pl.col("image_a").cv.pipe(hash_pipe),
        pl.col("image_b").cv.pipe(hash_pipe),
        hash_bits=64,  # Match your hash size
    )
)

Display

show_images

Display images from a binary column in Jupyter notebooks. Falls back to a text summary outside notebook environments.

from polars_cv import show_images

show_images(df, "image_column", max_rows=10, max_width=200)

Supports PNG, JPEG, WebP, TIFF, BMP, GIF, VIEW protocol blobs, and numpy-sink struct columns. Format is auto-detected.

Types

ColorSpace

Color space identifiers for convert_color.

from polars_cv import ColorSpace

ColorSpace.RGB
ColorSpace.BGR
ColorSpace.HSV
ColorSpace.LAB
ColorSpace.YCBCR
ColorSpace.GRAY

CloudOptions

Configuration for cloud storage access. Named fields cover the common credentials; storage_options forwards arbitrary options to the underlying object_store backend using its native config keys (winning over named fields on collision); gcs_bearer_token supplies a pre-obtained GCS OAuth access token for credential types object_store cannot load natively (e.g. federated external_account_authorized_user ADC).

from polars_cv import CloudOptions

options = CloudOptions(
    aws_region="us-east-1",
    aws_access_key_id="...",
    aws_secret_access_key="...",
)

# GCS via an Application Default Credentials file:
options = CloudOptions(
    storage_options={"google_application_credentials": "/path/adc.json"}
)

# GCS via a pre-minted OAuth access token (federated / brokered credentials):
options = CloudOptions(gcs_bearer_token=token)

pipe = Pipeline().source("file_path", cloud_options=options)

dtype_name_for

Name a Polars dtype the way source(dtype=) and cast() take it (pl.Float32 → "f32"), for building a pipeline over a column whose dtype is only known at run time.

from polars_cv import Pipeline, dtype_name_for

pipe = Pipeline().source("list", dtype=dtype_name_for(df.schema["values"].inner))

IMAGENET_MEAN / IMAGENET_STD

The ImageNet per-channel RGB mean [0.485, 0.456, 0.406] and standard deviation [0.229, 0.224, 0.225], on a [0, 1] scale.

HashAlgorithm

Perceptual hash algorithm selection.

from polars_cv import HashAlgorithm

Pipeline().perceptual_hash(algorithm=HashAlgorithm.PERCEPTUAL)
Pipeline().perceptual_hash(algorithm=HashAlgorithm.AVERAGE)
Pipeline().perceptual_hash(algorithm=HashAlgorithm.DIFFERENCE)
Pipeline().perceptual_hash(algorithm=HashAlgorithm.BLOCKHASH)

Threading

How the plugin's thread pool and Polars' share the cores is described in Streaming & Scaling.

set_row_parallelism

set_row_parallelism(enabled: bool) -> bool

Turn polars-cv's own row parallelism on or off for this process.

On (the default), a plugin call spreads its rows over polars-cv's thread pool when that pool has idle threads. Off, every call runs its rows on the thread polars called it on, so the only parallelism is polars' own — the streaming engine's morsels, or one thread per call on the in-memory engine. Turn it off when the streaming engine already keeps every core busy, or on hosts where waking the pool's threads costs more than the rows (many hyperthreaded cores and cheap rows). Outputs are identical either way.

Parameters:

Name Type Description Default
enabled bool

Whether calls may split their rows. Must be a bool.

required

Returns:

Type Description
bool

The previous setting, so a caller can restore it.

Examples:

>>> previous = polars_cv.set_row_parallelism(False)
>>> lf.collect(engine="streaming")
>>> polars_cv.set_row_parallelism(previous)

row_parallelism

row_parallelism() -> bool

Whether polars-cv splits a call's rows over its own thread pool.

Optimization

OptFlags dataclass

Bases: _OptFlagFields

Which optimization passes run, one boolean per pass (both tiers).

The fields are generated from the pass catalogue, one per pass. Construct directly (OptFlags(scalar_fusion=False)), with the :meth:all/:meth:none shorthands, or from the environment via :meth:from_env. Defaults are all-on, matching the always-on behaviour that predated the explicit phase.

all classmethod

all() -> 'OptFlags'

Every pass enabled.

none classmethod

none() -> 'OptFlags'

Every pass disabled — serialize the logical graph verbatim.

enabled

enabled(name: str) -> bool

Is the named pass on? Raises KeyError for an unknown pass.

engine_opt

engine_opt() -> 'dict[str, bool]'

The engine-tier toggles, keyed by their Rust OptConfig field name.

Serialized as the graph's opt object; the key set equals :data:ENGINE_PASS_NAMES, which equals the Rust OptConfig fields.

from_env classmethod

from_env() -> 'OptFlags'

Read the global default from :data:OPT_ENV_VAR.

An unset or blank value means all-on (the omitted-keyword default). Otherwise the value is parsed by :meth:parse.

parse classmethod

parse(raw: str) -> 'OptFlags'

Parse a flag string into :class:OptFlags.

Grammar (comma-separated, whitespace-insensitive):

  • all / none as the first token sets the base; otherwise the base is all-off.
  • a bare pass name turns that pass on; -name turns it off.

Examples: "all", "none", "scalar_fusion" (only that one on), "all,-scalar_fusion" (all but that one).

An unknown pass name raises ValueError rather than being silently ignored — an unrecognised flag is a bug, not a no-op.

Build Information

build_info

build_info() -> dict[str, str | None]

Report the versions of the things that can disagree.

The install is editable, so edits to the Python sources are live — but the compiled extension is not. After a git pull that touches Rust, _lib.abi3.so keeps its build-time version until maturin develop is re-run, and new Python then runs against old Rust. When these three values disagree, re-run maturin develop.

Returns:

Type Description
dict[str, str | None]

Dict with: - version: polars_cv.__version__, from the imported Python source. - plugin_version: the compiled Rust extension's version, baked in from Cargo.toml at build time. None if the plugin is not built. - dist_version: the installed distribution metadata version. None if the package is not installed (e.g. run from a checkout). - plugin_source_hash: the hash of the Rust sources the extension was built from. None if the plugin is not built. - source_hash: the same hash recomputed from the working tree. None outside a source checkout.

The two hashes are what actually detect staleness. The versions cannot: they are the release version, identical until the next bump, so they agree throughout the entire window in which Rust is edited without rebuilding.

Example
>>> import polars_cv
>>> info = polars_cv.build_info()
>>> len(set(v for v in info.values() if v is not None)) == 1  # all agree
True