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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�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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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