The program cuts an image into objects, learns your classes from a dozen or so examples, and at the end tells you honestly how good the map is.
Part 1 of 4
Download the version that suits you and check that your images and computer are up to it.
Each computes exactly the same thing. Choose the one that fits your work.
You pick examples by clicking on the map. Accuracy assessment is available only here: point sampling, error matrix and areas with confidence intervals. English interface.
A window with two tabs: training and classification of a whole folder. The installer does not need administrator rights. Polish interface.
Train a model once and run it on a folder. It fits into a script and resumes after an interruption. Polish commands and messages. How to classify a whole folder.
Installing the plugin: the plugin is in the official QGIS plugin repository. In QGIS open Plugins, Manage and Install Plugins, search for "GeoAI Classification" and click Install Plugin. QGIS will tell you about new versions. You can also download the ZIP file and choose Install from ZIP. The same plugin with a Polish interface is GeoAI Klasyfikator; both give identical results.
A browser warning for the program file means the publisher's signature is missing, not that a virus was found. The files are not signed yet; the project is preparing an application to the SignPath Foundation programme (Free code signing provided by SignPath.io, certificate by SignPath Foundation); the rules are in the code signing policy. SHA-256 checksums of all files are in the latest release.
| Item | Required? | What for |
|---|---|---|
| RGB image | yes | A drone orthomosaic, an aerial photo or a satellite scene. Three bands are enough. |
| Examples | yes | A dozen or so places per class that you recognise for sure. |
| Elevation model (DSM) | no, but… | Without it 19 features, with it 24. It tells a roof from asphalt and a tree from a lawn. If you have one, use it. |
Dozens or hundreds of flights are classified with one command, without stitching them into a mosaic.
You do not have to merge images into one mosaic. You train the model once, on one representative image. Then you point to a folder, and the program takes the images one by one, classifies each separately and saves a separate map. At the end you get a summary report with the area of each class in each image.
Batch classification of folders is done by the command-line
program and the Windows application. Both have a Polish interface,
so the commands below keep their Polish names: ucz
means train and klasyfikuj means classify.
ucz command; in the Windows
application, the training tab. The model is saved to a
.geoai file.klasyfikuj --folder; in the
Windows application, the tab for classifying a whole folder. The
program can also search subfolders..qml style file,
and in the output folder a raport.csv file
(semicolon separator, Excel-friendly encoding) with the area of
each class in each image.python geoai.py ucz --zdjecie flights/flight_01.tif --probki examples.gpkg ^
--nmpt dsm/flight_01_nmpt.tif --model my_model.geoai
python geoai.py klasyfikuj --folder flights --model my_model.geoai ^
--wyjscie maps --nmpt-folder dsm
| Option | What it does |
|---|---|
--zdjecie, --probki, --nmpt, --wyjscie | image, training samples (polygons with a klasa column), DSM, output folder |
--rekurencyjnie | also searches subfolders for images |
--nmpt-folder | folder with elevation models; for the image flight_01.tif the program picks the file flight_01_nmpt.tif (change the suffix with --nmpt-przyrostek) |
--mmu 10 | removes patches smaller than 10 m² |
--nadpisuj | also reprocesses images that already have a result |
--przerwij-na-bledzie | stops at the first problem; without this option the program reports the error for that image and carries on |
Without the --nadpisuj option the program skips
images that already have a result, so you resume an interrupted
run with the same command.
A model works for images similar to those it learned from: the same camera, a similar season and lighting. A June flight and a September flight need two models.
The QGIS plugin works on one image at a time; all three versions compute with the same engine.
What you need, what slows the computation down and how to speed it up without buying a new computer.
| Version | What it needs |
|---|---|
| QGIS plugin | QGIS 3.22 or newer and nothing else |
| Windows application | 64-bit Windows; the installer (about 96 MB) contains all libraries, including the ones that speed things up |
| Command line | Python 3.9 or newer and the
numpy, rasterio and fiona
libraries; scikit-image, scikit-learn
and scipy are also recommended |
| Graphics card | not needed: the program does not use neural networks |
Memory is the main limit. The computation takes about 100 bytes per pixel of the working grid, so the largest allowed grid, 15 million pixels, needs about 1.5 GB of memory for the computation alone, on top of what the system and QGIS use. The program refuses a larger grid and suggests a coarser working resolution.
| Factor | Why |
|---|---|
| Number of pixels in the working grid | every step goes through all pixels; a resolution twice as coarse means four times fewer pixels |
| Number of objects | texture, shape and context are measured separately for each object; smaller objects mean more objects |
| Acceleration libraries | with scikit-image and scikit-learn
the computation runs several times faster; without
scikit-image the practical limit is about half a
million pixels |
| Elevation model (DSM) | adds 5 features and estimation of the ground surface with a morphological filter |
| Extras | the class separability check, greenness refinement and removal of small patches are separate steps |
| Processor | Random Forest from scikit-learn spreads the
trees over all processor cores |
For fields, plots and roofs a 5 to 10 cm grid is enough. A drone image with 2 cm pixels processed on an 8 cm grid has 16 times fewer pixels. You need the finest grid, 3 to 5 cm, only for single trees and shrubs.
In the plugin, tick processing only what is visible on screen. You can fine-tune the settings and examples in moments and process the whole image at the end.
The Windows application includes them. In the plugin, click
Install acceleration libraries in step 1 and
restart QGIS. For the command line, install
scikit-image, scikit-learn and
scipy. The computation
gets faster and the results match the reference implementation;
for research work the full setup is recommended anyway.
An object should be smaller than the narrowest thing you want to show, but no smaller than that. When mapping fields, a 20 m² object instead of 5 m² means four times fewer objects to measure.
Run many images as a whole-folder classification, for example overnight. An interrupted run resumes with the same command, and finished images are skipped.
When a single image exceeds the limit even at a sensible resolution, split it into tiles (for example with the GDAL Retile tool in QGIS) and classify the folder of tiles with one model.
Training takes seconds, but preparing examples takes time. In
the command-line program and the Windows application the model is
saved to a .geoai file, which you apply to further
similar images without training again.
Close other programs, especially a browser with many tabs. Close to the memory limit, this can be the difference between a result and an out-of-memory message.
It works without a drone too: 10 metres, the whole Earth, free of charge.
Sentinel-2 are satellites of the European Space Agency (Drusch et al., 2012). They image the same place every few days, and the archive goes back to 2015. A pixel is 10 metres, so one pixel covers 100 m² of ground. That is too coarse for a single tree but just right for fields, forests, water and built-up areas.
The most common trap. The program reads the first three bands of the file as red, green and blue. In a raw Sentinel-2 scene band one is aerosol, band two is blue and band three is green; red is not among them at all. The program will not report an error; it will compute something that looks like a map but is wrong. So always provide a ready RGB composite.
The easiest way is the Sentinel Download plugin by the same author (Polish version: Pobieranie Sentinel). It downloads Sentinel-2 (L2A and L1C) and Sentinel-1 (GRD) images from the Copernicus Data Space and assembles ready products, including an RGB composite with the bands in the right order.
Sentinel Download and click Install.| Aspect | Drone | Sentinel-2 |
|---|---|---|
| Pixel | 2 to 10 cm | 10 m |
| What can be recognised | single trees, field margins, parts of a roof | large forms: fields, forest, built-up areas, water |
| Elevation model | usually available (DSM from the flight) | usually not, so 19 features instead of 24 |
| Object size | square metres | a few dozen pixels are already thousands of square metres |
A scene with 40 percent cloud cover can be useless even if your area happens to be clear, because cloud shadows remain on the ground.
A model trained on drone images is not suitable for Sentinel-2 and vice versa. A model works for data similar to what it learned from, so train it from scratch for satellite scenes.
Part 2 of 4
Why the program works on objects and how it learns classes from your examples.
The whole method rests on this, so it is worth understanding once.
Classical classification looks at each pixel separately. A pixel knows only its colour, and that is not enough to tell a grey roof from grey asphalt. The result is a map sprinkled with single dots.
Here the image is first cut into objects: groups of neighbouring pixels with similar colour. Such an object is a piece of a field, part of a roof, a tree crown. And it knows much more about itself: its colour, its texture, its shape, how high it is and what surrounds it.
Nineteen numbers from the image alone, twenty-four if you add an elevation model. These numbers, not the picture, go into the classifier.
Mean and variability of red, green and blue, and brightness. A ragged crown varies differently from a smooth roof.
ExG (Woebbecke et al., 1995), VARI (Gitelson et al., 2002), NGRDI (Hunt et al., 2005). Computed on colour proportions, so a leaf in shade and in sun look alike. They replace NDVI, which drone cameras do not provide.
How much neighbouring pixels differ from each other. This tells a tree crown from metal roofing of the same colour.
Area, perimeter, circularity and convexity. A long, narrow plot is something different from a compact square.
The brightness of an object compared with those that touch it. A ditch or a gap in a stand comes out darker than everything around it.
From the DSM: height, its variability, slope and, most importantly, height above ground. Roof versus asphalt, tree versus lawn.
Why there is no NDVI. Ordinary drone cameras record only red, green and blue and do not see near infrared at all. That is why we work with visible-band indices, texture, shape and height. In practice they are enough.
The whole algorithm on one small picture. The animation really computes; it is not a recording.
The picture is 96 by 64 pixels with five kinds of land. The animation uses the same steps as the program, simplified: it computes 10 of the 19 features (colour and greenness), and the "operator" who picks examples is simulated by drawing objects of known class. The numbers in the panel come from this picture, not from real images.
.geoai file and can be
applied to further similar images.The formulas, all constants and the literature are described in
UWARUNKOWANIA_NAUKOWE.md
(in Polish), chapters 3, 5 and 6.
Part 3 of 4
From segmentation through training to the accuracy assessment of the map.
One setting decides everything.
Object size is the most important decision of the whole job. There is one simple rule:
An object must be smaller than the narrowest thing you want to show on the map.
An object that is too large merges two kinds of land into one, and no classifier can fix that. One that is too small means more clicking for no gain and longer processing.
| What you map | Working resolution (m) | Object size |
|---|---|---|
| Fields, plots, roofs | 0.05–0.10 | 5–20 m² |
| Single trees, shrubs | 0.03–0.05 | 2–5 m² |
| Canopy gaps, crowns | 0.10–0.25 | 10–30 m² |
| Large flights, hundreds of hectares | 0.20–0.25 | 20–50 m² |
Before you process everything: in the plugin, tick "Process only what is visible on screen", zoom to a typical part of the image and choose the object size there. It will save you a lot of waiting.
After segmentation, look at the result and check two things: that no object lies on two different kinds of land at once (that is a problem: make the objects smaller), and that a uniform meadow has not broken into hundreds of specks (that is only an inconvenience you can live with).
This, not the settings, decides the quality of the map.
You add classes and show the program what is what. In the plugin
you click objects on the map; in the other versions you draw polygons
in QGIS and save them to a file with a klasa (class)
column.
Not in one corner. Light, shadow and soil moisture change from place to place, and the model learns what you show it.
"Bare soil" is freshly ploughed land, a trodden path and a dry embankment. If you show only one, the program will take the rest for something else.
One wrong example teaches a wrong rule. Leave out an object on the border of two classes; the program will reject it anyway and tell you so.
How many examples? A dozen or so per class to start. You will add more anyway, and that is the normal course of work, not a sign that something went wrong.
How many classes? Four to six to begin with. Each new class is another pair to tell apart, and pairs grow faster than classes: with six classes there are already fifteen. Four well-separated classes are better than ten mixed ones.
The first map is never good. That is not a bug, it is the method.
Before you invest in clicking, the program computes how much your examples differ, pair by pair. The scale runs from 0 to 2:
| Score | What it means | What to do |
|---|---|---|
| above 1.9 | clearly different classes | nothing, carry on |
| 1.7 to 1.9 | fair, there will be errors | add examples for this pair |
| below 1.7 | these classes will merge | add examples or merge them into one |
The program also shows which feature separates each pair most. If "grass" and "bare soil" differ mainly in greenness, that is a good sign. If they differ in object area, that is chance, not a pattern.
A hint from the program: after each map you are told how many objects got less than half of the tree votes. That is exactly where the program hesitates and where your examples will make the biggest difference.
A strip of grass between plots is often narrower than an object and gets lost. This option hands the decision to single pixels, but only for the two selected classes; the rest of the map stays object-based.
Note: recovered strips come out slightly wider than in reality, so be careful when reporting their area.
Removes single specks by absorbing them into their surroundings. Set it for how the map will be used, not to raise the accuracy score: a sieve tuned to the check points is no longer an independent test.
The part QGIS lacks, and the one that decides whether the result can be published.
A map is worth as much as you know about its errors. Judging "by eye" is not enough: you need to draw points, label them independently and count how often the map was right.
Why blind. If you can see the program's answer while labelling points, you will subconsciously agree with it. The result will be inflated by several to over a dozen percentage points, and a reviewer will notice.
With stratified sampling a small class gets proportionally many points, so that something can be said about it at all. As a result the share of points is not the share of area, and a plain average over points distorts the result.
That is why the program computes everything weighting each class by its area and reports separately:
| Figure | What it is | Cite it? |
|---|---|---|
| Mapped area | how many pixels the program assigned to the class | no, it contains classification errors |
| Estimated area | corrected for the errors found by the sample | yes, together with the interval |
| 95% confidence interval | how precisely it was estimated | yes, always next to the area |
The procedure follows the recommendations of Olofsson et al. (2014) and Stehman (2014), which is what reviewers in remote sensing journals require today. The kappa coefficient is deliberately not computed: current recommendations advise against it (Pontius and Millones, 2011).
One honest remark: points labelled from the air are high-quality reference data, but not the same as field measurements. They should be described that way in a publication.
The most common problems and what to do about them.
| Symptom | Cause | What to do |
|---|---|---|
| The map is "noisy", full of specks | objects too small or too few examples | increase the object size; set a minimum patch; add examples |
| Two classes keep getting confused | they are too similar in the measured features | check separability; if below 1.7, merge them or add an elevation model |
| Thin strips of grass disappear | they are narrower than an object | turn on greenness refinement or make the objects smaller |
| Roofs have zero height above ground | you are processing a small extent, narrower than 40 m | process the whole image, because the ground is estimated from wide surroundings |
| "Too large for the computer's memory" | the working grid has more than 15 million pixels | enter in Working resolution (m) at least the value given in the message, or process only the visible part |
| A class ended up without a single training object | polygons smaller than objects | draw larger polygons or reduce the object size |
| Processing takes very long | acceleration libraries are missing | in the plugin, the Install acceleration libraries button (step 1), then restart QGIS; results match the reference implementation, several times faster |
| Accuracy suspiciously high (above 97%) | the classification layer was visible while labelling points | turn it off and label the points again; otherwise the result is worthless |
Part 4 of 4
The method with sources, how to cite the program, and references.
Everything the program does has a source in the literature or in the documentation of the libraries used. Expand a subsection.
The program performs geographic object-based image analysis (GEOBIA) (Blaschke, 2010): the decision unit is not a pixel but an object, a coherent group of neighbouring pixels of similar colour. An object carries information unavailable to a single pixel, such as internal variability, texture, shape and relation to its surroundings. The output map contains no isolated misclassified pixels. Its thematic resolution is, however, limited by the object size: a feature narrower than an object will not be mapped.
The program interprets the first three bands of the image as red, green and blue and requires a projected coordinate reference system in metres. Brightness values are scaled to the range 0–1 according to the data type of the file (8-bit data divided by 255, unsigned 16-bit by 65,535, signed 16-bit by 32,767), not according to the value range in the image, so images stored in the same format remain comparable. The image is resampled to a working grid at a resolution chosen by the user, with pixel values averaged when coarsening. A working grid larger than 15 million pixels is rejected with a message. An optional digital surface model (DSM) must use the same coordinate reference system as the image. For Sentinel-2 scenes (Drusch et al., 2012) a ready RGB composite is required, because in a raw multiband product the first three bands do not correspond to red, green and blue.
Segmentation is performed by the SLIC algorithm (Achanta et al., 2012), that is, local k-means clustering in a five-dimensional space made of three colour components in CIELAB space and two pixel coordinates. The distance of a pixel from a cluster centre combines the colour difference dc with the spatial distance ds normalised by the seed grid interval S and weighted by compactness m: D = √(dc² + (ds/S)²·m²). The default compactness is 0.15. Before segmentation the image is smoothed with a Gaussian filter (σ = 2.5), and clustering runs for 10 iterations. Objects smaller than half of the ratio of image area to the number of objects are merged into a neighbour, so that every object forms one connected patch. When the scikit-image library (van der Walt et al., 2014) is available, the program calls its SLIC implementation with the same parameters; otherwise its own implementation of the same algorithm is used. At low compactness the realised number of objects can be two to three times smaller than requested, so the program reports the actual number of objects and the median and maximum of their areas.
Each object is described by a vector of 19 features, or 24 when a DSM is available. The colour group comprises the means and standard deviations of the three bands and the mean brightness (7 features). The visible-band vegetation indices ExG (Woebbecke et al., 1995), VARI (Gitelson et al., 2002) and NGRDI (Hunt et al., 2005) are computed on chromatic coordinates (r = R/(R+G+B), and likewise g and b), which removes the brightness component and reduces the effect of variable illumination (3 features). Texture is described by four grey-level co-occurrence matrix measures (Haralick et al., 1973): contrast, homogeneity, energy and entropy. They are computed for luminance quantised globally to 15 levels, at a distance of 1 pixel and in four directions (0°, 45°, 90°, 135°); objects smaller than 16 pixels receive neutral values. Shape is described by area, perimeter estimated with the Crofton method, circularity 4πA/P² and convexity relative to the convex hull (4 features). Context is the difference between the brightness of an object and the mean brightness of adjacent objects in a 4-connected adjacency graph (1 feature). From the DSM the program derives the mean and standard deviation of height, mean slope, height relative to neighbours and height above ground (5 features). The ground surface is estimated from the DSM alone: minimum in blocks of about 1 m, morphological opening with a 40 m window and smoothing with a moving average. Features are not standardised, because Random Forest splits the feature space with thresholds and is insensitive to monotonic transformations of a single feature.
The decision rule is Random Forest (Breiman, 2001), a classifier widely used in remote sensing (Belgiu and Drăguţ, 2016). The model consists of 100 trees. Each tree learns from a bootstrap sample, and at each node it chooses a split among √p randomly selected features (the integer part of √p, that is 4 for both 19 and 24 features) using the Gini criterion (Breiman et al., 2017), without class weights and without a depth limit (up to 40 levels in the built-in implementation). The seed of the pseudo-random number generator is 42, which makes the result repeatable. An object receives the class with the largest share in the averaged class distribution from the leaves of all trees (soft voting), and that share is stored as the confidence of the decision. The program counts objects with confidence below 0.5 and warns when the ratio between the most and least frequent training class exceeds 5:1. When the scikit-learn library (Pedregosa et al., 2011) is available, its implementation is used with these parameters; otherwise the built-in implementation is used.
Before training, the program estimates the separability of each pair of classes with the Jeffries-Matusita distance, JM = 2(1 − e−B), where B is the Bhattacharyya distance. The classical JM computed in the full feature space on the same samples from which the parameters were estimated reaches values close to 2 for almost every pair of classes with 24 features and a few hundred samples. A variant with five-fold cross-validation is therefore used. On the training part, the Fisher discriminant direction (Fisher, 1936) is determined with a covariance matrix estimated with Ledoit-Wolf shrinkage (Ledoit and Wolf, 2004), and the distance is computed on the projections of the test part. This measure is a lower bound of the full multivariate distance. The thresholds 1.9 and 1.7 are therefore indicative, and the most useful outputs are the ranking of class pairs and the ranking of the features that separate them.
Two optional corrections break the purely object-based character of the map and, if used, should be reported. The first decides the membership of single pixels for two selected classes from ExG averaged in a 3 × 3 pixel window. The threshold is set on the training samples by maximising the mean accuracy of both classes. The correction recovers features narrower than an object at the cost of slightly widening the recovered strips. The second correction, the minimum mapping unit, absorbs patches smaller than the threshold into the surrounding class using the sieve filter of the GDAL library (GDAL/OGR contributors, 2026) with 4-connectivity. The sieve threshold should follow from the purpose of the map, not from optimising the accuracy assessment.
Accuracy assessment is based on stratified sampling by map class: each class receives at least 50 points (default value), and the remaining points are allocated in proportion to area. Check points do not fall on objects used for training, so the target population is the map outside those objects. The area weights of the strata are stored at the moment of sampling, so that the strata remain attached to the sample even if the map changes later (Stehman, 2014). The interpreter labels the points with the classification layer hidden, which limits confirmation bias. From the error matrix (Congalton and Green, 2019) the program derives overall accuracy, user's and producer's accuracy, and class proportions and areas using area-weighted estimators with 95 percent confidence intervals (Olofsson et al., 2014). The implementation reproduces the worked example from that paper (Table 8): overall accuracy is 0.9449 against the published value of 0.944. The kappa coefficient is not computed (Pontius and Millones, 2011).
The program is written in Python and uses the NumPy (Harris et al., 2020) and SciPy (Virtanen et al., 2020) libraries. Reading and writing spatial data is done by GDAL (GDAL/OGR contributors, 2026) in the QGIS plugin and by the rasterio and Fiona libraries in the command-line program. The QGIS plugin, the command-line program and the Windows desktop application share the same computational engine; the English and Polish editions of the plugin are generated from the same code. The Windows application is built automatically in GitHub Actions from the code in the repository, and each release is published with SHA-256 checksums. The source code is available under the GPL-3.0-or-later licence. To reproduce a result, report the program version, the versions of scikit-image and scikit-learn (or that the built-in implementations were used), the random seed and the values of all user-set parameters.
The method uses only the visible range, so it does not use near infrared or indices such as NDVI. A model does not transfer between flights without retraining, because illumination, phenological stage and camera settings shift the feature distributions. Features narrower than an object are underestimated. Under continuous cover wider than 40 m the estimated ground surface rises onto that cover, which underestimates height above ground. Without class weights, rare classes are systematically underestimated when the training set is imbalanced. The program does not use features derived from deep neural networks, so classes that differ only in spatial pattern, not in material, may remain inseparable.
The program is archived in Zenodo and has persistent DOI identifiers.
G. Górniak, "GeoAI Klasyfikator," version 1.0.3, Zenodo, 2026, doi: 10.5281/zenodo.24011434.
The archived software is GeoAI Klasyfikator; the English plugin, GeoAI Classification, is built from the same code, so cite the same record.
| Identifier | DOI | When to use it |
|---|---|---|
| All versions | 10.5281/zenodo.24005560 | referring to the program in general; always resolves to the latest version |
| Version 1.0.3 | 10.5281/zenodo.24011434 | a result computed with exactly this version, for example in a paper |
Elsevier style (Harvard, with titles). All entries ready to import into Zotero: bibliografia.bib (in Zotero: File, Import).