Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add DetectionsMask label class #4500

Closed
wants to merge 2 commits into from

Conversation

kemaleren
Copy link
Contributor

@kemaleren kemaleren commented Jun 14, 2024

What changes are proposed in this pull request?

This PR adds a new Label class called DetectionsMask for mask-based detections, analogous to how masks in the Segmentation work for semantic segmentations. It also adds support for TIFF mask files, and it fixes an off-by-one bug in _parse_stuff_instance() and _parse_thing_instances().

This functionality is needed because large images with lots of detections can easily exceed the 16mb storage capacity of a MongoDB document. Moreover, many computer vision models work directly with detection masks, so it makes sense for FiftyOne to support them. These masks can be easily compressed for efficient storage and bandwidth, and they can stored on disk just like segmentation masks. In the future, they could also be chunked for more efficient access, such as with Zarr.

These masks are similar to semantic segmentation masks, but instead of each value in the mask corresponding to a segmentation class, it corresponds to an individual object. For instance this mask for a 5x5 image:

mask = [
  [0, 0, 0, 0, 0],
  [0, 1, 1, 0, 0],
  [0, 1, 1, 2, 2],
  [0, 3, 3, 3, 2],
  [0, 3, 3, 0, 0]
]

with labels = {1: “A”, 2: “A”, 3: “B”} corresponds to three detected objects, with labels “A” for the first two objects and “B” for the third.

The new class can be used similarly to the Segmentations class:

dm = DetectionsMask(mask, labels=labels, default_label="A")

# look up the label of an object
dm.get_idx_label(1)  # => `"A"`

# write the mask to disk
dm.export_mask("/path/to/mask_file.tif", update=True)

# read the mask from disk or memory
mask = dm.get_mask()

# convert to other label types
dets = dm.to_detections()
seg = dm.to_segmentation()
poly = dm.to_polylines()

# convert back to DetectionMask
dets.to_detections_mask(frame_size=(5, 5))
seg.to_detections_mask(mask_types='thing')
poly.to_detections_mask(frame_size=(5, 5))

I chose to implement this new DetectionsMask class after considering multiple other options:

  • Option 1: add a new mask type to Segmentation, in addition to the 'stuff' and 'thing' types. This mask type would be called something like 'objects' or 'instances', to specify that each unique value in the mask corresponds to a single object, not to a connected component. I list this option for completeness, but I think it is the worst one, so it will not be considered further.

    • Pros
      • Easiest to code, since the current segmentation code already implements similar functionality.
      • The Segmentation class already has a notion of 'thing' segmentations, which is the same idea as this except that it uses connected components to separate objects. This option would simply adds the option to be explicit about masking individual objects, instead of connected components.
    • Cons
      • The 'stuff' and 'thing' distinction is already hard to remember. Adding a third would be even more confusing.
      • Detections aren’t actually a segmentation, so it would be confusing to have to sometimes use the Detections class and sometimes use Segmentation class, depending on how the data is stored.
      • Segmentations can be stored as RGB masks, which does not make sense for detections.
  • Option 2: Extend the Detections class to support a mask and mask_path attribute, much like Segmentation does.

    • Pros
      • Mirrors the implementation of masks in Segmentation.
      • Uses the same class for all types of detections.
    • Cons
      • Lots of extra bookkeeping to keep track of whether a Detections object is being used to store a list of Detection objects or a mask. Is the mask, the mask_path, or the detections list the canonical set of detections? If it’s detections, then what should the labels attribute be set to?
      • When converting another Label to Detections, should it create a list of detections, or a mask?
  • Option 3: Create a new class called DetectionMask, which has a mask and mask_path attribute like Segmentation instead of a detections list.

    • Pros
      • Makes it explicit that the detections are stored in a mask, so there’s no confusion about how to sync the detections in the mask with the detections stored in a list.
    • Cons
      • Ideally, code using the Detections label shouldn’t have to care about how the detections are stored.
      • Need to extend all code that uses detections to also work for this class.

This PR implements option 3. If we decide option 2 make more sense, the two classes can be merged instead.

The main reason I’ve chosen option 3 is that Detections can store overlapping detections, whereas DetectionsMask cannot. It would be confusing if a single instance of Detections could lose information depending on its internal representation. There is already precedence in FiftyOne for conversions between different label types to lose information.

The tradeoff is now there are more changes that need to be made in the rest of the code base to support the new DetectionsMask class. In most cases, I expect a simple conversion from DetectionsMask to Detections, which does not lose information, to be sufficient.

Rather than duplicate the mask functionality from Segmentation, the code for dealing with masks has been broken out into a _HasMask mixin. It moves _read_mask() and _write_mask() into the mixin as abstract methods, so that each class using the mixin has to implement them. Making each class that uses the mixin implement these read/write functions keeps backwards compatibility for the Segmentation class, which can store masks as either grayscale or RGB images. Storing masks as RGB images confuses the data (the mask itself) with its visual representation (the color), which does not make sense for object detections. Therefore, DetectionsMask implements these functions differently, to ensure the mask is always a grayscale PNG or TIFF. This approach of only allowing 2D arrays has the following advantages:

  • Object detection masks should not be visualized by a hardcoded color, so storing them in an RGB image could lead to confusion or bugs in code that uses these masks.
  • The number of object detections could exceed the limit of RGB images. TIFF files support up to 64-bit integers.
  • The old code, which is still used in the Segmentation class, casts masks to integers, which could lead to bugs or loss of data. It is better fail early if the user tries to save a floating-point mask that contains non-integer values.

Other changes

  • Fixes an off-by-one bug found in _parse_stuff_instance() and _parse_thing_instance(). Reduced code duplication by reusing the former function as part of _parse_thing_instance(). Even if the rest of this PR is rejected, we should break this change out into its own PR.
  • Add support for TIFF mask files, to extend the number of detections that can be represented in a mask file without having to convert back and forth between RGB representations. This was only done for the new label class, but it could be extended to be used with Segmentation too.

Other issues

  • How to store labels for each detection? I chose to store a default_label and a dictionary labels mapping each object index to a label. This method will still eventually run out of space in a 16mb document, if there are enough objects with different labels. See the notes below about default_label.
  • Adds a bunch of new pairs of conversions to support. See the notes below about conversions to other Label types.
  • Dataset versioning does not work with mask files, just like it already doesn’t work for disk-backed segmentations.

About default_label

  • This is a primitive way of compressing the labels, and it gives a bit more information about the DetectionsMask if it only contains one label, or one common label.
  • Ideally this would be replaced with a better method. For instance a dictionary mapping labels to sets of indices would be more compressed and allow efficient lookups. However, this approach is not currently possible because there is no SetField.

Conversions to other Label types, and loss of information

  • Because of the way different Label types store data, conversions between them often lose information. This was already the case before adding this new Label type, but because this PR adds new conversion options, I detail them here.
  • DetectionMask <-> Detections should be identical (with possibly different representations) IF detections don’t overlap
    • If they do overlap, Detections -> DetectionMasks loses information. Whichever overlapping detection gets rendered last wins.
  • Detections/DetectionsMask <-> Polyline loses information because of tolerance and overlap. When objects overlap, last rendered wins.
  • Detections/DetectionsMask -> Segmentation loses information because of overlapping or even connected objects being merged into a single segmentation class.

Further work needed to complete this PR

  • Confirm design of the DetectionsMask class with FiftyOne team. Decide if they want to add this functionality at all, and if so, whether it should be a separate class or merged with Detections.
  • test_label_conversion() has gotten quite complicated. Maybe it should be broken up?
  • Update documentation.
  • Either update the rest of the code base and the web app to support DetectionsMask labels, or decide to merge Detections and DetectionsMask.
  • The web app needs to be updated to support this new label class. Also, the way the web app is currently implemented means these detection masks would take up a prohibitive amount of memory for large datasets and/or large images with many detections. So this pull request also needs the web app to be updated to use some sort of sparse storage for masks, or to convert the mask to a list of Detection objects on the client side. It also probably should have the option to only load DetectionMask labels in the modal view, to avoid trying to show all the detections in the grid view.
  • This PR only works for the open-source version of FiftyOne. FiftyOne for Teams will need to be updated to work with this new code.

How is this patch tested? If it is not, please explain why.

Added tests to tests/unittests/label_tests.py.

Release Notes

Is this a user-facing change that should be mentioned in the release notes?

  • No. You can skip the rest of this section.
  • Yes. Give a description of this change to be included in the release
    notes for FiftyOne users.

Adds a new Label class called DetectionsMask for mask-based detections, with support for TIFF mask files. Fixes an off-by-one bug during mask conversion.

What areas of FiftyOne does this PR affect?

  • App: FiftyOne application changes
  • Build: Build and test infrastructure changes
  • Core: Core fiftyone Python library changes
  • Documentation: FiftyOne documentation changes
  • Other

Summary by CodeRabbit

  • New Features

    • Introduced DetectionsMask entity for enhanced mask-related operations.
  • Enhancements

    • Improved mask manipulation and conversion capabilities within Segmentation and DetectionsMask classes.
  • Dependencies

    • Added "tifffile" as a new dependency to support enhanced mask functionalities.

Copy link
Contributor

coderabbitai bot commented Jun 14, 2024

Walkthrough

The latest update enhances the FiftyOne tool by integrating a new DetectionsMask feature. It expands mask-related functionalities within the core labels module, introduces new classes and methods for mask operations, and updates relevant dependencies. This significantly boosts capabilities for handling and converting detection masks and segmentations, supporting more advanced image analysis.

Changes

Files Change Summaries
fiftyone/public.py Added DetectionsMask to the list of exported entities.
fiftyone/core/labels.py Major updates: new classes (_HasMedia, _HasMask, DetectionsMask, Segmentation), methods for mask operations, and utility function modifications.
setup.py Added "tifffile" to the list of dependencies.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant FiftyOne
    participant Label
    participant DetectionsMask
    
    User->>FiftyOne: Request to handle detection mask
    FiftyOne->>Label: Convert to DetectionsMask
    Label->>DetectionsMask: Create instance
    DetectionsMask->>Label: Return mask representation
    Label->>FiftyOne: Pass mask data
    FiftyOne->>User: Provide detection mask info
Loading

Poem

In fields of codes, new masks arise,
To offer sights through clearer skies. 🌟
With DetectionsMask, we analyze and see,
The world of pixels, more vividly.
Dependencies added, we advance this day,
FiftyOne’s power, here to stay. 🐇✨


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

Share
Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai generate interesting stats about this repository and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (invoked as PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Outside diff range and nitpick comments (12)
tests/unittests/label_tests.py (2)

Line range hint 53-53: Use key in dict instead of key in dict.keys() for better performance and readability.

- if key in dict.keys():
+ if key in dict:

Line range hint 623-623: Remove assignments to unused variables id1 and id4 to clean up the code and avoid unnecessary memory usage.

- id1 = detections[0].id
- id4 = detections[3].id

Also applies to: 626-626

fiftyone/core/labels.py (10)

Line range hint 39-39: Remove unnecessary inheritance from object.

- class _NoDefault(object):
+ class _NoDefault:

Line range hint 100-100: Use f-string for formatting.

- "%s has no attribute '%s'" % (self.__class__.__name__, name)
+ f"{self.__class__.__name__} has no attribute '{name}'"

Line range hint 126-128: When re-raising exceptions, use raise ... from None to avoid chaining exceptions that are unrelated.

- raise AttributeError(
-    "%s has no attribute '%s'" % (self.__class__.__name__, name)
- )
+ raise AttributeError(
+    f"{self.__class__.__name__} has no attribute '{name}'"
+ ) from None

Line range hint 268-268: Use f-string for formatting.

- "%s has no attribute '%s'" % (self.__class__.__name__, name)
+ f"{self.__class__.__name__} has no attribute '{name}'"

Line range hint 307-310: When re-raising exceptions, use raise ... from None to avoid chaining exceptions that are unrelated.

- raise AttributeError(
-    "%s has no attribute '%s'" % (self.__class__.__name__, name)
- )
+ raise AttributeError(
+    f"{self.__class__.__name__} has no attribute '{name}'"
+ ) from None

Line range hint 343-343: Remove unnecessary inheritance from object.

- class _HasLabelList(object):
+ class _HasLabelList:

Line range hint 1015-1018: Use a ternary operator to simplify the assignment.

- if filled is not None:
-    _filled = filled
- else:
-    _filled = self.filled
+ _filled = filled if filled is not None else self.filled

Line range hint 1892-1892: Remove unnecessary parentheses.

- if (target is not None and not is_rgb and target > 255):
+ if target is not None and not is_rgb and target > 255:

Line range hint 1897-1900: Use a ternary operator to simplify the assignment.

- if target is not None:
-    is_rgb = fof.is_rgb_target(target)
- else:
-    is_rgb = False
+ is_rgb = fof.is_rgb_target(target) if target is not None else False

Line range hint 1906-1909: Use a ternary operator to simplify the assignment.

- if target is not None and not is_rgb and target > 255:
-    dtype = int
- else:
-    dtype = np.uint8
+ dtype = int if target is not None and not is_rgb and target > 255 else np.uint8
Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 31a5bb2 and be5ac20.

Files selected for processing (2)
  • fiftyone/core/labels.py (18 hunks)
  • tests/unittests/label_tests.py (1 hunks)
Additional context used
Ruff
tests/unittests/label_tests.py

53-53: Use key in dict instead of key in dict.keys() (SIM118)

Remove .keys()


623-623: Local variable id1 is assigned to but never used (F841)

Remove assignment to unused variable id1


626-626: Local variable id4 is assigned to but never used (F841)

Remove assignment to unused variable id4

fiftyone/core/labels.py

39-39: Class _NoDefault inherits from object (UP004)

Remove object inheritance


100-100: Use format specifiers instead of percent format (UP031)

Replace with format specifiers


126-128: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling (B904)


127-127: Use format specifiers instead of percent format (UP031)

Replace with format specifiers


268-268: Use format specifiers instead of percent format (UP031)

Replace with format specifiers


307-310: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling (B904)


308-309: Use format specifiers instead of percent format (UP031)

Replace with format specifiers


315-318: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling (B904)


316-317: Use format specifiers instead of percent format (UP031)

Replace with format specifiers


343-343: Class _HasLabelList inherits from object (UP004)

Remove object inheritance


583-583: No explicit stacklevel keyword argument found (B028)


635-635: No explicit stacklevel keyword argument found (B028)


650-650: Class _HasMedia inherits from object (UP004)

Remove object inheritance


1015-1018: Use ternary operator _filled = filled if filled is not None else self.filled instead of if-else-block (SIM108)

Replace if-else-block with _filled = filled if filled is not None else self.filled


1892-1892: Avoid extraneous parentheses (UP034)

Remove extraneous parentheses


1897-1900: Use ternary operator is_rgb = fof.is_rgb_target(target) if target is not None else False instead of if-else-block (SIM108)

Replace if-else-block with is_rgb = fof.is_rgb_target(target) if target is not None else False


1906-1909: Use ternary operator dtype = int if target is not None and not is_rgb and target > 255 else np.uint8 instead of if-else-block (SIM108)

Replace if-else-block with dtype = int if target is not None and not is_rgb and target > 255 else np.uint8


1924-1927: Use ternary operator is_rgb = False if mask_targets is None else fof.is_rgb_mask_targets(mask_targets) instead of if-else-block (SIM108)

Replace if-else-block with is_rgb = False if mask_targets is None else fof.is_rgb_mask_targets(mask_targets)


2121-2121: Use format specifiers instead of percent format (UP031)

Replace with format specifiers


2135-2135: Use format specifiers instead of percent format (UP031)

Replace with format specifiers


2254-2255: Use format specifiers instead of percent format (UP031)

Replace with format specifiers


2264-2265: Use format specifiers instead of percent format (UP031)

Replace with format specifiers


2274-2275: Use format specifiers instead of percent format (UP031)

Replace with format specifiers

Additional comments not posted (2)
tests/unittests/label_tests.py (2)

473-506: The new test cases for to_detections_mask and related conversions are comprehensive and cover various scenarios including conversions between different mask types and label types. This is crucial for ensuring that the new functionality behaves as expected across different use cases.


553-573: The tests for _parse_stuff_instance and _parse_thing_instance methods are well-implemented and check both the bounding box calculations and the correctness of the generated instance masks. These tests are essential for validating the changes made in the PR to fix the off-by-one error mentioned.

fiftyone/core/labels.py Show resolved Hide resolved
@benjaminpkane benjaminpkane self-assigned this Jun 17, 2024
@benjaminpkane
Copy link
Contributor

benjaminpkane commented Jun 18, 2024

Hi @kemaleren! 👋

This code looks great! Thank you for the thorough description 🙇

Others may chime in, but this addition makes sense to me. Note that CVPR is going on this week, so we will be short on cycles. But next week I will look into App support

@kemaleren kemaleren changed the title add DetectionsMask label class Add DetectionsMask label class Jun 18, 2024
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Outside diff range and nitpick comments (14)
setup.py (2)

Line range hint 27-28: Replace percent formatting with modern format specifiers for better readability and performance.

- "Release version does not match version: %s and %s" % (version, VERSION)
+ f"Release version does not match version: {version} and {VERSION}"

Line range hint 122-122: The open function's mode parameter is redundant here as the default mode is 'r' (read), which is what is being used.

- with open("README.md", "r") as fh:
+ with open("README.md") as fh:
tests/unittests/label_tests.py (2)

Line range hint 55-55: Refactor the check for dictionary keys to use a more Pythonic approach.

- if "key" in dict.keys():
+ if "key" in dict:

Line range hint 658-658: Variables id1 and id4 are assigned but never used within the scope of the function. Consider removing these if they are not needed.

- id1 = detections[0].id
- id4 = detections[3].id

Also applies to: 661-661

fiftyone/core/labels.py (10)

Line range hint 39-39: Remove unnecessary inheritance from object.

- class _NoDefault(object):
+ class _NoDefault:

Line range hint 100-100: Use format specifiers instead of percent format for string formatting.

- "%s has no attribute '%s'" % (self.__class__.__name__, name)
+ "{} has no attribute '{}'".format(self.__class__.__name__, name)

Line range hint 126-128: When re-raising exceptions, use raise ... from err or raise ... from None to distinguish them from errors in exception handling.

- raise AttributeError(
-     "%s has no attribute '%s'" % (self.__class__.__name__, name)
- )
+ raise AttributeError(
+     "{} has no attribute '{}'".format(self.__class__.__name__, name)
+ ) from None

Line range hint 268-268: Use format specifiers for string formatting.

- "%s has no attribute '%s'" % (self.__class__.__name__, name)
+ "{} has no attribute '{}'".format(self.__class__.__name__, name)

Line range hint 307-310: When re-raising exceptions, use raise ... from err to provide more context about the original exception.

- raise AttributeError(
-     "%s has no attribute '%s'" % (self.__class__.__name__, name)
- )
+ raise AttributeError(
+     "{} has no attribute '{}'".format(self.__class__.__name__, name)
+ ) from err

Line range hint 343-343: Remove unnecessary inheritance from object.

- class _HasLabelList(object):
+ class _HasLabelList:

Line range hint 1015-1018: Use a ternary operator to simplify the assignment.

- if filled is not None:
-     _filled = filled
- else:
-     _filled = self.filled
+ _filled = filled if filled is not None else self.filled

Line range hint 1892-1892: Avoid extraneous parentheses.

- if (target is not None and not is_rgb and target > 255):
+ if target is not None and not is_rgb and target > 255:

Line range hint 1897-1900: Use a ternary operator to simplify the assignment.

- if target is not None:
-     is_rgb = fof.is_rgb_target(target)
- else:
-     is_rgb = False
+ is_rgb = fof.is_rgb_target(target) if target is not None else False

Line range hint 1906-1909: Use a ternary operator to simplify the assignment.

- if target is not None and not is_rgb and target > 255:
-     dtype = int
- else:
-     dtype = np.uint8
+ dtype = int if target is not None and not is_rgb and target > 255 else np.uint8
Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

Commits

Files that changed from the base of the PR and between be5ac20 and e392b08.

Files selected for processing (4)
  • fiftyone/public.py (1 hunks)
  • fiftyone/core/labels.py (18 hunks)
  • setup.py (1 hunks)
  • tests/unittests/label_tests.py (3 hunks)
Additional context used
Ruff
setup.py

27-28: Use format specifiers instead of percent format (UP031)

Replace with format specifiers


122-122: Unnecessary open mode parameters (UP015)

Remove open mode parameters

fiftyone/__public__.py

16-31: Module level import not at top of file (E402)


17-17: .core.aggregations.Aggregation imported but unused (F401)

Remove unused import


18-18: .core.aggregations.Bounds imported but unused (F401)

Remove unused import


19-19: .core.aggregations.Count imported but unused (F401)

Remove unused import


20-20: .core.aggregations.CountValues imported but unused (F401)

Remove unused import


21-21: .core.aggregations.Distinct imported but unused (F401)

Remove unused import


22-22: .core.aggregations.FacetAggregations imported but unused (F401)

Remove unused import


23-23: .core.aggregations.HistogramValues imported but unused (F401)

Remove unused import


24-24: .core.aggregations.Mean imported but unused (F401)

Remove unused import


25-25: .core.aggregations.Quantiles imported but unused (F401)

Remove unused import


26-26: .core.aggregations.Schema imported but unused (F401)

Remove unused import


27-27: .core.aggregations.ListSchema imported but unused (F401)

Remove unused import


28-28: .core.aggregations.Std imported but unused (F401)

Remove unused import


29-29: .core.aggregations.Sum imported but unused (F401)

Remove unused import


30-30: .core.aggregations.Values imported but unused (F401)

Remove unused import


32-32: Module level import not at top of file (E402)


32-32: .core.collections.SaveContext imported but unused (F401)

Remove unused import: .core.collections.SaveContext


33-33: Module level import not at top of file (E402)


33-33: .core.config.AppConfig imported but unused (F401)

Remove unused import: .core.config.AppConfig


34-45: Module level import not at top of file (E402)


35-35: .core.dataset.Dataset imported but unused (F401)

Remove unused import


36-36: .core.dataset.list_datasets imported but unused (F401)

Remove unused import


37-37: .core.dataset.dataset_exists imported but unused (F401)

Remove unused import


38-38: .core.dataset.load_dataset imported but unused (F401)

Remove unused import


39-39: .core.dataset.delete_dataset imported but unused (F401)

Remove unused import


40-40: .core.dataset.delete_datasets imported but unused (F401)

Remove unused import


41-41: .core.dataset.delete_non_persistent_datasets imported but unused (F401)

Remove unused import


42-42: .core.dataset.get_default_dataset_name imported but unused (F401)

Remove unused import


43-43: .core.dataset.make_unique_dataset_name imported but unused (F401)

Remove unused import


44-44: .core.dataset.get_default_dataset_dir imported but unused (F401)

Remove unused import


46-50: Module level import not at top of file (E402)


47-47: .core.expressions.ViewField imported but unused (F401)

Remove unused import


48-48: .core.expressions.ViewExpression imported but unused (F401)

Remove unused import


49-49: .core.expressions.VALUE imported but unused (F401)

Remove unused import


51-81: Module level import not at top of file (E402)


52-52: .core.fields.flatten_schema imported but unused (F401)

Remove unused import


53-53: .core.fields.ArrayField imported but unused (F401)

Remove unused import


54-54: .core.fields.BooleanField imported but unused (F401)

Remove unused import


55-55: .core.fields.ClassesField imported but unused (F401)

Remove unused import


56-56: .core.fields.ColorField imported but unused (F401)

Remove unused import


57-57: .core.fields.DateField imported but unused (F401)

Remove unused import


58-58: .core.fields.DateTimeField imported but unused (F401)

Remove unused import


59-59: .core.fields.DictField imported but unused (F401)

Remove unused import


60-60: .core.fields.EmbeddedDocumentField imported but unused (F401)

Remove unused import


61-61: .core.fields.EmbeddedDocumentListField imported but unused (F401)

Remove unused import


62-62: .core.fields.Field imported but unused (F401)

Remove unused import


63-63: .core.fields.FrameNumberField imported but unused (F401)

Remove unused import


64-64: .core.fields.FrameSupportField imported but unused (F401)

Remove unused import


65-65: .core.fields.FloatField imported but unused (F401)

Remove unused import


66-66: .core.fields.GeoPointField imported but unused (F401)

Remove unused import


67-67: .core.fields.GeoLineStringField imported but unused (F401)

Remove unused import


68-68: .core.fields.GeoPolygonField imported but unused (F401)

Remove unused import


69-69: .core.fields.GeoMultiPointField imported but unused (F401)

Remove unused import


70-70: .core.fields.GeoMultiLineStringField imported but unused (F401)

Remove unused import


71-71: .core.fields.GeoMultiPolygonField imported but unused (F401)

Remove unused import


72-72: .core.fields.IntField imported but unused (F401)

Remove unused import


73-73: .core.fields.KeypointsField imported but unused (F401)

Remove unused import


74-74: .core.fields.ListField imported but unused (F401)

Remove unused import


75-75: .core.fields.ObjectIdField imported but unused (F401)

Remove unused import


76-76: .core.fields.PolylinePointsField imported but unused (F401)

Remove unused import


77-77: .core.fields.ReferenceField imported but unused (F401)

Remove unused import


78-78: .core.fields.StringField imported but unused (F401)

Remove unused import


79-79: .core.fields.MaskTargetsField imported but unused (F401)

Remove unused import


80-80: .core.fields.VectorField imported but unused (F401)

Remove unused import


82-82: Module level import not at top of file (E402)


82-82: .core.frame.Frame imported but unused (F401)

Remove unused import: .core.frame.Frame


83-83: Module level import not at top of file (E402)


83-83: .core.groups.Group imported but unused (F401)

Remove unused import: .core.groups.Group


84-107: Module level import not at top of file (E402)


85-85: .core.labels.Label imported but unused (F401)

Remove unused import


86-86: .core.labels.Attribute imported but unused (F401)

Remove unused import


87-87: .core.labels.BooleanAttribute imported but unused (F401)

Remove unused import


88-88: .core.labels.CategoricalAttribute imported but unused (F401)

Remove unused import


89-89: .core.labels.NumericAttribute imported but unused (F401)

Remove unused import


90-90: .core.labels.ListAttribute imported but unused (F401)

Remove unused import


91-91: .core.labels.Regression imported but unused (F401)

Remove unused import


92-92: .core.labels.Classification imported but unused (F401)

Remove unused import


93-93: .core.labels.Classifications imported but unused (F401)

Remove unused import


94-94: .core.labels.Detection imported but unused (F401)

Remove unused import


95-95: .core.labels.Detections imported but unused (F401)

Remove unused import


96-96: .core.labels.DetectionsMask imported but unused (F401)

Remove unused import


97-97: .core.labels.Polyline imported but unused (F401)

Remove unused import


98-98: .core.labels.Polylines imported but unused (F401)

Remove unused import


99-99: .core.labels.Keypoint imported but unused (F401)

Remove unused import


100-100: .core.labels.Keypoints imported but unused (F401)

Remove unused import


101-101: .core.labels.Segmentation imported but unused (F401)

Remove unused import


102-102: .core.labels.Heatmap imported but unused (F401)

Remove unused import


103-103: .core.labels.TemporalDetection imported but unused (F401)

Remove unused import


104-104: .core.labels.TemporalDetections imported but unused (F401)

Remove unused import


105-105: .core.labels.GeoLocation imported but unused (F401)

Remove unused import


106-106: .core.labels.GeoLocations imported but unused (F401)

Remove unused import


108-111: Module level import not at top of file (E402)


109-109: .core.logging.get_logging_level imported but unused (F401)

Remove unused import


110-110: .core.logging.set_logging_level imported but unused (F401)

Remove unused import


112-116: Module level import not at top of file (E402)


113-113: .core.metadata.Metadata imported but unused (F401)

Remove unused import


114-114: .core.metadata.ImageMetadata imported but unused (F401)

Remove unused import


115-115: .core.metadata.VideoMetadata imported but unused (F401)

Remove unused import


117-128: Module level import not at top of file (E402)


118-118: .core.models.apply_model imported but unused (F401)

Remove unused import


119-119: .core.models.compute_embeddings imported but unused (F401)

Remove unused import


120-120: .core.models.compute_patch_embeddings imported but unused (F401)

Remove unused import


121-121: .core.models.load_model imported but unused (F401)

Remove unused import


122-122: .core.models.Model imported but unused (F401)

Remove unused import


123-123: .core.models.ModelConfig imported but unused (F401)

Remove unused import


124-124: .core.models.EmbeddingsMixin imported but unused (F401)

Remove unused import


125-125: .core.models.TorchModelMixin imported but unused (F401)

Remove unused import


126-126: .core.models.ModelManagerConfig imported but unused (F401)

Remove unused import


127-127: .core.models.ModelManager imported but unused (F401)

Remove unused import


129-138: Module level import not at top of file (E402)


130-130: .core.odm.ColorScheme imported but unused (F401)

Remove unused import


131-131: .core.odm.DatasetAppConfig imported but unused (F401)

Remove unused import


132-132: .core.odm.DynamicEmbeddedDocument imported but unused (F401)

Remove unused import


133-133: .core.odm.EmbeddedDocument imported but unused (F401)

Remove unused import


134-134: .core.odm.KeypointSkeleton imported but unused (F401)

Remove unused import


135-135: .core.odm.Panel imported but unused (F401)

Remove unused import


136-136: .core.odm.SidebarGroupDocument imported but unused (F401)

Remove unused import


137-137: .core.odm.Space imported but unused (F401)

Remove unused import


139-154: Module level import not at top of file (E402)


140-140: .core.plots.plot_confusion_matrix imported but unused (F401)

Remove unused import


141-141: .core.plots.plot_pr_curve imported but unused (F401)

Remove unused import


142-142: .core.plots.plot_pr_curves imported but unused (F401)

Remove unused import


143-143: .core.plots.plot_roc_curve imported but unused (F401)

Remove unused import


144-144: .core.plots.lines imported but unused (F401)

Remove unused import


145-145: .core.plots.scatterplot imported but unused (F401)

Remove unused import


146-146: .core.plots.location_scatterplot imported but unused (F401)

Remove unused import


147-147: .core.plots.Plot imported but unused (F401)

Remove unused import


148-148: .core.plots.ResponsivePlot imported but unused (F401)

Remove unused import


149-149: .core.plots.InteractivePlot imported but unused (F401)

Remove unused import


150-150: .core.plots.ViewPlot imported but unused (F401)

Remove unused import


151-151: .core.plots.ViewGrid imported but unused (F401)

Remove unused import


152-152: .core.plots.CategoricalHistogram imported but unused (F401)

Remove unused import


153-153: .core.plots.NumericalHistogram imported but unused (F401)

Remove unused import


155-159: Module level import not at top of file (E402)


156-156: .core.runs.RunConfig imported but unused (F401)

Remove unused import


157-157: .core.runs.Run imported but unused (F401)

Remove unused import


158-158: .core.runs.RunResults imported but unused (F401)

Remove unused import


160-160: Module level import not at top of file (E402)


160-160: .core.sample.Sample imported but unused (F401)

Remove unused import: .core.sample.Sample


161-187: Module level import not at top of file (E402)


162-162: .core.threed.BoxGeometry imported but unused (F401)

Remove unused import


163-163: .core.threed.CylinderGeometry imported but unused (F401)

Remove unused import


164-164: .core.threed.PlaneGeometry imported but unused (F401)

Remove unused import


165-165: .core.threed.SphereGeometry imported but unused (F401)

Remove unused import


166-166: .core.threed.FbxMesh imported but unused (F401)

Remove unused import


167-167: .core.threed.GltfMesh imported but unused (F401)

Remove unused import


168-168: .core.threed.ObjMesh imported but unused (F401)

Remove unused import


169-169: .core.threed.PlyMesh imported but unused (F401)

Remove unused import


170-170: .core.threed.StlMesh imported but unused (F401)

Remove unused import


171-171: .core.threed.PerspectiveCamera imported but unused (F401)

Remove unused import


172-172: .core.threed.PointLight imported but unused (F401)

Remove unused import


173-173: .core.threed.DirectionalLight imported but unused (F401)

Remove unused import


174-174: .core.threed.AmbientLight imported but unused (F401)

Remove unused import


175-175: .core.threed.SpotLight imported but unused (F401)

Remove unused import


176-176: .core.threed.PointCloud imported but unused (F401)

Remove unused import


177-177: .core.threed.MeshBasicMaterial imported but unused (F401)

Remove unused import


178-178: .core.threed.MeshDepthMaterial imported but unused (F401)

Remove unused import


179-179: .core.threed.MeshLambertMaterial imported but unused (F401)

Remove unused import


180-180: .core.threed.MeshPhongMaterial imported but unused (F401)

Remove unused import


181-181: .core.threed.PointCloudMaterial imported but unused (F401)

Remove unused import


182-182: .core.threed.Scene imported but unused (F401)

Remove unused import


183-183: .core.threed.SceneBackground imported but unused (F401)

Remove unused import


184-184: .core.threed.Euler imported but unused (F401)

Remove unused import


185-185: .core.threed.Quaternion imported but unused (F401)

Remove unused import


186-186: .core.threed.Vector3 imported but unused (F401)

Remove unused import


188-230: Module level import not at top of file (E402)


189-189: .core.stages.Concat imported but unused (F401)

Remove unused import


190-190: .core.stages.Exclude imported but unused (F401)

Remove unused import


191-191: .core.stages.ExcludeBy imported but unused (F401)

Remove unused import


192-192: .core.stages.ExcludeFields imported but unused (F401)

Remove unused import


193-193: .core.stages.ExcludeFrames imported but unused (F401)

Remove unused import


194-194: .core.stages.ExcludeGroups imported but unused (F401)

Remove unused import


195-195: .core.stages.ExcludeLabels imported but unused (F401)

Remove unused import


196-196: .core.stages.Exists imported but unused (F401)

Remove unused import


197-197: .core.stages.FilterField imported but unused (F401)

Remove unused import


198-198: .core.stages.FilterLabels imported but unused (F401)

Remove unused import


199-199: .core.stages.FilterKeypoints imported but unused (F401)

Remove unused import


200-200: .core.stages.Flatten imported but unused (F401)

Remove unused import


201-201: .core.stages.GeoNear imported but unused (F401)

Remove unused import


202-202: .core.stages.GeoWithin imported but unused (F401)

Remove unused import


203-203: .core.stages.GroupBy imported but unused (F401)

Remove unused import


204-204: .core.stages.Limit imported but unused (F401)

Remove unused import


205-205: .core.stages.LimitLabels imported but unused (F401)

Remove unused import


206-206: .core.stages.MapLabels imported but unused (F401)

Remove unused import


207-207: .core.stages.Match imported but unused (F401)

Remove unused import


208-208: .core.stages.MatchFrames imported but unused (F401)

Remove unused import


209-209: .core.stages.MatchLabels imported but unused (F401)

Remove unused import


210-210: .core.stages.MatchTags imported but unused (F401)

Remove unused import


211-211: .core.stages.Mongo imported but unused (F401)

Remove unused import


212-212: .core.stages.Shuffle imported but unused (F401)

Remove unused import


213-213: .core.stages.Select imported but unused (F401)

Remove unused import


214-214: .core.stages.SelectBy imported but unused (F401)

Remove unused import


215-215: .core.stages.SelectFields imported but unused (F401)

Remove unused import


216-216: .core.stages.SelectFrames imported but unused (F401)

Remove unused import


217-217: .core.stages.SelectGroups imported but unused (F401)

Remove unused import


218-218: .core.stages.SelectGroupSlices imported but unused (F401)

Remove unused import


219-219: .core.stages.SelectLabels imported but unused (F401)

Remove unused import


220-220: .core.stages.SetField imported but unused (F401)

Remove unused import


221-221: .core.stages.Skip imported but unused (F401)

Remove unused import


222-222: .core.stages.SortBy imported but unused (F401)

Remove unused import


223-223: .core.stages.SortBySimilarity imported but unused (F401)

Remove unused import


224-224: .core.stages.Take imported but unused (F401)

Remove unused import


225-225: .core.stages.ToPatches imported but unused (F401)

Remove unused import


226-226: .core.stages.ToEvaluationPatches imported but unused (F401)

Remove unused import


227-227: .core.stages.ToClips imported but unused (F401)

Remove unused import


228-228: .core.stages.ToTrajectories imported but unused (F401)

Remove unused import


229-229: .core.stages.ToFrames imported but unused (F401)

Remove unused import


231-235: Module level import not at top of file (E402)


232-232: .core.session.close_app imported but unused (F401)

Remove unused import


233-233: .core.session.launch_app imported but unused (F401)

Remove unused import


234-234: .core.session.Session imported but unused (F401)

Remove unused import


236-242: Module level import not at top of file (E402)


237-237: .core.utils.disable_progress_bars imported but unused (F401)

Remove unused import


238-238: .core.utils.pprint imported but unused (F401)

Remove unused import


239-239: .core.utils.pformat imported but unused (F401)

Remove unused import


240-240: .core.utils.report_progress imported but unused (F401)

Remove unused import


241-241: .core.utils.ProgressBar imported but unused (F401)

Remove unused import


243-243: Module level import not at top of file (E402)


243-243: .core.view.DatasetView imported but unused (F401)

Remove unused import: .core.view.DatasetView


244-248: Module level import not at top of file (E402)


245-245: .utils.eval.classification.evaluate_classifications imported but unused (F401)

Remove unused import


246-246: .utils.eval.classification.ClassificationEvaluationConfig imported but unused (F401)

Remove unused import


247-247: .utils.eval.classification.ClassificationResults imported but unused (F401)

Remove unused import


249-253: Module level import not at top of file (E402)


250-250: .utils.eval.detection.evaluate_detections imported but unused (F401)

Remove unused import


251-251: .utils.eval.detection.DetectionEvaluationConfig imported but unused (F401)

Remove unused import


252-252: .utils.eval.detection.DetectionResults imported but unused (F401)

Remove unused import


254-258: Module level import not at top of file (E402)


255-255: .utils.eval.regression.evaluate_regressions imported but unused (F401)

Remove unused import


256-256: .utils.eval.regression.RegressionEvaluationConfig imported but unused (F401)

Remove unused import


257-257: .utils.eval.regression.RegressionResults imported but unused (F401)

Remove unused import


259-263: Module level import not at top of file (E402)


260-260: .utils.eval.segmentation.evaluate_segmentations imported but unused (F401)

Remove unused import


261-261: .utils.eval.segmentation.SegmentationEvaluationConfig imported but unused (F401)

Remove unused import


262-262: .utils.eval.segmentation.SegmentationResults imported but unused (F401)

Remove unused import


264-264: Module level import not at top of file (E402)


264-264: .utils.quickstart.quickstart imported but unused (F401)

Remove unused import: .utils.quickstart.quickstart

tests/unittests/label_tests.py

55-55: Use key in dict instead of key in dict.keys() (SIM118)

Remove .keys()


658-658: Local variable id1 is assigned to but never used (F841)

Remove assignment to unused variable id1


661-661: Local variable id4 is assigned to but never used (F841)

Remove assignment to unused variable id4

fiftyone/core/labels.py

39-39: Class _NoDefault inherits from object (UP004)

Remove object inheritance


100-100: Use format specifiers instead of percent format (UP031)

Replace with format specifiers


126-128: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling (B904)


127-127: Use format specifiers instead of percent format (UP031)

Replace with format specifiers


268-268: Use format specifiers instead of percent format (UP031)

Replace with format specifiers


307-310: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling (B904)


308-309: Use format specifiers instead of percent format (UP031)

Replace with format specifiers


315-318: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling (B904)


316-317: Use format specifiers instead of percent format (UP031)

Replace with format specifiers


343-343: Class _HasLabelList inherits from object (UP004)

Remove object inheritance


583-583: No explicit stacklevel keyword argument found (B028)


635-635: No explicit stacklevel keyword argument found (B028)


650-650: Class _HasMedia inherits from object (UP004)

Remove object inheritance


1015-1018: Use ternary operator _filled = filled if filled is not None else self.filled instead of if-else-block (SIM108)

Replace if-else-block with _filled = filled if filled is not None else self.filled


1892-1892: Avoid extraneous parentheses (UP034)

Remove extraneous parentheses


1897-1900: Use ternary operator is_rgb = fof.is_rgb_target(target) if target is not None else False instead of if-else-block (SIM108)

Replace if-else-block with is_rgb = fof.is_rgb_target(target) if target is not None else False


1906-1909: Use ternary operator dtype = int if target is not None and not is_rgb and target > 255 else np.uint8 instead of if-else-block (SIM108)

Replace if-else-block with dtype = int if target is not None and not is_rgb and target > 255 else np.uint8


1924-1927: Use ternary operator is_rgb = False if mask_targets is None else fof.is_rgb_mask_targets(mask_targets) instead of if-else-block (SIM108)

Replace if-else-block with is_rgb = False if mask_targets is None else fof.is_rgb_mask_targets(mask_targets)


2121-2121: Use format specifiers instead of percent format (UP031)

Replace with format specifiers


2135-2135: Use format specifiers instead of percent format (UP031)

Replace with format specifiers


2254-2255: Use format specifiers instead of percent format (UP031)

Replace with format specifiers


2264-2265: Use format specifiers instead of percent format (UP031)

Replace with format specifiers


2274-2275: Use format specifiers instead of percent format (UP031)

Replace with format specifiers

Additional comments not posted (5)
setup.py (1)

76-76: Addition of tifffile to handle TIFF files for the new DetectionsMask class.

fiftyone/__public__.py (2)

96-96: The new DetectionsMask class is added to the public interface, ensuring it is accessible as part of the FiftyOne library's API.

Tools
Ruff

96-96: .core.labels.DetectionsMask imported but unused (F401)

Remove unused import


Line range hint 16-31: Many imports in this file are flagged as unused. This could either be a false positive due to dynamic usage not detected by static analysis, or these imports are genuinely unused. Verifying this would require a broader review of the usage within the entire codebase.

Also applies to: 32-32, 33-33, 34-45, 46-50, 51-81, 82-82, 83-83, 84-107, 108-111, 112-116, 117-128, 129-138, 139-154, 155-159, 160-160, 161-187, 188-230, 231-235, 236-242, 243-243, 244-248, 249-253, 254-258, 259-263, 264-264

Tools
Ruff

93-93: .core.labels.Classifications imported but unused (F401)

Remove unused import


94-94: .core.labels.Detection imported but unused (F401)

Remove unused import


95-95: .core.labels.Detections imported but unused (F401)

Remove unused import


96-96: .core.labels.DetectionsMask imported but unused (F401)

Remove unused import


97-97: .core.labels.Polyline imported but unused (F401)

Remove unused import


98-98: .core.labels.Polylines imported but unused (F401)

Remove unused import


99-99: .core.labels.Keypoint imported but unused (F401)

Remove unused import

tests/unittests/label_tests.py (1)

475-553: The unit tests for the new DetectionsMask class look comprehensive, covering various conversions and checks for consistency across different label types and transformations.

fiftyone/core/labels.py (1)

650-650: Remove unnecessary inheritance from object.

- class _HasMedia(object):
+ class _HasMedia:

Likely invalid or redundant comment.

Tools
Ruff

650-650: Class _HasMedia inherits from object (UP004)

Remove object inheritance

fiftyone/core/labels.py Show resolved Hide resolved
Comment on lines +1924 to +1927
if mask_targets is None:
is_rgb = False
else:
is_rgb = fof.is_rgb_mask_targets(mask_targets)
Copy link
Contributor

Choose a reason for hiding this comment

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

Use a ternary operator to simplify the assignment.

- if mask_targets is None:
-     is_rgb = False
- else:
-     is_rgb = fof.is_rgb_mask_targets(mask_targets)
+ is_rgb = False if mask_targets is None else fof.is_rgb_mask_targets(mask_targets)
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if mask_targets is None:
is_rgb = False
else:
is_rgb = fof.is_rgb_mask_targets(mask_targets)
is_rgb = False if mask_targets is None else fof.is_rgb_mask_targets(mask_targets)
Tools
Ruff

1924-1927: Use ternary operator is_rgb = False if mask_targets is None else fof.is_rgb_mask_targets(mask_targets) instead of if-else-block (SIM108)

Replace if-else-block with is_rgb = False if mask_targets is None else fof.is_rgb_mask_targets(mask_targets)

@brimoor
Copy link
Contributor

brimoor commented Jun 19, 2024

@kemaleren thank you for the extremely thorough discussion of your thinking behind this PR! Very helpful 🙇

Just to establish some nomenclature in FiftyOne:

  1. Segmentation objects represent semantic segmentation data. When this data represents model predictions, it is evaluated via evaluate_segmentations()
  2. Detections objects that contain Detection instances with their mask attributes populated represent instance segmentation data. When this data represents model predictions, it is evaluated via evaluate_detections()

How would you evaluate data in your DetectionsMask class? Like 1 or 2?

If 2, then, to clarify: one option that you didn't explicitly mention is just adding a factory method like so:

detections = fo.Detections.from_mask(mask, mask_targets)

where mask is a detection mask in the format you describe, mask_targets is the dict mapping integers to label strings, and the Detections object returned is in "instance segmentation" format as described above. Format 2 is more general than DetectionsMask from a representational standpoint since it supports non-overlapping objects as a special case but could also represent overlapping objects if necessary.

However, if I understand correctly, your problem is that when there are many objects, storing data in format 2 could generate > 16MB of data per sample?

I expect the in-database size of a Detections and DetectionsMask instance with mask populated would be pretty close. So the primary thing that DetectionsMask solves for is the ability to store the data on disk via mask_path. And in exchange for that you give up the ability to represent overlapping instances that Detections provides.

I find that thinking about new label types in terms of how they would be evaluated (ie what task they represent) to be a helpful exercise. In general, it would be ideal to have a 1-1 correspondence between Label types and tasks, but we've already violated that convention for instance segmentation because we also allow them to be represented as Detections or Polylines. And in fact that was partly done for the same reason that I believe is motivating the DetectionsMask class: Polylines can be a more compact representation from a size standpoint.

Adding new Label types is a big undertaking because there are many aspects of the library that need to understand/support them (visualization, evaluation, import/export, conversion, etc).

@benjaminpkane benjaminpkane removed their assignment Jun 28, 2024
@kemaleren
Copy link
Contributor Author

@brimoor Sorry for not getting back to you earlier.

How would you evaluate data in your DetectionsMask class? Like 1 or 2?

Like 2.

If 2, then, to clarify: one option that you didn't explicitly mention is just adding a factory method like so:

Yes, this factory method would be another way of implementing option #2 from my list of three options. If you prefer doing it this way, it's not too much work to adapt this PR. Let me know if that's the direction you want to go.

However, if I understand correctly, your problem is that when there are many objects, storing data in format 2 could generate > 16MB of data per sample?

Exactly this, yes.

Adding new Label types is a big undertaking because there are many aspects of the library that need to understand/support them (visualization, evaluation, import/export, conversion, etc).

I know it's going to be a lot of work. I just don't see any other way around the 16MB limit, unless you were to switch to a different database, which would be even more work.

Let me know what else I can help with.

@kemaleren
Copy link
Contributor Author

Closing, since this PR is superseded by #4829

@kemaleren kemaleren closed this Sep 21, 2024
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