Added new image filters that allow an image to be viewed in colorblind modes, allow cartographers to see what their maps would appear like to a color blind person

This commit is contained in:
Blake Thompson 2015-08-04 17:41:31 -05:00
parent 6b05f19c38
commit 6245790e72
4 changed files with 145 additions and 2 deletions

View file

@ -674,6 +674,81 @@ void apply_filter(Src & src, scale_hsla const& transform)
}
}
template <typename Src, typename ColorBlindFilter>
void color_blind_filter(Src & src, ColorBlindFilter const& op)
{
using namespace boost::gil;
rgba8_view_t src_view = rgba8_view(src);
for (std::ptrdiff_t y = 0; y < src_view.height(); ++y)
{
rgba8_view_t::x_iterator src_it = src_view.row_begin(static_cast<long>(y));
for (std::ptrdiff_t x = 0; x < src_view.width(); ++x)
{
// formula taken from boost/gil/color_convert.hpp:rgb_to_luminance
uint8_t & r = get_color(src_it[x], red_t());
uint8_t & g = get_color(src_it[x], green_t());
uint8_t & b = get_color(src_it[x], blue_t());
double dr = static_cast<double>(r);
double dg = static_cast<double>(g);
double db = static_cast<double>(b);
// RGB to LMS matrix conversion
double L = (17.8824 * dr) + (43.5161 * dg) + (4.11935 * db);
double M = (3.45565 * dr) + (27.1554 * dg) + (3.86714 * db);
double S = (0.0299566 * dr) + (0.184309 * dg) + (1.46709 * db);
// Simulate color blindness
double l = (op.f0 * L) + (op.f1 * M) + (op.f2 * S);
double m = (op.f3 * L) + (op.f4 * M) + (op.f5 * S);
double s = (op.f6 * L) + (op.f7 * M) + (op.f8 * S);
// LMS to RGB matrix conversion
double R = (0.0809444479 * l) + (-0.130504409 * m) + (0.116721066 * s);
double G = (-0.0102485335 * l) + (0.0540193266 * m) + (-0.113614708 * s);
double B = (-0.000365296938 * l) + (-0.00412161469 * m) + (0.693511405 * s);
// Isolate invisible colors to color vision deficiency (calculate error matrix)
R = dr - R;
G = dg - G;
B = db - B;
// Shift colors towards visible spectrum (apply error modifications)
double RR = (0.0 * R) + (0.0 * G) + (0.0 * B);
double GG = (0.7 * R) + (1.0 * G) + (0.0 * B);
double BB = (0.7 * R) + (0.0 * G) + (1.0 * B);
// Add compensation to original values
R = RR + dr;
G = GG + dg;
B = BB + db;
// Clamp values
if(R < 0.0) R = 0.0;
if(R > 255.0) R = 255.0;
if(G < 0.0) G = 0.0;
if(G > 255.0) G = 255.0;
if(B < 0.0) B = 0.0;
if(B > 255.0) B = 255.0;
r = static_cast<uint8_t>(R);
g = static_cast<uint8_t>(G);
b = static_cast<uint8_t>(B);
}
}
}
template <typename Src>
void apply_filter(Src & src, color_blind_protanope const& op)
{
color_blind_filter(src, op);
}
template <typename Src>
void apply_filter(Src & src, color_blind_deuteranope const& op)
{
color_blind_filter(src, op);
}
template <typename Src>
void apply_filter(Src & src, color_blind_tritanope const& op)
{
color_blind_filter(src, op);
}
template <typename Src>
void apply_filter(Src & src, gray const& /*op*/)
{

View file

@ -80,6 +80,12 @@ image_filter_grammar<Iterator,ContType>::image_filter_grammar()
|
lit("invert") >> no_args [push_back(_val,construct<mapnik::filter::invert>())]
|
lit("color-blind-protanope") >> no_args [push_back(_val,construct<mapnik::filter::color_blind_protanope>())]
|
lit("color-blind-deuteranope") >> no_args [push_back(_val,construct<mapnik::filter::color_blind_deuteranope>())]
|
lit("color-blind-tritanope") >> no_args [push_back(_val,construct<mapnik::filter::color_blind_tritanope>())]
|
agg_blur_filter(_val)
|
scale_hsla_filter(_val)

View file

@ -55,6 +55,47 @@ struct x_gradient : image_filter_base {};
struct y_gradient : image_filter_base {};
struct invert : image_filter_base {};
// http://vision.psychol.cam.ac.uk/jdmollon/papers/colourmaps.pdf
struct color_blind_protanope : image_filter_base
{
const double f0 = 0.0;
const double f1 = 2.02344;
const double f2 = -2.52581;
const double f3 = 0.0;
const double f4 = 1.0;
const double f5 = 0.0;
const double f6 = 0.0;
const double f7 = 0.0;
const double f8 = 1.0;
};
struct color_blind_deuteranope : image_filter_base
{
const double f0 = 1.0;
const double f1 = 0.0;
const double f2 = 0.0;
const double f3 = 0.494207;
const double f4 = 0.0;
const double f5 = 1.24827;
const double f6 = 0.0;
const double f7 = 0.0;
const double f8 = 1.0;
};
struct color_blind_tritanope : image_filter_base
{
const double f0 = 1.0;
const double f1 = 0.0;
const double f2 = 0.0;
const double f3 = 0.0;
const double f4 = 1.0;
const double f5 = 0.0;
const double f6 = -0.395913;
const double f7 = 0.801109;
const double f8 = 0.0;
};
struct agg_stack_blur : image_filter_base
{
agg_stack_blur(unsigned rx_, unsigned ry_)
@ -169,7 +210,10 @@ using filter_type = util::variant<filter::blur,
filter::invert,
filter::scale_hsla,
filter::colorize_alpha,
filter::color_to_alpha>;
filter::color_to_alpha,
filter::color_blind_protanope,
filter::color_blind_deuteranope,
filter::color_blind_tritanope>;
inline std::ostream& operator<< (std::ostream& os, blur)
{
@ -247,6 +291,24 @@ inline std::ostream& operator<< (std::ostream& os, invert)
return os;
}
inline std::ostream& operator<< (std::ostream& os, color_blind_protanope)
{
os << "color-blind-protanope";
return os;
}
inline std::ostream& operator<< (std::ostream& os, color_blind_deuteranope)
{
os << "color-blind-deuteranope";
return os;
}
inline std::ostream& operator<< (std::ostream& os, color_blind_tritanope)
{
os << "color-blind-tritanope";
return os;
}
inline std::ostream& operator<< (std::ostream& os, colorize_alpha const& filter)
{
os << "colorize-alpha(";

@ -1 +1 @@
Subproject commit e3d79ed493485afbb5c79cd90913c8db488561b6
Subproject commit 22d44804277eec9e62964b4059c71d9f001747c7