Skip to content
Snippets Groups Projects
draw.cpp 13.5 KiB
Newer Older
  • Learn to ignore specific revisions
  • #include "stdafx.h"
    #include "draw.h"
    
    #include "help.h"
    #include "calcul.h"
    
    
    using namespace std;
    
    
    ///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 &params)
    
    	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.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 &params)
    
    {
    	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 &params)
    
    {
    	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);
    
    
    	if (params.drawMin)
    
    ///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 &params)
    
    	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 &params)
    
    	for (size_t i = 0; i < warpings.size(); i++)
    	{
    		for (size_t k = 0; k < warpings[i].pathCoords.size(); k++) 
    		{
    
    			int kd = (int)k + params.fd;
    
    			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
    
    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);
    
    ///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
    
    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
    
    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);
    
    ///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 &params)
    {
    	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 &params)
    {
    	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)
    
    string draw::generateFileName(inputInfo const &info, string path)
    
    	stringstream ss;
    
    	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) + ")"))
    
    	}
    	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) 
    		{
    
    			ss << path << ".bmp";
    
    ///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;
    	}
    
    	else						//white (peaks)
    
    	{
    		c.r = 255;
    		c.g = 255;
    		c.b = 255;
    	}
    
    	return c;
    }