Skip to content

Disable scale solving during alignment#2054

Merged
smathermather merged 1 commit into
OpenDroneMap:masterfrom
MJohnson459:fix-align-scaling
Jul 22, 2026
Merged

Disable scale solving during alignment#2054
smathermather merged 1 commit into
OpenDroneMap:masterfrom
MJohnson459:fix-align-scaling

Conversation

@MJohnson459

Copy link
Copy Markdown
Contributor

I apologies in advance for the length of this PR description for what is basically a one line fix. I'm still not familiar with all of this so I wanted to be explicit in case I messed up.

The OATS brighton alignment tests (--align with a GeoTIFF and a LAZ reference) have been failing: ODM completes with exit 0 but logs Cannot compute alignment matrix / Alignment will be skipped, and no registration.json is produced.

Bisecting release images against the same dataset I found that the error happened between 3.3.4 and 3.5.1 (relatively large range due to the docker images available, but there weren't too many that touched codem or align).

In 3.0.4 which is the first available image with the --align flag, the codem feature matching debug image (opensfm/stats/codem/dsm_feature_matches.png) looked like this:

working_304_2022era

In latest it looks like this:

failing_latest_freescale

In a70e7445 the default --feature-type changed from sift to dspsift. That doesn't touch the alignment code but it does change the reconstruction, whose DSM is what codem tries to match against the reference.

passing_latest_sift_badscale

This does fix the test, but rather than stop there, I wanted to understand why it was failing with dspsift. As I understand it, the --align flag is meant to align a new output with a secondary source - either an old run or even something third party? In the oats case, the brighton dataset has a generated dsm.tif and model.laz file that we use as the base to align to. These were generated with ~v3.0.2. When these were generated, we almost guarantee a match as we are using the same version to generate both the reference and comparison. However over time the output has drifted (i.e. getting more accurate), but the change to dspsift was the final nail where the reconstruction's DSM was different enough that this made it too hard for codem to find correct matches.

If this is the case, switching back to sift isn't necessarily the right move, it would just be a fix to the test, not the product. We could just update the datasets generated files, but that would effectively be the same as disabling the test.

Instead I had a look to see if we could make the feature matching more stable.

One thing that stood out to me is that the 3.5.1 and 3.5.6 releases fail due to a rejected scale being found. This seemed to be caused by bad feature matches. I also noted that the "correct" scale is actually always 1.0. This makes sense when we realise both sides of the match are georeferenced metric data in the same CRS. To test this, I removed the scale as a solving parameter in codem which effectively locked the scale to 1. This removes a degree of freedom from the solver which, in theory, means there might be fewer matches, but the matches should be higher quality.

working_latest_lockedscale

Notice that the images are almost exactly the same as the failing case, but the features are matched much better. This is very similar to the sift fix above, but this time the scale is a perfect 1.0 (as we fixed it). The sift fix while looking correct, actually had a scale of roughly 0.8 and so would have resized the model/outputs by 20%.

Version Alignment Solved scale
3.0.4 pass 0.999
3.3.4 pass 0.998
3.5.1 / 3.5.6 fail rejected in coarse (out of 0.67–1.5 band)
latest fail coarse solved 0.033
latest (sift) pass 0.797
this PR pass 1.0 (fixed)

One last thing, the dsm.tif in the brighton dataset actually has a different (wrong?) CSM "UTM zone 10N". This didn't seem to affect the bug though it might affect other outputs. The model.laz as a CSM of "UTM zone 15N" which I believe is correct but still failed above.

I believe this is the correct fix, but I would appreciate more experienced eyes on it @smathermather.

The reconstruction and the alignment reference are both expressed in
the same metric CRS by the time codem compares them, so the scale
between them is 1 by construction. Leaving the scale free lets RANSAC
fit degenerate solutions on feature-poor scenes (alignment fails) or
absorb noise into a nonphysical scale factor (outputs are silently
rescaled by 10-20%). Solving only for rotation and translation makes
registration robust on scenes where it currently fails and guarantees
alignment preserves the reconstruction's metric accuracy.
@MJohnson459

Copy link
Copy Markdown
Contributor Author

Here are the three orthophotos generated from the brighton oats runs:

Master: master_orthophoto.tif
Master with sift: master_sift_orthophoto.tif
This PR: noscaling_orthophoto.tif

It's clear when loading these in QGIS that the alternative fix of reverting to sift would result in substantially different output (its noticably smaller).

@smathermather

smathermather commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

This is a great analysis. Pull request to update the dataset:
OpenDroneMap/drone_dataset_brighton_beach#1

If I understand the problem correctly, this may mean that older tests with align will be more likely to fail, but newer ones (e.g. with dspsift) will be more likely to succeed.

edit:
We should probably be setting DSM_SOLVE_SCALE and ICP_SOLVE_SCALE to False by default.

@MJohnson459

Copy link
Copy Markdown
Contributor Author

Thanks!

If I understand the problem correctly, this may mean that older tests with align will be more likely to fail, but newer ones (e.g. with dspsift) will be more likely to succeed.

Yes, but mostly because of a quirk of the test. As we add more features or change algorithms the DSM will change. Eventually those changes build up to be large enough that the feature matching fails to find a good fit. But this is change relative to something, and in the test case that's files generated around 2022 (~v3.0.2).

In some ways this is exactly the value of the test. Feature matching against two identical images would be easy - the drift between versions has made the test harder over time but that doesn't make it broken. This PR allows us to work with a broader range of inputs.

Pull request to update the dataset: OpenDroneMap/drone_dataset_brighton_beach#1

So actually, given the above, I would argue to leave the dataset in its current state. As it is, the test is effectively performing the use case of "I generated this map with ODM 4 years ago, and I want to now see the difference". That is, it makes sense for --align to work with the output from older releases (which it does with this PR).

The other doubt for me is that with an updated dataset, I suspect the test would pass without this PR (same-version references nearly always match). That means the test would stop acting as a regression test for this bug.

We should probably be setting DSM_SOLVE_SCALE and ICP_SOLVE_SCALE to False by default.

As far as I can see we only create a CodemRunConfig here so this would set them to False for us. Is there somewhere else we use Codem I missed?

@smathermather

smathermather commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Makes sense, although I think the purpose of the test is whether align is working generally, and secondarily, it helped us find another issue.

So, the oats test probably needs rewritten to generate the dsm and pc, and then run again to align to those outputs, rather than using a static dataset. Or better yet, use a different, slightly larger dataset that can be split into two subsets to be aligned with CODEM.

Edit: without looking (away from keyboard), this should be the only use of CODEM.

@smathermather
smathermather merged commit 32cfaac into OpenDroneMap:master Jul 22, 2026
2 checks passed
@MJohnson459
MJohnson459 deleted the fix-align-scaling branch July 22, 2026 16:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants