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 a blend method to create temporal RGB from MultiScene #2488

Merged
merged 9 commits into from
Oct 11, 2023

Conversation

pnuu
Copy link
Member

@pnuu pnuu commented May 22, 2023

In #1748 we've discussed composites with temporal dependency. This is one attempt on one of the ideas.

import glob
from satpy import MultiScene
from satpy import Scene
from satpy.multiscene import temporal_rgb

# Three sets of data in ascending time order (latest last)
fnames = []
fnames.append(glob.glob('/data/*202305100800*))
fnames.append(glob.glob('/data/*202305100830*))
fnames.append(glob.glob('/data/*202305100855*))

scenes = [Scene(filenames=files, reader='seviri_l1b_hrit') for files in fnames]
mscn = MultiScene(scenes)
mscn.load(['HRV'])
mscn2 = mscn.resample('euron1', resampler='gradient_search')
blended = mscn2.blend(blend_function=temporal_rgb)
blended.save_datasets(base_dir='/tmp')
  • Closes #xxxx
  • Tests added
  • Fully documented

@pnuu pnuu self-assigned this May 22, 2023
@pnuu
Copy link
Member Author

pnuu commented May 22, 2023

And here is example from the above script, scaled down to smaller size:
HRV_20230510_082829_scaled

And here's one cropped part in full 1 km resolution that better shows the effect.
HRV_20230510_082829_crop

The default enhancement for HRV (0...100) isn't the best for this use, so creating new composite with a dedicated enhancement will bring out the features better.

@gerritholl
Copy link
Collaborator

Nice start. But rather specific to the use case of a T-2/T-1/T-0 RGB. Would be interested in a discussion on how we can set up a more generic way of configuring this in YAML, where this would be just one specific case.

@pnuu pnuu marked this pull request as ready for review May 22, 2023 11:50
@pnuu pnuu requested review from gerritholl and BENR0 May 22, 2023 11:50
@pnuu
Copy link
Member Author

pnuu commented May 22, 2023

Indeed, this is very specific to one of the cases, so didn't mark this to close the original issue.

I think the YAML variant would need to use glob() to find the files for the previous times. My thought was to:

  • add the functionality to Satpy for this T-2/T-1/T case
  • create a multi-temporal segment gatherer to pytroll-collectors to collect a range of time slots and publish a set with proper intervals (or approximates of them for polar data)
  • add a plugin to Trollflow2 to handle these messages and call MultiScene.blend(blend_function=temporal_rgb)

@gerritholl
Copy link
Collaborator

It looks like that if I have a MultiScene with more than three scenes, then all the scenes from the 4th onward get ignored?

@pnuu
Copy link
Member Author

pnuu commented May 22, 2023

Yes, that's by design and tested. We have only three channels in any way 😉

@gerritholl
Copy link
Collaborator

I think the YAML variant would need to use glob() to find the files for the previous times

Unless we have some way for the MultiScene to look up a YAML configuration?

  temporal_rgb:
    compositor: !!python/name:satpy.composites.GenericCompositor
    prerequisites:
    - name: HRV
      modifiers: [sunz_corrected]
      time: -2
    - name: HRV
      modifiers: [sunz_corrected]
      time: -1
    - name: HRV
      modifiers: [sunz_corrected]
      time: 0
    standard_name: temporal_rgb

This would be somehow found by mscn.load(...) or mscn.blend(...) (not sure).

Advantage: we can use modifiers and enhancements the same way we do for other composites — and we could replace GenericCompositor by something more fancy.

The weakness: I don't know how the "somehow" would work. A Scene couldn't load this composite, because a Scene has only one timestep.

In your example, how would modifiers and enhancements work?

@pnuu
Copy link
Member Author

pnuu commented May 22, 2023

In your example, how would modifiers and enhancements work?

Simply by defining a (single-channel) composite for a given data:

  hrv:
    compositor: !!python/name:satpy.composites.GenericCompositor
    prerequisites:
      - name: HRV
        modifiers: [sunz_corrected]
    standard_name: hrv

And to complement, an enhancement:

  hrv:
    standard_name: hrv
    operations:
    - name: stretch0
      method: *stretchfun
      kwargs:
        stretch: crude
        min_stretch: 0
        max_stretch: 100

And now instead of doing mscn.load(['HRV']) do mscn.load(['hrv']) to load the composite instead of the plain channel data. The results:

20230510_0855_Meteosat-11_hrv_scaled
20230510_0855_Meteosat-11_hrv_crop

@pnuu
Copy link
Member Author

pnuu commented May 22, 2023

I think the YAML variant would need to use glob() to find the files for the previous times

Unless we have some way for the MultiScene to look up a YAML configuration?

That's the point where we'd need to do some glob() or something to find the files needed for the previous time steps. By hand this is rather easy (like my script). There could also be a option for the MultiScene.group_files() to do this for the user 🤔 But for operations I think we need a new collector/gatherer to keep track of the last N scenes and then publish a suitable sets from them.

@gerritholl
Copy link
Collaborator

gerritholl commented May 22, 2023

I think the YAML variant would need to use glob() to find the files for the previous times

Unless we have some way for the MultiScene to look up a YAML configuration?

That's the point where we'd need to do some glob() or something to find the files needed for the previous time steps.

Why? The MultiScene already has the scenes for each time step, provided by the user directly or (for operations) by a multi-timestep gatherer in pytroll-collectors.

@djhoese
Copy link
Member

djhoese commented May 22, 2023

I approve of this PR as-is, but understand that it doesn't fully satisfy all of the use cases that some people had in mind.

As you've pointed out the MultiScene doesn't really have a concept or understanding of what the Scenes are that are contained within it. It doesn't know if they're multiple orbits, multiple timesteps/repeat cycles, different satellites/sensors, etc. So there will always have to be some level of understanding by the user that "functionality X only works in these specific cases of the MultiScene". At least as currently designed.

One additional thing to consider is using the blend method with timeseries to produce a DataArray with a time dimension. The blend method will give you a Scene so you could technically then request blended_scn.load([some_composite]) where some_composite could error out if its inputs don't have a time dimension. Downsides would be that the user would have to know what inputs are required for the time-based composite so they can load them before the time-series blending. Another downside is that because the blended Scene was created with this time-based DataArray that product will be included in the "wishlist" and not as a dependency of a composite. It would have to be manually removed if the user no longer wanted it and/or any future calls to something like save_dataset would have to specify datasets=["some_composite"] to avoid using these time-based blended DataArrays.

pnuu and others added 2 commits May 23, 2023 08:18
@pnuu
Copy link
Member Author

pnuu commented May 23, 2023

I think the YAML variant would need to use glob() to find the files for the previous times

Unless we have some way for the MultiScene to look up a YAML configuration?

That's the point where we'd need to do some glob() or something to find the files needed for the previous time steps.

Why? The MultiScene already has the scenes for each time step, provided by the user directly or (for operations) by a multi-timestep gatherer in pytroll-collectors.

True, but the composite loading happens at individual Scenes which have no information about other Scene objects, so the composite handling can't know about the other available time steps. I'll move over the discussion to #1748 for the additional stuff.

Comment on lines +193 to +194
compositor = GenericCompositor("temporal_composite")
composite = compositor((data_arrays[0], data_arrays[1], data_arrays[2]))
Copy link
Collaborator

Choose a reason for hiding this comment

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

If there is time metadata available, this would probably fail with IncompatibleTimes, due to the check in composites.check_times.

Copy link
Member Author

Choose a reason for hiding this comment

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

Any idea which data has that so I could test? SEVIRI HRIT and AVHRR L1b AAPP worked.

Copy link
Member

Choose a reason for hiding this comment

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

I think in a lot of cases we started removing the time metadata in the readers because it was so inconsistent. I could be completely misremembering though.

@codecov
Copy link

codecov bot commented May 25, 2023

Codecov Report

Merging #2488 (ec911db) into main (3b7a477) will increase coverage by 0.00%.
The diff coverage is 100.00%.

@@           Coverage Diff           @@
##             main    #2488   +/-   ##
=======================================
  Coverage   94.83%   94.83%           
=======================================
  Files         337      337           
  Lines       49430    49461   +31     
=======================================
+ Hits        46875    46906   +31     
  Misses       2555     2555           
Flag Coverage Δ
behaviourtests 4.43% <6.25%> (-0.01%) ⬇️
unittests 95.45% <100.00%> (+<0.01%) ⬆️

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

Impacted Files Coverage Δ
satpy/multiscene/_multiscene.py 92.81% <ø> (ø)
satpy/multiscene/__init__.py 100.00% <100.00%> (ø)
satpy/multiscene/_blend_funcs.py 100.00% <100.00%> (ø)
satpy/tests/multiscene_tests/test_blend.py 100.00% <100.00%> (ø)

@coveralls
Copy link

Pull Request Test Coverage Report for Build 5077181999

  • 32 of 32 (100.0%) changed or added relevant lines in 3 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage increased (+0.003%) to 95.402%

Totals Coverage Status
Change from base Build 5075221574: 0.003%
Covered Lines: 47019
Relevant Lines: 49285

💛 - Coveralls

"""
from satpy.composites import GenericCompositor

compositor = GenericCompositor("temporal_composite")
Copy link
Collaborator

Choose a reason for hiding this comment

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

GenericCompositor removes units and calibration information, which we probably don't want if we produce an RGB from three times the same channel. Maybe GenericCompositor contains other (implicit) assumptions that make it not optimal for temporal composites.

Copy link
Member

Choose a reason for hiding this comment

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

Yes, good point. GenericCompositor is really "generic image compositor" so it assumes things no longer represent "real physical data" anymore.

Copy link
Member Author

Choose a reason for hiding this comment

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

Units don't make sense for the returned RGB, so that's fine.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Why would units not make sense if I combine three times HRV or three times 10.8 µm?

Copy link
Member

Choose a reason for hiding this comment

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

As this is creating an RGB, we are already implicitely renaming the time dimension with bands, so I get we already moved out of the scientific realm to move into the imaging one.

@pnuu pnuu mentioned this pull request May 26, 2023
9 tasks
@pnuu
Copy link
Member Author

pnuu commented Oct 6, 2023

Apparently the data should be from times T/T-1/T not T-2/T-1/T, but the code don't care so that's possible as long as the correct times are used.

Copy link
Member

@mraspaud mraspaud left a comment

Choose a reason for hiding this comment

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

LGTM!

@mraspaud mraspaud added the enhancement code enhancements, features, improvements label Oct 10, 2023
@mraspaud mraspaud added this to the v0.44.0 milestone Oct 10, 2023
@mraspaud mraspaud merged commit ad543ef into pytroll:main Oct 11, 2023
16 checks passed
@pnuu pnuu deleted the feature-multiscene-temporal-rgb-blend branch October 20, 2023 07:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component:multiscene enhancement code enhancements, features, improvements
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

None yet

5 participants