Skip to content

Conversation

petebankhead
Copy link
Member

@petebankhead petebankhead commented Feb 26, 2025

Major performance improvements when creating complex ROIs (e.g. from a pixel classifier) and rendering them in the viewer.

ContourTracer improvements

Building on #1520, generating complex annotations can now be much faster.

For example, I applied a moderately high-resolution thresholder to OS-2.ndpi to generate a monster multipolygon with millions of vertices.

image

On the same MacBook Pro:

Version Time
v0.5.1 ~494 seconds
v0.6.0-rc3 ~133 seconds
With this PR ~15 seconds

The key step is to avoid creating polygons for each individual tile and unioning them at the end, because unioning is too expensive.

Instead, we trace all the lines across all the tiles and then pass them all in one big go to a Polygonizer at the end.

Some extra tricks and optimizations are used to speed this up, and also to reduce the number of vertices in the final result, without changing the shape.

The annotation above had about 7-8 million coordinates, while with this PR it is just under 5 million.

This was inspired by CoverageUnion not quite working... it was very fast, but gave invalid results.

Note that trying to generate regions at an even higher resolution is still unreasonably slow - so it's still not advised to apply pixel classification / thresholding to a whole slide image at full (or almost full) resolution: restricting to regions of interest can still be necessary.

Faster rendering

Generating huge ROIs is one thing, rendering them is another.

This PR also makes this much more efficient using 2 tricks... and then a 3rd trick that renders one of the first two moot.

The tricks are:

  1. Speed up ShapeSimplifier by replacing Java's built in PriorityQueue with something smaller & faster to handle removals and insertions
  2. Apply shape simplification progressively & across more resolutions when zooming out in the viewer (rather than simplifying the original shape at a few downsamples only)
  3. Skip ShapeSimplifier entirely in the viewer, and instead introduce DownsampledShapeCache. This uses a much simpler method of shape simplification, while also discarding very small fragments or holes.

The end result looks a bit different. I don't think it's worse, but the rendering might benefit from some tweaks in the future.

More critically, it is far more responsive. Previously, rendering the ROI might take up to a minute and it would be sluggish when zooming in & out until the simplified shapes were built. Viewing the image at a low resolution was slower than at a high resolution.

Now viewing at a low resolution should be fast, and the lag when zooming should be much less. Images with complex ROIs should also open much faster.

Inspired by CoverageUnion not quite working... it was very fast, but gave invalid results.
This code (still needs cleaned up!) avoids unioning many polygons by instead delaying polygonization until the final step.
This allows us to remove duplicates along tile boundaries before passing *everything* to a `Polygonizer`.

There is probably still scope for some optimizations (e.g. replacing `LineDissolver`) and a need to clean up the code and update the comments. But currently it shortens the time to trace ~8 million vertices to ~25 seconds on my laptop, rather than >> 100 seconds.

So committing this code now, lest I break it too badly...
@petebankhead petebankhead added this to the v0.6.0 milestone Feb 26, 2025
This is especially important when displaying complex polygons in the viewer.
Squeezing out a bit more performance.
Readability suffering further...
Calculate sequentially across downsamples & also improve caching.
Drop small regions/holes that aren't visible.
This speeds up the responsiveness of object lists
@petebankhead petebankhead changed the title Initial speed improvements for ContourTracing Much faster creation & rendering of complex ROIs Mar 1, 2025
@petebankhead petebankhead marked this pull request as ready for review March 1, 2025 21:03
@petebankhead petebankhead merged commit aab1127 into qupath:main Mar 2, 2025
3 checks passed
@petebankhead petebankhead deleted the contour-tracing branch March 2, 2025 13:59
* @param lines
* @return
*/
static Collection<CoordinatePair> removeDuplicatesCompletely(Collection<CoordinatePair> lines) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These kind of functions could be unit tested

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