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

Make muscle coloring (e.g. excitation, activation, etc.) more sensitive for lower values #933

Open
adamkewley opened this issue Oct 9, 2024 · 4 comments

Comments

@adamkewley
Copy link
Collaborator

adamkewley commented Oct 9, 2024

Reported by @tomvanwouwe1992

When muscles are excited/activated with lower signals (e.g. 0.2), it isn't very visually obvious that anything's going on.

Technical explanation: the current algorithm of LERPing Blue -> Red based on the coloring factor isn't visually sensitive enough and might require also going through some kind of easing/transform function (e.g. log it or similar).

@adamkewley
Copy link
Collaborator Author

adamkewley commented Oct 25, 2024

Example python code that's roughly equivalent to what's possible in the C++:

from PIL import Image
import numpy as np

# output params for this script
img_width = 255
img_height = 25

# model-wide state (can be tweaked)
zero_rgb = np.array([50.0 / 255.0, 50.0 / 255.0, 166.0 / 255.0])
one_rgb = np.array([255.0 / 255.0, 25.0 / 255.0, 25.0 / 255.0])

# returns `src` linearly blended with `dest` with the given (normalized) blending factor, `t`
def lerp_color(src, dest, t):
    return src + t*(dest - src)

# returns "eased" version of `t`, in the case where blending between zero and
# one shouldn't be applied linearly (e.g. more prevalance given to lower or
# higher values)
#
# see: https://easings.net
def apply_easing_function(t):
    return t

# e.g. https://easings.net/#easeOutQuint
def apply_easing_function_QUINT(t):
    return 1.0 - np.pow(1.0 - t, 5)

row_pixels = []
for step in range(0, img_width):
    t = apply_easing_function(step/img_width)
    color = 255 * lerp_color(zero_rgb, one_rgb, t)
    row_pixels += [color.astype(np.uint8)]

ary = np.array(img_height*[row_pixels], dtype=np.uint8)
img = Image.fromarray(ary)
img.save('img.png')

Where OSC engine is able to customize the start color, end color, and easing function. The code basically pulls the factor of interest (e.g. activation) from the muscle, which is equivalent to step/img_width in this visualization. Relevant code in OSC is:

An apply_easing_function could be added to this quite easily.

@adamkewley
Copy link
Collaborator Author

adamkewley commented Oct 25, 2024

I've had a think about this feature and a little discussion with @tomvanwouwe1992 and I think that the following design should hopefully cover some bases, without being too complicated, and with enough forward compatibility to enable further additions:

  1. Rename Coloring in the Muscle Styling panel to Color Source, because we want to extend "coloring behavior".
  2. Rename the OpenSim (Appearance Property) color source to Appearance Property, for clarification.
  3. Drop the OpenSim (i.e. same as OpenSim UI) color source. Instead, directly show the user that the color source is Excitation (for example), so that users don't have to know what OpenSim's default color source is.
  4. Add a Color Source Scaling section containing radio buttons/dropdowns, where the user can select how the Color Source is scaled. Defaults to None (no scaling), but could also be Model-Wide Min/Max, Trajectory-Wide Min/Max, or Model- and Trajectory-Wide Min/Max). This lets the user modify how the colors are scaled from their source value (e.g. normalized fiber length of one muscle) to the value that will be fed into the color ramp.
  5. Add a Color Ramp dropdown, where users can select from a predetermined list of color ramps, and which defaults to the color ramp that OSC already effectively uses (i.e. LERPing between blue and red). Although this isn't as customizable as letting the user select colors, easing functions etc., if it's populated with a few good defaults (e.g. high-sensitivity lows, vidris, rainbow, whatever) then it will probably be good enough for most use-cases.

@adamkewley
Copy link
Collaborator Author

I'm going to need a C++ abstraction for color mapping, matplotlib has a very nice explanation of its colormapping facilities, which I think should be mirrored in OSC:

https://matplotlib.org/stable/users/explain/colors/colormaps.html

@adamkewley
Copy link
Collaborator Author

Note to self: it would be a good idea to add an automated test for the auto-scaling feature. Create a dummy model with known muscle activations etc. and ensure that the decoration generator generates cylinders/spheres etc. with scaled colors.

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

No branches or pull requests

1 participant