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

GMT-like shaded relief illumination via view.py --dem-blend #1089

Merged
merged 17 commits into from
Sep 24, 2023

Conversation

yuankailiu
Copy link
Contributor

@yuankailiu yuankailiu commented Sep 17, 2023

Description of proposed changes

Hi @yunjunz,

Here is what MintPy has long been awaiting for 🌟!!! To be like GMT 😆

Rather than plotting DEM in the background and overlaying the data with transparency, I attempt to view data with color adjusted by the shaded relief DEM when provided. Just like gmt grdimage -I feature. Hereafter, I call this way of showing data as "DEM-blended data".

This can be done via matplotlib.colors.LightSource.shade_rgb() module (as opposed to what has been used in MintPy, i.e., the LightSource.shade() module, which only produces the shaded relief of the DEM itself).

Thus, I added two major functions in plot.py to handle this functionality of displaying DEM-blended data.

Changes in MintPy code are as follows.

  • arg_utils.py: allow the user to specify --dem-blend, --shade-frac, and --base-color arguments to activate and tune the DEM-blended data plotting.

    • --dem-blend : to activate the way of plotting the DEM-blended data
    • --shade-frac : tune the fraction or the contrast in shade_rgb()
    • --base-color : set the grayish color of the DEM background
  • plot.py:

    • shaded_image(): add illumination to the RGB data array based on a DEM file.
    • shaded_colorbar(): to plot a shaded illuminated colorbar. The shading is just schematic, not reflecting the real shades in the data (see future To-dos at the end of this PR).
    • plot_image4view(): handle the top layer plotting, this will call several other functions in plot.py, including the above two new functions.
  • view.py: adjust the current code to call new functions in plot.py. For a single plot, the DEM-blended data is shown well. We need to have some further tweaks for multiple subplots.

Calling view.py examples:

# use a DEM from `inputs/srtm.dem` (you can also use your inputs/geometryGeo.h5) as shaded relief to adjust/blend the color of your velocity data, mask out pixels when data is NaN
view.py velocity.h5 velocity -c RdBu_r --dem inputs/srtm.dem --mask maskTempCoh.h5 --unit mm --ref-lalo 29.5463 36.0810 --vlim -4 4 --dem-nocontour --shade-exag 0.2 --dem-blend --base-color=0.9 --figtitle fancy-gmt-style

Results:
image

Compare between the original view.py figure (left) and this PR (right).

Shading parameters (shown in both figure titles) are intentionally adjusted so that the two plots have a similar color tone. The only difference is the style in which the DEM is displayed.

# left (original MintPy way of plotting data overlaying on top of the DEM)
view.py velocity.h5 velocity -c RdBu_r --dem inputs/srtm.dem --mask maskTempCoh.h5 --unit mm --ref-lalo 29.5463 36.0810 --vlim -4 4 --dem-nocontour --shade-exag 0.5 --shade-min -6000 --shade-max 3000 --alpha 0.8

# right (this PR; I set the default blend_mode in LightSource.shade_rgb() as 'hsv' for better visualization of the map)
view.py velocity.h5 velocity -c RdBu_r --dem inputs/srtm.dem --mask maskTempCoh.h5 --unit mm --ref-lalo 29.5463 36.0810 --vlim -4 4 --dem-nocontour --dem-blend --shade-exag 0.5 --shade-frac 0.3 --base-color=0.9

# They are using the same light source, with MintPy default --shade-az (315.) and --shade-alt (45.)

image

Compare different blend_mode

There are three blend modes in matplotlib.colors.LightSource.shade_rgb(), {hsv,overlay,soft}. The default is hsv. Although matplotlib documentation says overlay or soft may be more realistic in showing elevation, hsv somehow looks better from my not-thoroughly tests (upper right panel in the below plot).

image

However, hsv seems to have some artificial color jumps (upper right panel in the below plot) when plotting these Solid Earth tides apparent velocity with a tiny data range.

image

Future To-do

  1. Know what was going on for hsv showing that color jump.

  2. Implement this in the multiple subplots function, such as plotting all the geometries (height, longitude, latitude, incidencAngle, etc. with DEM shaded effects):

view.py inputs/geometry.geo -d inputs/geometry.geo

I try to adapt the code and implement this in plotting multiple subplots. But it still looks like it is plotting as the original MintPy way. The DEM-blended data does not seem to show up.

  1. The current fancy version of the colorbar with the illumination effects is not indicating the real shading in the data image. It is now serve only as a schematic. Although maybe it is hard to tell from our eyes. I did this by creating a fake color data from vmin & vmax in the colorbar axis, and adding yet another fake cosine-function elevation profile (hardcoded values) with shaded relief across the colorbar axis to micmic the illumination effects. Here, I have to use soft blend_mode for better performance. I know now it sounds pretty hacky, but it works. Check (plot.py shaded_colorbar() function).

  2. Perhaps some refactoring is needed.

Reminders

  • Pass Pre-commit check (green)
  • Pass Codacy code review (green)
  • Pass Circle CI test (green)
  • Make sure that your code follows our style. Use the other functions/files as a basis.
  • If modifying functionality, describe changes to function behavior and arguments in a comment below the function declaration.
  • If adding new functionality, add a detailed description to the documentation and/or an example.

Rather than plotting DEM in the background and overlay the data with transparency, I attempt to view data with color adjusted by the shaded relief DEM when provided. Just like gmt grdimage -I option. Hereafter, I call this DEM-blended data.

This can be done via matplotlib.colors.LightSource.shade_rgb() module (as opposed to what has been used in MintPy, i.e., the shade() module, that only produces the shaded relief of the DEM itself).

Thus, I added two major functions in plot.py to handle this functionality of displaying DEM-blened data.

Changes in MintPy code are as follows.

+ arg_utils.py: allow user to specify --dem-blend --shade-frac --base-color arguments to activate and tune the DEM-blended data plotting

+ plot.py:
   - shaded_image(): add illumination to the rgb data array based on a DEM file.
   - shaded_colorbar(): to plot a shaded illuminated colorbar. The shading is just schematic, not reflecting the real shades in the data.
   - plot_image4view(): handle the top layer plotting, this will call several other functinos in plot.py, including the above two new functions.

+ view.py: adjust the current code to call new functions in plot.py. For single plot, the DEM-blended data is shown well. Need to have some further tweaks for multiple subplots.

update function comments
@yuankailiu yuankailiu requested a review from yunjunz September 17, 2023 05:59
@yunjunz yunjunz changed the title GMT-like shaded relief illumination in view.py GMT-like shaded relief illumination in view.py Sep 17, 2023
@yunjunz
Copy link
Member

yunjunz commented Sep 18, 2023

This is great @yuankailiu!! I have a few comments below:

Regarding the to-do list:

Implement this in the multiple subplots function, such as plotting all the geometries (height, longitude, latitude, incidencAngle, etc. with DEM shaded effects):

We don't need to worry about that at this PR. For a maintainable future, let's just add advanced features to the single subplot (I have been doing that for many years now). For multi-subplots, simplicity and efficiency are more important. People can run the command multiple times if they want nice figures, which is easy to do. Thus, I would suggest keeping this PR to the single plot only.

The current fancy version of the colorbar.

If I understand it correctly, we could not really interpret the color in the shaded version to a super-accurate level, because the shading of different slopes will give different colors, thus, it might be not doable/useful to have a shaded colorbar?

From my limited use of GMT, I don't remember seeing a shaded colorbar, so it's totally fine with me to use the regular colorbar in this case. Maybe I am wrong.

I like the utils.plot.plot_image4view() function, which simplifies the view.plot_slice(). But I find the modified prep/plot_dem_background() functions less clear, as they are not really pure DEM anymore. How about the following re-orgnization, so that the this PR is completely independent of existing sub-functions in utils.plot.py?

  • shaded_image() -> prep_blend_image()
  • combine the new code in prep/plot_dem_background() into plot_blend_image()
  • call prep/plot_blend_image() in plot_image4view()

Another reason for the independent prep/plot_blend_image() is that we could add the resampling capability there in the future. Let me know what you think please.

@yunjunz
Copy link
Member

yunjunz commented Sep 18, 2023

Know what was going on for hsv showing that color jump.

Yes, this is strange in the apparent solid Earth tides velocity.

Testing on the FernandinaSenDT128 example dataset, I actually finds the overlay mode more appealing. Shall we add an option for the blend mode, and switch to overlay as default?

blend

Similarly for the 2023 Turkey EQ from an ALOS-2 interferogram as well:

2023TurkeyEQ

@yuankailiu
Copy link
Contributor Author

yuankailiu commented Sep 18, 2023

Ok, overlay looks more realistic, hsv looks much more striking topography. I think we can use overlay as default and allow a --blend-mode input option.

@yunjunz
Copy link
Member

yunjunz commented Sep 19, 2023

Ok, overlay looks more realistic, hsv looks much more striking topography. I think we can use overlay as default and allow a --blend-mode input option.

Great, I am done with changes on my side for now.

@yuankailiu
Copy link
Contributor Author

Thanks, I agree and will work on the suggestions above regarding functions inside utils.plot.

Yes, we can just allow for single plotting for now. Keep it simple.

Yes, GMT does have a shaded color bar if your image is shaded with illumination. But I don't know what they do exactly.

+ keep prep/plot_dem_background() functions as before

+ shaded_image() -> prep_blend_image()

+ combine the new code in prep/plot_dem_background() into plot_blend_image()

+ call prep/plot_blend_image() in plot_image4view()

+ view.py adapts to the changes; for plot_subplot4figure(), call plot_image4view() as well

TODO: we could add the resampling capability in plot_blend_image() in the future
@yuankailiu
Copy link
Contributor Author

Hi @yunjunz, I am done with the changes.

yuankailiu and others added 8 commits September 20, 2023 16:29
+ view.plot_subplot4figure(): revert back to call plot_dem_background() and imshow() separately

+ utils.plot.py:
    - rename prepare_dem_background() --> prep_dem_background()
    - plot_dem_background(): revert any() to all() during the if checking
    - light refactoring of plot_image4view()
Simplify the plot_blend_image() by leveraging the existing plot_dem_background(), instead of copying over the DEM contour code

+ cli.view.cmd_line_parse(): add checkings for --dem-blend option
    - turn off the disp_dem_shade option
    - check the existence of dem_file option

+ utils.plot.py:
   - prep_blend_image(): move the LightSource initiation within the func to simplify the inputs
   - plot_blend_image(): call prep_blend_image() within the func, then directly call imshow(); i.e., 1) remove the if checkings for preparing blended image and DEM contour; 2) remove the code for plotting the DEM contour
   - rename the old plot_blend_imgage() to plot_blend_image2(), which will be removed in the next commit

+ view.py
   - leveraging plot_dem_background() to plot the DEM contour, to simplify plot_blend_image()
   - then the plot_image4view() is not necessary anymore, thus, revert back the call of imshow() in view.py, for simplicity

TO-DO: refactoring prep_blend_image() and plot_blend_colorbar()
+ rename plot_blend_colorbar() to blend_colorbar(), to be more consistent with matplotlib colorbar() func name

+ blend_colorbar() refactoring:
   - remove epsilon/vmin/vmax as it can be given from plot_colorbar()
   - create normalized array without vmin/vmax, which is unnecessary anyway
   - support blended colorbar for both vertical and horizontal orientations
   - fine tune the axis format config
   - support custom ticks

+ utils.plot.plot_colorbar(): integrate blend_colorbar():
   - simplify the calls of plt.colorbar()
   - call blend_colorbar() to give an similar appearence as plt.colorbar(), so that they could share the laterward customization in a consistent way. The two styles can be recognized via cbar variable, which is None for blend_colorbar()
   - support customized ticks/labels for both styles

+ view.plot_slice():
   - revert back to the same old call of pp.plot_colorbar() for simplicity
   - fine tune the tick position for colorbar at the bottom
Copy link
Member

@yunjunz yunjunz left a comment

Choose a reason for hiding this comment

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

All looks great to me now. Thank you @yuankailiu for this PR, I like the new style a lot: which has increased the contrast between the valid/invalid data, and removed the reliance on transparency, thus, resulting in a much prettier figure as well.

I want to use this new style more after the PR and try it more intensively (to fine-tune the parameters), maybe we could switch to this style by default. Cheers!

Here is the comparison between the old and new style, for bookkeeping purposes, look how pretty it is!

  • Old style: hillshade + transparent data
view.py filt_220916-230217_5rlks_28alks_msk.int phase --dem 220916-230217_5rlks_28alks.hgt -c cmy

view_orig

  • New style: hillshade + DEM-blended data
view.py filt_220916-230217_5rlks_28alks_msk.int phase --dem 220916-230217_5rlks_28alks.hgt -c cmy --dem-blend

view_new

although local testing passed without error
@yunjunz yunjunz changed the title GMT-like shaded relief illumination in view.py GMT-like shaded relief illumination via view.py --dem-blend Sep 24, 2023
@yunjunz yunjunz merged commit 20a395e into insarlab:main Sep 24, 2023
5 checks passed
@hvasbath
Copy link

hvasbath commented Sep 25, 2023

Awesome! This looks great! Do you mind if I use this as well in BEAT?

@yuankailiu
Copy link
Contributor Author

yuankailiu commented Sep 25, 2023

Hello Hannes, it's great to connect here! Of course not, you are very welcome to use it in BEAT.

The major functions are:

  • plot.prep_blend_image(): Prepare the RGBT array with an input DEM and an input data array. You can test those parameters and blend_modes to optimize the visualizations.
  • plot.plot_blend_image(): Plot the RGBT array with pyplot.imshow()
  • plot.blend_colorbar(): Plot the cool colorbar. This one right now is still a little hacky as you will see. elev = np.ones_like(arr) * np.cos(0.6*x), I used an arbitrary cosine function as a fake elevation profile across the colorbar width to reflect the illumination effects. I don't know if it is the right way to do it, but it looks okay. Try to mimic this GMT plot or like in Sjonni's paper

@yuankailiu yuankailiu deleted the dev.view branch September 25, 2023 18:02
yuankailiu added a commit to yuankailiu/MintPy that referenced this pull request Sep 26, 2023
…ab#1089)

Rather than plotting DEM in the background and overlaying the data with transparency, I attempt to view data with color adjusted by the shaded relief DEM when provided. Just like `gmt grdimage -I` option. Hereafter, I call this DEM-blended data.

This can be done via `matplotlib.colors.LightSource.shade_rgb()` module (as opposed to what has been used in MintPy, i.e., the `shade()` module, that only produces the shaded relief of the DEM itself). This PR adds this support for the single-subplot scenario via changes in `view.plot_slice()`, the multi-subplot scenarios are not supported.

Detailed changes in MintPy code are as follows:

+ `utils.arg_utils.add_dem_argument()`: add the following new options to allow users to activate and tune the DEM-blended data plotting: `--dem-blend`, `--blend-mode`, `--shade-frac` and `--base-color`

+ `utils.plot.py`:
   - add `prep_blend_image()` to prepare the illuminated RGB array from a given data and DEM
   - add `plot_blend_image()` to call `prep_blend_image()` and `imshow` for plotting
   - add `blend_colorbar()` to create a DEM-blended colorbar, to be called within `plot_colorbar()`
   - `plot_colorbar()`: add generic customization support for the DEM-blended colorbar from `blend_colorbar()`
   - rename: `prepare_dem_background()` --> `prep_dem_background()`

+ `cli.view.cmd_line_parse()`: add checkings for `--dem-blend` option
    - turn off the disp_dem_shade option
    - check the existence of `--dem` option

+ `view.plot_slice()`: call `plot.plot_blend_image()` for the DEM-blended style.

TODO: we could add the resampling capability in plot_blend_image(), to handle the different resolution between data and DEM.

---------

Co-authored-by: Zhang Yunjun <[email protected]>
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