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 |
Image Viewer (ImageCanvas)
All image display in GRDK is built on a shared ImageCanvas component — a QGraphicsView-based interactive viewer 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:
ImageCanvas— full interactive viewer for main display areasImageCanvasThumbnail— 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
149+ 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 |
|---|---|
image_canvas.py |
ImageCanvas, ImageCanvasThumbnail, DisplaySettings, normalize_array |
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.0.tar.gz.
File metadata
- Download URL: grdk-0.1.0.tar.gz
- Upload date:
- Size: 120.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a6d7564ecec69787a131178d8eba8fb57590902efe62998d7c43b3d1016b39ab
|
|
| MD5 |
c05cbb66900279df6c0f2bef7dfc9d21
|
|
| BLAKE2b-256 |
fc74066f20feb02e873a82f606cf1e935d6a4c19da6b47cb0deeb925c0d6ba4c
|
Provenance
The following attestation bundles were made for grdk-0.1.0.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.0.tar.gz -
Subject digest:
a6d7564ecec69787a131178d8eba8fb57590902efe62998d7c43b3d1016b39ab - Sigstore transparency entry: 984512132
- Sigstore integration time:
-
Permalink:
GEOINT/grdk@3107a16d716151654f847e09196ed85002553354 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/GEOINT
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3107a16d716151654f847e09196ed85002553354 -
Trigger Event:
release
-
Statement type:
File details
Details for the file grdk-0.1.0-py3-none-any.whl.
File metadata
- Download URL: grdk-0.1.0-py3-none-any.whl
- Upload date:
- Size: 125.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2e5dc7e88c0d0572cc42d4aa855f00d187435ddc885b712c22bb9914163fd440
|
|
| MD5 |
43759a0f706880e823c659369151fb26
|
|
| BLAKE2b-256 |
50638bfc448483d0c3468da10f38fe0daa823723397e164343d9f239ff5aa2f2
|
Provenance
The following attestation bundles were made for grdk-0.1.0-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.0-py3-none-any.whl -
Subject digest:
2e5dc7e88c0d0572cc42d4aa855f00d187435ddc885b712c22bb9914163fd440 - Sigstore transparency entry: 984512137
- Sigstore integration time:
-
Permalink:
GEOINT/grdk@3107a16d716151654f847e09196ed85002553354 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/GEOINT
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3107a16d716151654f847e09196ed85002553354 -
Trigger Event:
release
-
Statement type: