Skip to content

Geometry Operations

polars-cv provides two expression namespaces for geometry: .contour for polygon operations and .point for point operations.

Schemas

Geometry data uses Polars Struct columns:

from polars_cv import CONTOUR_SCHEMA, POINT_SCHEMA, BBOX_SCHEMA

# POINT_SCHEMA: Struct({x: f64, y: f64})
# BBOX_SCHEMA: Struct({x: f64, y: f64, width: f64, height: f64})
# CONTOUR_SCHEMA: Struct({exterior: List(POINT), holes: List(List(POINT)), is_closed: bool})

Contours

The .contour namespace operates on polygon columns.

Measurements

df.with_columns(
    area=pl.col("contour").contour.area(),
    perimeter=pl.col("contour").contour.perimeter(),
    centroid=pl.col("contour").contour.centroid(),
    bbox=pl.col("contour").contour.bounding_box(),
)

Transforms

df.with_columns(
    moved=pl.col("contour").contour.translate(dx=10, dy=20),
    scaled=pl.col("contour").contour.scale(sx=2.0, sy=2.0),
    simplified=pl.col("contour").contour.simplify(tolerance=1.0),
    hull=pl.col("contour").contour.convex_hull(),
)

Rasterization

Convert contours to binary masks:

pipe = Pipeline().source("contour", width=200, height=200)

result = df.with_columns(
    mask=pl.col("contour").cv.pipe(pipe).sink("numpy")
)

Infer dimensions from an existing image:

img = pl.col("image").cv.pipe(Pipeline().source("image_bytes").resize(height=200, width=200))
mask = pl.col("contour").cv.pipe(Pipeline().source("contour", shape=img))

Points

The .point namespace operates on point columns.

Transforms

df.with_columns(
    normalized=pl.col("point").point.normalize(ref_width=100, ref_height=100),
    absolute=pl.col("point").point.to_absolute(ref_width=100, ref_height=100),
    moved=pl.col("point").point.translate(dx=10, dy=20),
    scaled=pl.col("point").point.scale(sx=2.0, sy=2.0),
    rotated=pl.col("point").point.rotate(math.pi / 2),
)

Distances

df.with_columns(
    euclidean=pl.col("p1").point.distance(pl.col("p2")),
    manhattan=pl.col("p1").point.manhattan_distance(pl.col("p2")),
    to_boundary=pl.col("point").point.distance_to_contour(pl.col("contour")),
    signed=pl.col("point").point.signed_distance_to_contour(pl.col("contour")),
)

Geometric Operations

df.with_columns(
    angle=pl.col("p1").point.angle_to(pl.col("p2")),
    mid=pl.col("p1").point.midpoint(pl.col("p2")),
    interp=pl.col("p1").point.interpolate(pl.col("p2"), t=0.25),
    nearest=pl.col("point").point.nearest_point_on_contour(pl.col("contour")),
    inside=pl.col("point").point.within_bbox(pl.col("bbox")),
)

Bounding Boxes

The .bbox namespace operates on List[BBOX_SCHEMA] columns for detection tasks.

Pairwise IoU

Compute IoU between two sets of bounding boxes:

df.with_columns(
    iou_matrix=pl.col("pred_bboxes").bbox.pairwise_iou(pl.col("gt_bboxes")),
)

Detection Matching

Greedy one-to-one matching between predicted and ground-truth bounding boxes:

df.with_columns(
    match_result=pl.col("pred_bboxes").bbox.match_detections(
        pl.col("gt_bboxes"),
        threshold=0.5,
        scores=pl.col("pred_scores"),
    ),
)

Mask Metrics

Pixel-based metrics for binary masks:

from polars_cv import mask_iou, mask_dice

result = df.with_columns(
    iou=mask_iou(pred_expr, gt_expr),
    dice=mask_dice(pred_expr, gt_expr),
)