**DJI Mavic 3M: NDVI quality gap vs Pix4Dfields on the identical flight — seam leveling, band alignm

DerpZee

So i have been wrangling with this for over 3 weeks and i think i am finally at the stage where i need some professional opinion.

Attached our two NDVI indices generated .. one from WebODM and the second one from Pix4D of the exact same files but completely different results.

On my flight i see banding that follow flight pattern vs pix4d its evenly structured across... Its more visible when there is low canopy... in a denser canopy its hard to notice but i have noticed in old images that it still comes up.

Dataset: https://drive.google.com/drive/folders/1d0-UyHzgCIfhYu7OLHIl1PIcAm6wdD6n?usp=sharing

Flight & data

  • DJI Mavic 3 Multispectral — 20 MP RGB + 4×5 MP MS bands (G 560 / R 650 / RE 730 / NIR 860 nm, 2592×1944)
  • 286 capture stations = 1,430 images over a ~20 ha row-crop field 280 ft / ~85 m AGL, native GSD 3.63 cm/px
  • Flown 2026-07-20 14:11 local, partly cloudy — irradiance shifted mid-flight (visible in the DLS values)
  • PPK-corrected geotags (RINEX obs/nav logged), --gps-accuracy 0.159
  • ODX latest version
--orthophoto-resolution 2 --feature-quality high --dem-resolution 2 --dsm --dtm --pc-quality medium --sfm-algorithm incremental --skip-3dmodel --skip-report --no-gpu --use-exif --gps-accuracy 0.159 --auto-boundary --radiometric-calibration camera --min-num-features 16000 --matcher-neighbors 16 --max-concurrency 12

40 Replies

pierotofy

I'm going to guess that radiometric-calibration did not work, the M3M has a sun sensor if I remember? Did you try camera+sun as an option?

DerpZee OP

i did but i think camera+sun is not supported for this cam.. results were worse

pierotofy

Did Pix4D radiometrically calibrate the images also, or are those digital numbers?

cwood256

I'm getting reasonable results from my M3M over a forested hilly area, with caveats.

First, I'm flying at 100 m with terrain awareness, and 77-85% overlap for the MS missions. 85% overlap for the RGB missions, which I use to obtain the DSM. RGB and MS photos are processed separately. I'm using RTK as well as GCPs. I identify the GCP targets in the RGB images with the WebODM Lightning GCP tool, and upload the file with the RGB photos. GCPs are too hard to identify in the MS images, but more clear in the MS ortho, so I just do a final georeference of the MS ortho.

The WebODM settings I use for MS processing are: primary-band:NIR, radiometric-calibration:camera+sun, auto-boundary:true, crop:5, feature-type:hahog, matcher-neighbors:8, pc-quality:high, skip-3dmodel:true, texturing-keep-unseen-faces:true, texturing-skip-global-seam-leveling:true, orthophoto-resolution:3

I use modified versions of multispectral.py and photo.py in ODX/opendm to be consistent with the processing steps described in the DJI document Mavic_3M_Image_Processing_Guide_EN.pdf. The changes to photo.py are needed to use the tags that DJI recommends, and the changes to multispectral.py are needed to avoid an incorrect factor of pi multiplication and an incorrect truncation of the relative reflectance values (they are relative, so the max value is arbitrary, not 1).

Even though the corrected reflectance values from the photos are non-negative, the constructed MS ortho can have negative values and large positive outliers. Presumably this is a byproduct of the reconstruction and/or texturing process, however it's a problem when computing indices if band values are < 0, huge, or NaN. I handle this by postprocessing the geotiffs to set bad values to a NoData value. I haven't checked if the alpha channel also does this.

💛 1
DerpZee OP

@pierotofy

checked their processing log to be sure: Pix4Dfields radiometrically calibrated everything. The log reports 286 / 286 images allow correction type sky_sensor_with_angle_pose_from_imu, and their exported band mosaic is float32 reflectance-style values (nodata -10000), not DNs. So the comparison is calibrated-vs-calibrated.

DerpZee OP

i got claude to feed in some of the feedback in this thread .. image got slightly better ...

