Skip to main content

Chloros Python SDK

Official Python SDK for MAPIR Chloros image processing software. Provides programmatic access to the Chloros API for automation, integration, and custom workflows.

🚀 Quick Start

from chloros_sdk import ChlorosLocal
from pathlib import Path

# Initialize SDK (auto-starts backend)
chloros = ChlorosLocal()

# Create project and import images
chloros.create_project("MyProject", camera="Survey3N_RGN")
chloros.import_images(str(Path.home() / "DroneImages" / "Flight001"))

# Configure settings
chloros.configure(
    vignette_correction=True,
    reflectance_calibration=True,
    indices=["NDVI", "NDRE", "GNDVI"]
)

# Process images
chloros.process(mode="parallel", wait=True)

One-Line Processing

from chloros_sdk import process_folder
from pathlib import Path

images_dir = Path.home() / "DroneImages" / "Flight001"
results = process_folder(str(images_dir), indices=["NDVI", "NDRE"])

For LATTICE captures (no calibration panel — per-serial factory calibration is read from each image's XMP), use the LATTICE-tuned wrapper:

from chloros_sdk import process_lattice_capture

results = process_lattice_capture("C:/Captures/2026-05-13_Field", indices=["NDVI"])

📋 Requirements

Requirement Details
Chloros Desktop Must be installed locally
License Chloros+ required (paid plan)
Operating System Windows 10/11 or Linux (64-bit)
Python Python 3.7 or higher
Memory 8GB RAM minimum (16GB recommended)

⚠️ License Requirement: The Chloros SDK requires an active Chloros+ subscription — Copper, Bronze, Silver, or Gold. The free Iron tier has no SDK/CLI access. Upgrade at https://cloud.mapir.camera/pricing

The floor is enforced by the backend, not just the client: an SDK call from an account without a paid plan fails with 403 PLAN_UPGRADE_REQUIRED, while a logged-out caller gets 401 AUTH_REQUIRED. ChlorosLocal surfaces the 403 as ChlorosLicenseError; the connect_* session helpers raise ChlorosConnectError (note it derives from plain Exception, so except ChlorosError will not catch it). Re-running login won't help in either case — the account is authenticated, just not entitled. Access keeps working offline for the plan's grace period (30 days on monthly plans, to expiry on yearly) and stops when that lapses until the machine reaches the server once.

📥 Installation

From PyPI (Recommended)

pip install chloros-sdk

Progress monitoring (the progress_callback on process()) works with the base install — no extras required.

From Source

git clone https://github.com/mapircamera/chloros-sdk.git
cd chloros-sdk
pip install -e .

Platform-Specific Notes

Linux:

  • Requires exiftool: sudo apt install libimage-exiftool-perl
  • Data stored in ~/.local/share/chloros (XDG compliant)
  • Config stored in ~/.config/chloros

Windows:

  • ExifTool bundled with Chloros Desktop
  • Data stored in %LOCALAPPDATA%\Chloros

🔌 Smart Connect

Drive live hardware straight from Python. The connect_* helpers are thin HTTP wrappers over the same local backend endpoints the Chloros GUI and CLI use, so connect/prep behavior is identical across all three surfaces. Each helper returns a session handle that works as a context manager, and each auto-starts the backend if it isn't already running.

Single LATTICE camera

import chloros_sdk

with chloros_sdk.connect_camera("213800234") as cam:
    cam.set_settings(exposure_time=10000)
    frames = cam.capture("output/", processing="all")

connect_camera(serial, *, preset=None, settings=None, backend_url=..., timeout=60.0, auto_start_backend=True) preserves the camera's current sticky GenICam state by default; pass preset="default" or settings={...} to seed specific values. The returned CameraSession exposes set_settings(**kwargs), read_nodes(names), capture(output_dir, processing=..., levels=..., force_daq=...), and disconnect().

Synchronized camera array

# serials[0] is the MASTER; the smart prep flow runs network analysis,
# picks a frame size that fits true simultaneous capture, and enables PTP.
serials = ["214701288", "213800234", "214000533"]

with chloros_sdk.connect_array(serials) as arr:
    result = arr.capture("output/", processing="all", aligned=True)
    print(len(result), "frames saved;", result.skipped)

ArraySession also provides status(), capture_fastest() (raw + .daq sidecar for later reprocessing), capture_repeated(), record() / burst() (returning a RecorderHandle with stats() / stop()), and disconnect(). Capture calls return a CaptureResult — a plain list of saved-frame dicts with an extra .skipped attribute explaining per-camera skips.

To grab a handle to an array that the GUI, CLI, or a previous script already connected (without reconnecting it):

arr = chloros_sdk.attach_array("array-1779862544497")   # or a list of member serials

DAQ spectral sensor

# Smart-detect: opens whichever sensor is visible (ETH > BLE > USB)
with chloros_sdk.connect_daq_sensor() as daq:
    for frame in daq.latest(n=10):
        print(frame["spectrum"][:5])

# Or pin a transport / address:
daq = chloros_sdk.connect_daq_sensor(eth_host="daq-e-def330.local")  # DAQ-E
daq = chloros_sdk.connect_daq_sensor(mac="AA:BB:CC:DD:EE:FF")        # DAQ-M (BLE)
daq = chloros_sdk.connect_daq_sensor(transport="usb", port="COM3")   # DAQ-U

# Don't know the address? Scan for it. This is the only way to get a
# DAQ-M's BLE MAC -- it isn't printed on the device or listed by the OS.
for s in chloros_sdk.discover_daq_sensors(transports=["ble"]):
    print(s["transport"], s["address"], s["model"])   # ble C3:D8:.. DAQ-M

The sensor stays open in the backend pool, so SDK scripts, the CLI, and the GUI share one live handle instead of fighting over the serial port. DAQSensorSession exposes status(), latest(n=...), stream_start() / stream_stop(), record_start() / record_stop() (calibrated .daq logging), and disconnect().

Backend auto-start

ChlorosLocal and every connect_* helper probe the backend URL first; if nothing is listening on a local URL, they locate the installed Chloros backend executable, start it window-less, and wait for it to come up (60 s default — backend_startup_timeout on ChlorosLocal). Set auto_start_backend=False to disable this, e.g. when pointing at a remote backend (remote URLs are never spawned). A backend started by a connect_* helper stays running for reuse; a backend started by ChlorosLocal is shut down when the instance is used as a context manager (or via shutdown_backend()).

🔬 DAQ Spectral Sensors

MAPIR's three spectral sensors (DAQ-U USB, DAQ-M BLE, DAQ-E Ethernet) are reachable two ways:

  1. Backend-pooled sessions — chloros_sdk.connect_daq_sensor() (shown above). This is the pip-friendly path: pure HTTP against the local backend, works with the base pip install chloros-sdk.
  2. In-process driver classes — DAQUSensor / DAQMSensor / DAQESensor / SensorFleet, which own the sensor transport directly with no backend required. These ship with the Chloros Desktop install (chloros-backend / chloros-cli), not with the pip package — on a pip-only install, chloros_sdk.DAQ_AVAILABLE is False and the classes are unavailable. Transport libraries install via plain pip: pyserial for DAQ-U, bleak for DAQ-M, zeroconf for DAQ-E discovery.

On a machine with Chloros Desktop installed, the in-process API looks like this:

# Desktop install only — on pip-only installs chloros_sdk.DAQ_AVAILABLE
# is False and this import fails
from chloros_sdk import DAQESensor

sensor = DAQESensor(host="daq-e-def330.local", transport="multicast",
                    integration_time=32, frame_avg_num=20)
sensor.connect()

def on_spectrum(spectrum, is_saturated, integration_time, x, y, z):
    print(f"{integration_time} ms  Y={y:.2f}  sat={is_saturated}")

sensor.add_spectrum_callback(on_spectrum)
sensor.start_streaming()
# ... later ...
sensor.stop()

DAQ-E and LATTICE cameras share a PTP time domain anchored by the Chloros host; pass wait_ptp=True so the sensor blocks until PTP lock before streaming when strict frame-to-spectrum alignment matters. See the DAQ sensor guide in the Chloros manual.

📖 Documentation

Complete documentation available at: https://mapir.gitbook.io/chloros/api-python-sdk (full API detail: https://mapir.gitbook.io/chloros/reference/sdk-reference)

🎯 Use Cases

Research & Academia

# Integrate Chloros into analysis pipelines
from chloros_sdk import ChlorosLocal

chloros = ChlorosLocal()

for survey in field_surveys:
    chloros.create_project(survey.name)
    chloros.import_images(survey.folder)
    chloros.configure(indices=["NDVI"])
    results = chloros.process()
    # Exported index TIFFs land in the project's output folder,
    # ready for rasterio / numpy / pandas analysis

Batch Processing

# Process multiple flights automatically
from chloros_sdk import ChlorosLocal

chloros = ChlorosLocal()

for flight in flight_database:
    chloros.create_project(flight.name)
    chloros.import_images(flight.folder)
    chloros.configure(indices=flight.requested_indices)
    chloros.process()

Custom Workflows

# Advanced progress monitoring
from pathlib import Path

def progress_callback(progress, message):
    print(f"[{progress}%] {message}")

chloros = ChlorosLocal()
chloros.create_project("CustomWorkflow")
chloros.import_images(str(Path.home() / "Data"))
chloros.configure(indices=["NDVI", "NDRE"])
chloros.process(progress_callback=progress_callback)

🔑 License Activation

The SDK uses the same license as Chloros Desktop:

  1. Open Chloros Desktop GUI
  2. Login with your Chloros+ credentials (one-time)
  3. SDK automatically uses cached license
  4. License persists across reboots (30-day offline support on monthly plans; yearly plans stay valid offline until subscription expiration)

🛠️ API Reference

ChlorosLocal Class

Main SDK class for local Chloros processing.

chloros = ChlorosLocal(
    backend_url="http://127.0.0.1:5000",  # Backend URL (canonical name;
                                          #   api_url is the legacy alias —
                                          #   backend_url wins if both given)
    auto_start_backend=True,              # Auto-start backend if not running
    backend_exe=None,                     # Auto-detect backend path
    timeout=30,                           # Per-request timeout (seconds)
    backend_startup_timeout=60,           # Wait for auto-started backend (seconds)
    processing_timeout=14400,             # Hard cap for a process() run (4 h)
    processing_stuck_timeout=1800,        # Abort if progress stalls (30 min;
                                          #   resets on any progress change)
)

Methods

create_project(project_name, camera=None)

Create a new Chloros project.

import_images(folder_path, recursive=False)

Import images from a folder.

configure(**settings)

Configure processing settings.

process(mode="parallel", wait=True, progress_callback=None, poll_interval=2.0)

Start processing images. progress_callback(progress, message) is polled every poll_interval seconds. On newer backends the returned dict includes a summary of what the run produced, with actionable hints surfaced as Python warnings.

get_config()

Get current project configuration.

get_status()

Get backend status.

Module-Level Functions

  • process_folder(folder_path, ...) — create project + import + configure + process in one call.
  • process_lattice_capture(folder_path, ...) — LATTICE-tuned process_folder wrapper (per-serial XMP calibration, standard debayer, mixed Survey3/LATTICE folders supported).
  • read_image_audit_tags(image_path) — read the Chloros:* audit metadata (calibration source, vignette source, processing level, ...) from a processed image.
  • analyze_array_network(master_serial, slave_serials=None, width=2048, height=1536, pixel_format="BayerRG8", binning=1, ...) — network capability check for a proposed LATTICE array. The returned status is ok, auto_shrunk (use the returned recommended settings), needs_force_slip, or error.
  • connect_camera / connect_array / attach_array / connect_daq_sensor — smart-connect sessions (see above), plus list_cameras(), list_arrays(), list_daq_sensors(), discover_daq_sensors(), and discover_lattice_cameras().
  • open_project(path) — open a saved Chloros project; the returned ChlorosProject exposes .cameras / .arrays / .sensors from the saved manifests, with connect_all(), capture_all(), and disconnect_all().

Availability Flags

Optional surfaces degrade gracefully — check these module constants instead of catching ImportError:

  • chloros_sdk.CAMERA_AVAILABLE — in-process lattice_sdk camera control (requires the Arena SDK runtime).
  • chloros_sdk.DAQ_AVAILABLE — in-process DAQ driver classes (DAQUSensor / DAQMSensor / DAQESensor / SensorFleet); False on pip-only installs — use connect_daq_sensor() instead.
  • chloros_sdk.PROJECT_AVAILABLE — the saved-project surface (open_project / ChlorosProject).

🔐 Security

  • Proprietary Software: Licensed under MAPIR proprietary license
  • Local Processing: All processing happens locally (localhost API)
  • License Enforcement: Requires active Chloros+ subscription
  • No Data Transmission: Images never leave your computer

💡 Examples

See complete examples in the SDK reference.

🐛 Support

📄 License

Copyright (c) 2026 MAPIR Inc. All rights reserved.

This is proprietary software requiring an active Chloros+ subscription. Unauthorized use, distribution, or modification is prohibited.

🔄 Version History

v1.1.0 – v1.1.5 (2026)

  • Smart-connect sessions: connect_camera / connect_array / attach_array / connect_daq_sensor with backend-pooled CameraSession, ArraySession, and DAQSensorSession handles
  • LATTICE capture processing: process_lattice_capture, read_image_audit_tags
  • Array network analysis: analyze_array_network
  • process() post-run processing summary with actionable hints
  • New enhanced color profile for RGB captures

v1.0.4 (2025)

  • Added Linux support
  • Cross-platform path handling
  • XDG-compliant directories on Linux

v1.0.0 (2025)

  • Initial release
  • Full API coverage for local processing
  • Auto-backend startup
  • Progress monitoring support
  • Context manager support

Metadata

Release files for chloros-sdk 1.2.0

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

Source distribution (sdist)

Source distribution for chloros-sdk 1.2.0
File Size Uploaded
chloros_sdk-1.2.0.tar.gz 16.4 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for chloros-sdk 1.2.0
File Interpreter ABI Platform
chloros_sdk-1.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 17.1 MB

Release files / chloros_sdk-1.2.0.tar.gz

Download URL chloros_sdk-1.2.0.tar.gz
Size 16.4 MB
Tags Source
SHA-256 checksum
How to use checksums
59445ffed2b2d5a540c27278bf98f633fa8e363234d4d97f301185fe9ceed792
BLAKE2b-256 checksum
How to use checksums
0c92d8f57b8a0f47cfd99fb922c6c54f3acc58cb2ce443034f4384e5847ba14e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.10.11

Release files / chloros_sdk-1.2.0-py3-none-any.whl

Download URL chloros_sdk-1.2.0-py3-none-any.whl
Size 672.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d9c6e1b04e14e563f68c033a66751188cf3e69c372b6d8f608a04dbb08c4e995
BLAKE2b-256 checksum
How to use checksums
51a4bd06a051b9182655dfbc9d5e617d86ecdf0aa48e191b5a0d21558fb2aacf
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.10.11

Release history Release notifications | RSS feed

This release

1.2.0 This release

2 release files

1.1.3

2 release files

1.1.2

2 release files

1.1.1

1 release file

1.1.0

2 release files

1.0.5

2 release files

1.0.4

2 release files

1.0.3

2 release files

1.0.0

2 release files

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