#pragma once
#include <cmath>
#include <vector>
#include <string>
#include <sstream>
#include <fstream>
#include <iostream>
#include <algorithm>
template <typename T>
class histogram
{
private:
T mMinValue, mMaxValue;
size_t mValueCount, mErrorValueCount;
T mBucketSize;
std::vector<size_t> mvBuckets;
size_t mBucketIdRange[2];
size_t mBucketIdMax;
public:
histogram(size_t buckets, T minValue = 0.0, T maxValue = 1.0)
{
this->init(buckets, minValue, maxValue);
}
void init(size_t buckets, T minValue, T maxValue, size_t value = 0)
{
this->mMinValue = minValue;
this->mMaxValue = maxValue;
this->mValueCount = 0;
this->mErrorValueCount = 0;
this->mBucketIdRange[0] = std::string::npos;
this->mBucketIdRange[1] = 0;
this->resize(buckets);
this->mvBuckets.resize(buckets, value);
}
T getBucketSize() const { return mBucketSize; }
size_t getBucketIdMin() const { return mBucketIdRange[0]; }
size_t getBucketIdMax() const { return mBucketIdRange[1]; }
size_t getBucketValue(size_t bucketId) const { return mvBuckets[bucketId]; }
size_t size() const { return mvBuckets.size(); }
T getMinValue() const { return this->mMinValue; }
T getMaxValue() const { return this->mMaxValue; }
T getBucketStep() const { return (this->mMaxValue - this->mMinValue) / this->mvBuckets.size(); }
void clear(size_t value = 0)
{
this->mvBuckets.resize(mvBuckets.size(), value);
}
void resize(size_t buckets)
{
this->mBucketSize = (this->mMaxValue - this->mMinValue) / buckets;
this->mvBuckets.resize(buckets);
}
size_t valueBucketId(T value) const
{
if (value < this->mMinValue || value > this->mMaxValue)
return std::string::npos;
size_t bucketId = size_t(double(value) / this->mBucketSize);
if (bucketId == this->mvBuckets.size())
{
bucketId--;
}
return bucketId;
}
void inc(T value, size_t amount = 1)
{
size_t bucketId = valueBucketId(value);
if (bucketId != std::string::npos)
{
this->mvBuckets[bucketId] += amount;
this->mValueCount += amount;
this->mBucketIdRange[0] = std::min(this->mBucketIdRange[0], bucketId);
this->mBucketIdRange[1] = std::max(this->mBucketIdRange[1], bucketId);
}
else
{
mErrorValueCount += amount;
}
}
std::string toPython(std::string fileName, size_t numPixels, T meanValue, T maxValue, T minValue, T weightedMedian, T firstWeightedQuartile, T thirdWeightedQuartile, const bool optionLog, const bool includeValues, const float yMax) const
{
std::stringstream ss;
T bucketStep = getBucketStep();
ss << "import matplotlib.pyplot as plt\n";
ss << "import os\n";
ss << "import sys\n";
ss << "import numpy as np\n";
ss << "from matplotlib.ticker import (MultipleLocator)\n\n";
ss << "dimensions = (25, 15) # centimeters\n\n";
ss << "lineColor = 'blue'\n";
ss << "fillColor = 'lightblue'\n";
ss << "meanLineColor = 'red'\n";
ss << "weightedMedianLineColor = 'gray'\n";
ss << "quartileLineColor = 'purple'\n";
ss << "fontSize = 14\n";
ss << "numPixels = " << numPixels << "\n\n";
ss << "meanValue = " << meanValue << "\n";
ss << "maxValue = " << maxValue << "\n";
ss << "minValue = " << minValue << "\n\n";
ss << "weightedMedianValue = " << weightedMedian << "\n\n";
ss << "firstWeightedQuartileValue = " << firstWeightedQuartile << "\n\n";
ss << "thirdWeightedQuartileValue = " << thirdWeightedQuartile << "\n\n";
ss << "dataX = [";
for (size_t bucketId = 0; bucketId < this->mvBuckets.size(); bucketId++)
{
ss << (bucketId > 0 ? ", " : "");
ss << bucketStep * bucketId + 0.5 * bucketStep;
}
ss << "]\n\n";
ss << "dataFLIP = [";
for (size_t bucketId = 0; bucketId < this->mvBuckets.size(); bucketId++)
{
ss << (bucketId > 0 ? ", " : "");
ss << this->mvBuckets[bucketId];
}
ss << "]\n\n";
ss << "bucketStep = " << bucketStep << "\n";
ss << "weightedDataFLIP = np.empty(" << this->mvBuckets.size() << ")\n";
ss << "moments = np.empty(" << this->mvBuckets.size() << ")\n";
ss << "for i in range(" << this->mvBuckets.size() << ") :\n";
ss << "\tweight = (i + 0.5) * bucketStep\n";
ss << "\tweightedDataFLIP[i] = dataFLIP[i] * weight\n";
ss << "weightedDataFLIP /= (numPixels /(1024 * 1024)) # normalized with the number of megapixels in the image\n\n";
if (optionLog)
{
ss << "for i in range(" << this->mvBuckets.size() << ") :\n";
ss << "\tif weightedDataFLIP[i] > 0 :\n";
ss << "\t\tweightedDataFLIP[i] = np.log10(weightedDataFLIP[i]) # avoid log of zero\n\n";
}
if (yMax != 0.0f)
{
ss << "maxY = " << yMax << "\n\n";
}
else
{
ss << "maxY = max(weightedDataFLIP)\n\n";
}
ss << "sumWeightedDataFLIP = sum(weightedDataFLIP)\n\n";
ss << "font = { 'family' : 'serif', 'style' : 'normal', 'weight' : 'normal', 'size' : fontSize }\n";
ss << "lineHeight = fontSize / (dimensions[1] * 15)\n";
ss << "plt.rc('font', **font)\n";
ss << "fig = plt.figure()\n";
ss << "axes = plt.axes()\n";
ss << "axes.xaxis.set_minor_locator(MultipleLocator(0.1))\n";
ss << "axes.xaxis.set_major_locator(MultipleLocator(0.2))\n\n";
ss << "fig.set_size_inches(dimensions[0] / 2.54, dimensions[1] / 2.54)\n";
if(optionLog)
ss << "axes.set(title = 'Weighted \\uA7FBLIP Histogram', xlabel = '\\uA7FBLIP error', ylabel = 'log(weighted \\uA7FBLIP sum per megapixel)')\n\n";
else
ss << "axes.set(title = 'Weighted \\uA7FBLIP Histogram', xlabel = '\\uA7FBLIP error', ylabel = 'Weighted \\uA7FBLIP sum per megapixel')\n\n";
ss << "plt.bar(dataX, weightedDataFLIP, width = " << bucketStep << ", color = fillColor, edgecolor = lineColor)\n\n";
if (includeValues)
{
ss << "plt.text(0.99, 1.0 - 1 * lineHeight, 'Mean: ' + str(f'{meanValue:.4f}'), ha = 'right', fontsize = fontSize, transform = axes.transAxes, color=meanLineColor)\n\n";
ss << "plt.text(0.99, 1.0 - 2 * lineHeight, 'Weighted median: ' + str(f'{weightedMedianValue:.4f}'), ha = 'right', fontsize = fontSize, transform = axes.transAxes, color=weightedMedianLineColor)\n\n";
ss << "plt.text(0.99, 1.0 - 3 * lineHeight, '1st weighted quartile: ' + str(f'{firstWeightedQuartileValue:.4f}'), ha = 'right', fontsize = fontSize, transform = axes.transAxes, color=quartileLineColor)\n\n";
ss << "plt.text(0.99, 1.0 - 4 * lineHeight, '3rd weighted quartile: ' + str(f'{thirdWeightedQuartileValue:.4f}'), ha = 'right', fontsize = fontSize, transform = axes.transAxes, color=quartileLineColor)\n\n";
ss << "plt.text(0.99, 1.0 - 5 * lineHeight, 'Min: ' + str(f'{minValue:.4f}'), ha = 'right', fontsize = fontSize, transform = axes.transAxes)\n";
ss << "plt.text(0.99, 1.0 - 6 * lineHeight, 'Max: ' + str(f'{maxValue:.4f}'), ha = 'right', fontsize = fontSize, transform = axes.transAxes)\n";
}
ss << "axes.set_xlim(0.0, 1.0)\n";
ss << "axes.set_ylim(0.0, maxY * 1.05)\n";
if (includeValues)
{
ss << "axes.axvline(x = meanValue, color = meanLineColor, linewidth = 1.5)\n\n";
ss << "axes.axvline(x = weightedMedianValue, color = weightedMedianLineColor, linewidth = 1.5)\n\n";
ss << "axes.axvline(x = firstWeightedQuartileValue, color = quartileLineColor, linewidth = 1.5)\n\n";
ss << "axes.axvline(x = thirdWeightedQuartileValue, color = quartileLineColor, linewidth = 1.5)\n\n";
ss << "axes.axvline(x = minValue, color='black', linestyle = ':', linewidth = 1.5)\n\n";
ss << "axes.axvline(x = maxValue, color='black', linestyle = ':', linewidth = 1.5)\n\n";
}
ss << "plt.savefig(\"" << fileName.substr(0, fileName.size() - 3) << ".pdf\")";
ss << std::endl;
return ss.str();
}
};
template <typename T>
class pooling
{
private:
size_t mValueCount;
T mValueSum;
T mSquareValueSum;
T mMinValue;
T mMaxValue;
uint32_t mMinCoord[2];
uint32_t mMaxCoord[2];
histogram<T> mHistogram = histogram<T>(100);
bool mDataSorted = false;
std::vector<T> mvData;
public:
pooling() { clear(); }
pooling(size_t buckets) { mHistogram.resize(buckets); clear(); }
histogram<T>& getHistogram() { return mHistogram; }
T getMinValue(void) const { return mMinValue; }
T getMaxValue(void) const { return mMaxValue; }
T getMean(void) const { return mValueSum / mValueCount; }
double getWeightedPercentile(const double percent) const
{
double weight;
double weightedValue;
double bucketStep = mHistogram.getBucketStep();
double sumWeightedDataValue = 0.0;
for (size_t bucketId = 0; bucketId < mHistogram.size(); bucketId++)
{
weight = (bucketId + 0.5) * bucketStep;
weightedValue = mHistogram.getBucketValue(bucketId) * weight;
sumWeightedDataValue += weightedValue;
}
double sum = 0;
size_t weightedMedianIndex = 0;
for (size_t bucketId = 0; bucketId < mHistogram.size(); bucketId++)
{
weight = (bucketId + 0.5) * bucketStep;
weightedValue = mHistogram.getBucketValue(bucketId) * weight;
weightedMedianIndex = bucketId;
if (sum + weightedValue > percent * sumWeightedDataValue)
break;
sum += weightedValue;
}
weight = (weightedMedianIndex + 0.5) * bucketStep;
weightedValue = mHistogram.getBucketValue(weightedMedianIndex) * weight;
double discrepancy = percent * sumWeightedDataValue - sum;
double linearWeight = discrepancy / weightedValue; double percentile = (weightedMedianIndex + linearWeight) * bucketStep;
return percentile;
}
T getPercentile(const float percent, const bool bWeighted = false)
{
if (!mDataSorted)
{
std::sort(mvData.begin(), mvData.end());
mDataSorted = true;
}
T percentile = T(0);
if (bWeighted)
{
T runningSum = T(0);
for (size_t i = 0; i < mvData.size(); i++)
{
runningSum += mvData[i];
if (runningSum > percent * mValueSum)
{
percentile = mvData[i];
break;
}
}
}
else
{
percentile = mvData[size_t(std::ceil(mvData.size() * percent))];
}
return percentile;
}
void clear()
{
mValueCount = 0;
mValueSum = T(0);
mSquareValueSum = T(0);
mMinValue = std::numeric_limits<T>::max();
mMaxValue = std::numeric_limits<T>::min();
mvData.clear();
mHistogram.clear();
}
void update(uint32_t xcoord, uint32_t ycoord, T value)
{
mValueCount++;
mValueSum += value;
mSquareValueSum += (value * value);
mHistogram.inc(value);
mvData.push_back(value);
mDataSorted = false;
if (value < mMinValue)
{
mMinValue = value;
mMinCoord[0] = xcoord;
mMinCoord[1] = ycoord;
}
if (value > mMaxValue)
{
mMaxValue = value;
mMaxCoord[0] = xcoord;
mMaxCoord[1] = ycoord;
}
}
void save(const std::string& fileName, size_t imgWidth, size_t imgHeight, const bool optionLog, const std::string referenceFileName, const std::string testFileName, const bool includeValues, const float yMax)
{
std::ofstream file;
std::string pyFileName = fileName;
size_t area = size_t(imgWidth) * size_t(imgHeight);
std::ofstream pythonHistogramFile;
pythonHistogramFile.open(pyFileName);
pythonHistogramFile << mHistogram.toPython(pyFileName, area, getMean(), getMaxValue(), getMinValue(), getPercentile(0.5f, true), getPercentile(0.25f, true), getPercentile(0.75f, true), optionLog, includeValues, yMax);
pythonHistogramFile.close();
}
};