Initial commit: Final state of the master project

This commit is contained in:
2017-09-16 09:41:37 +02:00
commit 696180d43b
832 changed files with 169717 additions and 0 deletions

View File

@@ -0,0 +1,18 @@
#pragma once
#include <map>
#include <vector>
template<typename T, typename Comparer>
class BaseQuantizer
{
public:
virtual std::map<T, T, Comparer>* QuantizeMaterials(std::vector<T> materials) const = 0;
virtual std::string GetQuantizerDescriptor() const = 0;
};
template<typename T>
class QuickQuantizer
{
public:
virtual T Quantize(const T& material) const = 0;
};

View File

@@ -0,0 +1,48 @@
#pragma once
#include <map>
#include <vector>
#include "BaseQuantizer.h"
#include "ColorQuantizer/BaseColorQuantizer.h"
#include "../ColorAndNormal.h"
#include "../ColorAndOpacity.h"
#include "../ColorAndNormalAndValue.h"
#include "../../../core/CollectionHelper.h"
// Basically quantizes the colors using the given color quantizer and leaves the values as-is (assuming they are quantized enough already)
// Deleting this object also deletes the associated color quantizer
template<typename T, typename TCompare>
class ColorAndValueQuantizer : public BaseQuantizer<ColorAndValue<T>, ColorAndValueCompare<T, TCompare>>
{
private:
BaseColorQuantizer* mColorQuantizer;
public:
ColorAndValueQuantizer(BaseColorQuantizer* colorQuantizer)
: mColorQuantizer(colorQuantizer)
{}
~ColorAndValueQuantizer()
{
delete mColorQuantizer;
}
virtual std::map<ColorAndValue<T>, ColorAndValue<T>, ColorAndValueCompare<T, TCompare>>* QuantizeMaterials(std::vector<ColorAndValue<T>> materials) const
{
std::vector<Color> colors(materials.size());
for (size_t i = 0; i < materials.size(); i++) colors[i] = materials[i].GetFirst();
CollectionHelper::Unique(colors, ColorCompare());
auto quantizedColors = mColorQuantizer->QuantizeMaterials(colors);
std::map<ColorAndValue<T>, ColorAndValue<T>, ColorAndValueCompare<T, TCompare>>* res = new std::map<ColorAndValue<T>, ColorAndValue<T>, ColorAndValueCompare<T, TCompare>>();
for (auto mat : materials)
{
ColorAndValue<T> replacer(quantizedColors->at(mat.GetFirst()), mat.GetSecond());
res->insert(std::make_pair(mat, replacer));
}
delete quantizedColors;
return res;
}
virtual std::string GetQuantizerDescriptor() const { return mColorQuantizer->GetQuantizerDescriptor(); }
};
typedef ColorAndValueQuantizer<ColorAndNormal, ColorAndNormalCompare> ColorAndNormalQuantizer;
typedef ColorAndValueQuantizer<ColorAndOpacity, ColorAndOpacityCompare> ColorAndOpacityQuantizer;
typedef ColorAndValueQuantizer<ColorAndNormalAndValue, ColorAndNormalAndValueCompare> ColorAndNormalAndValueQuantizer;

View File

