Rasterberekening in QGIS: geteste formules en 4 stille valkuilen
Gepubliceerd op
Open de QGIS Raster Calculator via Raster > Raster Calculator (in de Nederlandse interface: Raster > Rasterberekeningen) of via de Processing Toolbox. Gebruik voor Sentinel-2 NDVI ("B08@1" - "B04@1") / ("B08@1" + "B04@1"): de calculator in het menu rekent automatisch in Float32. Let op drie dingen: de GDAL-calculator laat unsigned integers overlopen, maskers met * schrijven 0 in plaats van nodata, en hellingtools gaan mis op rasters in graden.
Ik heb dat allemaal getest op 7 november 2026 in QGIS 3.44.15, op een venster van 5 bij 5 km heuvelachtige landbouwgrond ten zuiden van Machado, Minas Gerais, Brazilië (2.500 ha, 250.000 pixels van 10 m in UTM 23S, EPSG:32723). Ik gebruikte een Sentinel-2 L2A-scène van 3 november 2026 en een Copernicus GLO-30 DEM van 30 m, en controleerde elke uitvoer tegen een 64-bit floating-point referentie in numpy, buiten QGIS.
Wat is de QGIS Raster Calculator, en welke open je eigenlijk?
Een raster calculator evalueert een wiskundige of booleaanse expressie pixel voor pixel over een of meer rasterbanden en schrijft een nieuw raster. In QGIS verwijs je naar een band als "layername@band" (bijvoorbeeld "B08@1" voor band 1 van laag B08). Je combineert banden met +, -, *, /, <, >, AND, OR of if(condition, true, false), en schrijft de uitvoer naar een GeoTIFF of naar een virtuele laag in het geheugen.
Wat mensen in de war brengt: QGIS heeft vijf tools die "Raster calculator" heten, en ze draaien niet allemaal op dezelfde engine. Vier gebruiken de eigen expressie-parser van QGIS en rekenen in floating point. De GDAL-versie (onder de motorkap gdal_calc.py) gebruikt lettervariabelen A/B/C en evalueert numpy-expressies in het datatype van de invoerband zelf.
| Tool in QGIS 3.44 | Bandsyntaxis | Uitvoertype | Pixels die > 0,01 afwijken bij uint16 NDVI (van 250.000) |
|---|---|---|---|
Raster > Raster Calculator (QgsRasterCalculator) | "B08@1" | Float32 | 0 |
Processing: Raster calculator (native:rastercalc) | "B08@1" | Float32 | 0 |
Processing: Raster calculator (virtual) (native:virtualrastercalc) | "B08@1" | Float64 (in geheugen) | 0 |
Processing: Raster calculator (legacy) (qgis:rastercalculator) | "B08@1" | Float32 | 0 |
Processing: GDAL Raster calculator (gdal:rastercalculator) | A, B | Float32-bestand, rekenwerk in het invoertype | 703 (0,28%, waarden tot 126,7) |
Wil je gewoon een formule die werkt zonder typen om te zetten, gebruik dan Raster > Raster Calculator in het hoofdmenu, of de eerste Raster calculator onder de eigen QGIS-groep in Processing.
Stap voor stap: NDVI berekenen in de QGIS Raster Calculator
Heb je eerst Sentinel-2-banden nodig, dan legt onze gids over Sentinel-2-beelden downloaden in QGIS de route via de STAC-catalogus uit. Zodra de banden B04 (rood, 10 m) en B08 (nabij-infrarood, 10 m) in je Layers-paneel staan, bereken je wat NDVI meet in vier stappen.
Selecteer de laag die je uitvoerraster bepaalt
Klik op B08 in het Layers-paneel voordat je het venster opent. Bij het openen leest Raster > Raster Calculator de uitvoerextent, het coördinatensysteem (CRS), de breedte en de hoogte uit de rasterlaag die op dat moment in het Layers-paneel geselecteerd is. Controleer dat de kop Extent (current: B08) toont.
Voer de NDVI-expressie in
Dubbelklik in Raster > Raster Calculator op B08@1 en B04@1 in de lijst Raster Bands (of typ ze met dubbele aanhalingstekens) om dit op te bouwen:
("B08@1" - "B04@1") / ("B08@1" + "B04@1")
Controleer dat de melding direct onder het expressievak Expression valid zegt. In dit venster hoef je de unsigned 16-bit Sentinel-2-banden niet naar float om te zetten.
Kies een uitvoerbestand of virtuele laag
Zet Output layer op een .tif-pad en laat Output format op GeoTIFF staan. Wil je alleen een snelle preview zonder bestand op schijf, vink dan Create on-the-fly raster instead of writing layer to disk aan (dat slaat een formule-URI op in het projectbestand, met absolute paden naar de invoer, en komt tot op 3e-8 overeen met het geschreven bestand).
Uitvoeren en het resultaat opmaken
Klik op OK. Op ons venster van 500 bij 500 pixels was de berekening in 0,12 seconden klaar, met waarden van -0.5507 tot 0.9547 en een maximaal verschil van 2.98e-8 met 64-bit numpy. Open Layer Properties > Symbology, kies Singleband pseudocolor, neem de kleurenschaal RdYlGn en zet min op 0 en max op 0.9.
Zo ziet het venster eruit vlak voor je op OK klikt, met de extent overgenomen van B08:

