From d0c888024d1611b7d9a30fa321a0f4d77eacf1da Mon Sep 17 00:00:00 2001 From: Bert Peters Date: Thu, 3 May 2018 13:33:02 +0200 Subject: [PATCH] Use global color interpolation. Now every place uses the globally defined colors rather than specific colors whenever needed. Next: make those colors configurable. --- src/fmri/ActivityAnimation.cpp | 26 +++++--------------- src/fmri/ActivityAnimation.hpp | 2 -- src/fmri/FlatLayerVisualisation.cpp | 8 ++----- src/fmri/LabelVisualisation.cpp | 2 +- src/fmri/LayerData.cpp | 10 ++++++++ src/fmri/LayerData.hpp | 3 +++ src/fmri/utils.cpp | 15 +++--------- src/fmri/utils.hpp | 30 ++++++++++++++++------- src/fmri/visualisations.cpp | 37 +++++++++-------------------- 9 files changed, 57 insertions(+), 76 deletions(-) diff --git a/src/fmri/ActivityAnimation.cpp b/src/fmri/ActivityAnimation.cpp index 4d60d13..6b6ac8e 100644 --- a/src/fmri/ActivityAnimation.cpp +++ b/src/fmri/ActivityAnimation.cpp @@ -11,35 +11,21 @@ using namespace std; using namespace fmri; -Color ActivityAnimation::colorBySign(float intensity) -{ - if (intensity > 0) { - return {0, 1, 0, 1}; - } else { - return {1, 0, 0, 1}; - } -} - ActivityAnimation::ActivityAnimation( const std::vector>> &interactions, const float *aPositions, const float *bPositions) : - ActivityAnimation(interactions, aPositions, bPositions, ActivityAnimation::colorBySign) -{ -} - -ActivityAnimation::ActivityAnimation( - const std::vector>> &interactions, - const float *aPositions, const float *bPositions, ColoringFunction coloring) - : bufferLength(3 * interactions.size()), delta(bufferLength) { - CHECK(coloring) << "Invalid coloring function passed."; startingPos.reserve(bufferLength); vector endPos; endPos.reserve(bufferLength); - transform(interactions.begin(), interactions.end(), back_inserter(colorBuffer), [&coloring](auto e) { - return coloring(e.first); + transform(interactions.begin(), interactions.end(), back_inserter(colorBuffer), [](auto e) { + 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()); diff --git a/src/fmri/ActivityAnimation.hpp b/src/fmri/ActivityAnimation.hpp index 4d17b44..7cf4393 100644 --- a/src/fmri/ActivityAnimation.hpp +++ b/src/fmri/ActivityAnimation.hpp @@ -23,8 +23,6 @@ namespace fmri const float *aPositions, const float *bPositions, ColoringFunction coloring); void draw(float timeScale) override; - static Color colorBySign(float intensity); - private: std::size_t bufferLength; std::vector startingPos; diff --git a/src/fmri/FlatLayerVisualisation.cpp b/src/fmri/FlatLayerVisualisation.cpp index 646ccce..850d8de 100644 --- a/src/fmri/FlatLayerVisualisation.cpp +++ b/src/fmri/FlatLayerVisualisation.cpp @@ -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); if (intensity > 0) { - destination[0] = saturation; - destination[1] = saturation; - destination[2] = 1; + destination = interpolate(saturation, NEUTRAL_COLOR, POSITIVE_COLOR); } else { - destination[0] = 1; - destination[1] = saturation; - destination[2] = saturation; + destination = interpolate(saturation, NEUTRAL_COLOR, NEGATIVE_COLOR); } if constexpr (alphaEnabled()) { // We have an alpha channel, set it to 1. diff --git a/src/fmri/LabelVisualisation.cpp b/src/fmri/LabelVisualisation.cpp index 831402c..55f74dd 100644 --- a/src/fmri/LabelVisualisation.cpp +++ b/src/fmri/LabelVisualisation.cpp @@ -43,7 +43,7 @@ LabelVisualisation::LabelVisualisation(const std::vector &positions, cons 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); nodeLabels.emplace_back(labels[i]); } diff --git a/src/fmri/LayerData.cpp b/src/fmri/LayerData.cpp index 0fd2766..14d0ebf 100644 --- a/src/fmri/LayerData.cpp +++ b/src/fmri/LayerData.cpp @@ -47,6 +47,16 @@ const DType &LayerData::operator[](std::size_t i) const 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) { o << layer.name() << '('; diff --git a/src/fmri/LayerData.hpp b/src/fmri/LayerData.hpp index 039929a..a44b4b1 100644 --- a/src/fmri/LayerData.hpp +++ b/src/fmri/LayerData.hpp @@ -32,6 +32,9 @@ namespace fmri DType const *data() const; std::size_t numEntries() const; + DType const *begin() const; + DType const *end() const; + const DType& operator[] (std::size_t i) const; private: string name_; diff --git a/src/fmri/utils.cpp b/src/fmri/utils.cpp index 5bbce8d..5e51ad2 100644 --- a/src/fmri/utils.cpp +++ b/src/fmri/utils.cpp @@ -3,18 +3,9 @@ float fmri::LAYER_X_OFFSET = 10; -std::default_random_engine &fmri::rng() -{ - static std::default_random_engine rng; - static std::default_random_engine::result_type seed = 0; - - if (seed == 0) { - std::random_device dev; - rng.seed(seed = dev()); - } - - return rng; -} +fmri::Color fmri::NEUTRAL_COLOR = {1, 1, 1, 1}; +fmri::Color fmri::NEGATIVE_COLOR = {1, 0, 0, 1}; +fmri::Color fmri::POSITIVE_COLOR = {0, 0, 1, 1}; const std::vector & fmri::animate(const std::vector &start, const std::vector &delta, float time) { diff --git a/src/fmri/utils.hpp b/src/fmri/utils.hpp index f58afa6..6a8fc61 100644 --- a/src/fmri/utils.hpp +++ b/src/fmri/utils.hpp @@ -19,6 +19,27 @@ namespace fmri typedef std::array 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. * @@ -160,15 +181,6 @@ namespace fmri 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. * diff --git a/src/fmri/visualisations.cpp b/src/fmri/visualisations.cpp index 0dfafce..9adfc8e 100644 --- a/src/fmri/visualisations.cpp +++ b/src/fmri/visualisations.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include "visualisations.hpp" #include "DummyLayerVisualisation.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()); if (curState.shape().size() == 2) { - rescale(changes.begin(), changes.end(), 0, 1); EntryList results; for (auto i : Range(curState.numEntries())) { if (curState.data()[i] > EPSILON) { @@ -176,10 +176,7 @@ static Animation *getReLUAnimation(const fmri::LayerData &prevState, } } - return new ActivityAnimation(results, prevPositions.data(), curPositions.data(), - [](float i) -> Color { - return {1 - i, 1 - i, 1}; - }); + return new ActivityAnimation(results, prevPositions.data(), curPositions.data()); } else { return new ImageInteractionAnimation(changes.data(), prevState.shape(), prevPositions, curPositions); } @@ -189,36 +186,26 @@ static Animation *getNormalizingAnimation(const fmri::LayerData &prevState, cons const vector &prevPositions, const vector &curPositions) { CHECK(prevState.shape() == curState.shape()) << "Shapes should be of equal size" << endl; - vector scaling(std::accumulate(prevState.shape().begin(), prevState.shape().end(), 1u, multiplies())); - caffe::caffe_div(scaling.size(), prevState.data(), curState.data(), scaling.data()); + valarray scaling(prevState.data(), prevState.numEntries()); + scaling /= valarray(curState.data(), curState.numEntries()); // 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) { + scaling /= scaling.max(); EntryList entries; entries.reserve(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)); } } + 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 { - transform(scaling.begin(), scaling.end(), scaling.begin(), [](float x) { return log(x); }); - return new ImageInteractionAnimation(scaling.data(), prevState.shape(), prevPositions, curPositions); + return new ImageInteractionAnimation(&scaling[0], 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)); } - return new ActivityAnimation(entries, prevPositions.data(), curPositions.data(), [](auto i) -> Color { - return {1 - i, 1 - i, 1}; - }); + return new ActivityAnimation(entries, prevPositions.data(), curPositions.data()); } Animation * fmri::getActivityAnimation(const fmri::LayerData &prevState, const fmri::LayerData &curState,