最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

python - Find contrast between two colours - Stack Overflow

programmeradmin3浏览0评论

I would like to write a function in Python that compares two colours. Let's say I have a background colour and a font colour. And I want wo check if the font has enough contrast to be visible on the background. I receive the colours in RGB format like 16777215 (white). How can I do that?

What does the difference between two colour values, e.g. 16777215 - 12788622 mean? Does the difference say anything about the contrast?

Edit: A solution based on the comments of @VC.One that loops through a pdf document to find text with low contrast on a white background:

import streamlit as st
import pymupdf

def main():
    # File Uploader
    uploaded_file = st.file_uploader("Select PDF file: ", type='pdf')

    # Search document
    if uploaded_file is not None:
        find_low_contrast_font(uploaded_file)
       
def find_low_contrast_font(uploaded_file):
    # Open document
    doc = pymupdf.open("pdf", uploaded_file.read())

    # Loop through the pages
    for page_num in range(doc.page_count):
        page = doc[page_num]
        for text in page.get_text("dict")["blocks"]:
            for line in text["lines"]:
                for span in line["spans"]:
                    if contrast_is_low(span["color"]):
                        st.write(f"Low contrast font on page {page_num + 1}: {span['text']}")

    # Close document
    doc.close()

def contrast_is_low(font_color):
    background_color = 16777215  # White
    minimum_distance = 40 # (DF,DF,DF) / (223,223,223) / 14671839 / light grey: distance to white is 32
    low_contrast = False

    background_color_rgb_dict = get_rgb_values_from_integer(background_color)
    background_color_luminance = calculate_luminance(background_color_rgb_dict)

    font_color_rgb_dict = get_rgb_values_from_integer(font_color)
    font_color_luminance = calculate_luminance(font_color_rgb_dict)

    distance = calculate_distance(background_color_luminance, font_color_luminance)

    if distance <= minimum_distance:
        low_contrast = True

    return low_contrast

def get_rgb_values_from_integer(rgb_int):
    blue = rgb_int & 255
    green = (rgb_int >> 8) & 255
    red = (rgb_int >> 16) & 255
    rgb_dict = {
        "r": red,
        "g": green,
        "b": blue
    }
    return rgb_dict

def calculate_luminance(rgb_dict):
    return 0.299 * rgb_dict["r"] + 0.587 * rgb_dict["g"] + 0.114 * rgb_dict["b"]

def calculate_distance(background_color_luminance, font_color_luminance):
    return abs(background_color_luminance - font_color_luminance)

if __name__ == "__main__":
    main()

I would like to write a function in Python that compares two colours. Let's say I have a background colour and a font colour. And I want wo check if the font has enough contrast to be visible on the background. I receive the colours in RGB format like 16777215 (white). How can I do that?

What does the difference between two colour values, e.g. 16777215 - 12788622 mean? Does the difference say anything about the contrast?

Edit: A solution based on the comments of @VC.One that loops through a pdf document to find text with low contrast on a white background:

import streamlit as st
import pymupdf

def main():
    # File Uploader
    uploaded_file = st.file_uploader("Select PDF file: ", type='pdf')

    # Search document
    if uploaded_file is not None:
        find_low_contrast_font(uploaded_file)
       
def find_low_contrast_font(uploaded_file):
    # Open document
    doc = pymupdf.open("pdf", uploaded_file.read())

    # Loop through the pages
    for page_num in range(doc.page_count):
        page = doc[page_num]
        for text in page.get_text("dict")["blocks"]:
            for line in text["lines"]:
                for span in line["spans"]:
                    if contrast_is_low(span["color"]):
                        st.write(f"Low contrast font on page {page_num + 1}: {span['text']}")

    # Close document
    doc.close()

def contrast_is_low(font_color):
    background_color = 16777215  # White
    minimum_distance = 40 # (DF,DF,DF) / (223,223,223) / 14671839 / light grey: distance to white is 32
    low_contrast = False

    background_color_rgb_dict = get_rgb_values_from_integer(background_color)
    background_color_luminance = calculate_luminance(background_color_rgb_dict)

    font_color_rgb_dict = get_rgb_values_from_integer(font_color)
    font_color_luminance = calculate_luminance(font_color_rgb_dict)

    distance = calculate_distance(background_color_luminance, font_color_luminance)

    if distance <= minimum_distance:
        low_contrast = True

    return low_contrast

