mirror of https://github.com/KLayout/klayout.git
Fixing color interpolation of false color maps
Problem was when an interval was separated from the rest through a discontinuous color and did not have any color range (start and end color same). The new scheme fixes the problem by computing a data mapping that has two slightly distinct values for the two discontinuous colors.
This commit is contained in:
parent
472a7aa7fd
commit
95271e3edc
|
|
@ -47,6 +47,65 @@
|
||||||
namespace img
|
namespace img
|
||||||
{
|
{
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
struct compare_first_of_node
|
||||||
|
{
|
||||||
|
bool operator() (const std::pair <double, std::pair<tl::Color, tl::Color> > &a, const std::pair <double, std::pair<tl::Color, tl::Color> > &b) const
|
||||||
|
{
|
||||||
|
return a.first < b.first;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static tl::Color
|
||||||
|
interpolated_color2 (const std::pair<double, std::pair<tl::Color, tl::Color> > &n1, const std::pair<double, std::pair<tl::Color, tl::Color> > &n2, double x)
|
||||||
|
{
|
||||||
|
double x1 = n1.first;
|
||||||
|
double x2 = n2.first;
|
||||||
|
|
||||||
|
unsigned int h1 = 0, s1 = 0, v1 = 0;
|
||||||
|
n1.second.second.get_hsv (h1, s1, v1);
|
||||||
|
|
||||||
|
unsigned int h2 = 0, s2 = 0, v2 = 0;
|
||||||
|
n2.second.first.get_hsv (h2, s2, v2);
|
||||||
|
|
||||||
|
int h = int (0.5 + h1 + double(x - x1) * double (int (h2) - int (h1)) / double(x2 - x1));
|
||||||
|
int s = int (0.5 + s1 + double(x - x1) * double (int (s2) - int (s1)) / double(x2 - x1));
|
||||||
|
int v = int (0.5 + v1 + double(x - x1) * double (int (v2) - int (v1)) / double(x2 - x1));
|
||||||
|
|
||||||
|
return tl::Color::from_hsv ((unsigned int) h, (unsigned int) s, (unsigned int) v);
|
||||||
|
}
|
||||||
|
|
||||||
|
tl::Color
|
||||||
|
interpolated_color (const DataMapping::false_color_nodes_type &nodes, double x)
|
||||||
|
{
|
||||||
|
if (nodes.size () < 1) {
|
||||||
|
|
||||||
|
return tl::Color ();
|
||||||
|
|
||||||
|
} else if (nodes.size () < 2) {
|
||||||
|
|
||||||
|
return x < nodes[0].first ? nodes[0].second.first : nodes[0].second.second;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
std::vector<std::pair<double, std::pair<tl::Color, tl::Color> > >::const_iterator p = std::lower_bound (nodes.begin (), nodes.end (), std::make_pair (x, std::make_pair (tl::Color (), tl::Color ())), compare_first_of_node ());
|
||||||
|
if (p == nodes.end ()) {
|
||||||
|
return nodes.back ().second.second;
|
||||||
|
} else if (p == nodes.begin ()) {
|
||||||
|
return nodes.front ().second.first;
|
||||||
|
} else {
|
||||||
|
return interpolated_color2 (p[-1], *p, x);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
// img::DataMapping implementation
|
// img::DataMapping implementation
|
||||||
|
|
||||||
|
|
@ -153,6 +212,21 @@ DataMapping::operator< (const DataMapping &d) const
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline double color_to_channel_value (const tl::Color &c, unsigned int channel)
|
||||||
|
{
|
||||||
|
double y = 0.0;
|
||||||
|
|
||||||
|
if (channel == 0) {
|
||||||
|
y = c.red ();
|
||||||
|
} else if (channel == 1) {
|
||||||
|
y = c.green ();
|
||||||
|
} else if (channel == 2) {
|
||||||
|
y = c.blue ();
|
||||||
|
}
|
||||||
|
|
||||||
|
return y / 255.0;
|
||||||
|
}
|
||||||
|
|
||||||
tl::DataMappingBase *
|
tl::DataMappingBase *
|
||||||
DataMapping::create_data_mapping (bool monochrome, double xmin, double xmax, unsigned int channel) const
|
DataMapping::create_data_mapping (bool monochrome, double xmin, double xmax, unsigned int channel) const
|
||||||
{
|
{
|
||||||
|
|
@ -191,6 +265,8 @@ DataMapping::create_data_mapping (bool monochrome, double xmin, double xmax, uns
|
||||||
if (monochrome && false_color_nodes.size () > 1) {
|
if (monochrome && false_color_nodes.size () > 1) {
|
||||||
|
|
||||||
tl::TableDataMapping *gray_to_color = new tl::TableDataMapping ();
|
tl::TableDataMapping *gray_to_color = new tl::TableDataMapping ();
|
||||||
|
double xfence = false_color_nodes.front ().first - 1.0;
|
||||||
|
double xeps = 1e-5;
|
||||||
|
|
||||||
for (unsigned int i = 1; i < false_color_nodes.size (); ++i) {
|
for (unsigned int i = 1; i < false_color_nodes.size (); ++i) {
|
||||||
|
|
||||||
|
|
@ -212,35 +288,35 @@ DataMapping::create_data_mapping (bool monochrome, double xmin, double xmax, uns
|
||||||
|
|
||||||
for (int j = 0; j < n; ++j) {
|
for (int j = 0; j < n; ++j) {
|
||||||
|
|
||||||
tl::Color c = interpolated_color (false_color_nodes, x);
|
tl::Color c = interpolated_color2 (false_color_nodes [i - 1], false_color_nodes [i], x);
|
||||||
|
|
||||||
double y = 0.0;
|
double xx = std::max (xfence * (1.0 + xeps), x);
|
||||||
if (channel == 0) {
|
xfence = xx;
|
||||||
y = c.red ();
|
double y = color_to_channel_value (c, channel);
|
||||||
} else if (channel == 1) {
|
gray_to_color->push_back (xx, y);
|
||||||
y = c.green ();
|
|
||||||
} else if (channel == 2) {
|
|
||||||
y = c.blue ();
|
|
||||||
}
|
|
||||||
|
|
||||||
gray_to_color->push_back (x, y / 255.0);
|
|
||||||
|
|
||||||
x += dx;
|
x += dx;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// on discontinuous colors add another entry that manifests the discontinuity
|
||||||
|
if (i + 1 < false_color_nodes.size () && false_color_nodes [i].second.first != false_color_nodes [i].second.second) {
|
||||||
|
|
||||||
|
double x = false_color_nodes [i].first;
|
||||||
|
double xx = std::max (xfence * (1.0 + xeps), x);
|
||||||
|
xfence = xx;
|
||||||
|
double y = color_to_channel_value (false_color_nodes [i].second.first, channel);
|
||||||
|
gray_to_color->push_back (xx, y);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
double ylast = 0.0;
|
double x = false_color_nodes.back ().first;
|
||||||
if (channel == 0) {
|
double xx = std::max (xfence * (1.0 + xeps), x);
|
||||||
ylast = false_color_nodes.back ().second.second.red ();
|
xfence = xx;
|
||||||
} else if (channel == 1) {
|
double ylast = color_to_channel_value (false_color_nodes.back ().second.first, channel);
|
||||||
ylast = false_color_nodes.back ().second.second.green ();
|
gray_to_color->push_back (xx, ylast);
|
||||||
} else if (channel == 2) {
|
|
||||||
ylast = false_color_nodes.back ().second.second.blue ();
|
|
||||||
}
|
|
||||||
|
|
||||||
gray_to_color->push_back (false_color_nodes.back ().first, ylast / 255.0);
|
|
||||||
|
|
||||||
dm = new tl::CombinedDataMapping (
|
dm = new tl::CombinedDataMapping (
|
||||||
to_pixel,
|
to_pixel,
|
||||||
|
|
@ -266,57 +342,6 @@ DataMapping::create_data_mapping (bool monochrome, double xmin, double xmax, uns
|
||||||
return dm;
|
return dm;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
|
|
||||||
struct compare_first_of_node
|
|
||||||
{
|
|
||||||
bool operator() (const std::pair <double, std::pair<tl::Color, tl::Color> > &a, const std::pair <double, std::pair<tl::Color, tl::Color> > &b) const
|
|
||||||
{
|
|
||||||
return a.first < b.first;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
tl::Color
|
|
||||||
interpolated_color (const DataMapping::false_color_nodes_type &nodes, double x)
|
|
||||||
{
|
|
||||||
if (nodes.size () < 1) {
|
|
||||||
return tl::Color ();
|
|
||||||
} else if (nodes.size () < 2) {
|
|
||||||
return x < nodes[0].first ? nodes[0].second.first : nodes[0].second.second;
|
|
||||||
} else {
|
|
||||||
|
|
||||||
std::vector<std::pair<double, std::pair<tl::Color, tl::Color> > >::const_iterator p = std::lower_bound (nodes.begin (), nodes.end (), std::make_pair (x, std::make_pair (tl::Color (), tl::Color ())), compare_first_of_node ());
|
|
||||||
if (p == nodes.end ()) {
|
|
||||||
return nodes.back ().second.second;
|
|
||||||
} else if (p == nodes.begin ()) {
|
|
||||||
return nodes.front ().second.first;
|
|
||||||
} else {
|
|
||||||
|
|
||||||
double x1 = p[-1].first;
|
|
||||||
double x2 = p->first;
|
|
||||||
|
|
||||||
unsigned int h1 = 0, s1 = 0, v1 = 0;
|
|
||||||
p[-1].second.second.get_hsv (h1, s1, v1);
|
|
||||||
|
|
||||||
unsigned int h2 = 0, s2 = 0, v2 = 0;
|
|
||||||
p->second.first.get_hsv (h2, s2, v2);
|
|
||||||
|
|
||||||
int h = int (0.5 + h1 + double(x - x1) * double (int (h2) - int (h1)) / double(x2 - x1));
|
|
||||||
int s = int (0.5 + s1 + double(x - x1) * double (int (s2) - int (s1)) / double(x2 - x1));
|
|
||||||
int v = int (0.5 + v1 + double(x - x1) * double (int (v2) - int (v1)) / double(x2 - x1));
|
|
||||||
|
|
||||||
return tl::Color::from_hsv ((unsigned int) h, (unsigned int) s, (unsigned int) v);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
// img::DataHeader definition and implementation
|
// img::DataHeader definition and implementation
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -145,7 +145,7 @@ public:
|
||||||
/**
|
/**
|
||||||
* @brief A helper function to interpolate a color in the color bar at a given x
|
* @brief A helper function to interpolate a color in the color bar at a given x
|
||||||
*/
|
*/
|
||||||
tl::Color interpolated_color (const DataMapping::false_color_nodes_type &nodes, double x);
|
IMG_PUBLIC tl::Color interpolated_color (const DataMapping::false_color_nodes_type &nodes, double x);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A image object
|
* @brief A image object
|
||||||
|
|
|
||||||
|
|
@ -328,3 +328,46 @@ TEST(3)
|
||||||
EXPECT_EQ (image.mask (1, 2), false);
|
EXPECT_EQ (image.mask (1, 2), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// color interpolation
|
||||||
|
TEST(4)
|
||||||
|
{
|
||||||
|
img::DataMapping::false_color_nodes_type nodes;
|
||||||
|
|
||||||
|
nodes.push_back (std::make_pair (0.0, std::make_pair (tl::color_t (0xff0000), tl::color_t (0xff0000))));
|
||||||
|
nodes.push_back (std::make_pair (1.0, std::make_pair (tl::color_t (0x0000ff), tl::color_t (0xffffff))));
|
||||||
|
nodes.push_back (std::make_pair (2.0, std::make_pair (tl::color_t (0x000000), tl::color_t (0xffffff))));
|
||||||
|
|
||||||
|
EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 0.0)).to_string (), "#ff0000");
|
||||||
|
EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 0.001)).to_string (), "#ff0000");
|
||||||
|
EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 0.5)).to_string (), "#00ff00");
|
||||||
|
EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 0.999)).to_string (), "#0000ff");
|
||||||
|
EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 1.0)).to_string (), "#0000ff");
|
||||||
|
EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 1.001)).to_string (), "#ffffff");
|
||||||
|
EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 1.5)).to_string (), "#808080");
|
||||||
|
EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 1.999)).to_string (), "#000000");
|
||||||
|
EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 2.0)).to_string (), "#000000");
|
||||||
|
|
||||||
|
nodes.clear ();
|
||||||
|
|
||||||
|
nodes.push_back (std::make_pair (0.0, std::make_pair (tl::color_t (0xff0000), tl::color_t (0xff0000))));
|
||||||
|
nodes.push_back (std::make_pair (1.0, std::make_pair (tl::color_t (0xff0000), tl::color_t (0x0000ff))));
|
||||||
|
nodes.push_back (std::make_pair (2.0, std::make_pair (tl::color_t (0x0000ff), tl::color_t (0x0000ff))));
|
||||||
|
|
||||||
|
EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 0.0)).to_string (), "#ff0000");
|
||||||
|
EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 0.5)).to_string (), "#ff0000");
|
||||||
|
EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 0.999)).to_string (), "#ff0000");
|
||||||
|
EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 1.0)).to_string (), "#ff0000");
|
||||||
|
EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 1.001)).to_string (), "#0000ff");
|
||||||
|
EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 1.5)).to_string (), "#0000ff");
|
||||||
|
EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 1.999)).to_string (), "#0000ff");
|
||||||
|
EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 2.0)).to_string (), "#0000ff");
|
||||||
|
|
||||||
|
nodes.clear ();
|
||||||
|
|
||||||
|
nodes.push_back (std::make_pair (0.0, std::make_pair (tl::color_t (0x0000ff), tl::color_t (0x0000ff))));
|
||||||
|
nodes.push_back (std::make_pair (0.275591, std::make_pair (tl::color_t (0x0000ff), tl::color_t (0x000000))));
|
||||||
|
nodes.push_back (std::make_pair (0.643701, std::make_pair (tl::color_t (0xffffff), tl::color_t (0xff0000))));
|
||||||
|
nodes.push_back (std::make_pair (1.0, std::make_pair (tl::color_t (0xff0000), tl::color_t (0xff0000))));
|
||||||
|
|
||||||
|
EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 0.7)).to_string (), "#ff0000");
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue