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 support for rgb overlays #2483

Merged
merged 19 commits into from Jan 13, 2023
Merged

add support for rgb overlays #2483

merged 19 commits into from Jan 13, 2023

Conversation

sashankaryal
Copy link
Contributor

@sashankaryal sashankaryal commented Jan 4, 2023

Resolves #1730.

Change log

  • Adds support for visualizing RGB segmentations in the App
  • Adds support for RGB segmentations in evaluate_segmentations()
  • Adds support for passing RGB mask targets to objects_to_segmentations()
  • Adds a new transform_segmentations() utility to transform existing masks between grayscale <> RGB

Example usage

import fiftyone as fo
import fiftyone.zoo as foz
import fiftyone.utils.labels as foul

mask_targets = {"#FF6D04": "person", "#499CEF": "cat", "#6D04FF": "dog"}

dataset = foz.load_zoo_dataset(
    "coco-2017",
    split="validation",
    label_types=["segmentations"],
    classes=["person", "cat", "dog"],
    label_field="instances",
    max_samples=25,
    only_matching=True,
)

foul.objects_to_segmentations(
    dataset,
    "instances",
    "segmentations",
    mask_targets=mask_targets,
)

dataset.default_mask_targets = mask_targets

# Test in-database RGB segmentations in App
session = fo.launch_app(dataset)

foul.export_segmentations(
    dataset, "segmentations", "/tmp/segmentations", overwrite=True
)

# Test on-disk RGB segmentations in App
session.refresh()

# Test evaluating RGB segmentations
dataset.rename_sample_field("segmentations", "ground_truth")
dataset.clone_sample_field("ground_truth", "predictions")

results1 = dataset.evaluate_segmentations(
    "predictions", gt_field="ground_truth", mask_targets=mask_targets
)
results1.print_report()

results2 = dataset.evaluate_segmentations("predictions", gt_field="ground_truth")
results2.print_report()

# Convert to grayscale
targets_map = {"#FF6D04": 1, "#499CEF": 2, "#6D04FF": 3}
foul.import_segmentations(dataset, "ground_truth")
foul.transform_segmentations(dataset, "ground_truth", targets_map)
foul.transform_segmentations(
    dataset, "predictions", targets_map, update_mask_targets=True
)

# Edit grayscale values
foul.transform_segmentations(
    dataset, "predictions", {1: 70, 2: 140, 3: 210}, update_mask_targets=True
)

# Convert back to RGB
foul.transform_segmentations(
    dataset, "predictions", {70: "#FF6D04", 140: "#499CEF", 210: "#6D04FF"}
)

@codecov
Copy link

codecov bot commented Jan 4, 2023

Codecov Report

Base: 48.18% // Head: 61.73% // Increases project coverage by +13.55% 🎉

Coverage data is based on head (8130059) compared to base (f4e5c87).
Patch coverage: 64.30% of modified lines in pull request are covered.

Additional details and impacted files
@@             Coverage Diff              @@
##           develop    #2483       +/-   ##
============================================
+ Coverage    48.18%   61.73%   +13.55%     
============================================
  Files          186      207       +21     
  Lines        29099    39884    +10785     
  Branches       240      242        +2     
============================================
+ Hits         14020    24623    +10603     
- Misses       15079    15261      +182     
Flag Coverage Δ
app 48.18% <58.17%> (+<0.01%) ⬆️
python 99.39% <100.00%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
app/packages/state/src/utils.ts 21.27% <12.50%> (-1.04%) ⬇️
app/packages/looker/src/overlays/segmentation.ts 25.24% <17.27%> (-3.65%) ⬇️
app/packages/utilities/src/color.ts 25.22% <18.18%> (-0.76%) ⬇️
app/packages/state/src/recoil/selectors.ts 49.87% <23.07%> (-1.26%) ⬇️
app/packages/looker/src/index.ts 21.17% <33.33%> (-0.12%) ⬇️
app/packages/looker/src/numpy.ts 54.59% <42.85%> (-0.06%) ⬇️
app/packages/looker/src/overlays/util.ts 26.43% <50.00%> (+3.46%) ⬆️
app/packages/looker/src/overlays/detection.ts 20.66% <100.00%> (ø)
app/packages/looker/src/overlays/heatmap.ts 29.91% <100.00%> (ø)
app/packages/looker/src/state.ts 98.88% <100.00%> (+0.03%) ⬆️
... and 27 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report at Codecov.
📢 Do you have feedback about the report comment? Let us know in this issue.

