You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I made an implementation of the rainbow hsv2rgb conversion function from FastLED.
Convert an HSV value to RGB using a visually balanced rainbow.
This "rainbow" yields better yellow and orange than a straight mathematical "spectrum".
I'm not sure how to better incorporate it into the library.
A function in adafruit_fancyled.py and a helper in fastled_helpers.py, or all in fastled_helpers.py ?
hsv2rgb_spectrum() from this library (maximum brightness)
my implementation of FastLED's hsv2rgb_spectrum() (uniform brightness) (following this graph)
# SPDX-FileCopyrightText: Copyright FastLed https://github.com/FastLED# SPDX-FileCopyrightText: Copyright 2025 Neradoc, https://neradoc.me# SPDX-License-Identifier: MIT"""Ported to python from the FastLed library.https://github.com/FastLED/FastLED/blob/master/src/hsv2rgb.cpp"""# Yellow has a higher inherent brightness than# any other color; 'pure' yellow is perceived to# be 93% as bright as white. In order to make# yellow appear the correct relative brightness,# it has to be rendered brighter than all other# colors.# Level Y1 is a moderate boost, the default.# Level Y2 is a strong boost.Y1=1Y2=0# G2: Whether to divide all greens by two.# Depends GREATLY on your particular LEDsG2=0# Gscale: what to scale green down by.# Depends GREATLY on your particular LEDsGscale=0defscale8(i, scale):
return (i* (1+scale)) >>8defscale8_video(i, scale):
return (1ifiandscaleelse0) + ((i*scale) >>8)
defhsv2rgb_rainbow(hsv):
""" Convert an HSV value to RGB using a visually balanced rainbow. This "rainbow" yields better yellow and orange than a straight mathematical "spectrum". :param Tuple(int, int, int) hsv: Color tuple (hue, saturation, value) as ints 0-255. :return Tuple(int, int, int): (red, green, blue) color tuple as ints 0-255. """hue, sat, val=hsvoffset=hue&0x1F# 0..31offset8= (offset*8) %256third=scale8(offset8, (256//3)) # max = 85r, g, b=0, 0, 0ifnot (hue&0x80):
# 0XXifnot (hue&0x40):
# 00X# section 0-1ifnot (hue&0x20):
# 000# case 0: # R -> Or=255-thirdg=thirdb=0else:
# 001# case 1: # O -> YifY1:
r=171g=85+thirdb=0ifY2:
r=170+thirdtwothirds=scale8(offset8, ((256*2) //3)) # max=170g=85+twothirdsb=0else:
# 01X# section 2-3ifnot (hue&0x20):
# 010# case 2: # Y -> GifY1:
# uint8_t twothirds = (third << 1)twothirds=scale8(offset8, ((256*2) //3)) # max=170r=171-twothirdsg=170+thirdb=0ifY2:
r=255-offset8g=255b=0else:
# 011# case 3: # G -> Ar=0g=255-thirdb=thirdelse:
# section 4-7# 1XXifnot (hue&0x40):
# 10Xifnot (hue&0x20):
# 100# case 4: # A -> Br=0# uint8_t twothirds = (third << 1)twothirds=scale8(offset8, ((256*2) //3)) # max=170g=171-twothirds# 170?b=85+twothirdselse:
# 101# case 5: # B -> Pr=thirdg=0b=255-thirdelse:
ifnot (hue&0x20):
# 110# case 6: # P -- Kr=85+thirdg=0b=171-thirdelse:
# 111# case 7: # K -> Rr=170+thirdg=0b=85-third# This is one of the good places to scale the green down,# although the client can scale green down as well.ifG2:
g=g>>1ifGscale:
g=scale8_video(g, Gscale)
# Scale down colors if we're desaturated at all# and add the brightness_floor to r, g, and b.ifsat!=255:
ifsat==0:
r=255b=255g=255else:
desat=255-satdesat=scale8_video(desat, desat)
satscale=255-desat# satscale = sat # uncomment to revert to pre-2021 saturation behavior# nscale8x3_video( r, g, b, sat)# brightness_floor = desatr=scale8(r, satscale) +desatg=scale8(g, satscale) +desatb=scale8(b, satscale) +desat# Now scale everything down if we're at value < 255.ifval!=255:
val=scale8_video(val, val)
ifval==0:
r=0g=0b=0else:
# nscale8x3_video( r, g, b, val)r=scale8(r, val)
g=scale8(g, val)
b=scale8(b, val)
return (r, g, b)
importtimefromhsvtorgb_rainbowimporthsv2rgb_rainbowfromPILimportImage, ImageDraw, ImageColorfromadafruit_fancyled.fastled_helpersimporthsv2rgb_spectrumsampling=None# Image.Resampling.BICUBIC# BICUBIC BILINEAR BOX HAMMING LANCZOS NEARESTRED= (255, 0, 0)
GREEN= (0, 255, 0)
BLUE= (100, 100, 255) # brighter for easier viewingSPC=4SPECTRUM=80CHART=256PIL_TOP=0THIS_TOP=SPECTRUM+SPC+CHART+SPCHEIGHT=THIS_TOP*3img=Image.new("RGBA", (512, HEIGHT), color=(0, 0, 0))
draw=ImageDraw.Draw(img)
val=255sat=255forkinrange(512):
hue=k%256rgb=hsv2rgb_rainbow((hue, sat, val))
# Version generated by PIL basic RGB interpolationpilrgb=ImageColor.getrgb(f"hsv({hue*360/256}, {sat/2.56}%, {val/2.56}%)")
# Version from FancyLEDspec_color=hsv2rgb_spectrum(hue, sat, val)
specrgb=tuple(int(256*x) forxin (spec_color.red, spec_color.green, spec_color.blue))
# Uniform brightness version from the graph in the fastLED docsifhue<=85:
r=256-hue*3g=hue*3b=0elifhue<=170:
r=0g=256- (hue-85) *3b= (hue-85) *3else:
r= (hue-170) *3g=0b=256- (hue-170) *3r, g, b= (int(x*val/256) forxin (r, g, b))
r, g, b= (int(x*sat/256) + (256-sat) forxin (r, g, b))
fastleddoc= (r,g,b)
# Out of those we display this one# refrgb = specrgb# for top, rgb_ref in zip( [PIL_TOP, THIS_TOP], [refrgb, rgb] ):fortop, rgb_refinzip(
[PIL_TOP, THIS_TOP, 2*THIS_TOP],
[rgb, specrgb, fastleddoc]
):
# spectrumdraw.line((k, top, k, top+SPECTRUM), fill=rgb_ref, width=2)
# componentsoffset=top+SPECTRUM+SPCfori, colorinzip(rgb_ref, (RED, GREEN, BLUE)):
height= (256-i)
position= (k, offset+height)
endpoint= (k+1, offset+height+1)
#draw.line((position, endpoint), fill=color, width=2)#draw.point(position, fill=color)draw.circle(position, radius=0.5, fill=color)
resized=img.resize((img.width, img.height//2), sampling)
resized.save("_tmp_sample-out.png", )
The text was updated successfully, but these errors were encountered:
This FastLED hsv2rgb_rainbow() algorithm is my favorite and returns much more vibrant intermediary colors than the simple linear crossfades in rainbowio.colorwheel() and adafruit_fancyled.CHSV().
My only feature request, since this is actively being considered for inclusion, is to also accommodate normalized float (0.0-1.0) HSV inputs, as the FastLED 8-bit resolution is its only disadvantage compared to the existing functions listed above.
I made an implementation of the rainbow hsv2rgb conversion function from FastLED.
I'm not sure how to better incorporate it into the library.
A function in adafruit_fancyled.py and a helper in fastled_helpers.py, or all in fastled_helpers.py ?
Included is test code generating the picture below for comparison with the hue chart from FastLED.
Reference: https://fastled.io/docs/group___pixel_types.html#gab316cfeb8bd5f37d8faaf761ad3c834b
Adapted from: https://github.com/FastLED/FastLED/blob/master/src/hsv2rgb.cpp (MIT license)
Note the comment about "maximum brightness at any given hue" style, vs. the "uniform brightness for all hues" style at the top of the "HSV to RGB Conversion Functions" page.
In order:
The text was updated successfully, but these errors were encountered: