This is a really solid idea from @brecht here given that we have the XYZ role; the resulting spectral distribution can be directly plopped back into the scene linear rendering space if all of the spectral goodness is juiced out.
The only place where this becomes problematic is gamut mapping, in that the destination scene linear rendering space may be a different volume than the wide-open spectral calculation model. As a result, it can cause collapsing of certain discrete values into identical destination code values, and you end up with posterization.
Granted, it’s a great problem to have, and one that in theory can be resolved via a rendering transform in OpenColorIO I’d think.
import colour
from colour import plotting
colour.plotting.plot_single_sd(colour.PHOTOPIC_LEFS["CIE 1924 Photopic Standard Observer"])
Just use the luminance weights of the XYZ. That’s your luminous efficacy function baked into XYZ. If you need to operate on the spectral distribution directly, you’ll need the CIE 1924 luminous efficacy function itself. The luma coefficients code I contributed a while back will pull the coefficients from the OpenColorIO configuration, or the newer XYZ role that @lukasstockner97 added recently should suffice.
It should come with caution however, as some spectral wavelengths could have a rather large impact on the resulting chromaticity, and luminous efficacy isn’t the most tremendous metric here I believe. For example, there’s inherent metameric issues as you get near the spectral locus, and doubly so as you head toward the limits on the upper and lower ranges of the wavelengths.
Because I literally just output this, I figured I might as well include it here: