Skip to main content

contrek-python

Python bindings for Contrek, a fast raster-to-vector polygon tracing engine written in C++. This wraps the C++ core with pybind11 so you can call it from Python without losing the performance. Coordinates come back as NumPy arrays and the underlying engine is multi-threaded. You can find more info in the main repo.

Wrapper is MIT. Core (vendored as a git submodule at vendor/contrek) is AGPLv3.

Install

Source only, no prebuilt wheels — pip install compiles the core locally and tunes it for your CPU (-march=native, on by default in the core's own CMakeLists). Needs a C++17 compiler and CMake.

pip install contrek

For development (editable install, running tests):

git clone --recurse-submodules https://github.com/<your-user>/contrek-python.git
cd contrek-python
python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[test]"
pytest

Linux/macOS only (POSIX threads).

High-level API

Trace polygons from an image file in one call.

import contrek

result = contrek.contour("image.png", number_ot_threads=4, number_ot_tiles=4, treemap=True)
print(result.groups, result.width, result.height)

for poly in result.polygons:
    print(poly.outer)   # numpy int32 (N, 2)
    print(poly.inner)   # list[numpy int32 (N, 2)]
    print(poly.bounds)  # {min_x, min_y, max_x, max_y, is_empty}

Low-level API - Processing Modes

Mode 1: Single-threaded processing

Mode 1 The entire image is processed using a single core.

Profile: low speed; low memory efficiency.

Mode 1

Trace polygons from a PNG file (FastPngBitmap).

bitmap = contrek.FastPngBitmap("graphs_1024x1024.png")
result = contrek.find_polygons(
    bitmap,
    options={
      "versus": "clockwise",
      "bounds": True,
      "compress": {"linear": True}},
    target_color=contrek.rgb_to_target_color(255, 255, 255, 255),
    mode=contrek.MatchMode.EXACT_COLOR,
)

versus accepts "a" "o" for "anticlockwise"/"clockwise".

Mode 2: Parallel processing

Mode 1 The entire image is loaded first, then split into tiles and processed across multiple CPU cores. Partial results are progressively and dynamically merged: there is no predefined merge order, and adjacent pairs are processed as soon as they become available. This mode prioritizes performance, using parallelism both for tile processing and for merging partial results.

Profile: maximum speed, with processing time decreasing as more cores become available; low memory efficiency.

Mode 1

Use 4 threads and 4 tiles

bitmap = contrek.FastPngBitmap("sample_10240x10240.png")
result = contrek.find_polygons(
    bitmap,
    number_of_threads=4,
    options={
      "number_of_tiles": 4,
      "versus": "o",
      "bounds": True,
      "compress": {"uniq": True, "linear": True},
    },
    target_color=contrek.rgb_to_target_color(255, 255, 255, 255),
    mode=contrek.MatchMode.NOT_COLOR

Mode 3: Input streaming

Mode 1 The image does not need to be loaded entirely into memory. Instead, it can be read progressively using a fixed-size buffer. For example, with a PNG source, this can be done using libspng's progressive decoding. Adjacent tiles share an overlapping scanline to preserve geometry continuity across tile boundaries. Once all tiles have been added, the merge is performed (optionally using multiple threads) to reconstruct the complete geometry. This mode provides a trade-off between performance and memory usage: the entire raster does not need to be kept in RAM, while the vector state required to build the final result is retained.

Profile: medium speed; medium-high memory efficiency.

Mode 1

Trace polygons from two in-memory pattern strings (Bitmap, useful for synthetic tiles or tests). Up is 6 rows height, down is 5 rows. Total after merging: 10 rows, because one row is the shared scanline

  up =   (" 00000000000000               "
          " 00000000000000               "
          " 00          00               "
          " 00          00               "
          " 00          00               "
          " 00          00               ")

  down = (" 00          00               "
          " 00          00               "
          " 00          00               "
          " 00000000000000               "
          " 00000000000000               ")

  result_up = contrek.find_polygons_raw(
    contrek.Bitmap(up, 30),
    options={
      "versus": "a",
      "bounds": True,
    },
    target_color=ord("0"),
    mode=contrek.MatchMode.EXACT_COLOR
  )
  result_down = contrek.find_polygons_raw(
    contrek.Bitmap(down, 30),
    options={
      "versus": "a",
      "bounds": True,
    },
    target_color=ord("0"),
    mode=contrek.MatchMode.EXACT_COLOR
  )
  # results are obtained sequentially in the way you prefer
  # we collect geometry by add_tile()
  merger = contrek.VerticalMerger()
  merger.add_tile(result_up)
  merger.add_tile(result_down)

  # now finally calling process_info() you start merging and
  # get merged data
  result = merger.process_info()
  assert result["groups"] == 1
  assert result["width"] == 30
  assert result["height"] == 10

Mode 4: End-to-end streaming

Mode 1 This mode extends the incremental processing used in Mode 3 by streaming the output as well. The image is read and processed one tile at a time. Each new tile is immediately merged with the current state. As processing moves forward, whenever a geometry is complete and can no longer be affected by subsequent tiles, it is finalized and written directly to the SVG file. This limits both the amount of raster data kept in memory and the accumulation of generated vector geometries. It is particularly well suited to very large datasets or cases where the vector output itself can become significant in size.

Profile: low speed; maximum memory efficiency.

Mode 1

Trace polygons from 4 in-memory pattern strings.

  stripe1 =("00000000        "
            "00000000        "
            "00    00        "
            "00000000  000000"
            "00000000  000000"
            "          00  00")

  stripe2 =("          00  00"
            "          00  00"
            "0000000   00  00"
            "0000000   000000"
            "00   00   000000"
            "00   00         ")

  stripe3 =("00   00         "
            "00   00  0000000"
            "00   00  0000000"
            "00   00  00   00"
            "00   00  00   00"
            "00   00  0000000"
            "00   00  0000000"
            "00   00         ")

  stripe4 =("00   00         "
            "00   00         "
            "00   00         "
            "00   00         "
            "0000000         "
            "0000000         ")
  
  width = 16
  height = 23

  with tempfile.NamedTemporaryFile(suffix=".svg", delete=False) as shared_stream:
    temp_path = shared_stream.name

  try:
    merger = contrek.SvgStreamingMerger(
        options={"bounds": True},
        output_path=temp_path,
        width=width,
        height=height,
    )
    for i, stripe in enumerate(sample_stripes):
      bitmap = contrek.Bitmap(stripe, width)
      tile = contrek.find_polygons_raw(
        bitmap,
        options={
            "versus": "o",
            "bounds": True,
            "compress": {"uniq": True, "linear": True},
        },
        target_color=ord("0"),
        mode=contrek.MatchMode.EXACT_COLOR
      )
      is_last = (i == len(sample_stripes) - 1)
      merger.add_tile(tile, flush=is_last)
    # finally obtain accumulated geometry
    result = merger.process_info()

Building results from raw polygon data

make_result_from_polygons(polygons, width, height) builds a RawProcessResult straight from polygon coordinates you already have — no bitmap or tracing involved. Useful for testing mergers, or feeding in geometry computed elsewhere.

Tests

tests/ also doubles as usage examples — see the various test_*.py files for more ways to call the API.

License

  • Wrapper: MIT (LICENSE)
  • Core (submodule): AGPLv3 — details

Release files for contrek 0.1.4

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for contrek 0.1.4
File Size Uploaded
contrek-0.1.4.tar.gz 13.6 MB Details

Release files / contrek-0.1.4.tar.gz

Download URL contrek-0.1.4.tar.gz
Size 13.6 MB
Tags Source
SHA-256 checksum
How to use checksums
9a1dba3400fe987d23e36a1d84916d6a60796d3b02afc3147ffa7a0fd6209515
BLAKE2b-256 checksum
How to use checksums
3b8a33661ec232183cd250303e3b438c4aab40ecf8a27b1b2747b3148b4b25db
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.12

Release history Release notifications | RSS feed

0.1.12

1 release file

0.1.11

1 release file

0.1.10

1 release file

0.1.9

1 release file

0.1.8

1 release file

0.1.7

1 release file

0.1.6

1 release file

0.1.5

1 release file

This release

0.1.4 This release

1 release file

0.1.3

1 release file

0.1.2

1 release file

0.1.1

1 release file

0.1.0

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page