@@ -0,0 +1,53 @@
#include "BaseColorQuantizer.h"
#include "../../../../inc/tbb/parallel_for.h"
std::map<Color, Color, ColorCompare>* BaseColorQuantizer::QuantizeMaterials(std::vector<Color> materials) const
{
// Convert the "Colors" to u8vec3
std::vector<glm::u8vec3> colors(materials.size());
tbb::parallel_for(size_t(0), materials.size(), [&](size_t i)
{
colors[i] = materials[i].GetColor();
});
// Quantize the colors
auto quantizedColors = QuantizeColors(colors);
//PrintQuantizationStatistics(quantizedColors);
// Find out what the quantized materials are
std::map<Color, Color, ColorCompare>* quantizedMaterials = new std::map<Color, Color, ColorCompare>();
for (auto quantizedColor : *quantizedColors)
{
quantizedMaterials->insert(std::pair<Color, Color>(Color(quantizedColor.first), Color(quantizedColor.second)));
glm::vec3 error = glm::abs(glm::vec3(quantizedColor.first) - glm::vec3(quantizedColor.second));
}
delete quantizedColors;
return quantizedMaterials;
}
void BaseColorQuantizer::PrintQuantizationStatistics(std::map<glm::u8vec3, glm::u8vec3, u8vec3comparer>* quantizedColors) const
{
glm::vec3 sumError(0);
unsigned maxError = 0;
glm::uvec3 maxErrorValue(0);
float sumDeltaE = 0;
float maxDeltaE = 0;
for (auto quantizedColor : *quantizedColors)
{
glm::vec3 error = glm::abs(glm::vec3(quantizedColor.first) - glm::vec3(quantizedColor.second));
sumError += error;
unsigned errorU = error.r + error.g + error.b;
if (errorU > maxError)
{
maxError = errorU;
maxErrorValue = error;
}
float deltaE = ColorHelper::GetDeltaEFromRGB(quantizedColor.first, quantizedColor.second);
if (deltaE == deltaE) // Only sum if it is not NaN...
sumDeltaE += deltaE;
if (deltaE > maxDeltaE) maxDeltaE = deltaE;
}
glm::vec3 meanError = sumError / float(quantizedColors->size());
float meanDeltaE = sumDeltaE / float(quantizedColors->size());
printf("Mean errors: (%f, %f, %f), Max errors: (%u, %u, %u), Mean delta-E: %f, Max delta-E: %f\n", meanError.x, meanError.y, meanError.z, maxErrorValue.x, maxErrorValue.y, maxErrorValue.z, meanDeltaE, maxDeltaE);
}

View File

@@ -0,0 +1,13 @@
#pragma once
#include "../BaseQuantizer.h"
#include "../../Color.h"
class BaseColorQuantizer : public BaseQuantizer<Color, ColorCompare>
{
public:
virtual ~BaseColorQuantizer() {}
void PrintQuantizationStatistics(std::map<glm::u8vec3, glm::u8vec3, u8vec3comparer>* quantizedColors) const;
std::map<Color, Color, ColorCompare>* QuantizeMaterials(std::vector<Color> materials) const override;
virtual std::map<glm::u8vec3, glm::u8vec3, u8vec3comparer>* QuantizeColors(std::vector<glm::u8vec3> colors) const = 0;
};

View File

@@ -0,0 +1,28 @@
#include "ColorBitCutter.h"
ColorBitCutter::ColorBitCutter(unsigned8 bitCount)
{
assert(bitCount <= 8);
mBitCount = bitCount;
}
ColorBitCutter::~ColorBitCutter() {}
std::map<glm::u8vec3, glm::u8vec3, u8vec3comparer>* ColorBitCutter::QuantizeColors(std::vector<glm::u8vec3> colors) const
{
std::map<glm::u8vec3, glm::u8vec3, u8vec3comparer>* res = new std::map<glm::u8vec3, glm::u8vec3, u8vec3comparer>();
unsigned8 mask = BitHelper::GetHSMask<unsigned8>(0, mBitCount);
for (glm::u8vec3 color : colors)
{
glm::u8vec3 replacementColor = glm::u8vec3(
color.x & mask,
color.y & mask,
color.z & mask);
res->insert(std::pair<glm::u8vec3, glm::u8vec3>(color, replacementColor));
}
return res;
}
std::string ColorBitCutter::GetQuantizerDescriptor() const
{
return "b" + std::to_string(mBitCount);
}

View File

@@ -0,0 +1,19 @@
#pragma once
#include <map>
#include "../../../../inc/glm/common.hpp"
#include "../../../../core/Comparers.h"
#include "BaseColorQuantizer.h"
class ColorBitCutter : public BaseColorQuantizer
{
public:
ColorBitCutter(unsigned8 bitCount);
~ColorBitCutter() override;
std::map<glm::u8vec3, glm::u8vec3, u8vec3comparer>* QuantizeColors(std::vector<glm::u8vec3> colors) const override;
std::string GetQuantizerDescriptor() const override;
private:
unsigned8 mBitCount;
};

View File