Met RdYlGn opgemaakt verschijnt dichte vegetatie rond Machado donkergroen, kale grond en wegen rood, en de 703 pixels waar rood hoger is dan nabij-infrarood (water en donkere plekken) worden negatief. De gemiddelde NDVI over het hele venster is 0.727. Is je gewas zo dicht dat NDVI verzadigt, of ligt er kale grond tussen de rijen, bekijk dan hoe de formule verandert in onze vergelijking van NDVI vs EVI, SAVI en NDRE.

Formuleboek voor de QGIS Raster Calculator
Elke expressie hieronder is uitgevoerd in QGIS 3.44.15 (QgsRasterCalculator) op de Machado-dataset en pixel voor pixel tegen numpy gecontroleerd. Neem de syntaxis over met dubbele aanhalingstekens rond "layer@band".
| Taak | Geteste expressie (Raster > Raster Calculator) | Wat het opleverde op het testvenster bij Machado |
|---|---|---|
| NDVI (Sentinel-2 10 m) | ("B08@1" - "B04@1") / ("B08@1" + "B04@1") | Float32 van -0.5507 tot 0.9547 over 250.000 pixels; maximale fout 2.98e-8. |
| NDWI (waterindex van McFeeters) | ("B03@1" - "B08@1") / ("B03@1" + "B08@1") | Float32; "ndwi@1" > 0 isoleerde 637 waterpixels (6,4 ha vijvers en beken). |
| Onder een drempel maskeren naar nodata | "ndvi@1" / ("ndvi@1" > 0.3) | Deelt door 0 waar de voorwaarde onwaar is en schrijft echte nodata (11,343 pixels uitgesloten; gemiddelde 0.753). |
Voorwaardelijk vervangen (if) | if("ndvi@1" > 0.3, "ndvi@1", 0) | Wordt geparsed en houdt waarden boven 0.3, maar schrijft elders 0, net als het *-masker. |
| DEM herclassificeren in 5 klassen (booleaanse som) | ("dem@1" < 800) * 1 + ("dem@1" >= 800 AND "dem@1" < 850) * 2 + ("dem@1" >= 850 AND "dem@1" < 900) * 3 + ("dem@1" >= 900 AND "dem@1" < 1000) * 4 + ("dem@1" >= 1000) * 5 | Codes 1 tot 5 over 54.901 DEM-pixels; op elke pixel gelijk aan native:reclassifybytable. |
DEM herclassificeren in 5 klassen (geneste if) | if("dem@1" < 800, 1, if("dem@1" < 850, 2, if("dem@1" < 900, 3, if("dem@1" < 1000, 4, 5)))) | Dezelfde 5 klassen (96,9 ha, 1.089,4 ha, 1.323,1 ha, 1.759,7 ha, 582,7 ha). |
| Hellingsdrempel | "slope@1" > 15 | Schrijft 1 boven 15 graden en 0 op of onder 15 graden (17,327 van 52,979 geldige UTM-DEM-pixels, 32,61%). |
| Hoogte van meter naar voet | "dem@1" * 3.28084 | Maximale fout 0.98 ft (0.026% op 1.168 m) als je de 3.28 uit de QGIS-handleiding gebruikt in plaats van 3.28084. |
Vier stille valkuilen die verkeerde getallen geven (en hoe je ze herkent)
Geen van de vier fouten hieronder geeft een waarschuwing in QGIS. Elke berekening loopt af, voegt een laag aan je project toe en levert getallen op die geloofwaardig lijken tot je ze controleert.
1. De GDAL Raster Calculator laat unsigned integers overlopen bij negatieve NDVI
De eigen QGIS Raster Calculator heeft nooit last van een integer-deling op Sentinel-2-banden, maar de GDAL Raster calculator in Processing (gdal:rastercalculator) evalueert (A-B)/(A+B) in het numpy-datatype van het invoerbestand zelf en verminkt elke pixel waar rood hoger is dan NIR.
Sentinel-2 Level-2A-banden worden geleverd als unsigned 16-bit integers (uint16). In ons venster van 250.000 pixels hebben 703 pixels (0,28%, water en donkere plekken) een B04 die groter is dan B08. Als gdal:rastercalculator B04 van B08 aftrekt in uint16, loopt het negatieve resultaat over rond 65.536, en de tool schrijft die 703 pixels naar een Float32-bestand met waarden tussen 0 en 126.7 in plaats van negatieve NDVI.

Gebruik je de GDAL-calculator, zet dan het type om in de expressie met (A.astype(numpy.float32)-B)/(A+B) (dat bracht het aantal foute pixels in onze test terug tot 0), of gebruik in plaats daarvan de eigen QGIS Raster Calculator. En zet het uitvoertype van GDAL nooit op Int16 of Byte voor een index: met Int16 werden 249.973 van onze 250.000 pixels (99,99%) afgerond naar alleen -1, 0 of 1.
2. Vermenigvuldigen met een voorwaarde (*) schrijft 0, geen nodata, en trekt je gemiddelde omlaag
Als je een raster vermenigvuldigt met een booleaanse voorwaarde zoals ("ndvi@1" > 0.3) * "ndvi@1", krijgt elke uitgesloten pixel 0 in plaats van nodata. Daardoor zakte de gemiddelde NDVI van het behouden gebied van 0.753 naar 0.719.
De officiële QGIS-handleiding leert ("elevation@1" >= 0) * "elevation@1" aan als de standaardmanier om een raster te maskeren. In onze NDVI-laag van Machado liggen 11,343 pixels op of onder 0.3. Vermenigvuldig je met ("ndvi@1" > 0.3), dan worden alle 11,343 pixels geschreven als de geldige waarde 0.0. De laagstatistieken tellen dus nog steeds alle 250.000 pixels mee, met 11,343 nullen in elk gemiddelde, elk histogram en elke zonale statistiek die je daarna draait.
| Maskerexpressie | Wat uitgesloten pixels worden | Pixels geteld in laagstatistieken | Gemiddelde NDVI van behouden gebied |
|---|---|---|---|
("ndvi@1" > 0.3) * "ndvi@1" | 0.0 (geteld als data) | 250.000 | 0.719 (fout van -0.034) |
"ndvi@1" / ("ndvi@1" > 0.3) | nodata (-3.40282e+38) | 238.657 | 0.753 (exact) |
Om in één stap echte nodata te schrijven, deel je door de voorwaarde in plaats van te vermenigvuldigen: "ndvi@1" / ("ndvi@1" > 0.3). Waar de voorwaarde waar is (1), blijft de waarde gelijk; waar ze onwaar is (0), levert deling door nul nodata op.
3. Slope draaien op een DEM in graden vóór de calculator markeert 100% van je kaart als steil
Toen ik de Slope-tool van QGIS met standaardinstellingen draaide op een geografische DEM (zoals Copernicus GLO-30 in EPSG:4326) en daarna "slope@1" > 15 toepaste, werd 100% van onze heuvelachtige landbouwgrond als steiler dan 15 graden gemarkeerd, met een gemiddelde helling van 89.99 graden. Het echte cijfer na omprojecteren naar UTM is 32.61%.
Copernicus GLO-30 en andere gratis wereldwijde DEM's (zie onze gids over gratis DEM-downloads in QGIS) komen in geografische graden (EPSG:4326) met hoogtes in meters. Met de standaard Z-factor zet de Slope-tool een stijging in meters af tegen een horizontale stap van 0.000278 (een pixelbreedte in graden), waardoor elke heuvel eruitziet als een loodrechte rotswand.
Hoe Slope berekend werd vóór "slope@1" > 15 | Pixels > 15 graden | Gemiddelde helling |
|---|---|---|
Eigen Slope op DEM in EPSG:4326, standaard Z-factor 1 | 100.00% | 89.99° |
GDAL Slope op DEM in EPSG:4326, schaal 1 | 100.00% | 89.99° |
GDAL Slope op DEM in EPSG:4326, schaal 111120 | 31.61% | 12.54° |
| Eigen Slope na omprojecteren van de DEM naar UTM 23S 30 m (referentie) | 32.61% | 12.68° |
| GDAL Slope na omprojecteren van de DEM naar UTM 23S 30 m | 32.71% | 12.71° |
Zelfs de vuistregel met een schaal van 111120 meter per graad komt een vol procentpunt te laag uit (31.61% tegen 32.61%), omdat op 22°ZB een lengtegraad ongeveer 7% korter is dan een breedtegraad (cos 22° = 0.93). Projecteer je DEM dus altijd eerst naar de lokale UTM-zone (Raster > Projections > Warp (Reproject), bilineaire resampling, en een nodata-waarde van -9999 zodat de rand niet vol nep-hellingen van 87° komt te staan) voordat je Slope en de Raster Calculator draait.
4. De Sentinel-2-offset van +1000 aftrekken zonder de scène te controleren breekt 90,6% van de pixels
Toen ik 1000 aftrok van onze Sentinel-2 L2A-banden vóór de NDVI-berekening, vielen 226,413 van de 250,000 pixels (90,6%) buiten het geldige bereik [-1, 1], met waarden van -1273 tot 1217 en een gemiddelde van 1.39 in plaats van 0.727.
ESA voegde vanaf processing baseline 04.00 een offset van +1000 aan de digitale waarden toe, zodat donkere pixels niet op nul afkappen, en de STAC-metadata vermeldt scale: 0.0001 en offset: -0.1. Maar als je Cloud-Optimized GeoTIFFs ophaalt uit de Earth Search STAC API (zoals wij deden voor scène S2A_23KLR_20261103_0_L2A, baseline 05.13), heeft de aanbieder de waarden al verschoven en staat earthsearch:boa_offset_applied: true in de metadata van het item.
In ons B04-bestand hadden 226,773 van de 250,000 pixels (90,7%) ruwe digitale waarden onder 1000 (minimum 59, mediaan 491). Controleer de minimumwaarde van B04 in Layer Properties > Information voordat je aan de formule komt: liggen donkere pixels rond 50 tot 300, dan is de offset al verwijderd en is de ruwe (B08 - B04) / (B08 + B04) exact (delen door de schaalfactor 10,000 valt in de verhouding weg). Trek alleen 1000 af als je donkerste onbewolkte pixels boven 1000 liggen.
Bonus: wat "Expression valid" en de algemene rode foutbalk echt betekenen
Nog twee gedragingen van Raster > Raster Calculator kosten veel tijd bij het debuggen:
- Gemengde resoluties op een grof raster worden niet gemiddeld. Combineer je een band van 10 m (
B08) met een band van 20 m (B11), dan resamplet de eigen calculator ter plekke met nearest neighbour (de GDAL-calculator stopt meteen metError! Dimensions of file ... (250, 250) are different from other files (500, 500). Cannot proceed). Op een uitvoerraster van 10 m werkt het netjes: elke pixel van 20 m wordt 2 bij 2 herhaald. Op een uitvoerraster van 20 m middelt de calculator de vier pixels van 10 m niet: in62,250van de62,500cellen pakte hij gewoon de pixel rechtsboven van elk blok van 2 bij 2, met een verschil tot0.71ten opzichte van een gemiddelde van 4 pixels in(B08 - B11) / (B08 + B11). - "Expression valid" controleert alleen de syntaxis, niet de bandnamen. Laat je een operator achteraan staan, zoals
"B08@1" /, dan vangt het venster dat af (Expression invalid, OK grijs).

