Source code for accessiplot.detection.color_detection

from accessiplot.utils.logger import logger
import colorsys
from colorthief import ColorThief
from colorspacious import cspace_convert
from colorspacious import deltaE
import matplotlib.pyplot as plt
from matplotlib.colors import to_rgb
import numpy as np
import os


__all__ = [
    'get_common_colors_from_image',
    'get_common_colors_from_plot',
    'convert_image',
    'display_images',
    'compare_colors',
    'full_detection'
]


[docs]def get_common_colors_from_image(file_name: str, top_count: int = 6): """ Given a file name of an image, return a list of most common colors used in the image, in RGB. Parameters ---------- file_name : str The file name (path) to an image top_count : int The number of top colors to be returned Returns ------- palette: list A list of most commonly used colors in the image. """ color_thief = ColorThief(file_name) palette = color_thief.get_palette(color_count=top_count) palette.sort(key=lambda rgb: colorsys.rgb_to_hsv(*rgb)) palette = cspace_convert(palette, "sRGB255", "sRGB1") return palette
[docs]def get_common_colors_from_plot(plot): """ Given a line chart, return a list of colors used for the lines and background, in RGB. Parameters ---------- plt : matplotlib.pyplot A matplotlib.pyplot object in which a line chart is plotted. Returns ------- lines_colors: list A list of colors used for the lines in the plot. """ axes_object = plt.gca() lines_colors = [to_rgb(line.get_color()) for line in axes_object.lines] lines_colors.append(to_rgb(axes_object.get_facecolor())) lines_colors = cspace_convert(lines_colors, "sRGB1", "sRGB1") return lines_colors
[docs]def convert_image(img, color_vision_deficiency: str = "deuteranomaly", severity: int = 100): """ Given an image, simulate how a person with color vision deficiency will see it. Parameters ---------- img: arr An array-like of colors representing an image to be simulated. color_vision_deficiency: str The color vision deficiency to be simulated. It can be "deuteranomaly", "protanomaly", or "tritanomaly. severity: int The severity of color vision deficiency to be simulated, with 100 being the most severe one. Returns ------- result_image: arr An array-like of colors representing an image after simulation. """ assert color_vision_deficiency in ["deuteranomaly", "protanomaly", "tritanomaly"] assert severity > 0 and severity <= 100 cvd_space = {"name": "sRGB1+CVD", "cvd_type": color_vision_deficiency, "severity": severity} result_image = cspace_convert(img, cvd_space, "sRGB1") # .astype('uint8') return result_image
[docs]def display_images(old, new): """ Given an original version of an image and a tuple of its modified versions, display them in a single plot for readers to compare. Parameters ---------- odd: arr An array-like of colors representing an image. new: tuple A tuple of array-like of colors, each representing an image. """ image_width = 3.0 # inches total_width = (1 + len(new)) * image_width height = image_width / old.shape[1] * old.shape[0] fig = plt.figure(figsize=(total_width, height)) ax = fig.add_axes((0, 0, 1, 1)) ax.imshow((np.column_stack((old,) + new))) plt.show()
[docs]def compare_colors(colors, color_vision_deficiency: str = "deuteranomaly", severity: int = 100, threshold: int = 6): """ Given a list of colors, check if any two of them are too similar to each other in the eye of someone with a specified color vision deficiency. If so, print out a warning. Parameters ---------- colors: list A list of colors to be compared against each other. color_vision_deficiency: str The color vision deficiency to be simulated. It can be "deuteranomaly", "protanomaly", or "tritanomaly. severity: int The severity of color vision deficiency to be simulated, with 100 being the most severe one. threshold: int Threshold to detect if the Dealta-E value between two colors is below it. Returns ------- flag: bool Whether or not there exists a pair of colors in the given list that are too similar to each other. References ---------- .. [1] https://www.colorwiki.com/wiki/Delta_E """ colors_array = np.array(colors) new_colors_array = convert_image(colors_array, color_vision_deficiency, severity) count = 0 for i in range(len(new_colors_array)): for j in range(i+1, len(new_colors_array)): c1 = new_colors_array[i] c2 = new_colors_array[j] delta = deltaE(c1, c2, input_space="sRGB1") if delta <= threshold: if count == 0: logger.warning(f"For a person with {color_vision_deficiency}, those colors are too close to each other:\n" "str(c1) + ' and ' + str(c2)") # TODO: add a parameter for detecting the markers / labels count = count + 1 flag = count != 0 if not flag: pass # TODO: should we print out a message saying everything is good? else: logger.info("Ignore this warnings if for each line, the markers are different or the labels are present.") return flag
[docs]def full_detection(plt, threshold: int = 6, show_comparison_plots: bool = False): """ Given a line chart, detect if its use of color is unfriendly to people with either deuteranomaly, protanomaly or tritanomaly, and display the simulation for those color vision deficiencies. Parameters ---------- plt : matplotlib.pyplot A matplotlib.pyplot object in which a line chart is plotted. threshold: int Threshold to detect if the Dealta-E value between two colors is below it. show_comparison_plots: bool Switch to toggle whether to plot comparisons as part of the detection. Returns ------- flag: bool Whether or not there exists a pair of colors in the given list that are too similar to each other. detections: dict A dictionary that represents which color vision deficiency had a detection. """ file_name = "test.jpg" plt.savefig(file_name) img = cspace_convert(plt.imread(file_name), "sRGB255", "sRGB1") cvd_list = ["deuteranomaly", "protanomaly", "tritanomaly"] detections = {k: v for (k, v) in zip(cvd_list, [{}, {}, {}])} colors = get_common_colors_from_plot(plt) simulations = [] flag = False for cvd in cvd_list: is_an_accessibility_issue = compare_colors(colors, cvd, 100, threshold) if is_an_accessibility_issue: detections[cvd] = is_an_accessibility_issue flag = flag or is_an_accessibility_issue new_img = convert_image(img, cvd, 100) simulations.append(new_img) if show_comparison_plots: display_images(img, tuple(simulations)) os.remove(file_name) # clean up tmp file. return flag, detections