HokageM's picture
Initial app setup
f3bdd61 verified
Raw
History Blame Contribute Delete
3.88 kB
import gradio as gr
import numpy as np
from PIL import Image
theme_id = "HokageM/EVA_01"
theme = gr.themes.ThemeClass.from_hub(theme_id)
HEADER = """
This is a color vision correction tool.
The `Deuteranomaly` and `Protanomaly` sliders control the strength of the correction. In this context, correction means that the image colors are adjusted to make color differences more distinguishable for people with color vision deficiencies.
"""
DESCRIPTION_FORMULA = r"""
I have a mix of deuteranomaly and protanomaly, which motivated me to create this tool. With the correction enabled, colorblind tests become much more solvable for me.
The correction formula comes from this [paper](https://arxiv.org/abs/1711.10662) by Jinmi Lee:
$$
\begin{bmatrix}
R' \\
G' \\
B'
\end{bmatrix}
=
\begin{bmatrix}
1 - \frac{\alpha_d}{2} & \frac{\alpha_d}{2} & 0 \\
\frac{\alpha_p}{2} & 1 - \frac{\alpha_p}{2} & 0 \\
\frac{\alpha_p}{4} & \frac{\alpha_d}{4} & 1 - \frac{\alpha_p + \alpha_d}{4}
\end{bmatrix}
\begin{bmatrix}
R \\
G \\
B
\end{bmatrix}
$$
You can set $\alpha_d$ by adjusting the `Deuteranomaly` slider and $\alpha_p$ by adjusting the `Protanomaly` slider.
"""
def correction_for_colorblindness(image_array: np.array, degree_protanomaly: float, degree_deuteranomaly: float):
"""Apply a simple protanomaly/deuteranomaly correction matrix.
The input image is converted to float during calculation to avoid uint8
overflow/underflow. The result is clipped back into the valid RGB range.
"""
if degree_protanomaly < 0 or degree_deuteranomaly < 0:
raise ValueError("Colorblindness correction degrees must be >= 0.")
r = image_array[..., 0].astype(float)
g = image_array[..., 1].astype(float)
b = image_array[..., 2].astype(float)
corrected = image_array.astype(float).copy()
corrected[..., 0] = (1 - degree_deuteranomaly / 2) * r + (
degree_deuteranomaly / 2
) * g
corrected[..., 1] = (degree_protanomaly / 2) * r + (1 - degree_protanomaly / 2) * g
corrected[..., 2] = (
(degree_protanomaly / 4) * r
+ (degree_deuteranomaly / 4) * g
+ (1 - (degree_deuteranomaly + degree_protanomaly) / 4) * b
)
return np.clip(corrected, 0, 255).astype(np.uint8)
def load_demo():
return Image.open("./color_blind_test.jpg")
def unload():
return None, None
with gr.Blocks() as demo:
gr.Markdown("# Byakugan Visualizer")
gr.Markdown(HEADER)
with gr.Row():
with gr.Column():
deuter_degree = gr.Slider(step=0.5, minimum=0, maximum=5, label=r"Deuteranomaly")
prot_degree = gr.Slider(step=0.5, minimum=0, maximum=5, label="Protanomaly")
correction_btn = gr.Button("Create Correction", variant="primary")
gr.Markdown(DESCRIPTION_FORMULA, latex_delimiters=[{"left": "$$", "right": "$$", "display": True},
{"left": "$", "right": "$", "display": False}])
with gr.Row():
img = gr.Image()
processed_img = gr.Image()
with gr.Row():
load_demo_btn = gr.Button("Load Colorblind Test", variant="primary")
unload_btn = gr.Button("Unload Images", variant="secondary")
deuter_degree.change(
fn=correction_for_colorblindness,
inputs=[img, deuter_degree, prot_degree],
outputs=[processed_img],
)
prot_degree.change(
fn=correction_for_colorblindness,
inputs=[img, deuter_degree, prot_degree],
outputs=[processed_img],
)
correction_btn.click(
fn=correction_for_colorblindness,
inputs=[img, deuter_degree, prot_degree],
outputs=[processed_img],
)
load_demo_btn.click(
fn=load_demo,
outputs=[img],
)
unload_btn.click(
fn=unload,
outputs=[img, processed_img],
)
demo.launch(theme=theme)