Skip to content

WIP: Add greyscale and color-blindness filters. #3279

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions examples/pylab_examples/color_blindness.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import numpy as np
import matplotlib.cm as cm
import matplotlib.mlab as mlab
import matplotlib.pyplot as plt

delta = 0.025
x = y = np.arange(-3.0, 3.0, delta)
X, Y = np.meshgrid(x, y)
Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
Z = Z2-Z1 # difference of Gaussians

def plot_data(ax):
im = ax.imshow(Z, interpolation='bilinear', cmap='jet',
origin='lower', extent=[-3,3,-3,3],
vmax=abs(Z).max(), vmin=-abs(Z).max())
return im

# Display the same plot 4 different ways:
# - no filter
# - luminosity filter
# - Deuteranope color blindness filter
# - Tritanope color blindness filter

ax = plt.subplot(2, 2, 1)
plot_data(ax)
ax.set_title("Original")

ax = plt.subplot(2, 2, 2)
im = plot_data(ax)
im.set_agg_filter('luminosity')
ax.set_title("Luminosity")

ax = plt.subplot(2, 2, 3)
im = plot_data(ax)
im.set_agg_filter("deuteranope")
ax.set_title("Deuteranope")

ax = plt.subplot(2, 2, 4)
im = plot_data(ax)
im.set_agg_filter("tritanope")
ax.set_title("Tritanope")

plt.show()
3 changes: 3 additions & 0 deletions lib/matplotlib/artist.py
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,9 @@ def set_agg_filter(self, filter_func):
set agg_filter fuction.

"""
if isinstance(filter_func, six.string_types):
from . import colors
filter_func = colors.get_color_filter(filter_func)
self._agg_filter = filter_func

def draw(self, renderer, *args, **kwargs):
Expand Down
162 changes: 162 additions & 0 deletions lib/matplotlib/colors.py
Original file line number Diff line number Diff line change
Expand Up @@ -1630,3 +1630,165 @@ def from_levels_and_colors(levels, colors, extend='neither'):

norm = BoundaryNorm(levels, ncolors=n_data_colors)
return cmap, norm


def luminosity_filter(im):
"""
Convert a RGB or RGBA image to a greyscale image based on each
pixel's luminosity.

Parameters
----------
im : Nx3 or Nx4 array of floating-point pixels
The input image, in RGB or RGBA.

Returns
-------
im : Nx1 array of float-point greyscale pixels
The output image, converted to luminosity
"""
im = np.asarray(im)
im = im[..., 0:3]
return np.average(im, -1, weights=[.21, .72, 0.07])


def average_filter(im):
"""
Convert a RGB or RGBA image to a greyscale image by average the
red, green and blue values for each pixel.

Parameters
----------
im : Nx3 or Nx4 array of floating-point pixels
The input image, in RGB or RGBA.

Returns
-------
im : Nx1 array of float-point greyscale pixels
The output image
"""
im = np.asarray(im)
im = im[..., 0:3]
return np.average(im, -1)


_rgb_to_lms_matrix = np.array([
[17.8824, 43.5161, 4.11935],
[3.45565, 27.1554, 3.86714],
[0.0299566, 0.184309, 1.46709]]).T


_lms_to_rgb_matrix = np.linalg.inv(_rgb_to_lms_matrix)


_deuteranope_matrix = np.array([
[1, 0, 0],
[0.494207, 0, 1.24827],
[0, 0, 1]]).T


_deuteranope_chain = _rgb_to_lms_matrix.dot(
_deuteranope_matrix).dot(
_lms_to_rgb_matrix)


def deuteranope_filter(im):
"""
Convert a RGB or RGBA image to one that simulates Deuteranope
color blindness.

Parameters
----------
im : Nx3 or Nx4 array of floating-point pixels
The input image, in RGB or RGBA.

Returns
-------
im : Nx1 array of float-point greyscale pixels
The output image
"""
im = im[..., 0:3]
im = np.dot(im, _deuteranope_chain)
return im


_protanope_matrix = np.array([
[0, 2.02344, -2.52581],
[0, 1, 0],
[0, 0, 1]]).T


_protanope_chain = _rgb_to_lms_matrix.dot(
_protanope_matrix).dot(
_lms_to_rgb_matrix)


def protanope_filter(im):
"""
Convert a RGB or RGBA image to one that simulates Protanope
color blindness.

Parameters
----------
im : Nx3 or Nx4 array of floating-point pixels
The input image, in RGB or RGBA.

Returns
-------
im : Nx1 array of float-point greyscale pixels
The output image
"""
im = im[..., 0:3]
im = np.dot(im, _protanope_chain)
return im


_tritanope_matrix = np.array([
[1, 0, 0],
[0, 1, 0],
[-0.395913, 0.801109, 0]]).T


_tritanope_chain = _rgb_to_lms_matrix.dot(
_tritanope_matrix).dot(
_lms_to_rgb_matrix)


def tritanope_filter(im):
"""
Convert a RGB or RGBA image to one that simulates Tritanope
color blindness.

Parameters
----------
im : Nx3 or Nx4 array of floating-point pixels
The input image, in RGB or RGBA.

Returns
-------
im : Nx1 array of float-point greyscale pixels
The output image
"""
im = im[..., 0:3]
im = np.dot(im, _tritanope_chain)
return im


def get_color_filter(name):
"""
Given a color filter name, returns a color filter function.
"""
mapping = {
'luminosity': luminosity_filter,
'average': average_filter,
'deuteranope': deuteranope_filter,
'protanope': protanope_filter,
'tritanope': tritanope_filter
}

filter = mapping[name.lower()]

def wrap_filter(im, dpi):
return filter(im), 0, 0

return wrap_filter