Maar typ je een laag of band die niet in het project zit (zoals "B04@2", of "B02@1" terwijl B02 niet geladen is), dan zegt het label nog steeds Expression valid. Klik je op OK, dan schrijft QGIS een GeoTIFF van 500 bij 500 die voor 100% uit nodata bestaat, voegt niets aan de kaart toe en toont één rode balk: Raster calculator: An error occurred while performing the calculation. Zie je die balk, controleer dan elke "layer@band"-naam tegen de lijst Raster Bands.

Het venster overslaan: rasterberekeningen laten doen door een AI-agent in QGIS
Wij maken AI Agent, dus dit deel gaat over onze eigen plugin. Die zet een chatpaneel in QGIS dat STAC-catalogi kan doorzoeken, DEM's kan omprojecteren, rasterberekeningen kan uitvoeren in Python of Processing, en de uitvoer op je kaart kan opmaken.
Om te zien of de agent de valkuilen hierboven vermijdt op precies dezelfde Machado-data, tekende ik een boerderijpolygoon van 120,07 ha (E 405300-406300, N 7594400-7595600 in UTM 23S) in een leeg OpenStreetMap-project. Daarna stuurde ik drie prompts van één bericht, op Medium effort en Balanced permissions, met een nieuwe chat per taak.
Test 1: NDVI in 5 klassen met hectares per klasse
Ik typte:
Compute NDVI for this field from the latest cloud-free Sentinel-2 and class it in 5 classes with hectares per class
De agent doorzocht de STAC-catalogus, zag dat er een scène van 6 november 2026 bestond maar dat die zwaar bewolkt was, en koos dezelfde scène van 3 november 2026 (1.65% bewolking op de tegel) die ik voor de handmatige test had gebruikt. Hij haalde B08 en B04 op in 10 m, berekende NDVI, deelde die in vijf intervallen in, maakte de laag op en rapporteerde de oppervlakte per klasse op het metrische raster van 10 m.
| NDVI-klasse | Rapport van AI Agent | Mijn onafhankelijke controle in numpy (dezelfde 12.000 pixels) | Verschil |
|---|---|---|---|
-1 tot < 0 | 0.76 ha | 0.76 ha | 0.0% |
0 tot < 0.2 | 23.85 ha | 23.84 ha | < 0.1% |
0.2 tot < 0.4 | 9.27 ha | 9.27 ha | 0.0% |
0.4 tot < 0.6 | 8.70 ha | 8.69 ha | 0.1% |
0.6 tot 1 | 77.42 ha | 77.44 ha | < 0.1% |
| Totale geldige oppervlakte | 120.00 ha | 120.00 ha (van een polygoon van 120.07 ha) | 0.0% |
Pixel voor pixel op het geëxporteerde NDVI-raster was het gemiddelde absolute verschil met mijn referentie 5e-5, en het klassenraster week op 4 van de 12.000 pixels af van het mijne.

