Documentation | Tutorials | Running AI Models in Developer Kernel (Experimental)
This tutorial explains how to run Python-driven AI models on TatukGIS Viewer snapshots and raster layers.
Status (read this first):
TatukGIS AI runs inference through Python scripts.
What TatukGIS AI Model Runner Is:
What TatukGIS AI Model Runner Is Not:
Supported inputs
A Viewer snapshot can contain content from multiple layers, but it is less precise than a single layer input. Exporting an image with pixel-perfect precision is difficult when combining multiple layers with different dimensions.
An exported TGIS_LayerPixel maintains single-pixel precision. An imported layer with AI model postprocessing will align perfectly within the project.
Supported outputs (as implemented by GisAIModelOutput)
Install a Python 3.x runtime and make sure it is usable on the machine where the application runs. The sample and all predefined models were tested on Python 3.9 and that is the version I would recommend for running the sample.
You do not need to install required Python modules manually via pip. The TatukGIS AI feature allows you to specify the required modules for a given script. The system will automatically install these dependencies before executing the Python environment.
Install Python4Delphi in RAD Studio via GetIt or manual installation.
You must end up with:
Verification step: Upon installing Python4Delphi, you will find several included demos. Run one of these demos to verify that Python4Delphi correctly locates and uses the Python distribution on your machine. If Python initialization fails in the demo, the TatukGIS AI feature will not run. Resolve environmental issues before proceeding.
TatukGIS AI integration requires conditional compilation. After installing Python4Delphi extension, enable the Python4Delphi define in your project settings:
Use the following define:
GIS_PYTHON4DELPHIWithout this define, Python-backed AI classes will not work.
If you are using DK.Delphi Binary Version (installed through installer, not SourceCodeManager), having sources for GisAIPython.pas class is essential to run the SDK with the GIS_PYTHON4DELPHI define.
In latest version (DK 112) we have added the necessary source file to installation - it is located in $(DK_SRC)\Source. This path has been added to Rad Studio configuration (Library Paths) as well.
As a result, you may enable the define both in project settings, but also globally through adding:
{$DEFINE GIS_PYTHON4DELPHI}
to $(DK_SRC)\Source\GisAIPython.pas file.
If you are using DK.Delphi SourceCodeManager version, DK sources are recompiled when building the project. As so, adding the define to project options is enough.
But of course, you may also enable the define globally through adding:
{$DEFINE GIS_PYTHON4DELPHI}
to $(DK_SRC)\Source\GisAIPython.pas file.
Start with the AIModelRunner sample to validate the full pipeline.
The sample uses following command to download SampleData (models, config files, example images) onto your PC:
TGIS_Utils.GisSamplesDataDirDownload('AI.1')
It's around 200mb of data and it may take a moment to download the data.
I would recommend Python 3.9 to use with the sample. You may define which Python DLL should be used with following command:
PythonWorker.Python.SetPythonDllPath()
The sample includes a pre-configured setup for Real-ESRGAN, an AI model used for image resolution upscaling. This requires no additional configuration.
Validation steps:
If this step fails, investigate your Python/Python4Delphi environment setup. Do not attempt custom models yet.
The sample may also be used with an MMRotate model, an AI model used for oriented object detection. Unlike Real-ESRGAN, this model requires additional config files. They are included in SampleData downloaded through TGIS_Utils.GisSamplesDataDirDownload('AI.1').
MMRotate wrapper will create a vector layer for each object category it detected and will add it to Viewer.
Validation steps:
If this step fails, first investigate your Python/Python4Delphi environment setup. Make sure that all required model files, config files, and Python package dependencies are present and compatible before troubleshooting the TatukGIS integration itself.
Once predefined models work, you can use the AIModelRunner sample interface to load a custom model. You will need to provide the model file, the Python script, and specify the necessary dependencies. The steps for structuring custom models are detailed below.
Custom models are driven by TGIS_AIModelCustom (unit GisAIModelCustom). You must provide:
TGIS_AIModelOutput.
While the system defaults to looking for .onnx or .pt (PyTorch) extensions, the model file can be in any format. TatukGIS does not strictly enforce the extension; the provided Python script is solely responsible for loading and executing the model correctly.
This tutorial demonstrates the structural requirements for adapting an external model to the TatukGIS JSON contract. Ultralytics YOLO OBB (Oriented Bounding Box) is used as the conceptual example.
Third-party licensing notice (Ultralytics example): Ultralytics YOLO, theultralyticsPython package, model weights, and related documentation are third-party materials and are not part of any TatukGIS product, license, source code, or distribution. This section is provided solely to demonstrate how a third-party model may be adapted to interoperate with TatukGIS software through a Python script. TatukGIS does not grant, transfer, sublicense, or imply any rights to use Ultralytics software, models, trademarks, or documentation.
Use of Ultralytics software and models is governed exclusively by Ultralytics' own license terms, which may change over time. Personal, research, or open-source use may be available under Ultralytics' published terms. Proprietary or closed-source commercial use may require obtaining an appropriate license directly from Ultralytics. Before any internal, customer-facing, or commercial deployment, review the current terms on the official Ultralytics pages:
* Ultralytics licensing page
* Ultralytics plans / commercial licensing page
This documentation is technical guidance only and is not legal advice. You are responsible for verifying third-party licensing obligations before use or distribution.
Step 1: Define Required Modules
Configure TatukGIS to install the necessary packages required by the vendor's model. For a YOLO model, you would specify:
['ultralytics', 'numpy']
Step 2: Provide the Model File
Acquire the model file. For Ultralytics, download a pre-trained OBB model (e.g., yolo11n-obb.pt) from the Ultralytics GitHub repository or train your own custom weights. Pass this absolute path to the TatukGIS wrapper.
Step 3: Build the Python Script Wrapper
Due to licensing compliance, you must write the model execution code yourself using the vendor's documentation. For the specific inference code required to load the model and extract bounding boxes, refer to the Ultralytics OBB documentation.
The script below provides the required wrapper. It shows how to capture the input paths from TatukGIS and how to structure the final JSON output.
TatukGIS replaces the placeholders («MODEL_PATH» and «IMAGE_PATH») with single-quoted absolute paths at runtime.
import json # Import chosen third-party AI libraries here (e.g., ultralytics, numpy) # TatukGIS runtime parameters injected automatically MODEL_PATH = r<<MODEL_PATH>> IMAGE_PATH = r<<IMAGE_PATH>> def run_inference(): # ----------------------------------------------------------------- # 1. LOAD MODEL AND RUN INFERENCE # ----------------------------------------------------------------- # Example conceptual flow: # model = load_vendor_model(MODEL_PATH) # results = model(IMAGE_PATH) # ----------------------------------------------------------------- # 2. POSTPROCESS INTO TATUKGIS JSON CONTRACT # ----------------------------------------------------------------- detections = [] # Example mapping logic for Oriented Bounding Box # for result in results: # for obb in result.extracted_bounding_boxes: # detections.append({ # "LabelText": str(obb.class_name), # "ClassId": int(obb.class_id), # "Score": float(obb.confidence), # "GeomKind": "Polygon", # "Polygon": obb.points.tolist() # Format: [[x,y], [x,y], ...] # }) # Example fallback dimensions img_h, img_w = 0, 0 manifest = { "Version": 1, "Items": [ { "SourceType": "Detection", "CoordSpace": "Image", "ImageWidth": int(img_w), "ImageHeight": int(img_h), "Detections": detections } ] } # Output must be printed to stdout for TatukGIS to parse print(json.dumps(manifest, ensure_ascii=False)) if __name__ == "__main__": run_inference()
The TGIS_AIModelOutput.ParseJson() method expects a root JSON object containing an array of items.
| Property | Type | Requirement | Description |
|---|---|---|---|
| Version | Integer | Optional | Contract version (defaults to 1). |
| Items | Array | Required | Array of output item objects. |
1. SourceType: “Layer” (Loads a file-backed vector or raster layer)
| Property | Type | Requirement | Description |
|---|---|---|---|
| Path | String | Required | Absolute path to the file on disk. |
| Title | String | Optional | Layer name in the Viewer. |
2. SourceType: “Detection” (Generates vector polygon layers from bounding boxes/polygons)
| Property | Type | Requirement | Description |
|---|---|---|---|
| Detections | Array | Required | Array of Detection Objects (see below). |
| CoordSpace | String | Optional | “Image” (pixels, top-left origin) or “Map” (map units). |
| ImageWidth | Integer | Optional | Width of the source image. |
| ImageHeight | Integer | Optional | Height of the source image. |
Detection Object Structure:
| Property | Type | Requirement | Description |
|---|---|---|---|
| GeomKind | String | Required | “BBox” or “Polygon”. |
| LabelText | String | Optional | Classification label. |
| ClassId | Integer | Optional | Classification ID. |
| Score | Float | Optional | Confidence score. |
| BBoxXMin, BBoxYMin, BBoxXMax, BBoxYMax | Float | Conditional | Required if GeomKind is “BBox”. |
| Polygon | Array | Conditional | Required if GeomKind is “Polygon”. Format: [[x,y], [x,y], ...]. |
3. SourceType: “Logs” (Outputs processing logs)
| Property | Type | Requirement | Description |
|---|---|---|---|
| Messages | Array | Required | Array of string messages. |
4. SourceType: “Raw” (Fallback container for unparsed standard output)
| Property | Type | Requirement | Description |
|---|---|---|---|
| StdOut | String | Optional | Captured standard output. |
| StdErr | String | Optional | Captured standard error. |
{
"Version": 1,
"Items": [
{
"SourceType": "Layer",
"Path": "C:\\temp\\output_raster.tif",
"Title": "Processed Raster"
},
{
"SourceType": "Detection",
"CoordSpace": "Image",
"ImageWidth": 1024,
"ImageHeight": 1024,
"Detections": [
{
"GeomKind": "BBox",
"LabelText": "Vehicle",
"ClassId": 2,
"Score": 0.95,
"BBoxXMin": 150.0,
"BBoxYMin": 200.0,
"BBoxXMax": 350.0,
"BBoxYMax": 400.0
},
{
"GeomKind": "Polygon",
"LabelText": "Structure",
"ClassId": 5,
"Score": 0.88,
"Polygon": [[10, 10], [50, 10], [50, 50], [10, 50]]
}
]
}
]
}
To execute custom models in a background process or non-GUI application, bypass the UI components and interact directly with the core AI classes.
| Step | Action | Relevant Method / Class |
|---|---|---|
| 1 | Initialize Python runtime | TGIS_AIPythonManager.Create and .Initialize |
| 2 | Instantiate the model | TGIS_AIModelCustom.Create() |
| 3 | Export map state to image | TGIS_ImageExporter.ExportToImage() |
| 4 | Verify dependencies | Model.InstallModules() |
| 5 | Run inference | Model.Run() |
| 6 | Retrieve results | GetDetections(), GetDetectionLayers(), and GetLayers() |
procedure RunModelHeadless(const LayerPix: TGIS_LayerPixel; const Viewer: TGIS_ViewerWnd); var PythonManager: TGIS_AIPythonManager; Model: TGIS_AIModelCustom; RequiredModules: TArray<string>; ScriptText: string; ImagePath: string; AlignedExtent: TGIS_Extent; Dx, Dy: Double; RawDetections: TArray<TGIS_AIDetection>; Detection: TGIS_AIDetection; DetectionLayers: TObjectList<TGIS_LayerVector>; DetectionLayer: TGIS_LayerVector; LayerItem: TGIS_LayerAbstract; begin // 1. Initialize Python runtime PythonManager := TGIS_AIPythonManager.Create; try if not PythonManager.Initialize then raise Exception.Create('Python initialization failed.'); // 2. Instantiate the model SetLength(RequiredModules, 1); RequiredModules[0] := 'numpy'; ScriptText := TFile.ReadAllText('C:\Path\To\script.py'); Model := TGIS_AIModelCustom.Create( 'HeadlessModel', PythonManager, 'C:\Path\To\model.pt', ScriptText, RequiredModules ); try // 3. Export map state to image ImagePath := TGIS_ImageExporter.ExportToImage(LayerPix, Viewer.VisibleExtent, AlignedExtent); try // 4. Verify dependencies Model.InstallModules; // 5. Run inference if Model.Run(ImagePath) then begin // GetPixelSize determines how a single pixel from the output image translates // to distance in the map extent. This is required to properly map outputs. TGIS_ImageExporter.GetPixelSize(LayerPix, Dx, Dy); // 6a. Retrieve raw detections for manual processing RawDetections := Model.LastResult.GetDetections; for Detection in RawDetections do begin // Map the detection to a given layer or process geometry manually // e.g., read Detection.GeomKind, Detection.Polygon, or Detection.LabelText end; // 6b. Retrieve pre-built detection layers DetectionLayers := Model.LastResult.GetDetectionLayers(AlignedExtent, Dx, Dy); if Assigned(DetectionLayers) then begin for DetectionLayer in DetectionLayers do Viewer.Add(DetectionLayer); end; // 6c. Retrieve and render standard layers for LayerItem in Model.LastResult.GetLayers() do begin if LayerItem is TGIS_LayerVector then (LayerItem as TGIS_LayerVector).Extent := AlignedExtent else if LayerItem is TGIS_LayerPixel then (LayerItem as TGIS_LayerPixel).Extent := AlignedExtent; Viewer.Add(LayerItem); end; end; finally if FileExists(ImagePath) then TFile.Delete(ImagePath); end; finally Model.Free; end; finally PythonManager.Free; end; end;