Skip to content

Conversation

@LauLauThom
Copy link

This is a proposal to lift the maximum value for the group attribute of ROIs, from currently 255 to Integer.MAX_VALUE
This attribute is used for instance to group ROIs into categories, ROIs with the same group number would belong to the same category and will be depicted with the same color.

Currently, the group is limited to a max value of 255, I think because the original implementation was using a 8-bit LUT to get the color for a given ROI group. It turns out the Glasbey LUT used to depict the ROI can yield distinct color beyond this range (see (glasbeyLut.getRGB(group) in Roi.getGroupColor).
The rest of the code seems to also deal with the higher range without any issue.

The current implementation also allows storing corresponding names for the group. Those are stored in a String[]. The maximum size of an array is also Integer.MAX_VALUE, right ?

This proposal was motivated by people I had a chat with, who had the idea to use the group attribute to group ROIs across multiples Z-planes into pseudo 3D Rois.
But I think there could be other valid use cases where the current limitation of 255 might be problematic.

@LauLauThom LauLauThom marked this pull request as draft December 13, 2025 21:21
@LauLauThom LauLauThom force-pushed the increase-roigroup-max branch from cad2608 to ec04c7d Compare December 13, 2025 21:30
@LauLauThom LauLauThom marked this pull request as ready for review December 13, 2025 21:31
@ctrueden
Copy link
Member

@rasband What do you think? Do you like this change?

@rasband
Copy link
Member

rasband commented Dec 15, 2025

There’s a potential issue with this change: the group number is stored as a byte in the preferences file, and removing this limitation would not be straightforward.

@ctrueden
Copy link
Member

@rasband I looked at the ROI binary format, and had a brief conversation with Claude.ai about it:

What is the least disruptive change we can make to the ROI binary format to accommodate an increase in group size from uint8 to int32? Note the version field at the beginning of the format, documented in RoiDecoder. It appears the group field is part of the "header2".

Recommended Approach: Repurpose Reserved Bytes in Header2

Looking at the header2 structure:

  • Byte 30: GROUP (currently used, 1 byte = 0-255)
  • Byte 31: IMAGE_OPACITY (1 byte)
  • Bytes 32-35: IMAGE_SIZE (4 bytes, int)
  • Bytes 36-39: FLOAT_STROKE_WIDTH (4 bytes, float)

The key insight: Bytes 52-63 of header2 appear to be unused/reserved. Header2 is 64 bytes (HEADER2_SIZE = 64), and the last documented field is COUNTERS_OFFSET at byte 48 (4 bytes).

Proposed Solution:

  • Add a new int32 field at header2 offset 52 (or another unused offset in header2)
  • Increment VERSION to 229 (current is 228)
  • Backwards compatibility logic:
    • Writing: If version >= 229 and group > 255, write group as int32 at new offset; write 0 at byte 30 as a marker
    • Writing: If group <= 255, write to byte 30 as before for compatibility
    • Reading: If version >= 229 and byte 30 == 0, read int32 from new offset; otherwise use value at byte 30

This approach:

  • ✅ Maintains full backwards compatibility (old readers see byte 30)
  • ✅ Old files with groups 0-255 work unchanged
  • ✅ Minimal code changes
  • ✅ No changes to existing field positions
  • ✅ Uses reserved space efficiently
  • ✅ Graceful degradation: old readers reading new files with group>255 won't see the extended group assignment, which is semantically correct.

I agree with Claude that this approach could work. Thoughts?

@rasband
Copy link
Member

rasband commented Dec 17, 2025

Claude’s approach will probably work after the usual testing and debugging. Could we change the group number to a short to reduce the space used in header2? 64k groups should be sufficient.

I’m impressed by how knowledgeable Claude is about ImageJ programming. Can you get it to make the necessary changes?

Increments ROI format version to 229 and uses header2 offset 52
for groups > 255. Maintains backwards compatibility by using byte 30
as marker (value 0 indicates extended group at offset 52). Groups
<= 255 continue to use byte 30 for compatibility with older readers.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
@ctrueden
Copy link
Member

ctrueden commented Dec 17, 2025

Can you get it to make the necessary changes?

@rasband I asked Claude to implement the fix and make a git commit. Then I reviewed the result (which looks correct to me) and pushed the result to this PR.

Edit: I also pushed a followup commit decreasing the MAX_ROI_GROUP value to 65535.

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.

3 participants