def get_rgb_values_from_integer(rgb_int):
    blue = rgb_int & 255
    green = (rgb_int >> 8) & 255
    red = (rgb_int >> 16) & 255
    rgb_dict = {
        "r": red,
        "g": green,
        "b": blue
    }
    return rgb_dict

def calculate_luminance(rgb_dict):
    return 0.299 * rgb_dict["r"] + 0.587 * rgb_dict["g"] + 0.114 * rgb_dict["b"]

def calculate_distance(background_color_luminance, font_color_luminance):
    return abs(background_color_luminance - font_color_luminance)

if __name__ == "__main__":
    main()
Share Improve this question edited Jan 30 at 10:49 mike3467 asked Jan 23 at 10:04 mike3467mike3467 557 bronze badges 11
  • Please don't tag unrelated languages. If they are related to your question please edit it and explain how. – Federico klez Culloca Commented Jan 23 at 10:07
  • Also, you may want to explain what you mean by "is visible". You mean "has enough contrast"? – Federico klez Culloca Commented Jan 23 at 10:08
  • 1 @mike3467 You need to... (1) Extract R and G and B from 16777215 by using bit-shifts. (2) Pass the R, G and B through a luminosity (grey) calculating function (basically grey = R * 0.299 + G* 0.587 + B * 0.114 then it returns the new grey value)... (3) There is more contrast if there is a larger distance between grey of BG versus grey of the font, so just test + calibrate distance + re-test until your code knows a good target value/distance to classify as making a colour "more visible" against another colour. – VC.One Commented Jan 25 at 19:59
  • @VC.One What do you mean by "distance"? Difference? – mike3467 Commented Jan 28 at 12:58
  • 1 @mike3467 Yes the Difference (or some A Minus B result). If text (A) is grey 200 and the background (B) is grey 90, then the distance is 110 (ignore any minus sign). to calibrate the app, you check with your eyes if 110 is good contrast. If yes, hard-code it as some "contrast_best_distance = 110" variable. PS: A Grey of 128 is neutral grey.. Any Grey value going above 128 is now getting more White added into it, and a Grey that is going below 128 is now getting more Black added. Maybe use that knowledge to give advantage of quick "contrast" decisions by your app/code. – VC.One Commented Jan 29 at 14:58
 |  Show 6 more comments

1 Answer 1

Reset to default 0

PDF uses colors in many ways however most are counted as textual floats at a position. So if you have 1.00 red in the same position on 1.00 green then there is Human contrast (unless red/green colour blind)

Both are symbolically ÿ (when decompressed for rendering) and thus have a decimalised value of 255 (we tend to say 1.00 = \O000 or \x00 in such cases). This makes it difficult to determine PDF contrast in a simplistic fashion.

Here the foreground text Hello is value 127 and the background is [.5 .5 .5] yet both look like the same grey however may print as different colours by a printers profile). Mathematically wildly different, yet have no contrast whatsoever. There are many many similar ways to set PDF bytes as transparently the same contrast visually yet mathematically their bytes will be different values.

stream
q /GS0 gs .5 .5 .5 rg 0 0 438 540 re f 0.5 g BT /F0 12 Tf 1 0 0 1 100 400 Tm [(Hello)] TJ ET Q
endstream

When viewed in Adobe Reader there is a contrast seen even if mild!

But we can enhance Colour Contrast for viewing or printing

For this reason PDF/A verification requires a Human (Pre-flight attendant) be used to decide if a PDF has sufficiently contrasting colours.

These are very contrasting colours but all just the same 2 characters since colours in a PDF are predominantly described in ASCII or ANSI or Hex encoded textual terms (many other encodings are used but usually those are compressed as raw single byte colours).

And 2 colours as seen above are "Black" but render as 2 white squares.

发布评论

评论列表(0)

  1. 暂无评论