@brimoor brimoor added the feature Work on a feature request label Jan 5, 2023
@sashankaryal sashankaryal marked this pull request as ready for review January 5, 2023 15:44
Copy link
Contributor

@brimoor brimoor left a comment

Choose a reason for hiding this comment

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

@sashankaryal how's this PR coming? A couple things I noticed when testing just now:

  • The tooltip does not render correctly when viewing a dataset without mask targets explicitly defined in the App (does not consistency appear; and when it does seems to be showing the wrong data?)

I also noticed that pixel values that do not appear in the provided mask_targets are currently being rendered. Is that intentional? Or are you planning to switch to the currently documented behavior that pixels not in masks_targets are not rendered? After testing just now and experiencing the current behavior, I'm thinking it could be fine to go ahead and always render all non-zero classes and just show pixel value when a label is not available.

I can see value both ways:

  • If only mask_targets are painted, it would give a simple way to change what is rendered by editing mask_targets
  • If all non-zero values are painted, then there is never any confusion why user's data is not appearing

@sashankaryal
Copy link
Contributor Author

@sashankaryal how's this PR coming? A couple things I noticed when testing just now:

  • The tooltip does not render correctly when viewing a dataset without mask targets explicitly defined in the App (does not consistency appear; and when it does seems to be showing the wrong data?)

I also noticed that pixel values that do not appear in the provided mask_targets are currently being rendered. Is that intentional? Or are you planning to switch to the currently documented behavior that pixels not in masks_targets are not rendered? After testing just now and experiencing the current behavior, I'm thinking it could be fine to go ahead and always render all non-zero classes and just show pixel value when a label is not available.

I can see value both ways:

  • If only mask_targets are painted, it would give a simple way to change what is rendered by editing mask_targets
  • If all non-zero values are painted, then there is never any confusion why user's data is not appearing

Addressed in the new commits

Copy link
Contributor

@benjaminpkane benjaminpkane left a comment

Choose a reason for hiding this comment

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

Generally the code looks good. Nice job!

app/packages/looker/src/overlays/segmentation.ts Outdated Show resolved Hide resolved
throw new Error("mask targets is invalid");
}

return Object.keys(maskTargets)[0]?.startsWith("#") === true;
Copy link
Contributor

Choose a reason for hiding this comment

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

This will run quite often, could we cache this somehow?

DETECTIONS,
]);
// defined as labels that can have on-disk overlays
const DENSE_LABELS = new Set([SEGMENTATION, HEATMAP, DETECTION, DETECTIONS]);
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe define in @fiftyone/utilities for reuse?

Comment on lines 511 to 513
const r = targets[i * mapData.channels];
const g = targets[i * mapData.channels + 1];
const b = targets[i * mapData.channels + 2];
Copy link
Contributor

Choose a reason for hiding this comment

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

There is a fair bit of shared complexity in theses functions, like these for loops. You might be able to share some code to improve readability


if (!isRgbMaskTargets(maskTargets)) {
// getting color here is computationally inefficient, return no color for this edge case
return rgbSegmentationInfoWithoutColor;
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you elaborate here? I'm not sure I understand

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is for the edge case when the mask is multi-channel (RGB) but the mask targets is not RGB (or is missing). In this scenario, we render the RGB mask as is but the tooltip will behave slightly differently in two respects:

  1. The ribbon color won't reflect the segmentation color, it'll be static in the entire canvas (a shade of white, I think).
  2. We won't display any "pixel" info.

This comment is re: (1); while it's possible to get the color of the underlying mask, it involves parsing the RGB buffer. In the happy path case, the RGB buffer is reduced into a targets lookup cable and we don't have this problem.

Copy link
Contributor

@brimoor brimoor left a comment

Choose a reason for hiding this comment

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

LGTM

@brimoor brimoor merged commit 77adcef into develop Jan 13, 2023
@brimoor brimoor deleted the feature/rgb-mask branch January 13, 2023 12:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature Work on a feature request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[FR] Add support for RGB Segmentation/Heatmap arrays
3 participants