Test 2: helling boven 15 graden (zonder DEM in het project)
In een nieuwe chat, met alleen de boerderijpolygoon geladen, typte ik:
Show where slope is above 15 degrees on the farm layer
De agent haalde de Copernicus DEM van 30 m op, projecteerde die naar een metrisch raster in plaats van de helling in graden te berekenen, en maakte een laag met twee klassen op (15 degrees or less in lichtgeel, above 15 degrees in rood). Hij rapporteerde 331 van de 1,320 boerderijpixels boven 15 graden (25.08%, max 29.15°). Mijn eigen referentie met de eigen Slope-tool na omprojecteren naar UTM gaf op dezelfde rechthoek 341 van de 1,360 pixels (25.07%, max 29.29°), een verschil van 0.01 procentpunt.

Test 3: waar je de agent nog steeds moet controleren
In een derde chat vroeg ik:
Mask out everything with NDVI below 0.3 on the farm layer and tell me the mean NDVI of what is left
Merk op dat ik in deze prompt geen "cloud-free" of "latest" schreef. De agent ontweek de valkuil van het 0-masker: hij schreef -9999 als nodata voor elke gemaskeerde pixel en gaf een gemiddelde NDVI van 0.552 over 2,277 behouden pixels. Het gemiddelde van zijn uitvoerbestand was 0.5515; het mijne op dezelfde scène met dezelfde regel was 0.5513.
Alleen haalde hij de Sentinel-2-scène van 27 oktober 2026 op, en op die datum was 80.5% van de boerderij (9,656 van de 12,000 pixels in de Scene Classification-band) bedekt door wolken, schaduw of cirrus. Hij noemde de datum in zijn antwoord, maar zei nergens dat 0.552 alleen het heldere vijfde deel van het veld beschreef. Op de heldere scène van 3 november geeft dezelfde regel een gemiddelde van 0.766 over 8.785 pixels.
Mijn vuistregel: of je de Raster Calculator nu met de hand draait of een agent uit het QGIS AI-ecosysteem vraagt, controleer altijd de scènedatum en laad de ware-kleurenweergave of de SCL-wolkenband voordat je een samenvattend getal vertrouwt.
Hoe we dit gemeten hebben. Alle tests liepen op 7 november 2026 in QGIS 3.44.15-Solothurn op macOS (GDAL 3.13.3, numpy 2.5.2) over een venster van 5 bij 5 km ten zuiden van Machado, Minas Gerais, Brazilië (EPSG:32723, E 402000-407000, N 7591000-7596000), met Sentinel-2 L2A-scène S2A_23KLR_20261103_0_L2A (en de scène van 27 oktober 2026 voor test 3) van Earth Search en Copernicus GLO-30-tegel S22_00_W046_00. De succescriteria voor de handmatige tests en voor agenttests 1 en 2 zijn vastgelegd voordat er een rasterberekening liep; de criteria voor test 3 zijn opgesteld na het zien van tests 1 en 2. Referentierasters zijn berekend in 64-bit float numpy buiten QGIS. AI Agent 1.9.4 draaide op Medium effort en Balanced permissions, één bericht per chat. Eén scène en één locatie is een benchmark van één geval, geen statistisch onderzoek over baselines heen.
Veelgestelde vragen
Waarom is mijn NDVI in QGIS overal 0, -1 of 1?
Je hebt een integer-uitvoertype (Int16 of Byte) afgedwongen in de GDAL raster calculator. Die rondt elk decimaal getal tussen -1 en 1 af naar -1, 0 of 1 (99,99% van de pixels in onze test). Laat het uitvoertype op Float32 staan, of gebruik Raster > Raster Calculator in het hoofdmenu, dat automatisch Float32 uitvoert.
Waarom faalt de QGIS Raster Calculator met 'An error occurred while performing the calculation' en levert hij nodata op?
Een "layer@band"-naam in je formule komt niet overeen met een geladen laag of bandnummer (bijvoorbeeld "B04@2" typen bij een bestand met één band, of verwijzen naar een laag die je hebt verwijderd). De melding Expression valid onder het formulevak controleert alleen de syntaxis, niet of de banden bestaan. Voeg elke band opnieuw in door erop te dubbelklikken in de lijst Raster Bands.
Wat is het verschil tussen Raster > Raster Calculator en de GDAL Raster Calculator in Processing?
Raster > Raster Calculator (en de eigen Raster calculator in Processing) gebruikt de parser van QGIS zelf met "layer@band"-namen en rekent altijd in floating point. De GDAL Raster calculator (gdal_calc.py) gebruikt lettervariabelen (A, B) en evalueert numpy-rekenwerk in het datatype van de invoerband. Trek je unsigned 16-bit integer-banden van elkaar af, dan lopen negatieve getallen over rond 65,536, tenzij je omzet met A.astype(numpy.float32).
Hoe schrijf ik een IF-voorwaarde of zet ik waarden op nodata in de QGIS Raster Calculator?
Met if("ndvi@1" > 0.3, "ndvi@1", 0) vervang je waarden onder 0.3 door 0. Wil je dat uitgesloten pixels echte nodata worden, zodat ze het gemiddelde van je laag niet omlaag trekken, deel dan door de voorwaarde: "ndvi@1" / ("ndvi@1" > 0.3). Waar de voorwaarde onwaar is (0), schrijft de deling door nul nodata.
Verder lezenWat is NDVI? Betekenis, waarden en formule
Doe het in QGIS door erom te vragen
AI Agent voert de taak uit die je beschrijft, in je eigen QGIS-project. Gratis, geen kaart nodig.
Raster Calculator via Raster > Raster Calculator (in de Nederlandse interface: Raster > Rasterberekeningen) of via de Processing Toolbox. Gebruik voor Sentinel-2 NDVI 

