From 6cc1646fad50329ee2e2b27259efa8f903e147a6 Mon Sep 17 00:00:00 2001
From: Mike Trzaska <76908324+HokageM@users.noreply.github.com>
Date: Tue, 2 Apr 2024 23:45:51 +0200
Subject: [PATCH] feat: filter for helping users with deuteranomaly and/or
protanomaly (#1)
* format code
* adjust filters
* feat: add color blind correction algorithm
---
README.md | 77 ++++++++++++-
src/byakuganvisualizer/ImageFilter.py | 34 +++++-
src/byakuganvisualizer/main.py | 104 +++++++++++++-----
.../diff/Diff_naruto_naruto_modified_blue.jpg | Bin 10888 -> 10759 bytes
.../Diff_naruto_naruto_modified_green.jpg | Bin 17933 -> 16250 bytes
.../diff/Diff_naruto_naruto_modified_red.jpg | Bin 14016 -> 13268 bytes
.../Diff_naruto_naruto_modified_yellow.jpg | Bin 20015 -> 18315 bytes
...ruto_deuteranomaly_0.5_protanomaly_0.5.jpg | Bin 0 -> 32554 bytes
...d_naruto_deuteranomaly_0_protanomaly_2.jpg | Bin 0 -> 31829 bytes
...d_naruto_deuteranomaly_2_protanomaly_0.jpg | Bin 0 -> 31528 bytes
...d_naruto_deuteranomaly_2_protanomaly_2.jpg | Bin 0 -> 32359 bytes
.../filtered/Filtered_naruto_red.jpg | Bin 0 -> 23155 bytes
12 files changed, 174 insertions(+), 41 deletions(-)
create mode 100644 tests/test_images/filtered/Filtered_naruto_deuteranomaly_0.5_protanomaly_0.5.jpg
create mode 100644 tests/test_images/filtered/Filtered_naruto_deuteranomaly_0_protanomaly_2.jpg
create mode 100644 tests/test_images/filtered/Filtered_naruto_deuteranomaly_2_protanomaly_0.jpg
create mode 100644 tests/test_images/filtered/Filtered_naruto_deuteranomaly_2_protanomaly_2.jpg
create mode 100644 tests/test_images/filtered/Filtered_naruto_red.jpg
diff --git a/README.md b/README.md
index fd7b1cc..da088c0 100644
--- a/README.md
+++ b/README.md
@@ -2,29 +2,94 @@
-The ByakuganVisualizer repository hosts a Python tool designed to compare images and highlight their differences.
+The ByakuganVisualizer repository hosts a Python tool designed to compare images and highlight their differences.
It simplifies the process of identifying disparities between images, making it ideal for tasks like testing and quality
-assurance. Additionally, it offers options for customization, which can be helpful for color-blind users.
+assurance.
+Moreover, it offers a color filter that can be used to correct images for **color-blind users**.
+## Installation
+
+```bash
+pip install byakuganvisualizer
+```
+
## Usage
```
-usage: byakugan_vision [-h] [--version] --diff DIFF [--filter {red,blue,green,yellow}] [--out_dir OUT_DIR]
+usage: byakugan_vision [-h] [--version] [--diff DIFF] [--filter {red,blue,green,yellow}] [--images IMAGES] [--deuteranomaly DEUTERANOMALY]
+ [--protanomaly PROTANOMALY] [--out_dir OUT_DIR]
-ByakuganVisualizer: Tool for comparing images and highlighting differences.
+ByakuganVisualizer: Tool for correcting the color palett for color blind people and highlighting differences of images.
options:
-h, --help show this help message and exit
--version show program's version number and exit
- --diff DIFF String containing a list of tuples "Path_To_Image1a,Path_To_Image2a;Path_To_Image1b,Path_To_Image2b...". Each tuple contains two paths to images to be compared.
+ --diff DIFF String containing a list of tuples "Path_To_Image1a,Path_To_Image2a;Path_To_Image1b,Path_To_Image2b...". Each tuple
+ contains two paths to images to be compared.
--filter {red,blue,green,yellow}
Filter type (red, blue, green, yellow)
+ --images IMAGES List of image names to be manipulated by a filter. E.g.: A,B,C,D
+ --deuteranomaly DEUTERANOMALY
+ Expresses your degree of deuteranomaly, which will be used to correct the image. Default is 1.
+ --protanomaly PROTANOMALY
+ Expresses your degree of protanomaly, which will be used to correct the image. Default is 1.
--out_dir OUT_DIR Output directory for the difference images
+```
+
+## Image Correction for Color Blind People
+
+In the following examples the image is corrected for deuteranomaly and protanomaly.
+
+**Note:** The float values for deuteranomaly and protanomaly are between 0 and 10. The default value is 1.
+The used algorithm is based on the following paper: https://arxiv.org/abs/1711.10662.
+
+The image used in the example is from the following source:
+https://www.anime2you.de/news/606180/naruto-feiert-20-anime-jubilaeum/
+
+
+
+### Deuteranomaly Correction
+```bash
+byakugan_vision --images "tests/test_images/naruto.jpg" --deuteranomaly 2
+```
+
+
+
+### Protanomaly Correction
+
+```bash
+byakugan_vision --images "tests/test_images/naruto.jpg" --protanomaly 2
```
-## Example
+
+
+### Deuteranomaly and Protanomaly Correction
+
+```bash
+byakugan_vision --images "tests/test_images/naruto.jpg" --deuteranomaly 2 --protanomaly 2
+```
+
+
+
+
+```bash
+byakugan_vision --images "tests/test_images/naruto.jpg" --deuteranomaly 0.5 --protanomaly 0.5
+```
+
+
+
+### Filter an Image
+
+```bash
+byakugan_vision --images "tests/test_images/naruto.jpg" --filter red
+```
+
+
+
+
+## Differences between images
The left image used in the example is from the following source:
https://www.anime2you.de/news/606180/naruto-feiert-20-anime-jubilaeum/
diff --git a/src/byakuganvisualizer/ImageFilter.py b/src/byakuganvisualizer/ImageFilter.py
index 7fa88ef..1dc0f9e 100644
--- a/src/byakuganvisualizer/ImageFilter.py
+++ b/src/byakuganvisualizer/ImageFilter.py
@@ -14,7 +14,7 @@ def apply_red_filter(image_data):
:return:
"""
red_filtered = np.zeros_like(image_data)
- red_filtered[:, :, 0] = image_data[:, :, 0] * 2
+ red_filtered[:, :, 0] = image_data[:, :, 0]
return red_filtered
@staticmethod
@@ -24,7 +24,7 @@ def apply_green_filter(image_data):
:param image_data: The image data to apply the filter to.
"""
green_filtered = np.zeros_like(image_data)
- green_filtered[:, :, 1] = image_data[:, :, 1] * 2
+ green_filtered[:, :, 1] = image_data[:, :, 1]
return green_filtered
@staticmethod
@@ -35,7 +35,7 @@ def apply_blue_filter(image_data):
:return:
"""
blue_filtered = np.zeros_like(image_data)
- blue_filtered[:, :, 2] = image_data[:, :, 2] * 2
+ blue_filtered[:, :, 2] = image_data[:, :, 2]
return blue_filtered
@staticmethod
@@ -46,6 +46,30 @@ def apply_yellow_filter(image_data):
:return:
"""
yellow_filtered = np.zeros_like(image_data)
- yellow_filtered[:, :, 0] = image_data[:, :, 0] * 2
- yellow_filtered[:, :, 1] = image_data[:, :, 1] * 2
+ yellow_filtered[:, :, 0] = image_data[:, :, 0]
+ yellow_filtered[:, :, 1] = image_data[:, :, 1]
return yellow_filtered
+
+ @staticmethod
+ def correction_for_colorblindness(image_array, degree_protanomaly, degree_deuteranomaly):
+ """
+ Apply a colorblindness correction to the image data.
+ :param image_array:
+ :param degree_protanomaly:
+ :param degree_deuteranomaly:
+ :return:
+ """
+ r = image_array[..., 0]
+ g = image_array[..., 1]
+ b = image_array[..., 2]
+
+ corrected = np.copy(image_array)
+ r_corrected = (1 - degree_deuteranomaly / 2) * r + (degree_deuteranomaly / 2) * g
+ g_corrected = (degree_protanomaly / 2) * r + (1 - degree_protanomaly / 2) * g
+ b_corrected = ((degree_protanomaly / 4) * r + (degree_deuteranomaly / 4) * g +
+ (1 - (degree_deuteranomaly + degree_protanomaly) / 4) * b)
+
+ corrected[..., 0] = r_corrected
+ corrected[..., 1] = g_corrected
+ corrected[..., 2] = b_corrected
+ return corrected
diff --git a/src/byakuganvisualizer/main.py b/src/byakuganvisualizer/main.py
index f6ebb9e..cd5202e 100644
--- a/src/byakuganvisualizer/main.py
+++ b/src/byakuganvisualizer/main.py
@@ -41,7 +41,8 @@ def parse_args(args):
:obj:`argparse.Namespace`: command line parameters namespace
"""
parser = argparse.ArgumentParser(
- description="ByakuganVisualizer: Tool for comparing images and highlighting differences."
+ description="ByakuganVisualizer: Tool for correcting the color palett for color blind people and highlighting "
+ "differences of images."
)
parser.add_argument(
"--version",
@@ -51,7 +52,6 @@ def parse_args(args):
parser.add_argument(
'--diff',
type=parse_tuples,
- required=True,
help='String containing a list of tuples "Path_To_Image1a,Path_To_Image2a;Path_To_Image1b,Path_To_Image2b...". '
'Each tuple contains two paths to images to be compared.'
)
@@ -60,13 +60,29 @@ def parse_args(args):
choices=['red', 'blue', 'green', 'yellow'],
help='Filter type (red, blue, green, yellow)'
)
+ parser.add_argument(
+ '--images',
+ type=str,
+ help='List of image names to be manipulated by a filter. E.g.: A,B,C,D'
+ )
+ parser.add_argument(
+ '--deuteranomaly',
+ type=float,
+ default=0,
+ help='Expresses your degree of deuteranomaly, which will be used to correct the image. Default is 1.'
+ )
+ parser.add_argument(
+ '--protanomaly',
+ type=float,
+ default=0,
+ help='Expresses your degree of protanomaly, which will be used to correct the image. Default is 1.'
+ )
parser.add_argument(
'--out_dir',
type=str,
default='.',
help='Output directory for the difference images'
)
- # TODO: option for just filtering an image
return parser.parse_args(args)
@@ -77,33 +93,61 @@ def main(args):
os.makedirs(args.out_dir)
print(f"Output Directory: '{args.out_dir}' created.")
- for pair in args.diff:
- pair_name = os.path.basename(pair[0]).split('.')[0] + '_' + os.path.basename(pair[1]).split('.')[0]
-
- image1 = Image.open(pair[0])
- image2 = Image.open(pair[1])
-
- array1 = np.array(image1)
- array2 = np.array(image2)
-
- # Calculate the absolute difference between the two arrays
- difference_array = np.abs(array1 - array2)
-
- if args.filter == 'red':
- difference_array = ImageFilter.apply_red_filter(difference_array)
- pair_name += '_red'
- if args.filter == 'blue':
- difference_array = ImageFilter.apply_blue_filter(difference_array)
- pair_name += '_blue'
- if args.filter == 'green':
- difference_array = ImageFilter.apply_green_filter(difference_array)
- pair_name += '_green'
- if args.filter == 'yellow':
- difference_array = ImageFilter.apply_yellow_filter(difference_array)
- pair_name += '_yellow'
-
- difference_image = Image.fromarray(difference_array.astype('uint8'))
- difference_image.save(f'{args.out_dir}/Diff_{pair_name}.jpg')
+ if args.diff:
+ for pair in args.diff:
+ pair_name = os.path.basename(pair[0]).split('.')[0] + '_' + os.path.basename(pair[1]).split('.')[0]
+
+ image1 = Image.open(pair[0])
+ image2 = Image.open(pair[1])
+
+ array1 = np.array(image1)
+ array2 = np.array(image2)
+
+ # Calculate the absolute difference between the two arrays
+ difference_array = np.abs(array1 - array2)
+
+ if args.filter == 'red':
+ difference_array = ImageFilter.apply_red_filter(difference_array)
+ pair_name += '_red'
+ if args.filter == 'blue':
+ difference_array = ImageFilter.apply_blue_filter(difference_array)
+ pair_name += '_blue'
+ if args.filter == 'green':
+ difference_array = ImageFilter.apply_green_filter(difference_array)
+ pair_name += '_green'
+ if args.filter == 'yellow':
+ difference_array = ImageFilter.apply_yellow_filter(difference_array)
+ pair_name += '_yellow'
+
+ difference_image = Image.fromarray(difference_array.astype('uint8'))
+ difference_image.save(f'{args.out_dir}/Diff_{pair_name}.jpg')
+
+ if args.images:
+ args.images = args.images.split(',')
+ for img in args.images:
+ image_name = os.path.basename(img).split('.')[0]
+
+ image = Image.open(img)
+ rgb_array = np.array(image)
+
+ if args.filter == 'red':
+ rgb_array = ImageFilter.apply_red_filter(rgb_array)
+ image_name += '_red'
+ if args.filter == 'blue':
+ rgb_array = ImageFilter.apply_blue_filter(rgb_array)
+ image_name += '_blue'
+ if args.filter == 'green':
+ rgb_array = ImageFilter.apply_green_filter(rgb_array)
+ image_name += '_green'
+ if args.filter == 'yellow':
+ rgb_array = ImageFilter.apply_yellow_filter(rgb_array)
+ image_name += '_yellow'
+ if args.protanomaly > 0 or args.deuteranomaly > 0:
+ rgb_array = ImageFilter.correction_for_colorblindness(rgb_array, args.protanomaly, args.deuteranomaly)
+ image_name += f'_deuteranomaly_{args.deuteranomaly}_protanomaly_{args.protanomaly}'
+
+ filtered_image = Image.fromarray(rgb_array.astype('uint8'))
+ filtered_image.save(f'{args.out_dir}/Filtered_{image_name}.jpg')
def run():
diff --git a/tests/test_images/diff/Diff_naruto_naruto_modified_blue.jpg b/tests/test_images/diff/Diff_naruto_naruto_modified_blue.jpg
index 7442cad10718d56714c2cc8eda1100ea8a5182ae..4a73170d91d08bd2840d2a002862b05d3fddcfd6 100644
GIT binary patch
delta 7468
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
diff --git a/tests/test_images/diff/Diff_naruto_naruto_modified_green.jpg b/tests/test_images/diff/Diff_naruto_naruto_modified_green.jpg
index eae79dda0c9807b2dede48134d167d04358f7748..488f94605b2d66e81dd4d59b01c414a800dcad98 100644
GIT binary patch
delta 12759
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