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:
parent
6b05f19c38
commit
6245790e72
4 changed files with 145 additions and 2 deletions
|
@ -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>
|
template <typename Src>
|
||||||
void apply_filter(Src & src, gray const& /*op*/)
|
void apply_filter(Src & src, gray const& /*op*/)
|
||||||
{
|
{
|
||||||
|
|
|
@ -80,6 +80,12 @@ image_filter_grammar<Iterator,ContType>::image_filter_grammar()
|
||||||
|
|
|
|
||||||
lit("invert") >> no_args [push_back(_val,construct<mapnik::filter::invert>())]
|
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)
|
agg_blur_filter(_val)
|
||||||
|
|
|
|
||||||
scale_hsla_filter(_val)
|
scale_hsla_filter(_val)
|
||||||
|
|
|
@ -55,6 +55,47 @@ struct x_gradient : image_filter_base {};
|
||||||
struct y_gradient : image_filter_base {};
|
struct y_gradient : image_filter_base {};
|
||||||
struct invert : 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
|
struct agg_stack_blur : image_filter_base
|
||||||
{
|
{
|
||||||
agg_stack_blur(unsigned rx_, unsigned ry_)
|
agg_stack_blur(unsigned rx_, unsigned ry_)
|
||||||
|
@ -169,7 +210,10 @@ using filter_type = util::variant<filter::blur,
|
||||||
filter::invert,
|
filter::invert,
|
||||||
filter::scale_hsla,
|
filter::scale_hsla,
|
||||||
filter::colorize_alpha,
|
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)
|
inline std::ostream& operator<< (std::ostream& os, blur)
|
||||||
{
|
{
|
||||||
|
@ -247,6 +291,24 @@ inline std::ostream& operator<< (std::ostream& os, invert)
|
||||||
return os;
|
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)
|
inline std::ostream& operator<< (std::ostream& os, colorize_alpha const& filter)
|
||||||
{
|
{
|
||||||
os << "colorize-alpha(";
|
os << "colorize-alpha(";
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit e3d79ed493485afbb5c79cd90913c8db488561b6
|
Subproject commit 22d44804277eec9e62964b4059c71d9f001747c7
|
Loading…
Add table
Reference in a new issue