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::plot_pair(input_method const &input, result_dtw const &result, input_info const &info, parameter const ¶ms)
CImg<short> imgA;
CImg<short> imgB;
auto min = std::min(calcul::vtr_min(input.A), calcul::vtr_min(input.B));
auto max = std::max(calcul::vtr_max(input.A), calcul::vtr_max(input.B));
auto minMean = std::min(calcul::vtr_min(calcul::vtr_mean(input.A)), calcul::vtr_min(calcul::vtr_mean(input.B)));
auto maxMean = std::max(calcul::vtr_max(calcul::vtr_mean(input.A)), calcul::vtr_max(calcul::vtr_mean(input.B)));
if (input.A[0].size() > 1) {
imgA = (draw::draw_seriesBars(input.A, min, max).append(draw::draw_seriesMean(input.A, minMean, maxMean), 'y')).rotate(90);
imgB = draw::draw_seriesBars(input.B, min, max).append(draw::draw_seriesMean(input.B, minMean, maxMean), 'y');
imgA = draw::draw_series(input.A, min, max).rotate(90);
imgB = draw::draw_series(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::plot_segmets(input_method const &input, vtr2<result_dtw> const &result, input_info 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::vtr_min(input.A), calcul::vtr_min(input.B));
auto max = std::max(calcul::vtr_max(input.A), calcul::vtr_max(input.B));
if (input.A[0].size() > 1) {
serieA = (draw::draw_seriesBars(input.A, min, max).append(draw::draw_seriesMean(input.A, min, max))).rotate(90);
serieB = draw::draw_seriesBars(input.B, min, max).append(draw::draw_seriesMean(input.B, min, max), 'y');
serieA = draw::draw_series(input.A, min, max);
serieB = draw::draw_series(input.B, min, max);
draw_verticalBreaks(serieA, result, red);
draw_horizontalBreaks(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 (internaly 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 (Kocyan)
///@param[in] params parameters
///@return image
CImg<short> draw::draw_combine(vtr2<node> const &matrix, vtr<result_path> 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 };
draw_matrix(img, matrix, params);
if(params.drawFlex)
draw_flexibleWindow(img, matrix, warpings, purple, params);
draw_warpings(img, warpings, green);
if (params.drawMin)
draw_minimums(img, minims);
return img;
}
///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::draw_matrix(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 (Kocyan)
void draw::draw_minimums(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);
//for (size_t i = 1; i < matrix.size(); i++) { //row//y
// for (size_t j = 1; j < matrix[i].size(); j++)//coll//x
// {
// double current = matrix[i][j].value;
// if (i + 1 < matrix.size() && j + 1 < matrix[i].size() &&
// matrix[i - 1][j].value > current && matrix[i - 1][j + 1].value > current && matrix[i][j - 1].value > current &&
// matrix[i][j + 1].value > current && matrix[i + 1][j - 1].value > current && matrix[i + 1][j].value > current) {
// img.draw_point((int)j - 1, (int)i - 1, red);
// j++;
// }
// }
//}
}
///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::draw_warpings(CImg<short> &img, vtr<result_path> 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::draw_horizontalBreaks(CImg<short> &img, vtr2<result_dtw> const &result, const float *color)
{
int len = 0;
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::draw_verticalBreaks(CImg<short> &img, vtr2<result_dtw> const &result, const float *color)
{
int len = 0;
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::draw_flexibleWindow(CImg<short> &img, vtr2<node> const &matrix, vtr<result_path> 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::dtw_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::draw_series(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::draw_seriesBars(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 serieswith 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::draw_seriesMean(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::vtr_mean(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::dtw_wpassStart(i, w, coef);
const size_t end = calcul::dtw_wpassEnd(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::dtw_wpassStart(i, w, coef);
const size_t end = calcul::dtw_wpassEnd(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(input_info const &info, string path)
if (help::isPath(path)) {
if (help::isFolder(path))
ss << 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) + ")"))
<< ".bmp";
else
ss << path;
}
else
{
size_t nameIdx = path.find_last_of("\\/") + 1;
string nameOnly = path.substr(nameIdx, (int)path.size() - nameIdx);
size_t extIdx = nameOnly.find_last_of(".bmp") + 1;
if (extIdx != string::npos && nameOnly.size() - extIdx == 4)
ss << path;
else
ss << path << ".bmp";
}
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)
{
color c;
double mul = 0;
if (max != 0)
mul = 1275 / abs(max - min); //0-x ... x+1colors
float place = (float)(mul * value);
int under = (int)(place / 255) * 255;
float exact = (float)((place - under >= 1) ? place - under : 255);
if (place < 256) //blue
{
c.r = 0;
c.g = 0;
c.b = place;
}
else if (place < 2 * 256 - 1) // purple
{
c.r = exact;
c.g = 0;
c.b = 255;
}
else if (place < 3 * 256 - 2) // red
{
c.r = 255;
c.g = 0;
c.b = 255 - exact;
}
else if (place < 4 * 256 - 3) //orange //yellow
{
c.r = 255;
c.g = exact;
c.b = 0;
}
else if (place < 5 * 256 - 4) // white-ish
{
c.r = 255;
c.g = 255;
c.b = exact;
}
else //white (big numbers)
{
c.r = 255;
c.g = 255;
c.b = 255;
}
return c;
}