@@ -0,0 +1,96 @@
#include "MaxErrorClusterer.h"
#include <vector>
#include <string>
#include <forward_list>
#include "../../../../inc/glm/geometric.hpp"
// Quantize the colors in CIELAB-space clusters that have a maximum size maxDistance.
MaxErrorClusterer::MaxErrorClusterer(float maxDistance)
{
mMaxDistance = maxDistance;
}
MaxErrorClusterer::~MaxErrorClusterer() { }
std::map<glm::u8vec3, glm::u8vec3, u8vec3comparer>* MaxErrorClusterer::QuantizeColors(std::vector<glm::u8vec3> colors) const
{
// Transform all colors to CIELAB space:
ListNode<Color>* firstUnclusteredColor = new ListNode<Color>();
ListNode<Color>* cur = firstUnclusteredColor;
for (auto rgb : colors)
{
Color color(rgb);
cur->color = color;
cur->next = new ListNode<Color>();
cur = cur->next;
}
auto res = new std::map<glm::u8vec3, glm::u8vec3, u8vec3comparer>();
while (firstUnclusteredColor != NULL)
{
Color curColor = firstUnclusteredColor->color;
// Pop the head of the "list"
{
auto nextUnclusteredColor = firstUnclusteredColor->next;
delete firstUnclusteredColor;
firstUnclusteredColor = nextUnclusteredColor;
}
// Create a new cluster for this unclustered color
auto cluster = std::vector<Color>();
cluster.push_back(curColor);
// Loop through the unclustered colors
if (firstUnclusteredColor != NULL)
{
cur = firstUnclusteredColor;
ListNode<Color>* last = NULL;
ListNode<Color>* next = cur->next;
while (cur != NULL)
{
next = cur->next;
if (ColorHelper::GetDeltaEFromLAB(cur->color.lab, curColor.lab) <= mMaxDistance)
{
// If we are close enough to the last color, add the current color to the cluster
cluster.push_back(cur->color);
// Delete the current node from the list of unclustered nodes
delete cur;
// If this isn't the first node in the list, make sure the last node points to this node
if (last != NULL)
{
last->next = next;
cur = last;
}
else
{ // If this is the first node, we just deleted it, so replace the current first node by the next node
firstUnclusteredColor = next;
}
}
else
{
// If this node wasn't add to the cluster, the last node should be updated before advancing
last = cur;
}
// Advance one position
cur = next;
}
}
// Calculate the center of the current cluster
glm::vec3 clusterSum(0);
glm::u8vec3 clusterReplacementColor = curColor.rgb;
for (auto color : cluster)
res->insert(std::pair<glm::u8vec3, glm::u8vec3>(color.rgb, clusterReplacementColor));
}
return res;
}
std::string MaxErrorClusterer::GetQuantizerDescriptor() const
{
char str[7];
sprintf(str, "%.2f", mMaxDistance);
auto formattedMaxDistance = std::string(str);
return "de" + formattedMaxDistance;
}

View File

@@ -0,0 +1,43 @@
#pragma once
#include "BaseColorQuantizer.h"
#include "../../../../core/ColorHelper.h"
class MaxErrorClusterer :
public BaseColorQuantizer
{
public:
// Quantize the colors in CIELAB-space clusters that have a maximum size maxDistance.
// This is done using a greedy clustering algorithm that adds all colors to the cluster of a color if they're less than half the distance away.
MaxErrorClusterer(float maxDistance);
~MaxErrorClusterer() override;
std::map<glm::u8vec3, glm::u8vec3, u8vec3comparer>* QuantizeColors(std::vector<glm::u8vec3> colors) const override;
std::string GetQuantizerDescriptor() const override;
protected:
float mMaxDistance;
private:
template<typename T>
struct ListNode
{
public:
T color;
ListNode<T>* next = NULL;
ListNode(T value) { color = value; }
ListNode() { }
};
struct Color
{
glm::u8vec3 rgb;
glm::vec3 lab;
Color(glm::u8vec3 rgb)
{
this->rgb = rgb;
lab = ColorHelper::RGBtoLAB(rgb);
}
Color() : Color(glm::u8vec3(0)) {}
};
};

View File

@@ -0,0 +1,20 @@
#include "XiangCIELABClusterer.h"
#include "../../../../core/ColorHelper.h"
#include <string>
XiangCIELABClusterer::XiangCIELABClusterer(unsigned quantizedColorCount) : XiangClusterer(quantizedColorCount) {}
XiangCIELABClusterer::~XiangCIELABClusterer() {}
glm::vec3 XiangCIELABClusterer::ScaleColor(glm::u8vec3 color) const
{
return ColorHelper::RGBtoLAB(color);
}
glm::u8vec3 XiangCIELABClusterer::ScaleBackColor(glm::vec3 color) const
{
return ColorHelper::LABtoRGB(color);
}
std::string XiangCIELABClusterer::GetQuantizerDescriptor() const
{
return "lab" + std::to_string(mQuantizedColorCount);
}

