GEOINT Rapid Development Kit — GUI tooling for CUDA-optimized image processing workflow orchestration
Project description
GRDK — GEOINT Rapid Development Kit
GUI toolkit for CUDA-optimized image processing workflow orchestration. Built as Orange Data Mining add-on plugins on top of the GRDL library.
What It Does
GRDK turns GRDL's image processing algorithms into a visual drag-and-drop workflow builder. Load multi-band satellite/aerial imagery, co-register image stacks, chip regions of interest, label training data, build processing pipelines with real-time GPU preview, and publish reproducible workflows — all without writing code.
Two Modes
| Mode | Purpose | Widgets |
|---|---|---|
| GEODEV | Interactive GEOINT development | Image Loader, Stack Viewer, Co-Register, Processor, Orchestrator, Preview, Chipper, Labeler, Project, Publisher |
| Admin | Catalog management | Catalog Browser, Artifact Editor, Workflow Manager, Update Monitor |
Quick Start
# Install in editable mode
pip install -e .
# With dev tools (pytest, black, mypy)
pip install -e ".[dev]"
Launching the Canvas
GRDK provides the grdk-canvas command, which launches the Orange Canvas with the PyQt6 backend pre-configured and GRDK widgets registered:
grdk-canvas
This is the primary way to use GRDK interactively. The canvas loads two widget categories:
- GEODEV — Image Loader, Stack Viewer, Co-Register, Processor, Orchestrator, Preview, Chipper, Labeler, Project, Publisher
- GRDK Admin — Catalog Browser, Artifact Editor, Workflow Manager, Update Monitor
Drag widgets onto the canvas, connect them with signal wires, and build image processing workflows visually.
Note:
grdk-canvassetsQT_API=pyqt6and configures the AnyQt backend before importing Orange. Always usegrdk-canvasinstead oforange-canvasto ensure the correct Qt backend.
Headless Execution
Run workflows without a GUI:
python -m grdk workflow.yaml --input image.tif --output result.tif
python -m grdk workflow.yaml --input image.tif --output result.tif --no-gpu
Architecture
grdl (processing primitives — no framework awareness)
↓
grdl-runtime (execution framework — no GUI)
├── grdl_rt.execution/ — workflow engine, GPU backend, discovery, DSL
└── grdl_rt.catalog/ — artifact storage, search, updates
↓
grdk (Qt/Orange GUI)
├── viewers/ — embeddable Qt widgets (ImageCanvas, napari viewer, chip gallery)
├── widgets/
│ ├── _signals.py — custom Orange signal types
│ ├── _param_controls.py — dynamic parameter UI builder
│ ├── _display_controls.py — image display settings UI builder
│ ├── geodev/ — 10 GEODEV workflow widgets (ow_*.py)
│ └── admin/ — 4 Admin catalog widgets (ow_*.py)
├── _launcher.py — grdk-canvas entry point
└── _pyqt6_bootstrap.py — PyQt6/AnyQt backend setup
tests/ — pytest suite
docs/ — architecture and API documentation
Layer Rules
- Execution and catalog logic lives in grdl-runtime (
grdl_rtpackage) — GRDK widgets import fromgrdl_rt.executionandgrdl_rt.catalog, not from local directories viewers/are standalone Qt widgets, embeddable in any Qt parent — no Orange dependencywidgets/are OrangeOWBaseWidgetsubclasses that composeviewers/andgrdl_rt
Key Dependencies
| Dependency | Purpose |
|---|---|
| grdl-runtime | Execution framework (workflow engine, GPU backend, catalog, discovery) |
| grdl | Image I/O, processing algorithms, coregistration |
| numpy, scipy | Array operations |
| PyQt6 | Qt6 widget toolkit |
| orange3, orange-widget-base | Orange Canvas widget framework |
| napari | Stack viewer with polygon drawing |
Standalone Viewer (grdk-viewer)
GRDK includes a standalone geospatial image viewer for SAR, EO/IR, and multispectral imagery. Launch it from the command line:
grdk-viewer # empty window
grdk-viewer /path/to/image.tif # open a file on startup
grdk-viewer /path/to/product_dir # open a product directory (SAFE, BIOMASS, etc.)
Supported Formats
The viewer opens any format supported by GRDL's reader library:
| Format | Reader | Notes |
|---|---|---|
| GeoTIFF | GeoTIFFReader |
Single- and multi-band |
| NITF / SICD | SICDReader |
SAR complex data with full SICD metadata |
| SIDD | SIDDReader |
SAR detected images |
| CPHD | CPHDReader |
Compensated phase history |
| CRSD | CRSDReader |
Compensated received signal |
| BIOMASS L1 | BIOMASSL1Reader |
ESA BIOMASS P-band SAR (HH, HV, VH, VV) |
| Sentinel-1 IW SLC | Sentinel1SLCReader |
Per-swath (IW1/IW2/IW3), per-polarization (VV/VH/HH/HV) |
| Sentinel-2 L1C/L2A | Sentinel2Reader |
Per-band spectral data and TCI |
| TerraSAR-X / TanDEM-X | TerraSARReader |
StripMap, SpotLight, ScanSAR |
| NISAR RSLC / GSLC | NISARReader |
L-band and S-band, per-frequency/polarization |
| JPEG2000 | JP2Reader |
Wavelet-compressed imagery |
Display Features
- Tiled rendering — streams image tiles on demand; handles imagery of any size without loading the full image into memory
- Dual-pane view — side-by-side comparison with synchronized pan/zoom; automatic split when opening multi-band or multi-polarization data
- SAR complex remap — automatic log-magnitude display for complex-valued SAR data
- Contrast / brightness / gamma — real-time adjustment via per-pane controls
- Dynamic range windowing — percentile-based or manual min/max clipping
- Colormaps — grayscale, viridis, inferno, plasma, hot
- Band / polarization selector — combo box shows named bands (e.g. HH, HV, B04) with automatic reader swap for multi-polarization SAR
- Coordinate bar — live latitude/longitude readout under the cursor (requires geolocation metadata)
- Color bar — data-range reference strip alongside each pane
- GeoJSON vector overlay — load and display vector features on top of imagery
Sentinel-1 IW SLC
When opening a Sentinel-1 SAFE product the viewer prompts for swath selection (IW1 / IW2 / IW3) and automatically enables burst boundary masking to zero out invalid border samples. Multi-polarization products (e.g. VV + VH) are offered as a dual-pane split.
Geolocation
The viewer creates geolocation models automatically from reader metadata:
| Sensor | Geolocation Method |
|---|---|
| SICD | SICD projection model |
| BIOMASS | Ground control point interpolation |
| Sentinel-1 SLC | Annotation grid interpolation |
| TerraSAR-X | Annotation grid interpolation |
| NISAR RSLC | 3-D geolocation grid interpolation (middle height layer) |
| NISAR GSLC | Affine transform (already geocoded) |
| Sentinel-2 | Affine transform from CRS metadata |
| GeoTIFF | Affine transform from CRS metadata |
Processing Tools
Available from the Tools menu:
| Tool | Description |
|---|---|
| Orthorectify | Projects imagery onto a geographic grid. In dual mode, both panes are orthorectified to a shared output grid for geo-synced comparison. Supports SICD, BIOMASS, Sentinel-1, TerraSAR-X, NISAR, and GeoTIFF sources. |
| Combine to RGB | Builds a false-color RGB composite from any combination of available bands/polarizations (e.g. HH→R, HV→G, VV→B). Includes derived channels like HH+VV and HH−VV. |
Export
| Action | Shortcut | Output |
|---|---|---|
| Export pane as image | Ctrl+E | Saves the current pane view as PNG, JPEG, or BMP (screen capture) |
| Export data | Ctrl+Shift+E | Saves processing results: ortho → GeoTIFF, RGB composite → TIFF / PNG / JPEG (full resolution) |
Programmatic API
The viewer can be embedded in any PyQt6 application:
from grdk.viewers.main_window import ViewerMainWindow
window = ViewerMainWindow()
window.open_file("/path/to/image.tif") # file or directory
window.open_reader(reader, geolocation=geo) # grdl reader + geolocation
window.set_array(arr, geolocation=geo, title="…") # numpy array
window.show()
Image Canvas (ImageCanvas)
The underlying display component used by the viewer and all GRDK widgets. A QGraphicsView-based interactive image display with:
- Pan (mouse drag), Zoom (scroll wheel, double-click to fit)
- Contrast/Brightness adjustment (does not modify source data)
- Dynamic range windowing (manual min/max or percentile-based)
- Gamma correction
- Colormaps (grayscale, viridis, inferno, plasma, hot)
- Band selection for multi-band imagery
- Pixel inspector (hover to read raw values)
Two variants:
TiledImageCanvas— tiled, streaming display for large imagery (used by the standalone viewer)ImageCanvas— full interactive viewer for in-memory arraysImageCanvasThumbnail— non-interactive fixed-size variant for grids and galleries
See docs/image-canvas.md for the API reference.
GRDL Integration
GRDK automatically discovers and wraps GRDL processors:
- Processor discovery: Scans
grdl.image_processingandgrdl.coregistrationfor concrete classes - Tag filtering: Processors with
__processor_tags__can be filtered by modality (SAR, PAN, MSI, ...) and category (spatial_filter, contrast_enhancement, ...) - GPU dispatch: Respects
__gpu_compatible__flags — skips futile GPU attempts for scipy-dependent processors - Progress callbacks: Forwards
progress_callbackthrough the pipeline with per-step rescaling - Exception handling: Distinguishes
GrdlErrorsubtypes from general Python errors in logging - Normalization: Optional GRDL
data_prep.Normalizerintegration in the Chipper widget
Testing
# Full suite
pytest tests/ -v
# Specific module
pytest tests/test_image_canvas.py -v
# With coverage
pytest tests/ --cov=grdk --cov-report=term-missing
174+ tests across 15 test modules. Widget and Qt tests auto-skip when no display is available.
Project Structure (All Files)
Click to expand full file listing
Execution & Catalog (from grdl-runtime)
These modules are imported by GRDK widgets from the grdl_rt package:
| Package | Key Types Used by Widgets |
|---|---|
grdl_rt.execution.discovery |
discover_processors(), get_processor_tags() |
grdl_rt.execution.gpu |
GpuBackend |
grdl_rt.execution.workflow |
WorkflowDefinition, ProcessingStep, WorkflowState |
grdl_rt.execution.dsl |
DslCompiler |
grdl_rt.execution.chip |
Chip, ChipSet, ChipLabel, PolygonRegion |
grdl_rt.execution.tags |
WorkflowTags, ProjectTags |
grdl_rt.execution.project |
GrdkProject |
grdl_rt.execution.executor |
WorkflowExecutor |
grdl_rt.catalog.database |
ArtifactCatalog |
grdl_rt.catalog.models |
Artifact, UpdateResult |
grdl_rt.catalog.resolver |
resolve_catalog_path() |
grdl_rt.catalog.updater |
ArtifactUpdateWorker |
grdl_rt.catalog.pool |
ThreadExecutorPool |
Viewers (grdk/viewers/)
| File | Purpose |
|---|---|
main_window.py |
ViewerMainWindow — standalone viewer with menus, toolbar, ortho, RGB, export |
dual_viewer.py |
DualGeoViewer — synchronized dual-pane viewer with crop-to-overlap |
geo_viewer.py |
GeoViewer — single-pane viewer with geolocation, file opening, band info |
tiled_canvas.py |
TiledImageCanvas — streaming tiled display for large imagery |
tile_cache.py |
TileCache — async tile loading with LRU eviction |
image_canvas.py |
ImageCanvas, ImageCanvasThumbnail, DisplaySettings, normalize_array |
band_info.py |
BandInfo, get_band_info — named band metadata extraction per reader type |
coordinate_bar.py |
CoordinateBar — live lat/lon readout widget |
vector_overlay.py |
GeoJSON vector overlay on image canvases |
stack_viewer.py |
NapariStackViewer — multi-layer image viewer with polygon drawing |
chip_gallery.py |
ChipGalleryWidget — scrollable thumbnail grid with click-to-label |
polygon_tools.py |
chip_stack_at_polygons — polygon-based chip extraction |
Widgets — GEODEV (grdk/widgets/geodev/)
| Widget | Name | Purpose |
|---|---|---|
ow_image_loader.py |
Image Loader | Load TIFF/NITF/HDF5 into image stack |
ow_stack_viewer.py |
Stack Viewer | Napari viewer with polygon drawing |
ow_coregister.py |
Co-Register | ORB/SIFT feature-match registration (affine/homography) |
ow_processor.py |
Processor | Single GRDL processor with tunable params |
ow_orchestrator.py |
Orchestrator | Multi-step pipeline builder |
ow_preview.py |
Preview | Real-time before/after GPU preview |
ow_chipper.py |
Chipper | Extract chips from polygon definitions |
ow_labeler.py |
Labeler | Click-to-label chip classification |
ow_project.py |
Project | Save/load GRDK project directories |
ow_publisher.py |
Publisher | Publish workflows to catalog |
Widgets — Admin (grdk/widgets/admin/)
| Widget | Name | Purpose |
|---|---|---|
ow_catalog_browser.py |
Catalog Browser | Search/discover artifacts |
ow_artifact_editor.py |
Artifact Editor | Edit catalog artifact metadata |
ow_workflow_manager.py |
Workflow Manager | Import/export workflow files |
ow_update_monitor.py |
Update Monitor | Check for package updates |
Shared Widget Infrastructure (grdk/widgets/)
| File | Purpose |
|---|---|
_signals.py |
ImageStack, ChipSetSignal, ProcessingPipelineSignal, WorkflowArtifactSignal, GrdkProjectSignal |
_param_controls.py |
build_param_controls() — auto-generates Qt controls from TunableParameterSpec |
_display_controls.py |
build_display_controls() — contrast/brightness/gamma/colormap/window UI |
Remote GUI Visualization
GRDK's GUI can be displayed on a remote machine (via SSH X11 forwarding) or from inside a Docker/Podman container. PyQt6 (Qt6) requires the X11/XCB platform plugin and associated libraries on the remote/container side, and a running X server on the host side.
Prerequisites (Remote / Container Side)
Install the X11/XCB runtime libraries required by Qt6:
# Debian / Ubuntu
apt-get update && apt-get install -y \
libxcb1 libxcb-cursor0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 \
libxcb-randr0 libxcb-render-util0 libxcb-shape0 libxcb-xfixes0 \
libxcb-xinerama0 libxcb-xkb1 libxkbcommon-x11-0 libxkbcommon0 \
libegl1 libgl1-mesa-glx libglib2.0-0 libfontconfig1 libdbus-1-3 \
x11-utils
# RHEL / Fedora / Rocky
dnf install -y \
libxcb xcb-util-cursor xcb-util-image xcb-util-keysyms \
xcb-util-renderutil xcb-util-wm libxkbcommon-x11 libxkbcommon \
mesa-libEGL mesa-libGL glib2 fontconfig dbus-libs xorg-x11-utils
Environment Variables
Set these before launching GRDK:
# Force the XCB platform plugin (required for X11 forwarding)
export QT_QPA_PLATFORM=xcb
# Disable OpenGL for pure software rendering over X11 (avoids GLX errors)
export QT_QUICK_BACKEND=software
export LIBGL_ALWAYS_SOFTWARE=1
# If you see "Could not connect to display", verify DISPLAY is set:
echo $DISPLAY # Should show e.g. :0 or localhost:10.0
Option A: Native Linux — SSH X11 Forwarding
From your local machine (the one with the display):
# Connect with X11 forwarding enabled
ssh -X user@remote-host
# Or for trusted forwarding (faster, less restrictive):
ssh -Y user@remote-host
On the remote host:
# Verify DISPLAY is set (ssh -X sets it automatically)
echo $DISPLAY
# Set Qt platform and launch
export QT_QPA_PLATFORM=xcb
export QT_QUICK_BACKEND=software
# Install and run
pip install -e .
python -c "from PyQt6.QtWidgets import QApplication; print('PyQt6 OK')" # Quick test
grdk-canvas # Launch GRDK GUI
Option B: Docker Container with X11 Forwarding
Host setup (run once per session on your local machine):
# Allow local Docker containers to access the X server
xhost +local:docker
Run the container with X11 socket and DISPLAY forwarded:
docker run -it \
-e DISPLAY=$DISPLAY \
-e QT_QPA_PLATFORM=xcb \
-e QT_QUICK_BACKEND=software \
-e LIBGL_ALWAYS_SOFTWARE=1 \
-v /tmp/.X11-unix:/tmp/.X11-unix:rw \
--network=host \
your-grdk-image:latest \
bash
Example Dockerfile:
FROM python:3.12-slim
# Install X11/XCB runtime dependencies for Qt6
RUN apt-get update && apt-get install -y --no-install-recommends \
libxcb1 libxcb-cursor0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 \
libxcb-randr0 libxcb-render-util0 libxcb-shape0 libxcb-xfixes0 \
libxcb-xinerama0 libxcb-xkb1 libxkbcommon-x11-0 libxkbcommon0 \
libegl1 libgl1-mesa-glx libglib2.0-0 libfontconfig1 libdbus-1-3 \
&& rm -rf /var/lib/apt/lists/*
# Set Qt environment for X11 forwarding
ENV QT_QPA_PLATFORM=xcb
ENV QT_QUICK_BACKEND=software
ENV LIBGL_ALWAYS_SOFTWARE=1
WORKDIR /app
COPY . .
RUN pip install --no-cache-dir -e .
CMD ["grdk-canvas"]
Build and run:
docker build -t grdk-gui .
xhost +local:docker
docker run -it \
-e DISPLAY=$DISPLAY \
-v /tmp/.X11-unix:/tmp/.X11-unix:rw \
--network=host \
grdk-gui
Option C: Podman Container
Podman works similarly but uses --userns=keep-id for rootless operation:
xhost +local:
podman run -it \
-e DISPLAY=$DISPLAY \
-e QT_QPA_PLATFORM=xcb \
-e QT_QUICK_BACKEND=software \
-e LIBGL_ALWAYS_SOFTWARE=1 \
-v /tmp/.X11-unix:/tmp/.X11-unix:rw \
--userns=keep-id \
--network=host \
grdk-gui
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
Could not connect to display |
DISPLAY not set or X server not reachable |
Verify echo $DISPLAY, use ssh -X, mount X11 socket |
qt.qpa.xcb: could not connect |
Missing xcb libraries or no X server | Install libxcb* packages (see Prerequisites) |
Could not load the Qt platform plugin "xcb" |
Missing Qt XCB platform dependencies | Install all libxcb* and libxkbcommon* packages |
GLX/OpenGL errors |
Hardware GL not available over X11 | Set QT_QUICK_BACKEND=software and LIBGL_ALWAYS_SOFTWARE=1 |
Authorization required |
xhost not configured | Run xhost +local:docker on host |
No protocol specified |
Container user mismatch | Use xhost +local: or pass --userns=keep-id (Podman) |
| Blank/frozen window | Compositor issue over forwarding | Try export QT_X11_NO_MITSHM=1 |
| Slow rendering | Network-bound X11 pixel transfer | Use SSH compression (ssh -XC) or consider VNC |
Security Note
xhost +local:docker permits any local process running under the docker user to access your X server. For production or multi-user environments, prefer X11 cookie-based authentication:
# More secure alternative: share the X authority cookie
docker run -it \
-e DISPLAY=$DISPLAY \
-e XAUTHORITY=/tmp/.Xauthority \
-v $XAUTHORITY:/tmp/.Xauthority:ro \
-v /tmp/.X11-unix:/tmp/.X11-unix:rw \
--network=host \
grdk-gui
Publishing to PyPI
Dependency Management
All dependencies are defined in pyproject.toml. Keep these files synchronized:
pyproject.toml— source of truth for versions and dependenciesrequirements.txt— regenerate withpip freeze > requirements.txtafter updatingpyproject.toml.github/workflows/publish.yml— automated PyPI publication (do not edit manually)
Releasing a New Version
- Update the
versionfield inpyproject.toml(semantic versioning:major.minor.patch) - Update
requirements.txtif dependencies changed:pip install -e ".[all,dev]" && pip freeze > requirements.txt - Commit both files
- Create a git tag:
git tag v0.2.0(matches version inpyproject.toml) - Push to GitHub:
git push && git push --tags - Create a GitHub Release from the tag — this triggers the publish workflow automatically
The workflow:
- Builds wheels and source distribution using
python -m build - Publishes to PyPI with OIDC authentication (secure, no API keys)
- Artifacts are available at pypi.org/p/grdk
See CLAUDE.md for detailed dependency management guidelines.
License
MIT License. Copyright (c) 2026 geoint.org. See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file grdk-0.1.1.tar.gz.
File metadata
- Download URL: grdk-0.1.1.tar.gz
- Upload date:
- Size: 152.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ca32d7c27360e14056939a0a8478cd25d7ece3c60925c7ecf8f4e27e92829216
|
|
| MD5 |
48ee3c4e280db88508542f6f95a1ac1e
|
|
| BLAKE2b-256 |
5ccee2d7e9208ffe72f7ae49e8a736f6902ec95c15919f4ced3bfa86043eb03b
|
Provenance
The following attestation bundles were made for grdk-0.1.1.tar.gz:
Publisher:
publish.yml on GEOINT/grdk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
grdk-0.1.1.tar.gz -
Subject digest:
ca32d7c27360e14056939a0a8478cd25d7ece3c60925c7ecf8f4e27e92829216 - Sigstore transparency entry: 1393458749
- Sigstore integration time:
-
Permalink:
GEOINT/grdk@cbd58d4b2c36eae64f01a3b81f09a438aa12ddcb -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/GEOINT
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@cbd58d4b2c36eae64f01a3b81f09a438aa12ddcb -
Trigger Event:
release
-
Statement type:
File details
Details for the file grdk-0.1.1-py3-none-any.whl.
File metadata
- Download URL: grdk-0.1.1-py3-none-any.whl
- Upload date:
- Size: 168.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
65ba4251859ee1c91228d49781744f5fd8eeedf0f9575af9acd0a098971fa94c
|
|
| MD5 |
87692320044b9e0c33b8da6f6e05ae9d
|
|
| BLAKE2b-256 |
56deb73b40def11811edf13616bf600aa0f286b6721d39fc43867a5275d4d755
|
Provenance
The following attestation bundles were made for grdk-0.1.1-py3-none-any.whl:
Publisher:
publish.yml on GEOINT/grdk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
grdk-0.1.1-py3-none-any.whl -
Subject digest:
65ba4251859ee1c91228d49781744f5fd8eeedf0f9575af9acd0a098971fa94c - Sigstore transparency entry: 1393458751
- Sigstore integration time:
-
Permalink:
GEOINT/grdk@cbd58d4b2c36eae64f01a3b81f09a438aa12ddcb -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/GEOINT
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@cbd58d4b2c36eae64f01a3b81f09a438aa12ddcb -
Trigger Event:
release
-
Statement type: