"""
Geomedian widget: generates an interactive visualisation of
the geomedian summary statistic.
"""
# Load modules
import ipywidgets as widgets
import matplotlib.pyplot as plt
import numpy as np
import xarray as xr
from odc.algo import xr_geomedian
[docs]
def run_app():
"""
An interactive app that allows users to visualise the difference between the median and geomedian time-series summary statistics. By modifying the red-green-blue values of three timesteps for a given pixel, the user changes the output summary statistics.
This allows a visual representation of the difference through the output values, RGB colour, as well as showing values plotted as a vector on a 3-dimensional space.
Last modified: December 2021
"""
# Define the red-green-blue sliders for timestep 1
p1r = widgets.IntSlider(description="Red", max=255, value=58)
p1g = widgets.IntSlider(description="Green", max=255, value=153)
p1b = widgets.IntSlider(description="Blue", max=255, value=68)
# Define the red-green-blue sliders for timestep 2
p2r = widgets.IntSlider(description="Red", max=255, value=208)
p2g = widgets.IntSlider(description="Green", max=255, value=221)
p2b = widgets.IntSlider(description="Blue", max=255, value=203)
# Define the red-green-blue sliders for timestep 3
p3r = widgets.IntSlider(description="Red", max=255, value=202)
p3g = widgets.IntSlider(description="Green", max=255, value=82)
p3b = widgets.IntSlider(description="Blue", max=255, value=33)
# Define the median calculation for the timesteps
def f(p1r, p1g, p1b, p2r, p2g, p2b, p3r, p3g, p3b):
print("Red Median = {}".format(np.median([p1r, p2r, p3r])))
print("Green Median = {}".format(np.median([p1g, p2g, p3g])))
print("Blue Median = {}".format(np.median([p1b, p2b, p3b])))
# Define the geomedian calculation for the timesteps
def g(p1r, p1g, p1b, p2r, p2g, p2b, p3r, p3g, p3b):
print(
"Red Geomedian = {:.2f}".format(
xr_geomedian(
xr.Dataset({
"red": (("x", "y", "time"), [[[np.float32(p1r), np.float32(p2r), np.float32(p3r)]]]),
"green": (("x", "y", "time"), [[[np.float32(p1g), np.float32(p2g), np.float32(p3g)]]]),
"blue": (("x", "y", "time"), [[[np.float32(p1b), np.float32(p2b), np.float32(p3b)]]]),
})
).red.values.ravel()[0]
)
)
print(
"Green Geomedian = {:.2f}".format(
xr_geomedian(
xr.Dataset({
"red": (("x", "y", "time"), [[[np.float32(p1r), np.float32(p2r), np.float32(p3r)]]]),
"green": (("x", "y", "time"), [[[np.float32(p1g), np.float32(p2g), np.float32(p3g)]]]),
"blue": (("x", "y", "time"), [[[np.float32(p1b), np.float32(p2b), np.float32(p3b)]]]),
})
).green.values.ravel()[0]
)
)
print(
"Blue Geomedian = {:.2f}".format(
xr_geomedian(
xr.Dataset({
"red": (("x", "y", "time"), [[[np.float32(p1r), np.float32(p2r), np.float32(p3r)]]]),
"green": (("x", "y", "time"), [[[np.float32(p1g), np.float32(p2g), np.float32(p3g)]]]),
"blue": (("x", "y", "time"), [[[np.float32(p1b), np.float32(p2b), np.float32(p3b)]]]),
})
).blue.values.ravel()[0]
)
)
# Define the Timestep 1 box colour
def h(p1r, p1g, p1b):
fig1, axes1 = plt.subplots(figsize=(2, 2))
fig1 = plt.imshow([[(p1r, p1g, p1b)]])
axes1.set_title("Timestep 1")
axes1.axis("off")
plt.show(fig1)
# Define the Timestep 2 box colour
def hh(p2r, p2g, p2b):
fig2, axes2 = plt.subplots(figsize=(2, 2))
fig2 = plt.imshow([[(p2r, p2g, p2b)]])
axes2.set_title("Timestep 2")
axes2.axis("off")
plt.show(fig2)
# Define the Timestep 3 box colour
def hhh(p3r, p3g, p3b):
fig3, axes3 = plt.subplots(figsize=(2, 2))
fig3 = plt.imshow([[(p3r, p3g, p3b)]])
axes3.set_title("Timestep 3")
axes3.axis("off")
plt.show(fig3)
# Define the Median RGB colour box
def i(p1r, p1g, p1b, p2r, p2g, p2b, p3r, p3g, p3b):
fig4, axes4 = plt.subplots(figsize=(3, 3))
fig4 = plt.imshow([
[(int(np.median([p1r, p2r, p3r])), int(np.median([p1g, p2g, p3g])), int(np.median([p1b, p2b, p3b])))]
])
axes4.set_title("Median RGB - All timesteps")
axes4.axis("off")
plt.show(fig4)
# Define the Geomedian RGB colour box
def ii(p1r, p1g, p1b, p2r, p2g, p2b, p3r, p3g, p3b):
fig5, axes5 = plt.subplots(figsize=(3, 3))
fig5 = plt.imshow([
[
(
int(
xr_geomedian(
xr.Dataset({
"red": (("x", "y", "time"), [[[np.float32(p1r), np.float32(p2r), np.float32(p3r)]]]),
"green": (("x", "y", "time"), [[[np.float32(p1g), np.float32(p2g), np.float32(p3g)]]]),
"blue": (("x", "y", "time"), [[[np.float32(p1b), np.float32(p2b), np.float32(p3b)]]]),
})
).red.values.ravel()[0]
),
int(
xr_geomedian(
xr.Dataset({
"red": (("x", "y", "time"), [[[np.float32(p1r), np.float32(p2r), np.float32(p3r)]]]),
"green": (("x", "y", "time"), [[[np.float32(p1g), np.float32(p2g), np.float32(p3g)]]]),
"blue": (("x", "y", "time"), [[[np.float32(p1b), np.float32(p2b), np.float32(p3b)]]]),
})
).green.values.ravel()[0]
),
int(
xr_geomedian(
xr.Dataset({
"red": (("x", "y", "time"), [[[np.float32(p1r), np.float32(p2r), np.float32(p3r)]]]),
"green": (("x", "y", "time"), [[[np.float32(p1g), np.float32(p2g), np.float32(p3g)]]]),
"blue": (("x", "y", "time"), [[[np.float32(p1b), np.float32(p2b), np.float32(p3b)]]]),
})
).blue.values.ravel()[0]
),
)
]
])
axes5.set_title("Geomedian RGB - All timesteps")
axes5.axis("off")
plt.show(fig5)
# Define 3-D axis to display vectors on
def j(p1r, p1g, p1b, p2r, p2g, p2b, p3r, p3g, p3b):
fig6 = plt.figure()
axes6 = fig6.add_subplot(111, projection="3d")
x = [
p1r,
p2r,
p3r,
int(np.median([p1r, p2r, p3r])),
int(
xr_geomedian(
xr.Dataset({
"red": (("x", "y", "time"), [[[np.float32(p1r), np.float32(p2r), np.float32(p3r)]]]),
"green": (("x", "y", "time"), [[[np.float32(p1g), np.float32(p2g), np.float32(p3g)]]]),
"blue": (("x", "y", "time"), [[[np.float32(p1b), np.float32(p2b), np.float32(p3b)]]]),
})
).red.values.ravel()[0]
),
]
y = [
p1g,
p2g,
p3g,
int(np.median([p1g, p2g, p3g])),
int(
xr_geomedian(
xr.Dataset({
"red": (("x", "y", "time"), [[[np.float32(p1r), np.float32(p2r), np.float32(p3r)]]]),
"green": (("x", "y", "time"), [[[np.float32(p1g), np.float32(p2g), np.float32(p3g)]]]),
"blue": (("x", "y", "time"), [[[np.float32(p1b), np.float32(p2b), np.float32(p3b)]]]),
})
).green.values.ravel()[0]
),
]
z = [
p1b,
p2b,
p3b,
int(np.median([p1b, p2b, p3b])),
int(
xr_geomedian(
xr.Dataset({
"red": (("x", "y", "time"), [[[np.float32(p1r), np.float32(p2r), np.float32(p3r)]]]),
"green": (("x", "y", "time"), [[[np.float32(p1g), np.float32(p2g), np.float32(p3g)]]]),
"blue": (("x", "y", "time"), [[[np.float32(p1b), np.float32(p2b), np.float32(p3b)]]]),
})
).blue.values.ravel()[0]
),
]
labels = [" 1", " 2", " 3", " median", " geomedian"]
axes6.scatter(x, y, z, c=["black", "black", "black", "r", "blue"], marker="o")
axes6.set_xlabel("Red")
axes6.set_ylabel("Green")
axes6.set_zlabel("Blue")
axes6.set_xlim3d(0, 255)
axes6.set_ylim3d(0, 255)
axes6.set_zlim3d(0, 255)
for ax, ay, az, label in zip(x, y, z, labels):
axes6.text(ax, ay, az, label)
plt.title("Each band represents a dimension.")
plt.show()
# Define outputs
outf = widgets.interactive_output(
f, {"p1r": p1r, "p2r": p2r, "p3r": p3r, "p1g": p1g, "p2g": p2g, "p3g": p3g, "p1b": p1b, "p2b": p2b, "p3b": p3b}
)
outg = widgets.interactive_output(
g, {"p1r": p1r, "p2r": p2r, "p3r": p3r, "p1g": p1g, "p2g": p2g, "p3g": p3g, "p1b": p1b, "p2b": p2b, "p3b": p3b}
)
outh = widgets.interactive_output(h, {"p1r": p1r, "p1g": p1g, "p1b": p1b})
outhh = widgets.interactive_output(hh, {"p2r": p2r, "p2g": p2g, "p2b": p2b})
outhhh = widgets.interactive_output(hhh, {"p3r": p3r, "p3g": p3g, "p3b": p3b})
outi = widgets.interactive_output(
i, {"p1r": p1r, "p2r": p2r, "p3r": p3r, "p1g": p1g, "p2g": p2g, "p3g": p3g, "p1b": p1b, "p2b": p2b, "p3b": p3b}
)
outii = widgets.interactive_output(
ii, {"p1r": p1r, "p2r": p2r, "p3r": p3r, "p1g": p1g, "p2g": p2g, "p3g": p3g, "p1b": p1b, "p2b": p2b, "p3b": p3b}
)
outj = widgets.interactive_output(
j, {"p1r": p1r, "p2r": p2r, "p3r": p3r, "p1g": p1g, "p2g": p2g, "p3g": p3g, "p1b": p1b, "p2b": p2b, "p3b": p3b}
)
return widgets.HBox([
widgets.VBox([
widgets.HBox([outh, widgets.VBox([p1r, p1g, p1b])]),
widgets.HBox([outhh, widgets.VBox([p2r, p2g, p2b])]),
widgets.HBox([outhhh, widgets.VBox([p3r, p3g, p3b])]),
]),
widgets.VBox([widgets.HBox([widgets.VBox([outf, outi]), widgets.VBox([outg, outii])]), outj]),
])