Use global color interpolation.
Now every place uses the globally defined colors rather than specific colors whenever needed. Next: make those colors configurable.
This commit is contained in:
@@ -11,35 +11,21 @@
|
|||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace fmri;
|
using namespace fmri;
|
||||||
|
|
||||||
Color ActivityAnimation::colorBySign(float intensity)
|
|
||||||
{
|
|
||||||
if (intensity > 0) {
|
|
||||||
return {0, 1, 0, 1};
|
|
||||||
} else {
|
|
||||||
return {1, 0, 0, 1};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ActivityAnimation::ActivityAnimation(
|
ActivityAnimation::ActivityAnimation(
|
||||||
const std::vector<std::pair<DType, std::pair<std::size_t, std::size_t>>> &interactions,
|
const std::vector<std::pair<DType, std::pair<std::size_t, std::size_t>>> &interactions,
|
||||||
const float *aPositions, const float *bPositions) :
|
const float *aPositions, const float *bPositions) :
|
||||||
ActivityAnimation(interactions, aPositions, bPositions, ActivityAnimation::colorBySign)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
ActivityAnimation::ActivityAnimation(
|
|
||||||
const std::vector<std::pair<DType, std::pair<std::size_t, std::size_t>>> &interactions,
|
|
||||||
const float *aPositions, const float *bPositions, ColoringFunction coloring)
|
|
||||||
:
|
|
||||||
bufferLength(3 * interactions.size()),
|
bufferLength(3 * interactions.size()),
|
||||||
delta(bufferLength)
|
delta(bufferLength)
|
||||||
{
|
{
|
||||||
CHECK(coloring) << "Invalid coloring function passed.";
|
|
||||||
startingPos.reserve(bufferLength);
|
startingPos.reserve(bufferLength);
|
||||||
vector<float> endPos;
|
vector<float> endPos;
|
||||||
endPos.reserve(bufferLength);
|
endPos.reserve(bufferLength);
|
||||||
transform(interactions.begin(), interactions.end(), back_inserter(colorBuffer), [&coloring](auto e) {
|
transform(interactions.begin(), interactions.end(), back_inserter(colorBuffer), [](auto e) {
|
||||||
return coloring(e.first);
|
if (e.first > 0) {
|
||||||
|
return interpolate(e.first, POSITIVE_COLOR, NEUTRAL_COLOR);
|
||||||
|
} else {
|
||||||
|
return interpolate(e.first + 1, NEUTRAL_COLOR, NEGATIVE_COLOR);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
colorBuffer.reserve(interactions.size());
|
colorBuffer.reserve(interactions.size());
|
||||||
|
|
||||||
|
|||||||
@@ -23,8 +23,6 @@ namespace fmri
|
|||||||
const float *aPositions, const float *bPositions, ColoringFunction coloring);
|
const float *aPositions, const float *bPositions, ColoringFunction coloring);
|
||||||
void draw(float timeScale) override;
|
void draw(float timeScale) override;
|
||||||
|
|
||||||
static Color colorBySign(float intensity);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::size_t bufferLength;
|
std::size_t bufferLength;
|
||||||
std::vector<float> startingPos;
|
std::vector<float> startingPos;
|
||||||
|
|||||||
@@ -11,13 +11,9 @@ static inline void computeColor(float intensity, float limit, Color& destination
|
|||||||
{
|
{
|
||||||
const float saturation = std::min(-std::log(std::abs(intensity) / limit) / 10.0f, 1.0f);
|
const float saturation = std::min(-std::log(std::abs(intensity) / limit) / 10.0f, 1.0f);
|
||||||
if (intensity > 0) {
|
if (intensity > 0) {
|
||||||
destination[0] = saturation;
|
destination = interpolate(saturation, NEUTRAL_COLOR, POSITIVE_COLOR);
|
||||||
destination[1] = saturation;
|
|
||||||
destination[2] = 1;
|
|
||||||
} else {
|
} else {
|
||||||
destination[0] = 1;
|
destination = interpolate(saturation, NEUTRAL_COLOR, NEGATIVE_COLOR);
|
||||||
destination[1] = saturation;
|
|
||||||
destination[2] = saturation;
|
|
||||||
}
|
}
|
||||||
if constexpr (alphaEnabled()) {
|
if constexpr (alphaEnabled()) {
|
||||||
// We have an alpha channel, set it to 1.
|
// We have an alpha channel, set it to 1.
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ LabelVisualisation::LabelVisualisation(const std::vector<float> &positions, cons
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
colorBuffer.emplace_back(Color{1 - prevData[i] / maxVal, 1 - prevData[i] / maxVal, 1});
|
colorBuffer.emplace_back(interpolate(prevData[i] / maxVal, POSITIVE_COLOR, NEUTRAL_COLOR));
|
||||||
std::copy_n(positions.begin() + 3 * i, 3, nodeInserter);
|
std::copy_n(positions.begin() + 3 * i, 3, nodeInserter);
|
||||||
nodeLabels.emplace_back(labels[i]);
|
nodeLabels.emplace_back(labels[i]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,6 +47,16 @@ const DType &LayerData::operator[](std::size_t i) const
|
|||||||
return data_[i];
|
return data_[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DType const *LayerData::begin() const
|
||||||
|
{
|
||||||
|
return data();
|
||||||
|
}
|
||||||
|
|
||||||
|
DType const *LayerData::end() const
|
||||||
|
{
|
||||||
|
return data() + numEntries();
|
||||||
|
}
|
||||||
|
|
||||||
ostream& operator<< (ostream& o, const LayerData& layer)
|
ostream& operator<< (ostream& o, const LayerData& layer)
|
||||||
{
|
{
|
||||||
o << layer.name() << '(';
|
o << layer.name() << '(';
|
||||||
|
|||||||
@@ -32,6 +32,9 @@ namespace fmri
|
|||||||
DType const *data() const;
|
DType const *data() const;
|
||||||
std::size_t numEntries() const;
|
std::size_t numEntries() const;
|
||||||
|
|
||||||
|
DType const *begin() const;
|
||||||
|
DType const *end() const;
|
||||||
|
|
||||||
const DType& operator[] (std::size_t i) const;
|
const DType& operator[] (std::size_t i) const;
|
||||||
private:
|
private:
|
||||||
string name_;
|
string name_;
|
||||||
|
|||||||
@@ -3,18 +3,9 @@
|
|||||||
|
|
||||||
float fmri::LAYER_X_OFFSET = 10;
|
float fmri::LAYER_X_OFFSET = 10;
|
||||||
|
|
||||||
std::default_random_engine &fmri::rng()
|
fmri::Color fmri::NEUTRAL_COLOR = {1, 1, 1, 1};
|
||||||
{
|
fmri::Color fmri::NEGATIVE_COLOR = {1, 0, 0, 1};
|
||||||
static std::default_random_engine rng;
|
fmri::Color fmri::POSITIVE_COLOR = {0, 0, 1, 1};
|
||||||
static std::default_random_engine::result_type seed = 0;
|
|
||||||
|
|
||||||
if (seed == 0) {
|
|
||||||
std::random_device dev;
|
|
||||||
rng.seed(seed = dev());
|
|
||||||
}
|
|
||||||
|
|
||||||
return rng;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<float> & fmri::animate(const std::vector<float> &start, const std::vector<float> &delta, float time)
|
const std::vector<float> & fmri::animate(const std::vector<float> &start, const std::vector<float> &delta, float time)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -19,6 +19,27 @@ namespace fmri
|
|||||||
|
|
||||||
typedef std::array<float, 4> Color;
|
typedef std::array<float, 4> Color;
|
||||||
|
|
||||||
|
extern Color NEUTRAL_COLOR;
|
||||||
|
extern Color POSITIVE_COLOR;
|
||||||
|
extern Color NEGATIVE_COLOR;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Linearly interpolate between two colors
|
||||||
|
*
|
||||||
|
* @param aPart fraction of color A to use. [0, 1]
|
||||||
|
* @param a First color
|
||||||
|
* @param b Second color
|
||||||
|
* @return The interpolated color.
|
||||||
|
*/
|
||||||
|
inline Color interpolate(float aPart, const Color& a, const Color& b) {
|
||||||
|
Color r;
|
||||||
|
std::transform(a.begin(), a.end(), b.begin(), r.begin(), [aPart] (auto a, auto b) {
|
||||||
|
return aPart * a + (1 - aPart) * b;
|
||||||
|
});
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The distance between layers in the visualisation.
|
* The distance between layers in the visualisation.
|
||||||
*
|
*
|
||||||
@@ -160,15 +181,6 @@ namespace fmri
|
|||||||
return cols;
|
return cols;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a globally initialized random number generator.
|
|
||||||
*
|
|
||||||
* This RNG should always be used as a reference, to make sure the state actually updates.
|
|
||||||
*
|
|
||||||
* @return A reference to the global RNG.
|
|
||||||
*/
|
|
||||||
std::default_random_engine& rng();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the current animation offset for a particular animation.
|
* Get the current animation offset for a particular animation.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
#include <caffe/util/math_functions.hpp>
|
#include <caffe/util/math_functions.hpp>
|
||||||
|
#include <valarray>
|
||||||
#include "visualisations.hpp"
|
#include "visualisations.hpp"
|
||||||
#include "DummyLayerVisualisation.hpp"
|
#include "DummyLayerVisualisation.hpp"
|
||||||
#include "MultiImageVisualisation.hpp"
|
#include "MultiImageVisualisation.hpp"
|
||||||
@@ -168,7 +169,6 @@ static Animation *getReLUAnimation(const fmri::LayerData &prevState,
|
|||||||
caffe::caffe_sub(prevState.numEntries(), curState.data(), prevState.data(), changes.data());
|
caffe::caffe_sub(prevState.numEntries(), curState.data(), prevState.data(), changes.data());
|
||||||
|
|
||||||
if (curState.shape().size() == 2) {
|
if (curState.shape().size() == 2) {
|
||||||
rescale(changes.begin(), changes.end(), 0, 1);
|
|
||||||
EntryList results;
|
EntryList results;
|
||||||
for (auto i : Range(curState.numEntries())) {
|
for (auto i : Range(curState.numEntries())) {
|
||||||
if (curState.data()[i] > EPSILON) {
|
if (curState.data()[i] > EPSILON) {
|
||||||
@@ -176,10 +176,7 @@ static Animation *getReLUAnimation(const fmri::LayerData &prevState,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ActivityAnimation(results, prevPositions.data(), curPositions.data(),
|
return new ActivityAnimation(results, prevPositions.data(), curPositions.data());
|
||||||
[](float i) -> Color {
|
|
||||||
return {1 - i, 1 - i, 1};
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
return new ImageInteractionAnimation(changes.data(), prevState.shape(), prevPositions, curPositions);
|
return new ImageInteractionAnimation(changes.data(), prevState.shape(), prevPositions, curPositions);
|
||||||
}
|
}
|
||||||
@@ -189,36 +186,26 @@ static Animation *getNormalizingAnimation(const fmri::LayerData &prevState, cons
|
|||||||
const vector<float> &prevPositions,
|
const vector<float> &prevPositions,
|
||||||
const vector<float> &curPositions) {
|
const vector<float> &curPositions) {
|
||||||
CHECK(prevState.shape() == curState.shape()) << "Shapes should be of equal size" << endl;
|
CHECK(prevState.shape() == curState.shape()) << "Shapes should be of equal size" << endl;
|
||||||
vector<DType> scaling(std::accumulate(prevState.shape().begin(), prevState.shape().end(), 1u, multiplies<void>()));
|
valarray<DType> scaling(prevState.data(), prevState.numEntries());
|
||||||
caffe::caffe_div(scaling.size(), prevState.data(), curState.data(), scaling.data());
|
scaling /= valarray<DType>(curState.data(), curState.numEntries());
|
||||||
|
|
||||||
// Fix divisions by zero. For those cases, pick 1 since it doesn't matter anyway.
|
// Fix divisions by zero. For those cases, pick 1 since it doesn't matter anyway.
|
||||||
normalize(scaling.begin(), scaling.end());
|
normalize(begin(scaling), end(scaling));
|
||||||
|
scaling = log(scaling);
|
||||||
|
|
||||||
if (prevState.shape().size() == 2) {
|
if (prevState.shape().size() == 2) {
|
||||||
|
scaling /= scaling.max();
|
||||||
EntryList entries;
|
EntryList entries;
|
||||||
entries.reserve(scaling.size());
|
entries.reserve(scaling.size());
|
||||||
for (auto i : Range(scaling.size())) {
|
for (auto i : Range(scaling.size())) {
|
||||||
if (std::abs(curState.data()[i]) > EPSILON) {
|
if (std::abs(curState[i]) > EPSILON) {
|
||||||
entries.emplace_back(scaling[i], make_pair(i, i));
|
entries.emplace_back(scaling[i], make_pair(i, i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return new ActivityAnimation(entries, prevPositions.data(), curPositions.data());
|
||||||
|
|
||||||
auto max_val = *max_element(scaling.begin(), scaling.end());
|
|
||||||
|
|
||||||
return new ActivityAnimation(entries, prevPositions.data(), curPositions.data(),
|
|
||||||
[=](float i) -> Color {
|
|
||||||
auto intensity = clamp((i - 1) / (max_val - 1), 0.f, 1.f);
|
|
||||||
return {
|
|
||||||
1 - intensity,
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
1
|
|
||||||
};
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
transform(scaling.begin(), scaling.end(), scaling.begin(), [](float x) { return log(x); });
|
return new ImageInteractionAnimation(&scaling[0], prevState.shape(), prevPositions, curPositions);
|
||||||
return new ImageInteractionAnimation(scaling.data(), prevState.shape(), prevPositions, curPositions);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,9 +222,7 @@ static Animation *getSoftmaxAnimation(const fmri::LayerData &curState, const vec
|
|||||||
entries.emplace_back(intensities[i], make_pair(i, i));
|
entries.emplace_back(intensities[i], make_pair(i, i));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ActivityAnimation(entries, prevPositions.data(), curPositions.data(), [](auto i) -> Color {
|
return new ActivityAnimation(entries, prevPositions.data(), curPositions.data());
|
||||||
return {1 - i, 1 - i, 1};
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Animation * fmri::getActivityAnimation(const fmri::LayerData &prevState, const fmri::LayerData &curState,
|
Animation * fmri::getActivityAnimation(const fmri::LayerData &prevState, const fmri::LayerData &curState,
|
||||||
|
|||||||
Reference in New Issue
Block a user