pythoncolorspython-imaging-librarygrayscale

Detect almost grayscale image with Python


Inspired by this question and this answer (which isn't very solid) I realized that I often find myself converting to grayscale a color image that is almost grayscale (usually a color scan from a grayscale original). So I wrote a function meant to measure a kind of distance of a color image from grayscale:

import numpy as np
from PIL import Image, ImageChops, ImageOps, ImageStat

def distance_from_grey(img): # img must be a Pillow Image object in RGB mode
    img_diff=ImageChops.difference(img, ImageOps.grayscale(img).convert('RGB'))
    return np.array(img_diff.getdata()).mean()

img = Image.open('test.jpg')
print(distance_from_grey(img))

The number obtained is the average difference among all pixels of RGB values and their grayscale value, which will be zero for a perfect grayscale image.

What I'm asking to imaging experts is:


Solution

  • Given the following 3 images and using Colour:

    McDonald Lake Niagara Falls Colourful Pencils

    import numpy as np
    import colour
    
    image_1 = colour.read_image("mcdonald_lake.png")
    # "mcdonald_lake.png" is single channel, we convert it to 3
    image_1 = colour.utilities.tstack([image_1, image_1, image_1])
    image_2 = colour.read_image("niagara_falls.png")
    image_3 = colour.read_image("colouring_pencils.png")
    
    # Converting from assumed "sRGB" encoded, i.e. "Output-Referred" to "Oklab" using Colour's Automatic Colour Conversion Graph.
    image_1_OkLab = colour.convert(image_1, "Output-Referred RGB", "Oklab")
    image_2_OkLab = colour.convert(image_2, "Output-Referred RGB", "Oklab")
    image_3_OkLab = colour.convert(image_3, "Output-Referred RGB", "Oklab")
    
    # Converting from "Lightness" and "a", "b" opponent colour dimensions
    # to "Lightness", "Chroma" and "Hue".
    image_1_OkLab_JCh = colour.models.Jab_to_JCh(image_1_OkLab)
    image_2_OkLab_JCh = colour.models.Jab_to_JCh(image_2_OkLab)
    image_3_OkLab_JCh = colour.models.Jab_to_JCh(image_3_OkLab)
    
    print(np.mean(image_1_OkLab_JCh[..., 1]))
    print(np.mean(image_2_OkLab_JCh[..., 1]))
    print(np.mean(image_3_OkLab_JCh[..., 1]))
    
    6.14471772026e-05
    0.0292843706963
    0.0798391223111
    

    If you want to use ICtCp for example, you can simply change "Oklab" for "ICtCp" above.

    It is also possible to get a detailed overview of the computations ran by the graph by using the verbose={"mode": "Long"} argument:

    colour.convert(image_1, "Output-Referred RGB", "Oklab", verbose={"mode": "Long"})
    

    Google Colab Notebook: https://colab.research.google.com/drive/1aDyUa4hSeCn-Sj47nUOilRAghl0fpd_W?usp=sharing