diff --git a/plugins/processing/simple-visualization/src/box-algorithms/ovpCGrazMultiVisualization.cpp b/plugins/processing/simple-visualization/src/box-algorithms/ovpCGrazMultiVisualization.cpp index 3e5e02feb42e0e1a128a34a0bbb780a9100cf600..688423e02e8ef54c7639d183e0a0510fe93eb86e 100644 --- a/plugins/processing/simple-visualization/src/box-algorithms/ovpCGrazMultiVisualization.cpp +++ b/plugins/processing/simple-visualization/src/box-algorithms/ovpCGrazMultiVisualization.cpp @@ -2,12 +2,14 @@ #include <cmath> #include <iostream> +#include <vector> #include <algorithm> // std::min, max - #include <cstdlib> #include <sys/timeb.h> #include <tcptagging/IStimulusSender.h> #include <iomanip> +#include <array> +#include <functional> // greater #if defined TARGET_OS_Linux #include <unistd.h> @@ -86,6 +88,8 @@ bool CGrazMultiVisualization::initialize() m_nbPredictionsMin = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 5); m_nbModality = (getStaticBoxContext().getSettingCount() - m_NonModalitySettingsCount) / 2; + m_barScales.resize(m_nbModality); + for (auto& s : m_barScales) { s = 0; } vector<string> paths; paths.reserve(m_nbModality + 1); m_stimulationlist.reserve(m_nbModality); @@ -291,7 +295,7 @@ void CGrazMultiVisualization::drawReference() //--------------------------------------------------------------------------------------------------- void CGrazMultiVisualization::drawModality() { - const GdkPixbuf* modality = m_largeImgs[m_modality]; + const GdkPixbuf* modality = m_largeImgs[(m_showInstruction ? m_modality + 1 : 0)]; // Because the first element is the none instruction const gint w = m_windowW / 2, h = m_windowH / 2, x = w - gdk_pixbuf_get_width(modality) / 2, @@ -305,7 +309,7 @@ void CGrazMultiVisualization::drawBar() { if (m_modality != -1) { - const gint currX = (m_modality + 1) * m_modalityX, currH = gint(0.5 * m_barH); + const gint currX = (m_modality + 1) * m_modalityX, currH = gint(m_barScales[m_modality] < 0 ? 0 : m_barScales[m_modality] * m_barH); gdk_pixbuf_render_to_drawable(m_bar, m_widget->window, nullptr, 0, 0, currX - m_barW / 2, m_modalityY - currH, m_barW, currH, GDK_RGB_DITHER_NONE, 0, 0); } } @@ -396,22 +400,32 @@ void CGrazMultiVisualization::drawAccuracy() //***** COMPUTES ***** //******************** //--------------------------------------------------------------------------------------------------- -double CGrazMultiVisualization::aggregatePredictions(const bool all) +int CGrazMultiVisualization::aggregatePredictions(const bool all) { - this->getLogManager() << LogLevel_Warning << "aggregatePredictions(const bool all)\n"; - double vote = 0; + int vote = 0; if (m_amplitudes.size() >= m_nbPredictionsMin) { - // step backwards with rev iter to take the latest samples + // step backwards with rev iter to take the mean of the latest samples uint64_t count = 0; - double maxA = -DBL_MAX; + vector<double> amp(m_nbModality, 0); + //double maxA = -DBL_MAX; for (auto a = m_amplitudes.rbegin(); a != m_amplitudes.rend() && (all || count < m_nbPredictionsMin); ++a, ++count) { - vote += *a; - maxA = std::max<double>(maxA, abs(*a)); + for (size_t i = 0; i < a->size(); ++i) { amp[i] += a->at(i); } + //maxA = std::max<double>(maxA, abs(*a)); + } + for (auto& a : amp) { a /= count; } + // Computes bar scale for each modality, we substract the max value of other modality (other possibility substract the min value or the mean of the values) + for (size_t i = 0; i < amp.size(); ++i) + { + double max = 0; + for (size_t j = 0; j < amp.size(); ++j) + { + if (i != j && max < amp[j]) { max = amp[j]; } + } + m_barScales[i] = amp[i] - max; + if (amp[vote] < amp[i]) { vote = i; } } - vote /= maxA; - vote /= count; } return vote; } @@ -420,13 +434,11 @@ double CGrazMultiVisualization::aggregatePredictions(const bool all) //--------------------------------------------------------------------------------------------------- // @fixme for >2 classes -void CGrazMultiVisualization::updateConfusionMatrix(const double prediction) +void CGrazMultiVisualization::updateConfusionMatrix(const int vote) { - this->getLogManager() << LogLevel_Warning << "updateConfusionMatrix(const double prediction)\n"; if (m_modality != -1) { - this->getLogManager() << LogLevel_Warning << "Predictions : " << prediction << "\n"; - // + (m_confusion.getBuffer())[m_modality * m_nbModality + vote]++; // line = m_modality, col = vote } /* if (m_direction == EArrowDirection_Left || m_direction == EArrowDirection_Right) @@ -444,20 +456,28 @@ void CGrazMultiVisualization::updateConfusionMatrix(const double prediction) //--------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------- -void CGrazMultiVisualization::setMatrixBuffer(const double* pBuffer) +void CGrazMultiVisualization::setMatrixBuffer(const double* buffer) { - this->getLogManager() << LogLevel_Warning << "setMatrixBuffer(const double* pBuffer)\n"; if (m_state != 2) { return; } // No continuous feedback - double amp = 0; // Ad-hoc forcing to probability (range [0,1], sum to 1). This will make scaling easier // if run forever in a continuous mode. If the input is already scaled this way, no effect. + double sum = 0; + vector<double> values; + values.reserve(m_nbModality); + for (size_t i = 0; i < m_nbModality; ++i) + { + values.emplace_back(std::abs(buffer[i])); + sum += buffer[i]; + } + + if (sum != 0) { for (auto& v : values) { v /= sum; } } + else { for (auto& v : values) { v = 0.5; } } - amp = 0.5; - m_amplitudes.push_back(amp); + m_amplitudes.emplace_back(values); // Add this buffer to the list if (m_showFeedback && !m_delayFeedback) { - m_barScale = aggregatePredictions(false); - gdk_window_invalidate_rect(m_widget->window, nullptr, true); + aggregatePredictions(false); + //gdk_window_invalidate_rect(m_widget->window, nullptr, true); } /* @@ -466,7 +486,7 @@ void CGrazMultiVisualization::setMatrixBuffer(const double* pBuffer) // Ad-hoc forcing to probability (range [0,1], sum to 1). This will make scaling easier // if run forever in a continuous mode. If the input is already scaled this way, no effect. // - double value0 = std::abs(pBuffer[0]), value1 = std::abs(pBuffer[1]); + double value0 = std::abs(buffer[0]), value1 = std::abs(buffer[1]); const double sum = value0 + value1; if (sum != 0) { @@ -480,7 +500,7 @@ void CGrazMultiVisualization::setMatrixBuffer(const double* pBuffer) } predictedAmplitude = value1 - value0; } - else { predictedAmplitude = pBuffer[0]; } + else { predictedAmplitude = buffer[0]; } m_amplitudes.push_back(predictedAmplitude); @@ -503,14 +523,14 @@ void CGrazMultiVisualization::setStimulation(const uint64_t stimulation) m_state = 0; // Idle State if (m_showAccuracy || m_delayFeedback) { - const double prediction = aggregatePredictions(true); - updateConfusionMatrix(prediction); - m_barScale = prediction; + const int vote = aggregatePredictions(true); + updateConfusionMatrix(vote); + m_modality = -1; } break; case OVTK_GDF_End_Of_Session: m_state = 0; // Idle State - m_barScale = 0; // Reinitialize Bar + for (auto& s : m_barScales) { s = 0; } drawBar(); // Redraw break; case OVTK_GDF_Cross_On_Screen: @@ -519,7 +539,7 @@ void CGrazMultiVisualization::setStimulation(const uint64_t stimulation) case OVTK_GDF_Feedback_Continuous: m_state = 2; // Draw Feedback State m_amplitudes.clear(); // Clear the previous amplitudes - m_barScale = 0; // Bar size 0 to 1 + for (auto& s : m_barScales) { s = 0; } break; // Modalities stimulations @@ -530,13 +550,12 @@ void CGrazMultiVisualization::setStimulation(const uint64_t stimulation) if (m_stimulationlist[i] == stimulation) { m_modality = i; - m_barScale = 0.5; + for (auto& s : m_barScales) { s = 0.5; } break; // stop the loop (but not so usefull big number of modality is not so big generally) } } if (m_modality != -1) // If recognize stimulation { - this->getLogManager() << LogLevel_Warning << m_modality << " yeah man\n"; m_state = 3; // Draw Modality State } diff --git a/plugins/processing/simple-visualization/src/box-algorithms/ovpCGrazMultiVisualization.h b/plugins/processing/simple-visualization/src/box-algorithms/ovpCGrazMultiVisualization.h index 34c477011512455080c9b433bb8d3023cd9e8add..ff93c0894c5128fa71c6674098608608225d9605 100644 --- a/plugins/processing/simple-visualization/src/box-algorithms/ovpCGrazMultiVisualization.h +++ b/plugins/processing/simple-visualization/src/box-algorithms/ovpCGrazMultiVisualization.h @@ -58,7 +58,7 @@ namespace OpenViBEPlugins /// <summary> Update the box state according to the stimulation received.</summary> void setStimulation(uint64_t stimulation); - void setMatrixBuffer(const double* pBuffer); + void setMatrixBuffer(const double* buffer); /// <summary> Draw the reference.</summary> /// @@ -89,8 +89,8 @@ namespace OpenViBEPlugins /// <summary> Draw the accuracy on top left.</summary> void drawAccuracy(); - void updateConfusionMatrix(double prediction); - double aggregatePredictions(bool all); + void updateConfusionMatrix(int vote); + int aggregatePredictions(bool all); std::string infos() const; //gboolean resizeCB(GtkWidget* widget, GtkAllocation* allocation, void* data) { return TRUE; } @@ -115,6 +115,7 @@ namespace OpenViBEPlugins m_barW = 0, m_barH = 0, // Height/Width of the bar m_modalityX = 0, m_modalityY = 0, // Center Bottom Position (x, y) of the first modality (next is in x+(x+margin) m_modalityW = 0; // Half Width of the modality : (m_barW + 2 * m_margin) / 2 + std::vector<double> m_barScales; // 0 to 1 for each bar (depends of the feedback if only positive feedback or not) double m_barScale = 0.0; // 0 to 1 bool m_needRedraw = false; // if we need redraw size_t m_state = 0; // Actual state @@ -140,7 +141,7 @@ namespace OpenViBEPlugins //***** Other ***** OpenViBE::CMatrix m_confusion; - std::vector<double> m_amplitudes; // predictions for the current trial + std::vector<std::vector<double>> m_amplitudes; // All amplitudes the current trial uint64_t m_startTime = 0, m_endTime = 0; //Start and end time of the last buffer bool m_twoValueInput = false;