View File

@@ -0,0 +1,17 @@
#pragma once
#include "XiangClusterer.h"
class XiangCIELABClusterer :
public XiangClusterer
{
public:
// Quantize the colors to the given amount of colors, using the default RGB scaling, 0.5:1.0:0.25.
XiangCIELABClusterer(unsigned quantizedColorCount);
virtual ~XiangCIELABClusterer() override;
std::string GetQuantizerDescriptor() const override;
protected:
glm::vec3 ScaleColor(glm::u8vec3 color) const override;
glm::u8vec3 ScaleBackColor(glm::vec3 color) const override;
};

View File

@@ -0,0 +1,104 @@
#include "XiangClusterer.h"
#include <vector>
#include <string>
#include "../../../../inc/glm/geometric.hpp"
XiangClusterer::XiangClusterer(unsigned quantizedColorCount, glm::vec3 colorScale)
{
mQuantizedColorCount = quantizedColorCount;
mColorScale = colorScale;
mInvColorScale = 1.0f / mColorScale;
}
XiangClusterer::XiangClusterer(unsigned quantizedColorCount) : XiangClusterer(quantizedColorCount, glm::vec3(0.5, 1.0, 0.25)) {}
XiangClusterer::~XiangClusterer()
{
}
std::map<glm::u8vec3, glm::u8vec3, u8vec3comparer>* XiangClusterer::QuantizeColors(std::vector<glm::u8vec3> colors) const
{
// Set the initial cluster head to the first color in the set
std::vector<glm::vec3> h(mQuantizedColorCount);
h[0] = *colors.begin();
// Initialize all colors to be in the same cluster (cluster 0)
std::vector<ClusterColor*> B(colors.size());
int i = 0;
for (auto color : colors)
{
ClusterColor* cur = new ClusterColor();
cur->originalColor = color;
cur->color = ScaleColor(color);
cur->centerDistance = glm::distance(glm::vec3(color), h[0]);
cur->clusterID = 0;
B[i++] = cur;
}
ClusterColor* max = B[0];
// Start the algorithm: in each iteration create a new cluster
for (unsigned x = 1; x < mQuantizedColorCount; x++)
{
// Find the point with the maximum distance to the cluster center
for (auto color : B)
if (color->centerDistance > max->centerDistance)
max = color;
// Define this point as the center of a new cluster
max->clusterID = x;
h[x] = max->color;
// Move all colors that are closer to this new cluster than to their current cluster center over to the new cluster.
for (auto color : B)
{
float newDistance = glm::distance(color->color, h[x]);
if (newDistance < color->centerDistance)
{
color->clusterID = x;
color->centerDistance = newDistance;
}
}
}
// Find all cluster centers:
std::vector<glm::vec3> clusterSums(mQuantizedColorCount);
std::vector<float> invClusterCounts(mQuantizedColorCount);
for (auto color : B)
{
clusterSums[color->clusterID] += color->color;
invClusterCounts[color->clusterID]++;
}
// Inverse the cluster counts:
for (size_t i = 0; i < invClusterCounts.size(); i++) invClusterCounts[i] = 1.0f / invClusterCounts[i];
// Calculate the color replacers
std::vector<glm::u8vec3> clusterReplacers(mQuantizedColorCount);
for (unsigned i = 0; i < mQuantizedColorCount; i++)
clusterReplacers[i] = ScaleBackColor(clusterSums[i] * invClusterCounts[i]);
// Now that we have the clusters, build the replacement map
auto replacements = new std::map<glm::u8vec3, glm::u8vec3, u8vec3comparer>();
for (auto color : B)
replacements->insert(std::pair<glm::u8vec3, glm::u8vec3>(color->originalColor, clusterReplacers[color->clusterID]));
for (auto color : B)
delete color;
return replacements;
}
glm::vec3 XiangClusterer::ScaleColor(glm::u8vec3 color) const
{
return glm::vec3(color) * mColorScale;
}
glm::u8vec3 XiangClusterer::ScaleBackColor(glm::vec3 color) const
{
return glm::u8vec3(glm::round(color * mInvColorScale));
}
std::string XiangClusterer::GetQuantizerDescriptor() const
{
return std::to_string(mQuantizedColorCount);
}

