The technical term you’re looking for is Relative Luminance. It is a specific standard for calculating the brightness of colors as perceived by humans, with scientifically derived weights.
A rather well-illustrated Twitter/X thread on the subject.
Although the Processing algorithm for grayscale conversion (using filter(GRAY)
) is not an exact implementation of relative luminance, it’s conceptually similar and a close approximation for general use (only the weights differ slightly).
For comparison, here’s what the grayscale conversion function would look like if it used perceptual precision weightings.
def relative_luminance(img):
"""
Convert the image to grayscale using true relative luminance weights and bit shifting.
Reference -> https://en.wikipedia.org/wiki/Relative_luminance
"""
lum_img = createImage(img.width, img.height, RGB)
img.loadPixels()
lum_img.loadPixels()
for i in range(len(img.pixels)):
col = img.pixels[i]
# Extract RGB components using bit shifts
r = (col >> 16) & 0xff # Red component
g = (col >> 8) & 0xff # Green component
b = col & 0xff # Blue component
# Calculate the true relative luminance using scaled weights:
# Luminance = 0.2126 * Red + 0.7152 * Green + 0.0722 * Blue
# Approximation: 0.2126 * 256 = 54, 0.7152 * 256 = 183, 0.0722 * 256 = 18
lum = (54 * r + 183 * g + 18 * b) >> 8 # Bit-shift by 8 (dividing by 256)
# Set the grayscale pixel by combining the luminance value into RGB format
lum_img.pixels[i] = (col & 0xff000000) | (lum << 16) | (lum << 8) | lum
lum_img.updatePixels()
return lum_img