Newer
Older
#include "stdafx.h"
#include "draw.h"
#include <thread>
#include "help.h"
#include "calcul.h"
///Plots pair of the time series mapped on to the no/accumulated distance matrix.
///@param[in] input time series to be plotted
///@param[in] result dtw method results
///@param[in] info informations about the input time series
///@param[in] params parameters
void draw::plotPair(inputMethod const &input, resultMethod const &result, inputInfo const &info, parameter const ¶ms)
CImg<short> imgA;
CImg<short> imgB;
auto min = std::min(calcul::vtrMin(input.A), calcul::vtrMin(input.B));
auto max = std::max(calcul::vtrMax(input.A), calcul::vtrMax(input.B));
auto minMean = std::min(calcul::vtrMin(calcul::vtrMean(input.A)), calcul::vtrMin(calcul::vtrMean(input.B)));
auto maxMean = std::max(calcul::vtrMax(calcul::vtrMean(input.A)), calcul::vtrMax(calcul::vtrMean(input.B)));
if (input.A[0].size() > 1)
{
imgA = (draw::drawSeriesBars(input.A, min, max).append(draw::drawSeriesMean(input.A, minMean, maxMean), 'y')).rotate(90);
imgB = draw::drawSeriesBars(input.B, min, max).append(draw::drawSeriesMean(input.B, minMean, maxMean), 'y');
imgA = draw::drawSeries(input.A, min, max).rotate(90);
imgB = draw::drawSeries(input.B, min, max);
}
CImg<short> m2 = result.matrix_noacc;
m2 = m2.append(imgB, 'y');
CImg<short> all;
all = result.matrix_acc;
all = all.append(imgA);
all.save(generateFileName(info, params.outDraw).c_str());
if(params.drawSleep > 0)
std::this_thread::sleep_for(chrono::milliseconds(params.drawSleep));
///Plots segmented (voting experts) pair of time series mapped on to the no/accumulated distance matrix.
///@param[in] input time series to be plotted
///@param[in] result dtw method results
///@param[in] info informations about the input time series
///@param[in] params parameters
void draw::plotSegmets(inputMethod const &input, vtr2<resultMethod> const &result, inputInfo const &info, parameter const ¶ms)
{
CImg<short> matrix;
for (size_t i = 0; i < result.size(); i++)
{
CImg<short> row;
for (size_t j = 0; j < result[i].size(); j++)
{
row = row.append(result[i][j].matrix_noacc);
}
matrix = matrix.append(row, 'y');
}
const float red[] = { 255, 255,0 };
CImg<short> serieA;
CImg<short> serieB;
auto min = std::min(calcul::vtrMin(input.A), calcul::vtrMin(input.B));
auto max = std::max(calcul::vtrMax(input.A), calcul::vtrMax(input.B));
if (input.A[0].size() > 1)
{
serieA = (draw::drawSeriesBars(input.A, min, max).append(draw::drawSeriesMean(input.A, min, max))).rotate(90);
serieB = draw::drawSeriesBars(input.B, min, max).append(draw::drawSeriesMean(input.B, min, max), 'y');
serieA = draw::drawSeries(input.A, min, max);
serieB = draw::drawSeries(input.B, min, max);
drawVerticalBreaks(serieA, result, red);
drawHorizontalBreaks(serieB, result, red);
serieA.rotate(90);
}
auto all = matrix.append(serieB, 'y');
all = serieA.append(all);
all.save(generateFileName(info, params.outDraw).c_str());
if (params.drawSleep > 0)
std::this_thread::sleep_for(chrono::milliseconds(params.drawSleep));
///Plots minimums (Kocyan), flexible pass (Kocyan), and the distance matrix (internally used by dtw and lcss methods).
///@param[in] matrix distance matrix
///@param[in] warpings warping paths from the dtw or lcss methods
///@param[in] minims local minimums of the no-accumulated distance matrix
///@param[in] params parameters
///@return image
CImg<short> draw::drawCombine(vtr2<node> const &matrix, vtr<resultPath> const &warpings, vtr<coord> const &minims, parameter const ¶ms)
{
CImg<short> img((int)matrix[0].size() - 1, (int)matrix.size() - 1, 1, 3, 0);
const float green[] = { 0, 255, 0 };
const float purple[] = { 200, 0, 200 };
drawMatrix(img, matrix, params);
drawFlexibleWindow(img, matrix, warpings, purple, params);
drawWarpings(img, warpings, green);
drawMinimums(img, minims);
///Plots no/accumulated distance matrix.
///@param[in] img picture to plot into
///@param[in] matrix results from the dtw and lcss method
///@param[in] params parameters
void draw::drawMatrix(CImg<short> &img, vtr2<node> const &matrix, parameter const ¶ms)
double min = getMin(matrix, params);
double max = getMax(matrix, params);
for (size_t i = 1; i < matrix.size(); i++) //row//y
{
for (size_t j = 1; j < matrix[i].size(); j++) //coll//x
{
auto colorRGB = draw::getColor(min, max, matrix[i][j].value);
const float color[] = { colorRGB.r, colorRGB.g, colorRGB.b };
img.draw_point((int)j - 1, (int)i - 1, color);
}
}
}
///Draws minimums (Kocyan) into the no/accumulated distance matrix.
///@param[in] img picture to draw into
///@param[in] minims minimums to be plotted
void draw::drawMinimums(CImg<short> &img, vtr<coord> const &minims)
{
const float red[] = { 255, 0, 0 };
for (size_t i = 0; i < minims.size(); i++)
{
img.draw_point(minims[i].col - 1, minims[i].row - 1, red);
///Draws minimums (Kocyan) into the no/accumulated distance matrix.
///@param[in] img picture to draw into
///@param[in] warpings warping paths from the dtw or lcss methods
///@param[in] color warping paths color
void draw::drawWarpings(CImg<short> &img, vtr<resultPath> const &warpings, const float *color)
{
for (size_t i = 0; i < warpings.size(); i++)
for (size_t j = 0; j < warpings[i].pathCoords.size(); j++)
img.draw_point(warpings[i].pathCoords[j].col - 1, warpings[i].pathCoords[j].row - 1, color);
///Draws horizontal breaks of the segmented (Kocyan - voting experts) time series into the no/accumulated distance matrix.
///@param[in] img picture to draw into
///@param[in] result dtw sub matrix plots results (contains lengths of time series segments)
///@param[in] color horizontal breaks color
void draw::drawHorizontalBreaks(CImg<short> &img, vtr2<resultMethod> const &result, const float *color)
for (int i = 0; i < (int)result[0].size(); i++)
{
len += result[0][i].matrix_noacc.width();
img.draw_line(len - 1, 0, len - 1, (int)(img.height()), color);
///Draws vertical breaks of the segmented (Kocyan - voting experts) time series into the no/accumulated distance matrix.
///@param[in] img picture to draw into
///@param[in] result dtw sub matrix plots results (contains lengths of time series segments)
///@param[in] color vertical breaks color
void draw::drawVerticalBreaks(CImg<short> &img, vtr2<resultMethod> const &result, const float *color)
for (size_t i = 0; i < result.size(); i++)
{
len += result[i][0].matrix_noacc.height();
img.draw_line(len, 0, len, (int)(img.height()), color);
///Draws flexible warping pass (Kocyan) into the no/accumulated distance matrix.
///@param[in] img picture to draw into
///@param[in] matrix no/accumulated distance matrix
///@param[in] warpings warping path from the dtw method
///@param[in] color flexible pass color
///@param[in] params parameters
void draw::drawFlexibleWindow(CImg<short> &img, vtr2<node> const &matrix, vtr<resultPath> const &warpings, const float *color, parameter const ¶ms)
for (size_t i = 0; i < warpings.size(); i++)
{
for (size_t k = 0; k < warpings[i].pathCoords.size(); k++)
{
coord past = kd < (int)warpings[i].pathCoords.size() ? warpings[i].pathCoords[kd] : warpings[i].pathCoords.back();
for (int j = 0; j < (int)matrix[warpings[i].pathCoords[k].row].size(); j++)
{
if (calcul::isFlexiblePass(warpings[i].pathCoords[k].row, j, past, params.fw))
img.draw_point(j, warpings[i].pathCoords[k].row, color);
}
}
}
}
///Draws time series graph.
///@param[in] series time series to be plotted
///@param[in] min minimal value found in the two input time series
///@param[in] max maximal value found in the two input time series
///@return image
CImg<short> draw::drawSeries(vtr2<double> const &series, double min, double max)
int height = (int)ceil(series.size() * 0.1);
CImg<short> img((int)series.size(), height, 1, 3, 0);
double mul = (height - 1) / abs(max + abs(min));
int negativeFix = min < 0 ? (int)ceil(mul * abs(min)) : 0;
const float color[] = { 255, 0, 0 };
for (size_t i = 0; i < series.size() - 1; i++)
int y1 = (int)(mul * series[i][0]);
int y2 = (int)(mul * series[i + 1][0]);
img.draw_line((int)i, y1 + negativeFix, (int)i + 1, y2 + negativeFix, color);
}
return img.mirror('y');
}
///Draws time series in bar format.
///@param[in] series time series to be plotted
///@param[in] min minimal value found in the two input time series
///@param[in] max maximal value found in the two input time series
///@return image
CImg<short> draw::drawSeriesBars(vtr2<double> const &series, double min, double max)
{
int barSize = (int)ceil(series.size() * 0.01);
CImg<short> img((int)series.size(), barSize * (int)series[0].size(), 1, 3, 0);
size_t dims = series[0].size();
for (size_t i = 0; i < dims; i++)
{
for (size_t j = 0; j < series.size(); j++) //col x
{
color mycolor = getColor(min, max, series[j][i]);
const float color[] = { mycolor.r, mycolor.g, mycolor.b };
img.draw_line((int)j, (int)i * barSize, (int)j, ((int)i + 1) * barSize, color);
}
}
return img;
}
///Draws averaged time series (used for plotting time series with multiple dimensions).
///@param[in] series time series to be plotted
///@param[in] min minimal value found in the two input time series
///@param[in] max maximal value found in the two input time series
///@return image
CImg<short> draw::drawSeriesMean(vtr2<double> const &series, double min, double max)
int height = (int)ceil(series.size() * 0.1);
CImg<short> img((int)series.size(), height, 1, 3, 0);
double mul = (height - 1) / abs(max + abs(min)/*- floor(max - min)*/); // -2 ..means are double... so they overlap with bars
auto means = calcul::vtrMean(series);
int negativeFix = min < 0 ? (int)ceil(mul * abs(min)) : 0;
const float color[] = { 255, 0, 0 };
for (size_t i = 0; i < series.size() - 1; i++)
{
int y1 = (int)floor(mul * means[i]);
int y2 = (int)floor(mul * means[i + 1]);
img.draw_line((int)i, y1 + negativeFix, (int)i + 1, y2 + negativeFix, color);
}
return img.mirror('y');
}
///Finds maximal value in the distance matrix.
///@param[in] matrix distance matrix
///@param[in] params parameters
///@return maximal value in the distance matrix
double draw::getMax(vtr2<node> const &matrix, parameter const ¶ms)
{
int lenA = (int)matrix.size() - 1;
int lenB = (int)matrix[0].size() - 1;
double max = constant::MIN_double;
const int w = (int)(lenB * params.w);
double coef = lenB / static_cast<double>(lenA);
for (int i = 1; i < lenA + 1; i++) // Iterating over rows
{
const size_t start = calcul::dtwPassStart(i, w, coef);
const size_t end = calcul::dtwPassEnd(i, w, coef, lenB);
for (size_t j = start; j < end; j++)
{
if (matrix[i][j].value > max)
max = matrix[i][j].value;
}
}
return max;
}
///Finds minimal value in the distance matrix.
///@param[in] matrix distance matrix
///@param[in] params parameters
///@return maximal value in the distance matrix
double draw::getMin(vtr2<node> const &matrix, parameter const ¶ms)
{
int lenA = (int)matrix.size() - 1;
int lenB = (int)matrix[0].size() - 1;
double min = constant::MAX_double;
const int w = (int)(lenB * params.w);
double coef = lenB / static_cast<double>(lenA);
for (int i = 1; i < lenA + 1; i++) // Iterating over rows
{
const size_t start = calcul::dtwPassStart(i, w, coef);
const size_t end = calcul::dtwPassEnd(i, w, coef, lenB);
for (size_t j = start; j < end; j++)
{
if (matrix[i][j].value < min)
min = matrix[i][j].value;
}
}
return min;
}
///Generates file name for output image file (Only bmp format is supported).
///@param[in] info informations about the input time series
///@param[in] path output file path (folder or specific file name)
///@return file name (path + name)
string draw::generateFileName(inputInfo const &info, string path)
if (help::isPath(path))
{
<< info.nameA
<< ((info.nameA == to_string(info.idxA)) ? "" : ("(" + to_string(info.idxA + 1) + ")"))
<< info.nameB
<< ((info.nameB == to_string(info.idxB)) ? "" : ("(" + to_string(info.idxB + 1) + ")"))
}
else
{
size_t nameIdx = path.find_last_of("\\/") + 1;
string nameOnly = path.substr(nameIdx, (int)path.size() - nameIdx);
size_t extIdx = nameOnly.rfind(".bmp");
if (extIdx != string::npos && nameOnly.size() - (extIdx) == 4)
{
}
return ss.str();
}
///Generates distance matrix cell color.
///@param[in] min minimal value in the distance matrix
///@param[in] max maximal value in the distance matrix
///@param[in] value specific distance cell value
///@return color
color draw::getColor(double min, double max, double value)
{
double mul = (max != 0) ? 1275 / abs(max - min) : 0; //0-x ... x+1colors
float lvl = (float)(mul * value);
int lvlBorder = (int)(lvl / 255) * 255;
float exact = lvl - lvlBorder;
exact = exact > 0 ? exact : 255;
color c;
if (lvl <= 255) //black - blue
else if (lvl <= 2 * 255) //blue - purple
{
c.r = exact;
c.g = 0;
c.b = 255;
}
else if (lvl <= 3 * 255) //purple - red
{
c.r = 255;
c.g = 0;
c.b = 255 - exact;
}
else if (lvl <= 4 * 255) //red - orange - yellow
{
c.r = 255;
c.g = exact;
c.b = 0;
}
else if (lvl <= 5 * 255) //yellow - white
{
c.r = 255;
c.g = 255;
c.b = exact;
}
{
c.r = 255;
c.g = 255;
c.b = 255;
}
return c;
}