View File

@@ -0,0 +1,32 @@
#pragma once
#include "BaseColorQuantizer.h"
class XiangClusterer :
public BaseColorQuantizer
{
public:
XiangClusterer(unsigned quantizedColorCount, glm::vec3 colorScale);
// Quantize the colors to the given amount of colors, using the default RGB scaling, 0.5:1.0:0.25.
XiangClusterer(unsigned quantizedColorCount);
~XiangClusterer() override;
std::map<glm::u8vec3, glm::u8vec3, u8vec3comparer>* QuantizeColors(std::vector<glm::u8vec3> colors) const override;
std::string GetQuantizerDescriptor() const override;
protected:
virtual glm::vec3 ScaleColor(glm::u8vec3 color) const;
virtual glm::u8vec3 ScaleBackColor(glm::vec3 color) const;
unsigned mQuantizedColorCount;
private:
glm::vec3 mColorScale;
glm::vec3 mInvColorScale;
struct ClusterColor
{
public:
glm::u8vec3 originalColor;
glm::vec3 color;
float centerDistance;
int clusterID;
};
};

View File

@@ -0,0 +1,52 @@
#pragma once
#include <map>
#include <vector>
#include "NormalQuantizer.h"
#include "../../../core/BitHelper.h"
NormalQuantizer::NormalQuantizer(unsigned8 bits): mBits(bits)
{
// Bits must be an even number, as there are two channels for an ONV
assert(bits % 2 == 0);
// Precalculate the channel mask
unsigned32 channelStartHS = 32 - SmallNormal::BITS / 2;
mChannelMask = BitHelper::GetHSMask<unsigned32>(channelStartHS, channelStartHS + mBits / 2);
}
std::map<SmallNormal, SmallNormal, NormalCompare>* NormalQuantizer::QuantizeMaterials(std::vector<SmallNormal> normals) const
{
std::map<SmallNormal, SmallNormal, NormalCompare>* res = new std::map<SmallNormal, SmallNormal, NormalCompare>();
for (const SmallNormal& normal : normals)
{
res->insert(std::make_pair(normal, Quantize(normal)));
}
if (mBits <= QUANTIZE_ALL_UP_TO)
{
unsigned32 maxPerChannel = 1 << (mBits / 2);
unsigned8 shift = (SmallNormal::BITS - mBits) / 2;
for (unsigned32 x = 0; x < maxPerChannel; x++)
for (unsigned32 y = 0; y < maxPerChannel; y++)
{
SmallNormal v;
v.SetXComponent(x << shift);
v.SetYComponent(y << shift);
res->insert(std::make_pair(v, v));
}
}
return res;
}
SmallNormal NormalQuantizer::Quantize(const SmallNormal& normal) const
{
SmallNormal quantized;
quantized.SetXComponent(normal.GetXComponent() & mChannelMask);
quantized.SetYComponent(normal.GetYComponent() & mChannelMask);
return quantized;
}
std::string NormalQuantizer::GetQuantizerDescriptor() const
{
return std::to_string(mBits);
}

View File

@@ -0,0 +1,20 @@
#pragma once
#include <map>
#include <vector>
#include "../SmallNormal.h"
#include "BaseQuantizer.h"
// Super simple quantizer: takes the original value, sets some bits to zero so that only the given number of bits remain
class NormalQuantizer : public BaseQuantizer<SmallNormal, NormalCompare>, public QuickQuantizer<SmallNormal>
{
public:
NormalQuantizer(unsigned8 bits);
std::map<SmallNormal, SmallNormal, NormalCompare>* QuantizeMaterials(std::vector<SmallNormal> materials) const override;
std::string GetQuantizerDescriptor() const override;
SmallNormal Quantize(const SmallNormal& normal) const override;
private:
// if the number of bits is smaller than or equal to this number, all possible quantization values will be added.
static const unsigned8 QUANTIZE_ALL_UP_TO = 14;
unsigned8 mBits;
unsigned32 mChannelMask;
};