Skip to content

regionprops_table output placement and format - #1088

Merged
rapids-bot[bot] merged 6 commits into
rapidsai:release/26.08from
grlee77:grelee/regionprops-update
Jul 29, 2026
Merged

regionprops_table output placement and format#1088
rapids-bot[bot] merged 6 commits into
rapidsai:release/26.08from
grlee77:grelee/regionprops-update

Conversation

@grlee77

@grlee77 grlee77 commented May 22, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR adds two options to cucim.skimage.measure.regionprops_table:

  • copy_output_to_host: optionally copy returned tabular values from GPU to host.
  • to_table: optionally keep fixed-size multidimensional properties unsplit instead of expanding them into one table column per element.

Motivation

regionprops_table returns a table-like dictionary, but the current default values are CuPy arrays on the GPU. That is efficient for GPU-side follow-up work, but inconvenient for the common case where users want to pass the result directly to pandas or other host-side tabular tools.

At the same time, some GPU workflows benefit from keeping multidimensional outputs like centroid or inertia_tensor as device arrays with their natural shapes, instead of splitting them into separate 1D columns.

For cuCIM, regionprops_table(..., batch_processing=True) is the recommended performant region property API. It computes properties for all labels in batched GPU kernels, while regionprops returns RegionProperties objects whose properties are evaluated one region at a time. The RegionProperties and regionprops docstrings now call this out, and regionprops emits a one-time warning pointing users to the batch path.

Main usage modes

  1. Host tabular output for pandas:

    props = regionprops_table(
        labels,
        properties=("label", "area", "centroid"),
        batch_processing=True,
        to_table=True,
        copy_output_to_host=True,
    )

    In this mode, fixed-size multidimensional properties are split into table columns such as centroid-0 and centroid-1, and returned arrays are NumPy arrays suitable for direct pandas use.

  2. GPU-native structured output:

    props = regionprops_table(
        labels,
        properties=("label", "centroid", "inertia_tensor"),
        batch_processing=True,
        to_table=False,
        copy_output_to_host=False,
    )

    In this mode, outputs remain on the GPU and fixed-size multidimensional properties keep their natural shapes, for example centroid as (num_regions, ndim) and inertia_tensor as (num_regions, ndim, ndim).

    to_table=False is also supported with batch_processing=False for API symmetry, although batch_processing=True remains the recommended performant cuCIM path.

Behavior notes

  • Variable-size properties such as coords return a NumPy object array when to_table=True to match the scikit-image convention. The elements of the object array are either NumPy or CuPy arrays. When to_table=False a tuple of arrays is used instead of an object array.
  • regionprops emits a one-time warning recommending regionprops_table(..., batch_processing=True) for performant cuCIM GPU region property computation.

Tests

The updated tests cover:

  • Host vs device output for normal tabular regionprops_table output.
  • to_table=False preserving unsplit bbox, centroid and inertia_tensor shapes in both non-batch and batch paths.
  • coords behavior for unsplit variable-size outputs.
  • The one-time regionprops performance warning.

grlee77 added 3 commits May 18, 2026 08:58
…put format

copy_output_to_host : if True, all columns will correspond to host NumPy arrays
to_table=False for batch_processing=True: do not split fixed size multidimensional arrays like centroid or inertia_tensor to one column per value
@grlee77 grlee77 added this to the v26.08 milestone May 22, 2026
@grlee77
grlee77 requested a review from a team as a code owner May 22, 2026 16:05
@grlee77 grlee77 added this to cucim May 22, 2026
@grlee77 grlee77 added improvement Improves an existing functionality non-breaking Introduces a non-breaking change labels May 22, 2026
@jakirkham
jakirkham changed the base branch from main to release/26.08 July 16, 2026 20:07

@jakirkham jakirkham left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks Greg! 🙏

This looks like a nice improvement.

Was wondering if we could take some of the changes here further for more perf gains.

Comment on lines +987 to +993
column_buffer = np.empty(n, dtype=dtype)
if copy_to_host:
for i in range(n):
column_buffer[i] = cp.asnumpy(regions[i][prop])
else:
for i in range(n):
column_buffer[i] = regions[i][prop]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Idk if this is possible, but would it make sense to structure regions to reverse the indexing?

IOW is it possible to do something like regions[prop][i]?

If so, this would allow this to become something like...

Suggested change
column_buffer = np.empty(n, dtype=dtype)
if copy_to_host:
for i in range(n):
column_buffer[i] = cp.asnumpy(regions[i][prop])
else:
for i in range(n):
column_buffer[i] = regions[i][prop]
column_buffer = np.empty(n, dtype=dtype)
if copy_to_host:
column_buffer[...] = cp.asnumpy(regions[i][prop])
else:
column_buffer[...] = regions

...or maybe even eliminate that last copy altogether.

Admittedly this might be beyond the scope of this PR, but want to raise it since it seems like there are some efficiency gains on the table here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

If you reach this _props_to_dict helper function code you are already on the MUCH slower path (batch_processing=False) within regionprops_table, so I don't see much point in trying to optimize this helper. For performance it is best to always call regionprops_table with batch_processing=True so a single kernel computes for all regions rather than launching kernels separately for each individual region.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Agree we should prefer the table approach. Maybe we should consider deprecating the fallback at some point

@jakirkham

Copy link
Copy Markdown
Member

/merge

@jakirkham

Copy link
Copy Markdown
Member

Thanks Greg! 🙏

@rapids-bot
rapids-bot Bot merged commit 6c30b64 into rapidsai:release/26.08 Jul 29, 2026
68 checks passed
@github-project-automation github-project-automation Bot moved this to Done in cucim Jul 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

improvement Improves an existing functionality non-breaking Introduces a non-breaking change

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants