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 gets401 AUTH_REQUIRED.ChlorosLocalsurfaces the 403 asChlorosLicenseError; theconnect_*session helpers raiseChlorosConnectError(note it derives from plainException, soexcept ChlorosErrorwill not catch it). Re-runningloginwon'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:
- 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 basepip install chloros-sdk. - 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_AVAILABLEisFalseand the classes are unavailable. Transport libraries install via plain pip:pyserialfor DAQ-U,bleakfor DAQ-M,zeroconffor 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:
- Open Chloros Desktop GUI
- Login with your Chloros+ credentials (one-time)
- SDK automatically uses cached license
- 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-tunedprocess_folderwrapper (per-serial XMP calibration, standard debayer, mixed Survey3/LATTICE folders supported).read_image_audit_tags(image_path)— read theChloros:*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 returnedstatusisok,auto_shrunk(use the returnedrecommendedsettings),needs_force_slip, orerror.connect_camera/connect_array/attach_array/connect_daq_sensor— smart-connect sessions (see above), pluslist_cameras(),list_arrays(),list_daq_sensors(),discover_daq_sensors(), anddiscover_lattice_cameras().open_project(path)— open a saved Chloros project; the returnedChlorosProjectexposes.cameras/.arrays/.sensorsfrom the saved manifests, withconnect_all(),capture_all(), anddisconnect_all().
Availability Flags
Optional surfaces degrade gracefully — check these module constants instead of catching ImportError:
chloros_sdk.CAMERA_AVAILABLE— in-processlattice_sdkcamera control (requires the Arena SDK runtime).chloros_sdk.DAQ_AVAILABLE— in-process DAQ driver classes (DAQUSensor/DAQMSensor/DAQESensor/SensorFleet);Falseon pip-only installs — useconnect_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
- Email: info@mapir.camera
- Website: https://www.mapir.camera
- Documentation: https://mapir.gitbook.io/chloros
- Pricing: https://cloud.mapir.camera/pricing
📄 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_sensorwith backend-pooledCameraSession,ArraySession, andDAQSensorSessionhandles - 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
enhancedcolor 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)
| File | Size | Uploaded | |
|---|---|---|---|
| chloros_sdk-1.2.0.tar.gz | 16.4 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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
|