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

Data Quality plugin for Imviz #2767

Merged
merged 16 commits into from
Apr 17, 2024
Merged

Conversation

bmorris3
Copy link
Contributor

@bmorris3 bmorris3 commented Mar 22, 2024

This PR implements a Data Quality plugin, with a focus on Imviz. This is part of a larger effort which will support other configs too.

The Data Quality plugin allows a user to load science data and an associated DQ array (using #2761). Imviz will recognize DQ extensions at load time, identify the correct flag mapping (either JWST or Roman, for now; see #2765).

The plugin will assign colors to the flags, and show the DQ array in the viewer. Pixels without flags have no additional color overlay (via glue-viz/glue#2468). The data quality for a pixel is shown in the coordinate info in the top toolbar by appending (DQ: <int>) on pixels with flags (screenshot below).

There's a multi-select dropdown labeled "Filter by bits" where a user can select flags to visualize by checking any of the decomposed bits that each flag contains. The visibility and color of each flag can be set manually.

There's a relative opacity slider for the DQ layer, which sets the DQ layer opacity relative to the science layer. There's a callback on the science layer opacity, so they stay in sync.

Screen Shot 2024-04-09 at 13 08 25

Demo video

I took the unusual step to record a narrated, five-minute screen recording with a DQ demo, to help you see the features. The video is too big for GitHub, so you can view it on Box. One feature that is not demonstrated in this video is color selection – if you click the color of the DQ flag in the plugin, you can select any color you like from a full color palette.

Example notebook

Try out the concept notebook included in this PR.

Other notes

  • I used the no-changelog-entry-needed label because this should produce no user facing changes once we do the last item on the to-do list below.

  • I could use advice from a wizard (@kecnry 🧙🏻?) to improve the vertical spacing of the vue elements.

Tickets addressed

Three tickets are completed with this PR, and one is partly addressed.

  • JDAT-4350: implement DQ plugin UI features
  • JDAT-4347: DQ mouseover info
  • JDAT-4352: Provide controls for DQ visibility options
  • 🟨 JDAT-4351: Investigate DQ bit logic from the UI
    • It is possible to do one specific type of bit logic with the plugin implemented in this PR. You can use the "filter by bits" dropdown to find any DQ flags containing bits, e.g., "show flags that when decomposed contain 2, 3, or 5."
  • JDAT-4390: DQ plugin tests
    • this PR doesn't introduce tests specific to DQ. Testing will be added later on for this ticket.

To do list


Change log entry

  • Is a change log needed? If yes, is it added to CHANGES.rst? If you want to avoid merge conflicts,
    list the proposed change log here for review and add to CHANGES.rst before merge. If no, maintainer
    should add a no-changelog-entry-needed label.

Checklist for package maintainer(s)

This checklist is meant to remind the package maintainer(s) who will review this pull request of some common things to look for. This list is not exhaustive.

  • Are two approvals required? Branch protection rule does not check for the second approval. If a second approval is not necessary, please apply the trivial label.
  • Do the proposed changes actually accomplish desired goals? Also manually run the affected example notebooks, if necessary.
  • Do the proposed changes follow the STScI Style Guides?
  • Are tests added/updated as required? If so, do they follow the STScI Style Guides?
  • Are docs added/updated as required? If so, do they follow the STScI Style Guides?
  • Did the CI pass? If not, are the failures related?
  • Is a milestone set? Set this to bugfix milestone if this is a bug fix and needs to be released ASAP; otherwise, set this to the next major release milestone. Bugfix milestone also needs an accompanying backport label.
  • After merge, any internal documentations need updating (e.g., JIRA, Innerspace)? 🐱

@github-actions github-actions bot added imviz plugin Label for plugins common to multiple configurations labels Mar 22, 2024
Copy link

codecov bot commented Mar 22, 2024

Codecov Report

Attention: Patch coverage is 88.96321% with 33 lines in your changes are missing coverage. Please review.

Project coverage is 88.82%. Comparing base (f1ba4a6) to head (a6de416).
Report is 34 commits behind head on main.

❗ Current head a6de416 differs from pull request most recent head a7e748b. Consider uploading reports for the commit a7e748b to get more accurate results

Files Patch % Lines
...z/configs/default/plugins/data_quality/dq_utils.py 80.35% 11 Missing ⚠️
jdaviz/configs/imviz/plugins/parsers.py 78.57% 9 Missing ⚠️
...nfigs/default/plugins/data_quality/data_quality.py 94.93% 8 Missing ⚠️
jdaviz/app.py 20.00% 4 Missing ⚠️
jdaviz/core/template_mixin.py 92.85% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2767      +/-   ##
==========================================
+ Coverage   88.72%   88.82%   +0.10%     
==========================================
  Files         110      111       +1     
  Lines       16353    16879     +526     
==========================================
+ Hits        14509    14993     +484     
- Misses       1844     1886      +42     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@bmorris3 bmorris3 added this to the 3.10 milestone Mar 25, 2024
Comment on lines 30 to 32
# TODO: uncomment this line before merging into main:
# irrelevant_msg = Unicode("Data Quality plugin is in development.").tag(sync=True)
Copy link
Member

Choose a reason for hiding this comment

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

you can always uncomment this and just reset it to empty in your own testing notebook and any tests 🤷‍♂️

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Of course! I just wanted devs to be able to play with this by checking out the PR, without having to change source code to use it.

Copy link
Member

Choose a reason for hiding this comment

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

is the intention still to include this or is it mature enough that we can remove it entirely and have it enabled in main on merge (and whenever 3.10 is released)?

Copy link
Member

@kecnry kecnry Apr 16, 2024

Choose a reason for hiding this comment

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

(we could also have this be considered irrelevant if there are no data quality layers available... 🤔 )

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I’m in favor of keeping it out until we decide it’s more stable. It’s likely some things will change when we add support for other configs. Hopefully they aren’t major things, but just in case, let’s defer.

@bmorris3
Copy link
Contributor Author

bmorris3 commented Mar 27, 2024

I got some early ~"user feedback" by giving a demo today to @tddesjardins, who will be a Roman-focused DQ plugin user. Tyler likes where things are headed. The outstanding tasks in our epic that he specifically touched on in our conversation included:

  • Show DQ bit (with or without its list of decomposed values) in mouseover info, ideally with the flux from the science layer
  • It's OK to limit bit selection from the UI to only (a) toggle specific bits, and (b) show/hide all bits. Roman data will most often be accessed through Imviz on the science platform, where the user must be using a Jupyter notebook, and could use the API to load their highly customized masks.

Feature ideas from Tyler that we don't yet have ticketed:

  • It would be really really useful to pass on DQ information to other plugins. For example, if I load science data with a DQ array, any pixel with any DQ value other than "GOOD" should be masked in aperture photometry. That way I can "trust the photometry results more" (at least with understood caveats).

Note from Brett on the above request: the DQ-to-boolean-mask part of this request is trivial to implement. The slightly less trivial effort is adding the infrastructure to the aperture photometry plugin to check science data for an associated DQ array loaded in the viewer, and passing that along to photutils. But if accomplish that effort, it would be almost no extra effort to pass along the ERR array to photutils too, which would also give us uncertainties from photutils-derived outputs.

Further note on the above note: DQ arrays only exist for L2 products, but ERR arrays exist for L2+. So while it might seem like a narrow use-case (only L2) to implement this for DQ, the ERR use-case applies much more broadly (L2 and beyond).

@javerbukh
Copy link
Contributor

@bmorris3 Would PR #2718 be helpful in accessing the DQ array associated with the science data?

@camipacifici
Copy link
Contributor

Thank you very much for the feedback @tddesjardins!!
Propagated uncertainties on aperture photometry are on the wish list. Not sure when we will implement though.
I agree that passing the DQ to other plugins would be extremely useful, but as it is now, the plugins are sort of designed to work on level 3 products, which usually do not carry the DQ (at least for JWST). In any case, I will add this as well to the wishlist and see what we can do!

@bmorris3
Copy link
Contributor Author

@javerbukh Thanks for mentioning that PR – I have a ticket for doing what you did there for all configs, so I'm glad to see you already got Cubeviz 🙌🏻.

@bmorris3
Copy link
Contributor Author

bmorris3 commented Mar 28, 2024

I got more feedback from giving a demo to @rgcosentino:

  • important, simplifying UI feature request: add a multiselect dropdown for selecting specific bits to show in the decoded list of flags in the plugin. use AND logic to combine multiple selected flags, i.e.: “when I click to select flags 2, 10, and 18, only show flags that contain jumps AND nonlinearity AND etc…”
  • can we summarize the fraction/number of pixels with a given flag in the UI? can we list the multiselect dropdown in descending order of the most frequently occurring flags?
  • consider: should we keep (or giv an option to keep) DQ visible while blinking in Imviz? if you have a source in multiple exposures, you might want to check which exposures are not affected by certain flags
  • future: this would be handy when visualizing ramps! 👀

@camipacifici
Copy link
Contributor

Thank you @rgcosentino!!
About the blinking keeping the DQ visible, @bmorris3 do you have a sense how hard/easy that is?

@bmorris3
Copy link
Contributor Author

@camipacifici It's easy to implement, but may not be very performant. I'll look into it 👌🏻

@bmorris3
Copy link
Contributor Author

Today I added @rgcosentino's UI feature request, which solves a couple of the UI challenges in one go:

dq-bit-filter.mov

Comment on lines +252 to +253
def layer_is_not_dq(data):
return not data.label.endswith('[DQ]')
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The surest way to identify a DQ layer is to rely on the data label if we're always producing it consistently. I think we can rely on this for now as written, but we could instead, e.g., add a private metadata flag.

Copy link
Contributor

Choose a reason for hiding this comment

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

In #2718 there is a app._jdaviz_helper._loaded_mask_cube variable that we can use instead of this. That way, if the DQ data is renamed, the _loaded_mask_cube should be able to track that (it does not currently do that in that PR but it would be a useful addition). We've been moving away from using data labels since we are planning (at some point in the future) to enable label renaming. I'm also worried about the scenario where a user manually loads the DQ layer with a custom name, which would mean it is not caught by this line.

Copy link
Member

Choose a reason for hiding this comment

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

@javerbukh - but we intentionally want to be able to support multiple DQ layers. I agree that the eventual solution is probably to move all of this "what type of data is this entry considered" into private metadata that can be populated by the parser.

Comment on lines +458 to +465
if associated_dq_layer is not None:
if np.isnan(dq_value):
dq_text = ''
else:
dq_text = f' (DQ: {int(dq_value):d})'
else:
dq_text = ''
self.row1b_text = f'{value:+10.5e} {unit}{dq_text}'
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 the simplest and fastest possible DQ injection into coords-info that I could do. We could also decompose the bit flag, but that is (1) hard to fit in the horizontal space, and (2) a tiny bit more expensive.

@bmorris3 bmorris3 added the no-changelog-entry-needed changelog bot directive label Apr 8, 2024
@bmorris3 bmorris3 changed the title Initial data quality plugin UI Data Quality plugin for Imviz Apr 9, 2024
@bmorris3 bmorris3 marked this pull request as ready for review April 9, 2024 15:33
@javerbukh
Copy link
Contributor

The DQ plugin so far is a great addition to Imviz, nice work! I left a couple comments in the code but I also want to mention the DQ relative opacity slider. I was confused at first why only the DQ array was loaded, until I moved that slider and saw the data underneath. Is there a reason we cannot use the opacity sliders in plot options to handle this functionality?

I was also nervous to see the amount of lines added and no test coverage for them. I understand that adding the bulk of test coverage will be a follow-up effort, but can you add some simple tests just to show the intended workflow with this plugin? Let me know your thoughts, I'm happy to re-review again soon because it would be great to get this merged!

@bmorris3
Copy link
Contributor Author

bmorris3 commented Apr 15, 2024

@javerbukh:

I was confused at first why only the DQ array was loaded, until I moved that slider and saw the data underneath.

By default the DQ opacity is 0.9 * (science opacity). Do you think it would be less confusing if we brought it down to 0.5 or so?

Is there a reason we cannot use the opacity sliders in plot options to handle this functionality?

I agree that DQ opacity is a "plot option" in the literal sense, but we need a different strategy for the DQ opacity slider than for the other layers. We wouldn't allow the user to change any but one of the options (opacity) on DQ layers – not the colormap, bias, contrast, stretch, etc. And DQ opacity is defined relative to the science layer opacity, which would mean its opacity slider behaves differently from the others.

None of the above is to say that we couldn't put DQ plot options in Plot Options, but that we'd have to find a way to make these subtleties less confusing.

Copy link
Member

@kecnry kecnry left a comment

Choose a reason for hiding this comment

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

This is really coming along nicely (and quickly)! Just a few comments from a first pass - would you mind exposing whatever you intend to expose and think is fairly stable through the user API so that its obvious what is expected to be tested directly vs internally?

@bmorris3
Copy link
Contributor Author

Thank you @kecnry, all your points are now addressed.


dq_layer.state.alpha = self.dq_layer_opacity

def update_visibility(self, index):
Copy link
Member

Choose a reason for hiding this comment

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

is index (here and in update_color) convenient for the user? This isn't a big deal since we're not exposing this in the user API yet, but definitely something to think about as we play with this in workflows, etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point. Right now, the index corresponds to an entry in self.decoded_flags, which is a list of dictionaries. We could instead choose to make decoded_flags a dictionary, where the key is the flag. I made it a list because I had better luck iterating over a list in the vue file than a dict.

Copy link
Member

Choose a reason for hiding this comment

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

Right, but even if it internally is a dictionary, maybe we should support passing the key and just internally iterating to look up the entry to change?

Comment on lines 30 to 32
# TODO: uncomment this line before merging into main:
# irrelevant_msg = Unicode("Data Quality plugin is in development.").tag(sync=True)
Copy link
Member

@kecnry kecnry Apr 16, 2024

Choose a reason for hiding this comment

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

(we could also have this be considered irrelevant if there are no data quality layers available... 🤔 )

Comment on lines 263 to 278
@property
def user_api(self):
return PluginUserApi(
self,
expose=(
'science_layer', 'dq_layer',
'decoded_flags', 'flags_filter'
)
)
Copy link
Member

Choose a reason for hiding this comment

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

for now, I think we also at least want viewer (and possibly flag_map - but that would require using a PluginSelectComponent for that instead of handling the items manually here). How about dq_layer_opacity?

I do think it makes sense to hold back on the color/visibility methods if we're not sure of their API yet.... but eventually I'm guessing we'll want those exposed as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in c2cd379.

Copy link
Contributor

@javerbukh javerbukh left a comment

Choose a reason for hiding this comment

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

I noticed a strange bug where if I filter by bit (10 in the concept notebook in this PR), then change the color of 1311747 for example, the pixels are no longer colored in at all. The color comes back if I press "clear filter".

Copy link
Contributor

@javerbukh javerbukh left a comment

Choose a reason for hiding this comment

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

Works great! Once CI is working I'll approve.

Copy link
Contributor

@javerbukh javerbukh left a comment

Choose a reason for hiding this comment

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

LGTM!

@bmorris3
Copy link
Contributor Author

Last remaining failure exists on main and is expected (for now). Thanks all!

@bmorris3 bmorris3 merged commit a9c475c into spacetelescope:main Apr 17, 2024
13 of 14 checks passed
@bmorris3 bmorris3 mentioned this pull request Apr 18, 2024
9 tasks
bmorris3 added a commit to bmorris3/jdaviz that referenced this pull request Jul 17, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
imviz no-changelog-entry-needed changelog bot directive plugin Label for plugins common to multiple configurations
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants