#pragma once #include #include #include #include #include #include // makes life easier #if UF_USE_DC_TEXCONV #include #include #include #else #include #include #include namespace uf { namespace stl { using unordered_map = std::unordered_map; using vector = std::vector; using string = std::string; } } #endif typedef uint32_t uint; enum FilterMode { NEAREST, BILINEAR }; struct RGBA { uint8_t r, g, b, a; }; // N-dimensional vectors, for input to a VectorQuantizer. template class Vec { public: Vec(uint hval = 0) : hashVal(hval) {} Vec(const Vec& other); void zero(); void operator= (const Vec& other); bool operator== (const Vec& other) const; void operator+= (const Vec& other); void operator-= (const Vec& other); Vec operator+ (const Vec& other) const; Vec operator- (const Vec& other) const; void addMultiplied(const Vec& other, float x); void operator/= (float x); float& operator[] (int index); const float& operator[] (int index) const; void set(int index, float value); float lengthSquared() const; float length() const; void setLength(float len); void normalize(); static float distanceSquared(const Vec& a, const Vec& b); uint hash() const; void setHash(uint h) { hashVal = h; } private: float v[N]; uint hashVal; // Only used for the constant input vectors, so we only need to calc once. uint64_t lololol; // Speeds up the average compression by a couple of seconds on my machine. Probably some alignment stuff. }; template inline Vec::Vec(const Vec &other) { *this = other; } template inline void Vec::zero() { for (uint i=0; i inline void Vec::operator= (const Vec& other) { for (uint i=0; i inline bool Vec::operator== (const Vec& other) const { for (uint i=0; i 0.001f) return false; return true; } template inline Vec Vec::operator+ (const Vec& other) const { Vec ret; for (uint i=0; i inline Vec Vec::operator- (const Vec& other) const { Vec ret; for (uint i=0; i inline void Vec::addMultiplied(const Vec& other, float x) { for (uint i=0; i inline void Vec::operator+= (const Vec& other) { for (uint i=0; i inline void Vec::operator-= (const Vec& other) { for (uint i=0; i inline void Vec::operator/= (float x) { const float invx = 1.0f / x; for (uint i=0; i inline float& Vec::operator[] (int index) { return v[index]; } template inline const float& Vec::operator[] (int index) const { return v[index]; } template inline void Vec::set(int index, float value) { v[index] = value; } template inline float Vec::length() const { return sqrt(lengthSquared()); } template inline float Vec::lengthSquared() const { float ret = 0; for (uint i=0; i inline void Vec::setLength(float len) { float x = (1.0f / length()) * len; for (uint i=0; i inline void Vec::normalize() { const float invlen = 1.0f / length(); for (uint i=0; i float Vec::distanceSquared(const Vec& a, const Vec& b) { return (a - b).lengthSquared(); } template inline uint Vec::hash() const { return hashVal; } template uint qHash(const Vec& vec) { return vec.hash(); } namespace std { template struct hash> { size_t operator()(const Vec& v) const { uint32_t h=2166136261u; for(uint i=0;i class VectorQuantizer { public: struct Code { Vec codeVec; Vec vecSum; int vecCount = 0; float maxDistance = 0; Vec maxDistanceVec; }; uf::stl::vector codes; int codeCount() const { return (int)codes.size(); } const Vec& codeVector(int i) const { return codes[i].codeVec; } int findClosest(const Vec& vec) const; int findBestSplitCandidate() const; void removeUnusedCodes(); void place(const uf::stl::unordered_map,int>& vecs); void split(); void splitCode(int index); void compress(const uf::stl::vector>& vectors,int numCodes); bool writeReportToFile(const uf::stl::string& filename); }; inline uint32_t packColor(const RGBA& c) { return (uint32_t(c.a)<<24)|(uint32_t(c.r)<<16)|(uint32_t(c.g)<<8)|uint32_t(c.b); } inline RGBA unpackColor(uint32_t argb) { RGBA c; c.a = (argb>>24)&0xFF; c.r = (argb>>16)&0xFF; c.g = (argb>>8)&0xFF; c.b = argb&0xFF; return c; } template inline void rgb2vec(uint32_t rgb, Vec& vec, uint offset=0) { RGBA c = unpackColor(rgb); vec[offset+0] = c.r/255.f; vec[offset+1] = c.g/255.f; vec[offset+2] = c.b/255.f; } template inline void argb2vec(uint32_t argb, Vec& vec, uint offset=0) { RGBA c = unpackColor(argb); vec[offset+0] = c.a/255.f; vec[offset+1] = c.r/255.f; vec[offset+2] = c.g/255.f; vec[offset+3] = c.b/255.f; } template inline void vec2rgb(const Vec& vec, uint32_t& rgb, uint offset=0) { RGBA c; c.r = (uint8_t)(vec[offset+0]*255); c.g = (uint8_t)(vec[offset+1]*255); c.b = (uint8_t)(vec[offset+2]*255); c.a = 255; rgb = packColor(c); } template inline void vec2argb(const Vec& vec, uint32_t& argb, uint offset=0) { RGBA c; c.a = (uint8_t)(vec[offset+0]*255); c.r = (uint8_t)(vec[offset+1]*255); c.g = (uint8_t)(vec[offset+2]*255); c.b = (uint8_t)(vec[offset+3]*255); argb = packColor(c); } template int VectorQuantizer::findClosest(const Vec& vec) const { if (codes.size() <= 1) return 0; int closestIndex = 0; float closestDist = Vec::distanceSquared(codes[0].codeVec, vec); for(size_t i=1; i::distanceSquared(codes[i].codeVec, vec); if(d < closestDist){ closestDist=d; closestIndex=(int)i; if (closestDist < 0.0001f) return closestIndex; } } return closestIndex; } template int VectorQuantizer::findBestSplitCandidate() const { int idx=-1; float furthest=0; for(size_t i=0;i1 && codes[i].maxDistance>furthest){ furthest=codes[i].maxDistance; idx=(int)i; } } return idx; } template void VectorQuantizer::removeUnusedCodes() { size_t oldSize=codes.size(); codes.erase( std::remove_if(codes.begin(), codes.end(), [](const Code& c){ return c.vecCount==0; }), codes.end() ); if(codes.size() void VectorQuantizer::place(const uf::stl::unordered_map,int>& vecs) { for(auto& code:codes){ code.vecCount=0; code.vecSum.zero(); code.maxDistance=0; code.maxDistanceVec.zero(); } for(const auto& kv:vecs){ const Vec& vec=kv.first; int count=kv.second; Code& code = codes[findClosest(vec)]; code.vecSum.addMultiplied(vec,count); code.vecCount+=count; float dist=Vec::distanceSquared(code.codeVec,vec); if(dist>code.maxDistance){ code.maxDistance=dist; code.maxDistanceVec=vec; } } for(auto& code:codes){ if(code.vecCount>0){ code.vecSum /= (float)code.vecCount; code.codeVec=code.vecSum; } } } template void VectorQuantizer::split() { int SIZE=(int)codes.size(); for(int i=0;i1){ splitCode(i); } } } template void VectorQuantizer::splitCode(int index) { Code& code=codes[index]; Vec diff=code.maxDistanceVec-code.codeVec; diff.setLength(0.01f); Vec newVec=code.codeVec; for(uint i=0;i void VectorQuantizer::compress(const uf::stl::vector>& vectors,int numCodes) { using clock=std::chrono::steady_clock; auto start=clock::now(); uf::stl::unordered_map,int> rle; for(const auto& v:vectors) rle[v]++; // UF_MSG_DEBUG("RLE result: {} => {}", vectors.size(), rle.size()); codes.clear(); codes.resize(1); codes.reserve(numCodes); place(rle); int splits=0, repairs=0; while((int)(codes.size()*2)<=numCodes){ size_t before=codes.size(); split(); place(rle); place(rle); place(rle); removeUnusedCodes(); if(codes.size()==before){ //UF_MSG_DEBUG("No further improvement by splitting"); break; } splits++; //UF_MSG_DEBUG("Split {} done. COdes: {}", splits, codes.size()); } while((int)codes.size()(clock::now()-start).count(); //UF_MSG_DEBUG("Compression completed in {} ms", ms); } template bool VectorQuantizer::writeReportToFile(const uf::stl::string& fname){ std::ofstream f(fname); if(!f.is_open()){ UF_MSG_ERROR("Failed to open: {}", fname); return false; } for(int i=0;i<(int)codes.size();i++){ f<<"Code: "<