What was changed:

  1. Black-level subtraction (photo.py): the M3M writes BlackLevel only as drone-dji:BlackLevel (3200) — stock code reads only EXIF/IFD0 + Camera:BlackCurrent, so dark subtraction silently never ran. The DN floor of real frames sits at 3376/3584, so the offset is genuinely in the data — for the Red band it's ~half the median DN, and additive offsets don't cancel in NDVI.
  2. Vignette center per Guide Eq. 8 (photo.py): polynomial now centered on drone-dji:CalibratedOpticalCenterX/Y; stock used Camera:VignettingCenter (~44 px off on ours). The polynomial math itself was already spec-correct.
  3. No π, no [0,1] clamp for DJI (multispectral.py, credit to cwood256): π isn't in DJI's model, and the scale is relative so the 1.0 clamp destroys bright values.
  4. Smoothed sun-sensor division (multispectral.py + run_opensfm.py): rolling median over 7 captures per band (clamped 0.2–5× band median) instead of raw per-frame division — frame brightness anti-correlated (−0.54) with raw DLS under scattered clouds (drone sensor ≠ ground light). Side finding: DJI's Irradiance is already tilt-compensated — raw LS values track cos(sun→sensor angle) with slope 1.04, the compensated value with slope ≈ 0 — so no angle model is needed.
  5. Per-capture band alignment (multispectral.py + run_opensfm.py): removed the 30-sample cap; one warp matrix per capture with the consensus as fallback (the M3M's 30.66 mm lens separation swings the true NIR↔Red offset ~32 px between opposite headings).
  6. Seam leveling force-skipped for DJI MS (mvstex.py): per-band global+local leveling retones patches independently per band → NDVI quilt. Note: --texturing-skip-local-seam-leveling is deprecated in the CLI while texrecon still supports the flag, so engine-side was the only way to disable local leveling.
pierotofy Best answer

I don't know if I would trust Claude on this

👍 3
DerpZee OP

i dont either .. burning some tokens to see what it does with fable ... trying to buy time until i get some cycles to go through it myself

DerpZee OP

another example ... usually follows the flight path ... i guess with sun hitting it and creating shadows and mismatches...

the RGB ortho its hard to tell if there are shadows on the field

DerpZee OP

3 images of full NDVI, one with 50% opacity so you can see the ground and last one pure RGB ORTHO....

visualyly only in NDVI you can see the different stripes

DerpZee OP

just from that to me it looks like sun creates shadow in different ways opposite of sun during a flight vs when hitting flight....

pierotofy

rather than the orthomosaic, I would focus on the individual images, e.g. isolate the code that calls https://github.com/WebODM/ODX/blob/master/opendm/multispectral.py#L17 and run it on an image (or a set of images). Does it behave as expected? You might need a calibration target to see if it's doing the calibration correctly. Looking at the orthomosaic is a next step

☑️ 1
cwood256

Here are some M3M results from a totally different setting - forestry. There's an RGB ortho and an NDVI generated from the postprocessed MS ortho, all plotted in ArcGIS (orthos are 40% transparent over the slopeshade of the DSM to better show individual trees).

When I looked into WebODM's MS processing a few months ago, it seemed like the raw images were getting corrected more-or-less OK. Instead, it seemed like the biggest potential source of problems lay in the details of generating the MS orthos. I ran out of time to adequately understand those details.

Support for calibration panels would be nice but I don't think it would make much difference for NDVI (and adds another source of uncertainty if the calibration isn't done correctly or the results are inconsistent).

Perlator

I'd suggest lowering AGL to 60m to get more details and prevent honeycombing artefacts (what is visible in the initial post). Terrain-Follow OFF! These fields are quite uniform, making it hard for the feature matching algo. So lowering the flight altitude would be my go-to solution here.

For radiometric calibration, you might want to use a Sentera Reflectance Panel and pipe all TIFFs through this tool: https://github.com/Eggpl4ntE4ter/m3m_radcal

It also takes care of vignetting, dark current issues and other sensor related corrections.

All tested with the M3M. Works like a charm.

DerpZee OP

are you suggestion to remove terrain follow?

also i understand i can make flight changes and do hatch pattern flights to make this work better but i am still at the stage where both drone deploy and pix4d seem to not have issue with the same dataset.

Looking at the other repo... thats very helpful thanks

DerpZee OP

that tool was helpful! confirmed that the radiometric calibration changes i made locally are in the right path!

👍 1
Perlator

Yes, flights without terrain follow over predominantly flat terrain tend to produce better multispectral orthos - at least for me.

DerpZee OP

woah i think i had a breakthrough ... will post results soon

Perlator

Super interested in your results! Also interested in a ndvi_mean comparison for the whole ortho from WebODM, Pix4D and DroneDeploy. Are you able to provide this by any chance?

DerpZee OP

yup! i have it all.. dronedeploy export is janky ...

DerpZee OP
DerpZee OP

way better than what it was previosuly...

just the flight banding i am working on now

DerpZee OP
DerpZee OP

got it with blending

Perlator

Congrats! Can you recall which corrections and parameters you applied?

DerpZee OP

yeah i will make a post with everything .. just have to hit up a wedding.. i have a few more differeny varrying datasets just want to make sure it didnt introduce odd behaviours

DerpZee OP

they are in different conditions.. full canopy , low canopy.. various fields

DerpZee OP

just doing bit of rigorous testing...

DerpZee OP

i have a bunch of tests running still ... will get everything out hopefully today if not tomorrow

DerpZee OP

decided to run a really large matrice flight as well

DerpZee OP

claude findings

DerpZee OP

i did a bunch of tests and got it to smooth and work

DerpZee OP

ill put up the code changes later today !

💛 3
DerpZee OP

sorry for delay... still running final testing .. hopefully by this weekend .. i forked the repo so ill just publish it that way

poltrojan

I'm following up on this, because I plan to have a future purchase on Mavic3 Enterprise package and do ideally what you do

DerpZee OP

just a little busy but i will post the fork soon...

DerpZee OP

FYI i only made changes to ODX not WebODM

DerpZee OP

but you could swap the version i am sure to point to fork to generate teh ortho and see how it goes

👍 2 ❤️ 1
DerpZee OP

found one more bug .. running tests for it

rig support wasnt being used that opensfm supports ... adding that to odx as support as i caught an edge case with one of my test data

Need more help?

This discussion happened in the WebODM community Discord. Join and ask your own question.

Join View Original Thread

This page mirrors a public discussion from the WebODM Discord. How to request a removal.