I can't find anywhere online or in documentation that explains clearly how to make this conversion using Python. In my situation I need to do it with OpenImageIO- I just need to feed it a path and save out the converted image, and ideally it would be great to output a single channel grayscale image. Anyone know how? Thanks in advance!
-
You could for example calculate the average of the R, G, and B channels. – mkrieger1 Jan 08 '21 at 01:50
-
I took your suggestion mkrieger1 and was able to come up with a workable solution with some additional processing, so thank you for that. By averaging channel values there is a considerable departure from the initial image however (the reason for this fits into a process for converting specular maps to metallic maps in an *attempt* at automation. What I wanted was to see a comparable shift in values with when converting an sRGB image to Grayscale in Photoshop for instance- doing created some very noticeable shift in value but it was still helpful in arriving at a solution for now. Thanks! – bblack Jan 08 '21 at 08:17
-
if anyone reads this and knows of a better way to convert to Grayscale I'm still very interested in alternatives, and would very much appreciate it. The previous suggestion works, but its not ideal. – bblack Jan 08 '21 at 08:22
1 Answers
The "averaging" approach is wrong, because your eyes are not uniformly sensitive to all wavelengths, so R, G, and B do not contribute equally to your perception of brightness. That means that the grayscale image you get by averaging won't seem to have the same relative brightnesses as the original color image. Also, sRGB is not a linear encoding, so "average" (or any weighted sum) of the encoded value is not what you imagine it should be.
The fully correct way to do this with OpenImageIO from Python is:
import OpenImageIO as oiio
# Read the image, force it to be float for full precision
img = oiio.ImageBuf("input.jpg")
img.read(convert='float')
# linearize it -- this assumes sRGB, which jpeg always is
lin = oiio.ImageBufAlgo.colorconvert(img, "sRGB", "linear")
# generate a luminance image with proper channel weights
luma = oiio.ImageBufAlgo.channel_sum (img, (.2126, .7152, .0722))
# convert back to sRGB for proper jpeg output
luma = oiio.ImageBufAlgo.colorconvert(luma, "linear", "sRGB")
luma.write("luma.jpg")
If you're just interested in doing this from the command line (not Python), it's extra easy:
oiiotool --autocc rgb.jpg --chsum:weight=.2126,.7152,.0722 -o luma.jpg
(the autocc does the automatic linearizing upon input and back to sRGB upon output to jpeg)
Note that in both cases, you're outputting a single channel image with the luminance. It's only a couple extra lines if you really need to convert it to an RGB image where R, G, and B are all equal.

- 13,331
- 5
- 42
- 42
-
Hi Larry- thanks so much for taking the time to answer my question. What you are saying makes perfect sense, but when I processed an image using the code you provided, the output is still coming out significantly lighter than the original image. Is there possibly anything missing from it? A comparison of what should be the same area yields: – bblack Jan 11 '21 at 00:05
-
-
I should also note that many of these specular textures I'm needing to process are RGB images, but have identical values for each channel (noticed this when I entered the comparison above). Some are like this, but not *all*. Should this process you provided still output the same results, regardless of what each pixel's channel values are? Again appreciate any insight you can provide for understanding this process! – bblack Jan 11 '21 at 00:18
-
Sorry, @bblack, I didn't see this for a long time. That does sound strange. If you are still having trouble, can you ask the question on the oiio-dev mail list or file an issue on its GitHub, and we can help you investigate? – Larry Gritz Mar 10 '21 at 22:11