Skip to content

Commit

Permalink
feat: filter for helping users with deuteranomaly and/or protanomaly (#1
Browse files Browse the repository at this point in the history
)

* format code

* adjust filters

* feat: add color blind correction algorithm
  • Loading branch information
HokageM authored Apr 2, 2024
1 parent 4a1cba1 commit 6cc1646
Show file tree
Hide file tree
Showing 12 changed files with 174 additions and 41 deletions.
77 changes: 71 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,94 @@

<img src="logo/logo.jpeg" width="200">

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/

<img src="tests/test_images/naruto.jpg" style="width: 200px">

### Deuteranomaly Correction

```bash
byakugan_vision --images "tests/test_images/naruto.jpg" --deuteranomaly 2
```

<img src="tests/test_images/filtered/Filtered_naruto_deuteranomaly_2_protanomaly_0.jpg" style="width: 200px">

### Protanomaly Correction

```bash
byakugan_vision --images "tests/test_images/naruto.jpg" --protanomaly 2
```

## Example
<img src="tests/test_images/filtered/Filtered_naruto_deuteranomaly_0_protanomaly_2.jpg" style="width: 200px">

### Deuteranomaly and Protanomaly Correction

```bash
byakugan_vision --images "tests/test_images/naruto.jpg" --deuteranomaly 2 --protanomaly 2
```

<img src="tests/test_images/filtered/Filtered_naruto_deuteranomaly_2_protanomaly_2.jpg" style="width: 200px">


```bash
byakugan_vision --images "tests/test_images/naruto.jpg" --deuteranomaly 0.5 --protanomaly 0.5
```

<img src="tests/test_images/filtered/Filtered_naruto_deuteranomaly_0.5_protanomaly_0.5.jpg" style="width: 200px">

### Filter an Image

```bash
byakugan_vision --images "tests/test_images/naruto.jpg" --filter red
```

<img src="tests/test_images/filtered/Filtered_naruto_red.jpg" style="width: 200px">


## 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/
Expand Down
34 changes: 29 additions & 5 deletions src/byakuganvisualizer/ImageFilter.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
104 changes: 74 additions & 30 deletions src/byakuganvisualizer/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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.'
)
Expand All @@ -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)


Expand All @@ -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():
Expand Down
Binary file modified tests/test_images/diff/Diff_naruto_naruto_modified_blue.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/test_images/diff/Diff_naruto_naruto_modified_green.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/test_images/diff/Diff_naruto_naruto_modified_red.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/test_images/diff/Diff_naruto_naruto_modified_yellow.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 6cc1646

Please sign in to comment.