diff --git a/include/Algo/Decimation/decimation.hpp b/include/Algo/Decimation/decimation.hpp index 2b50ccfcc160cbfbce2eb0790139d62f0e6a4ea7..bf66d292874c01cd80feb22f9b3e155cfeeed70e 100644 --- a/include/Algo/Decimation/decimation.hpp +++ b/include/Algo/Decimation/decimation.hpp @@ -196,6 +196,12 @@ void decimate( case S_hLightfieldKCL : selector = new HalfEdgeSelector_LightfieldKCL(map, position, approximators) ; break ; + case S_hColorExperimental: + selector = new HalfEdgeSelector_ColorExperimental(map, position, approximators, selected) ; + break ; + case S_hLFexperimental: + selector = new HalfEdgeSelector_LFexperimental(map, position, approximators, selected) ; + break ; } for(typename std::vector*>::iterator it = approximators.begin(); it != approximators.end(); ++it) @@ -213,9 +219,6 @@ void decimate( return ; } - if (edgeErrors != NULL) - selector->getEdgeErrors(edgeErrors) ; - unsigned int nbVertices = map.template getNbOrbits() ; bool finished = false ; Dart d ; @@ -257,6 +260,9 @@ void decimate( callback_wrapper(callback_object, &nbVertices) ; } + if (edgeErrors != NULL) + selector->getEdgeErrors(edgeErrors) ; + delete selector ; for(typename std::vector*>::iterator it = approximators.begin(); it != approximators.end(); ++it) diff --git a/include/Algo/Decimation/halfEdgeSelector.h b/include/Algo/Decimation/halfEdgeSelector.h index 4d26300f41fe49a9189d5ddd7c5f67a89a88f3bd..ba6aeb055a97f36b893882479e54edb9a14f7f7a 100644 --- a/include/Algo/Decimation/halfEdgeSelector.h +++ b/include/Algo/Decimation/halfEdgeSelector.h @@ -466,7 +466,7 @@ public: (*errors)[d] = halfEdgeInfo[d].it->first ; } Dart dd = this->m_map.phi2(d) ; - if (halfEdgeInfo[dd].valid && halfEdgeInfo[dd].it->first > (*errors)[d]) + if (halfEdgeInfo[dd].valid && halfEdgeInfo[dd].it->first < (*errors)[d]) { (*errors)[dd] = halfEdgeInfo[dd].it->first ; } @@ -475,10 +475,202 @@ public: } } ; +/***************************************************************************************************************** + * HALF-EDGE COLOR EXPERIMENTAL * + *****************************************************************************************************************/ +template +class HalfEdgeSelector_ColorExperimental : public EdgeSelector +{ +public: + typedef typename PFP::MAP MAP ; + typedef typename PFP::REAL REAL ; + typedef typename PFP::VEC3 VEC3 ; + +private: + typedef struct + { + typename std::multimap::iterator it ; + bool valid ; + static std::string CGoGNnameOfType() { return "ColorExperimentalHalfEdgeInfo" ; } + } QEMextColorHalfEdgeInfo ; + typedef NoMathIOAttribute HalfEdgeInfo ; + + DartAttribute halfEdgeInfo ; + VertexAttribute > m_quadric ; + + VertexAttribute m_pos, m_color ; + int m_approxindex_pos, m_attrindex_pos ; + int m_approxindex_color, m_attrindex_color ; + + std::vector* > m_approx ; + + std::multimap halfEdges ; + typename std::multimap::iterator cur ; + + void initHalfEdgeInfo(Dart d) ; + void updateHalfEdgeInfo(Dart d) ; + void computeHalfEdgeInfo(Dart d, HalfEdgeInfo& einfo) ; + //void recomputeQuadric(const Dart d, const bool recomputeNeighbors = false) ; + void recomputeQuadric(const Dart d) ; + + typename PFP::REAL computeExperimentalColorError(const Dart& v0, const Dart& v1) ; + + +public: + HalfEdgeSelector_ColorExperimental(MAP& m, VertexAttribute& pos, std::vector*>& approx, const FunctorSelect& select = allDarts) : + EdgeSelector(m, pos, approx, select), + m_approxindex_pos(-1), + m_attrindex_pos(-1), + m_approxindex_color(-1), + m_attrindex_color(-1) + { + halfEdgeInfo = m.template addAttribute("halfEdgeInfo") ; + m_quadric = m.template addAttribute, VERTEX>("QEMquadric") ; + } + ~HalfEdgeSelector_ColorExperimental() + { + this->m_map.removeAttribute(m_quadric) ; + this->m_map.removeAttribute(halfEdgeInfo) ; + } + SelectorType getType() { return S_hColorExperimental ; } + bool init() ; + bool nextEdge(Dart& d) ; + void updateBeforeCollapse(Dart d) ; + void updateAfterCollapse(Dart d2, Dart dd2) ; + + void updateWithoutCollapse() { } + + void getEdgeErrors(EdgeAttribute *errors) + { + assert(errors != NULL || !"EdgeSelector::setColorMap requires non null vertexattribute argument") ; + if (!errors->isValid()) + std::cerr << "EdgeSelector::setColorMap requires valid edgeattribute argument" << std::endl ; + assert(halfEdgeInfo.isValid()) ; + + TraversorE travE(this->m_map) ; + for(Dart d = travE.begin() ; d != travE.end() ; d = travE.next()) + { + (*errors)[d] = -1 ; + if (halfEdgeInfo[d].valid) + { + (*errors)[d] = halfEdgeInfo[d].it->first ; + } + Dart dd = this->m_map.phi2(d) ; + if (halfEdgeInfo[dd].valid && halfEdgeInfo[dd].it->first < (*errors)[d]) + { + (*errors)[d] = halfEdgeInfo[dd].it->first ; + } + } + } +} ; + +/***************************************************************************************************************** + * HALF-EDGE LF EXPERIMENTAL METRIC * + *****************************************************************************************************************/ +template +class HalfEdgeSelector_LFexperimental : public EdgeSelector +{ +public: + typedef typename PFP::MAP MAP ; + typedef typename PFP::REAL REAL ; + typedef typename PFP::VEC3 VEC3 ; + +private: + typedef struct + { + typename std::multimap::iterator it ; + bool valid ; + static std::string CGoGNnameOfType() { return "LightfieldExpHalfEdgeInfo" ; } + } LightfieldHalfEdgeInfo ; + typedef NoMathIOAttribute HalfEdgeInfo ; + + DartAttribute halfEdgeInfo ; + + VertexAttribute > m_quadric ; + + VertexAttribute m_visualImportance ; + VertexAttribute m_avgColor ; + + int m_approxindex_pos, m_attrindex_pos ; + int m_approxindex_FT, m_attrindex_FT ; + int m_approxindex_FB, m_attrindex_FB ; + int m_approxindex_FN, m_attrindex_FN ; + std::vector m_approxindex_HF, m_attrindex_HF ; + unsigned int m_K ; + + std::vector* > m_approx ; + + std::multimap halfEdges ; + typename std::multimap::iterator cur ; + + void initHalfEdgeInfo(Dart d) ; + void updateHalfEdgeInfo(Dart d) ; + void computeHalfEdgeInfo(Dart d, HalfEdgeInfo& einfo) ; + void recomputeQuadric(const Dart d) ; + + REAL computeLightfieldError(const Dart& v0, const Dart& v1) ; + REAL computeSquaredLightfieldDifference(const Dart& d1, const Dart& d2) ; + +public: + HalfEdgeSelector_LFexperimental(MAP& m, VertexAttribute& pos, std::vector*>& approx, const FunctorSelect& select = allDarts) : + EdgeSelector(m, pos, approx, select), + m_approxindex_pos(-1), + m_attrindex_pos(-1), + m_approxindex_FT(-1), + m_attrindex_FT(-1), + m_approxindex_FB(-1), + m_attrindex_FB(-1), + m_approxindex_FN(-1), + m_attrindex_FN(-1), + m_K(0) + { + halfEdgeInfo = m.template addAttribute("halfEdgeInfo") ; + m_quadric = m.template addAttribute, VERTEX>("QEMquadric") ; + m_avgColor = m.template getAttribute("color") ; + assert(m_avgColor.isValid()) ; + } + ~HalfEdgeSelector_LFexperimental() + { + this->m_map.removeAttribute(m_quadric) ; + this->m_map.removeAttribute(halfEdgeInfo) ; + } + + SelectorType getType() { return S_hLFexperimental ; } + bool init() ; + bool nextEdge(Dart& d) ; + void updateBeforeCollapse(Dart d) ; + void updateAfterCollapse(Dart d2, Dart dd2) ; + + void updateWithoutCollapse() { } + + void getEdgeErrors(EdgeAttribute *errors) + { + assert(errors != NULL || !"EdgeSelector::setColorMap requires non null vertexattribute argument") ; + if (!errors->isValid()) + std::cerr << "EdgeSelector::setColorMap requires valid edgeattribute argument" << std::endl ; + assert(halfEdgeInfo.isValid()) ; + + TraversorE travE(this->m_map) ; + for(Dart d = travE.begin() ; d != travE.end() ; d = travE.next()) + { + (*errors)[d] = -1 ; + if (halfEdgeInfo[d].valid) + { + (*errors)[d] = halfEdgeInfo[d].it->first ; + } + Dart dd = this->m_map.phi2(d) ; + if (halfEdgeInfo[dd].valid && halfEdgeInfo[dd].it->first < (*errors)[d]) + { + (*errors)[d] = halfEdgeInfo[dd].it->first ; + } + } + } +} ; + } // namespace Decimation -} +} // namespace Surface } // namespace Algo diff --git a/include/Algo/Decimation/halfEdgeSelector.hpp b/include/Algo/Decimation/halfEdgeSelector.hpp index fafbf905410b7c05cbc075ce2c82cb317eb2e133..ffd808b0a16dc570c11b5a9a6cae796d71ca5504 100644 --- a/include/Algo/Decimation/halfEdgeSelector.hpp +++ b/include/Algo/Decimation/halfEdgeSelector.hpp @@ -1710,19 +1710,19 @@ typename PFP::REAL HalfEdgeSelector_LightfieldKCL::computeLightfieldError(D err += computeSquaredLightfieldDifference(v1,vi) ; } } - return err ; -*/ + return err ;*/ + // return computeSquaredLightfieldDifference(v0,v1) ; Traversor2VVaE tv(this->m_map,v1) ; // all vertices surrounding vertex v0 for (Dart vi = tv.begin() ; vi != tv.end() ; vi = tv.next()) { VEC3 edgeL = this->m_position[v1] - this->m_position[vi] ; - err += sqrt(computeSquaredLightfieldDifference(v1,vi)) ;//* edgeL.norm() ; + err += sqrt(computeSquaredLightfieldDifference(v1,vi)) * edgeL.norm() ; //std::cout << "1 : " << edgeL.norm() << std::endl ; edgeL = this->m_position[v0] - this->m_position[vi] ; //std::cout << "2 : " << edgeL.norm() << std::endl ; - err -= sqrt(computeSquaredLightfieldDifference(v0,vi)) ;//* edgeL.norm() ; + err -= sqrt(computeSquaredLightfieldDifference(v0,vi)) * edgeL.norm() ; } return fabs(err) ; } @@ -1791,12 +1791,871 @@ typename PFP::REAL HalfEdgeSelector_LightfieldKCL::computeSquaredLightfield } +/************************************************************************************ + * COLOR EXPERIMENTAL * + ************************************************************************************/ + +template +bool HalfEdgeSelector_ColorExperimental::init() +{ + MAP& m = this->m_map ; -} // namespace Decimation + // Verify availability of required approximators + unsigned int ok = 0 ; + for (unsigned int approxindex = 0 ; approxindex < this->m_approximators.size() ; ++approxindex) + { + assert(this->m_approximators[approxindex]->getType() == A_hQEM + || this->m_approximators[approxindex]->getType() == A_hHalfCollapse + || !"Approximator for selector (HalfEdgeSelector_ColorExperimental) must be of a half-edge approximator") ; + + bool saved = false ; + for (unsigned int attrindex = 0 ; attrindex < this->m_approximators[approxindex]->getNbApproximated() ; ++ attrindex) + { + // constraint : 2 approximators in specific order + if(ok == 0 && this->m_approximators[approxindex]->getApproximatedAttributeName(attrindex) == "position") + { + ++ok ; + m_approxindex_pos = approxindex ; + m_attrindex_pos = attrindex ; + m_pos = this->m_position ; + if (!saved) + { + m_approx.push_back(reinterpret_cast* >(this->m_approximators[approxindex])) ; + saved = true ; + } + } + else if(ok == 1 && this->m_approximators[approxindex]->getApproximatedAttributeName(attrindex) == "color") + { + ++ok ; + m_approxindex_color = approxindex ; + m_attrindex_color = attrindex ; + m_color = m.template getAttribute("color") ; + assert(m_color.isValid() || !"EdgeSelector_QEMextColor: color attribute is not valid") ; + if (!saved) + { + m_approx.push_back(reinterpret_cast* >(this->m_approximators[approxindex])) ; + saved = true ; + } + } + } + } + + if(ok != 2) + return false ; + + CellMarker vMark(m) ; + for(Dart d = m.begin(); d != m.end(); m.next(d)) + { + if(!vMark.isMarked(d)) + { + Utils::Quadric q ; // create one quadric + m_quadric[d] = q ; // per vertex + vMark.mark(d) ; + } + } + + DartMarker mark(m) ; + for(Dart d = m.begin(); d != m.end(); m.next(d)) + { + if(!mark.isMarked(d)) + { + Dart d1 = m.phi1(d) ; // for each triangle, + Dart d_1 = m.phi_1(d) ; // initialize the quadric of the triangle + Utils::Quadric q(this->m_position[d], this->m_position[d1], this->m_position[d_1]) ; + m_quadric[d] += q ; // and add the contribution of + m_quadric[d1] += q ; // this quadric to the ones + m_quadric[d_1] += q ; // of the 3 incident vertices + mark.markOrbit(d) ; + } + } + + // Init multimap for each Half-edge + halfEdges.clear() ; + + for(Dart d = m.begin(); d != m.end(); m.next(d)) + { + initHalfEdgeInfo(d) ; // init the edges with their optimal info + } // and insert them in the multimap according to their error + + cur = halfEdges.begin() ; // init the current edge to the first one + + return true ; +} + +template +bool HalfEdgeSelector_ColorExperimental::nextEdge(Dart& d) +{ + if(cur == halfEdges.end() || halfEdges.empty()) + return false ; + d = (*cur).second ; + return true ; +} + +template +void HalfEdgeSelector_ColorExperimental::updateBeforeCollapse(Dart d) +{ + MAP& m = this->m_map ; + + const Dart& v0 = m.phi1(d) ; + + Traversor2VVaE tv(m,v0) ; + for (Dart v = tv.begin() ; v != tv.end() ; v = tv.next()) + { + Traversor2VE te(m,v) ; + for (Dart he = te.begin() ; he != te.end() ; he = te.next()) + { + HalfEdgeInfo* edgeE = &(halfEdgeInfo[he]) ; + if(edgeE->valid) + { + edgeE->valid = false ; + halfEdges.erase(edgeE->it) ; + } + Dart de = m.phi2(he) ; + edgeE = &(halfEdgeInfo[de]) ; + if(edgeE->valid) + { + edgeE->valid = false ; + halfEdges.erase(edgeE->it) ; + } + } + } + +// HalfEdgeInfo* edgeE = &(halfEdgeInfo[d]) ; +// if(edgeE->valid) +// halfEdges.erase(edgeE->it) ; +// +// edgeE = &(halfEdgeInfo[m.phi1(d)]) ; +// if(edgeE->valid) // remove all +// halfEdges.erase(edgeE->it) ; +// +// edgeE = &(halfEdgeInfo[m.phi_1(d)]) ; // the halfedges that will disappear +// if(edgeE->valid) +// halfEdges.erase(edgeE->it) ; +// // from the multimap +// Dart dd = m.phi2(d) ; +// assert(dd != d) ; +// if(dd != d) +// { +// edgeE = &(halfEdgeInfo[dd]) ; +// if(edgeE->valid) +// halfEdges.erase(edgeE->it) ; +// +// edgeE = &(halfEdgeInfo[m.phi1(dd)]) ; +// if(edgeE->valid) +// halfEdges.erase(edgeE->it) ; +// +// edgeE = &(halfEdgeInfo[m.phi_1(dd)]) ; +// if(edgeE->valid) +// halfEdges.erase(edgeE->it) ; +// } +} + +/** + * Update quadric of a vertex + * Discards quadrics of d and assigns freshly calculated + * quadrics depending on the actual planes surrounding d + * @param dart d + */ +template +void HalfEdgeSelector_ColorExperimental::recomputeQuadric(const Dart d) +{ + Dart dFront,dBack ; + Dart dInit = d ; + + // Init Front + dFront = dInit ; + + m_quadric[d].zero() ; + + do { + // Make step + dBack = this->m_map.phi1(dFront) ; + dFront = this->m_map.phi2_1(dFront) ; + + if (this->m_map.phi2(dFront) != dFront) { // if dFront is no border + m_quadric[d] += Utils::Quadric(this->m_position[d],this->m_position[dBack],this->m_position[this->m_map.phi1(dFront)]) ; + } + } while(dFront != dInit) ; +} + + +///** +// * Update quadric of a vertex +// * Discards quadrics of d and assigns freshly calculated +// * quadrics depending on the actual planes surrounding d +// * @param dart d +// */ +//template +//void HalfEdgeSelector_ColorExperimental::recomputeQuadric(const Dart d, const bool recomputeNeighbors) +//{ +// Dart dFront,dBack ; +// Dart dInit = d ; +// +// // Init Front +// dFront = dInit ; +// +// m_quadric[d].zero() ; +// +// do { +// // Make step +// dBack = this->m_map.phi1(dFront) ; +// dFront = this->m_map.phi2_1(dFront) ; +// +// if (this->m_map.phi2(dFront) != dFront) { // if dFront is no border +// m_quadric[d] += Utils::Quadric(this->m_position[d],this->m_position[dBack],this->m_position[this->m_map.phi1(dFront)]) ; +// } +// +// if (recomputeNeighbors) +// recomputeQuadric(dBack, false) ; +// +// } while(dFront != dInit) ; +//} + +template +void HalfEdgeSelector_ColorExperimental::updateAfterCollapse(Dart d2, Dart dd2) +{ + MAP& m = this->m_map ; + + const Dart& v1 = d2 ; + + recomputeQuadric(v1) ; + Traversor2VVaE tv(m,v1) ; + for (Dart v = tv.begin() ; v != tv.end() ; v = tv.next()) + { + recomputeQuadric(v) ; + } + + for (Dart v = tv.begin() ; v != tv.end() ; v = tv.next()) + { + Traversor2VE te(m,v) ; + for (Dart e = te.begin() ; e != te.end() ; e = te.next()) + { + updateHalfEdgeInfo(e) ; + updateHalfEdgeInfo(m.phi2(e)) ; + } + } +// MAP& m = this->m_map ; +// +// recomputeQuadric(d2, true) ; +// +// Dart vit = d2 ; +// do +// { +// updateHalfEdgeInfo(vit, true) ; +// Dart d = m.phi2(vit) ; +// if (d != vit) +// updateHalfEdgeInfo(d, true) ; +// +// updateHalfEdgeInfo(m.phi1(vit), true) ; +// d = m.phi2(m.phi1(vit)) ; +// if (d != m.phi1(vit)) +// updateHalfEdgeInfo(d, true) ; +// +// Dart stop = m.phi2(vit) ; +// assert (stop != vit) ; +// Dart vit2 = m.phi12(m.phi1(vit)) ; +// do { +// updateHalfEdgeInfo(vit2, true) ; +// d = m.phi2(vit2) ; +// if (d != vit2) +// updateHalfEdgeInfo(d, true) ; +// +// updateHalfEdgeInfo(m.phi1(vit2), false) ; +// d = m.phi2(m.phi1(vit2)) ; +// if (d != m.phi1(vit2)) +// updateHalfEdgeInfo(d, false) ; +// +// vit2 = m.phi12(vit2) ; +// } while (stop != vit2) ; +// vit = m.phi2_1(vit) ; +// } while(vit != d2) ; + + cur = halfEdges.begin() ; // set the current edge to the first one +} + +template +void HalfEdgeSelector_ColorExperimental::initHalfEdgeInfo(Dart d) +{ + MAP& m = this->m_map ; + HalfEdgeInfo heinfo ; + if(m.edgeCanCollapse(d)) + computeHalfEdgeInfo(d, heinfo) ; + else + heinfo.valid = false ; + + halfEdgeInfo[d] = heinfo ; +} + +template +void HalfEdgeSelector_ColorExperimental::updateHalfEdgeInfo(Dart d) +{ + MAP& m = this->m_map ; + HalfEdgeInfo& heinfo = halfEdgeInfo[d] ; + + if(!heinfo.valid && m.edgeCanCollapse(d)) + computeHalfEdgeInfo(d, heinfo) ; +} + +template +void HalfEdgeSelector_ColorExperimental::computeHalfEdgeInfo(Dart d, HalfEdgeInfo& heinfo) +{ + MAP& m = this->m_map ; + Dart dd = m.phi1(d) ; + + Utils::Quadric quad ; + quad += m_quadric[d] ; // compute the sum of the + quad += m_quadric[dd] ; // two vertices quadrics + + + // compute all approximated attributes + for(typename std::vector*>::iterator it = this->m_approximators.begin() ; + it != this->m_approximators.end() ; + ++it) + { + (*it)->approximate(d) ; + } + + // Get all approximated attributes + // New position + const VEC3& newPos = this->m_approx[m_approxindex_pos]->getApprox(d,m_attrindex_pos) ; // get newPos + // New normal + const VEC3& newColor = this->m_approx[m_approxindex_color]->getApprox(d,m_attrindex_color) ; // get new color + + const Dart& v0 = dd ; + const Dart& v1 = d ; + + assert(newPos == m_pos[v1]) ; + assert(newColor == m_color[v1]) ; + + // Compute errors + // Position + Utils::Quadric quadGeom ; + quadGeom += m_quadric[d] ; // compute the sum of the + quadGeom += m_quadric[dd] ; // two vertices quadrics + + //std::cout << quadGeom(newPos) / (alpha/M_PI + quadHF(newHF)) << std::endl ; + // sum of QEM metric and frame orientation difference + const REAL& err = + quadGeom(newPos) + // geom + computeExperimentalColorError(v0,v1) // color + ; + //std::cout << quadGeom(newPos) / computeExperimentalColorError(v0,v1) << std::endl ; + + // Check if errated values appear + if (err < -1e-6) + heinfo.valid = false ; + else + { + heinfo.it = this->halfEdges.insert(std::make_pair(std::max(err,REAL(0)), d)) ; + heinfo.valid = true ; + } +} + +template +typename PFP::REAL +HalfEdgeSelector_ColorExperimental::computeExperimentalColorError(const Dart& v0, const Dart& v1) +{ + MAP& m = this->m_map ; + + Traversor2VF tf(m,v0) ; + const unsigned int& v1_vertexId = m.template getEmbedding(v1) ; + + const VEC3& P0 = m_pos[v0] ; + const VEC3& P1 = m_pos[v1] ; + const VEC3& c0 = m_color[v0] ; + const VEC3& c1 = m_color[v1] ; + + REAL res = 0 ; + for (Dart fi = tf.begin() ; fi != tf.end() ; fi = tf.next()) + { + const Dart& vi = m.phi1(fi) ; + const Dart& vj = m.phi_1(fi) ; + const VEC3& Pi = m_pos[vi] ; + const VEC3& Pj = m_pos[vj] ; + + const VEC3& ci = m_color[vi] ; + const VEC3& cj = m_color[vj] ; + + const VEC3 PiP0 = P0 - Pi ; + const VEC3 PjP0 = P0 - Pj ; + const VEC3 PiPj = Pj - Pi ; + + const VEC3 P0prim = Pi + (PiP0 * PiPj)*PiPj ; + const VEC3 P0P0prim = P0prim - P0 ; + const REAL h0 = P0P0prim.norm() ; + + const VEC3 coldiff01 = c1 - c0 ; + res += coldiff01.norm() * PiPj.norm() * h0 / (6. * sqrt(3)) ; + + // test if v1 is adjacent to this face + bool adjacent = false ; + Traversor2FV tv(m,fi) ; + for (Dart v = tv.begin() ; v != tv.end() ; v = tv.next()) + { + if (v1_vertexId == m.template getEmbedding(v)) + adjacent = true ; + } + + // if v1 is not adjacent to this face + if (!adjacent) + { + const VEC3 PiP1 = P0 - Pi ; + const VEC3 PjP1 = P0 - Pj ; + + const VEC3 P1prim = Pi + (PiP1 * PiPj)*PiPj ; + const VEC3 P1P1prim = P1prim - P1 ; + const REAL h1 = P1P1prim.norm() ; + + const VEC3 coldiff0i = ci - c0 ; + const VEC3 coldiff1i = ci - c1 ; + const VEC3 coldiff0j = cj - c0 ; + const VEC3 coldiff1j = cj - c1 ; + + res += (coldiff0i.norm() + coldiff0j.norm() + coldiff1i.norm() + coldiff1j.norm()) * PiPj.norm() * std::fabs(h1 - h0) / (6. * sqrt(3)) ; + } + } + + return res ; } +/************************************************************************************ + * LIGHTFIELD EXPERIMENTAL * + ************************************************************************************/ + +template +bool HalfEdgeSelector_LFexperimental::init() +{ + MAP& m = this->m_map ; + + // Verify availability of required approximators + unsigned int ok = 0 ; + unsigned int k = 0 ; + for (unsigned int approxindex = 0 ; approxindex < this->m_approximators.size() ; ++approxindex) + { + if (this->m_approximators[approxindex]->getType() != A_hHalfCollapse) + { + std::cerr << "Approximator for selector (HalfEdgeSelector_LFexperimental) must be of A_hHalfCollapse" << std::endl ; + return false ; + } + assert(this->m_approximators[approxindex]->getType() == A_hHalfCollapse + || !"Approximator for selector (HalfEdgeSelector_LFexperimental) must be of A_hHalfCollapse") ; + + bool saved = false ; + for (unsigned int attrindex = 0 ; attrindex < this->m_approximators[approxindex]->getNbApproximated() ; ++ attrindex) + { + // constraint : 2 approximators in specific order + if(ok == 0 && this->m_approximators[approxindex]->getApproximatedAttributeName(attrindex) == "position") + { + ++ok ; + m_approxindex_pos = approxindex ; + m_attrindex_pos = attrindex ; + if (!saved) + { + m_approx.push_back(reinterpret_cast* >(this->m_approximators[approxindex])) ; + saved = true ; + } + } + else if(ok == 1 && this->m_approximators[approxindex]->getApproximatedAttributeName(attrindex) == "frameT") + { + ++ok ; + m_approxindex_FT = approxindex ; + m_attrindex_FT = attrindex ; + if (!saved) + { + m_approx.push_back(reinterpret_cast* >(this->m_approximators[approxindex])) ; + assert(this->m_approx[approxindex]->getAttr(attrindex).isValid() || !"HalfEdgeSelector_LFexperimental: frameT attribute is not valid") ; + saved = true ; + } + } + else if(ok == 2 && this->m_approximators[approxindex]->getApproximatedAttributeName(attrindex) == "frameB") + { + ++ok ; + m_approxindex_FB = approxindex ; + m_attrindex_FB = attrindex ; + assert(this->m_approx[approxindex]->getAttr(attrindex).isValid() || !"HalfEdgeSelector_LFexperimental: frameB attribute is not valid") ; + if (!saved) + { + m_approx.push_back(reinterpret_cast* >(this->m_approximators[approxindex])) ; + saved = true ; + } + } + else if(ok == 3 && this->m_approximators[approxindex]->getApproximatedAttributeName(attrindex) == "frameN") + { + ++ok ; + m_approxindex_FN = approxindex ; + m_attrindex_FN = attrindex ; + assert(this->m_approx[approxindex]->getAttr(attrindex).isValid() || !"HalfEdgeSelector_LFexperimental: frameN attribute is not valid") ; + if (!saved) + { + m_approx.push_back(reinterpret_cast* >(this->m_approximators[approxindex])) ; + saved = true ; + } + } + else + { + std::stringstream s ; + s << "PBcoefs" << k ; + if(ok > 3 && this->m_approximators[approxindex]->getApproximatedAttributeName(attrindex) == s.str().c_str()) + { + ++ok ; + if (this->m_approx[approxindex]->getAttr(attrindex).isValid()) + { + ++k ; + m_approxindex_HF.push_back(approxindex) ; + m_attrindex_HF.push_back(attrindex) ; + if (!saved) + { + m_approx.push_back(reinterpret_cast* >(this->m_approximators[approxindex])) ; + saved = true ; + } + } + } + } + } + } + m_K = k ; + + if(ok < 6) + { + std::cerr << "HalfEdgeSelector_LFexperimental::init() not OK" << std::endl ; + return false ; + } + + TraversorV travV(m); + for(Dart dit = travV.begin() ; dit != travV.end() ; dit = travV.next()) + { + Utils::Quadric q ; // create one quadric + m_quadric[dit] = q ; // per vertex + } + + // Compute quadric per vertex + TraversorF travF(m) ; + for(Dart dit = travF.begin() ; dit != travF.end() ; dit = travF.next()) // init QEM quadrics + { + Dart d1 = m.phi1(dit) ; // for each triangle, + Dart d_1 = m.phi_1(dit) ; // initialize the quadric of the triangle + + Utils::Quadric q(this->m_position[dit], this->m_position[d1], this->m_position[d_1]) ; + m_quadric[dit] += q ; // and add the contribution of + m_quadric[d1] += q ; // this quadric to the ones + m_quadric[d_1] += q ; // of the 3 incident vertices + } + + // Init multimap for each Half-edge + halfEdges.clear() ; + + for(Dart d = m.begin() ; d != m.end() ; m.next(d)) + { + initHalfEdgeInfo(d) ; // init the edges with their optimal position + } // and insert them in the multimap according to their error + + cur = halfEdges.begin() ; // init the current edge to the first one + + return true ; +} + +template +bool HalfEdgeSelector_LFexperimental::nextEdge(Dart& d) +{ + if(cur == halfEdges.end() || halfEdges.empty()) + return false ; + d = (*cur).second ; + return true ; +} + +template +void HalfEdgeSelector_LFexperimental::updateBeforeCollapse(Dart d) +{ + MAP& m = this->m_map ; + + const Dart& v0 = m.phi1(d) ; + + Traversor2VVaE tv(m,v0) ; + for (Dart v = tv.begin() ; v != tv.end() ; v = tv.next()) + { + Traversor2VE te(m,v) ; + for (Dart he = te.begin() ; he != te.end() ; he = te.next()) + { + HalfEdgeInfo* edgeE = &(halfEdgeInfo[he]) ; + if(edgeE->valid) + { + edgeE->valid = false ; + halfEdges.erase(edgeE->it) ; + } + Dart de = m.phi2(he) ; + edgeE = &(halfEdgeInfo[de]) ; + if(edgeE->valid) + { + edgeE->valid = false ; + halfEdges.erase(edgeE->it) ; + } + } + } +} + +/** + * Update quadric of a vertex + * Discards quadrics of d and assigns freshly calculated + * quadrics depending on the actual planes surrounding d + * @param dart d + */ +template +void HalfEdgeSelector_LFexperimental::recomputeQuadric(const Dart d) +{ + Dart dFront,dBack ; + Dart dInit = d ; + + // Init Front + dFront = dInit ; + + m_quadric[d].zero() ; + + do { + // Make step + dBack = this->m_map.phi1(dFront) ; + dFront = this->m_map.phi2_1(dFront) ; + + if (this->m_map.phi2(dFront) != dFront) { // if dFront is no border + m_quadric[d] += Utils::Quadric(this->m_position[d],this->m_position[dBack],this->m_position[this->m_map.phi1(dFront)]) ; + } + } while(dFront != dInit) ; +} + +template +void HalfEdgeSelector_LFexperimental::updateAfterCollapse(Dart d2, Dart dd2) +{ + MAP& m = this->m_map ; + + const Dart& v1 = d2 ; + + recomputeQuadric(v1) ; + Traversor2VVaE tv(m,v1) ; + for (Dart v = tv.begin() ; v != tv.end() ; v = tv.next()) + { + recomputeQuadric(v) ; + } + + for (Dart v = tv.begin() ; v != tv.end() ; v = tv.next()) + { + Traversor2VE te(m,v) ; + for (Dart e = te.begin() ; e != te.end() ; e = te.next()) + { + updateHalfEdgeInfo(e) ; + updateHalfEdgeInfo(m.phi2(e)) ; + } + } + + cur = halfEdges.begin() ; // set the current edge to the first one +} + +template +void HalfEdgeSelector_LFexperimental::initHalfEdgeInfo(Dart d) +{ + MAP& m = this->m_map ; + HalfEdgeInfo heinfo ; + if(m.edgeCanCollapse(d)) + computeHalfEdgeInfo(d, heinfo) ; + else + heinfo.valid = false ; + + halfEdgeInfo[d] = heinfo ; +} + +template +void HalfEdgeSelector_LFexperimental::updateHalfEdgeInfo(Dart d) +{ + MAP& m = this->m_map ; + HalfEdgeInfo& heinfo = halfEdgeInfo[d] ; + + if(!heinfo.valid && m.edgeCanCollapse(d)) + computeHalfEdgeInfo(d, heinfo) ; +} + +template +void HalfEdgeSelector_LFexperimental::computeHalfEdgeInfo(Dart d, HalfEdgeInfo& heinfo) +{ + MAP& m = this->m_map ; + Dart dd = m.phi1(d) ; + + Utils::Quadric quad ; + quad += m_quadric[d] ; // compute the sum of the + quad += m_quadric[dd] ; // two vertices quadrics + + + // compute all approximated attributes + for(typename std::vector*>::iterator it = this->m_approximators.begin() ; + it != this->m_approximators.end() ; + ++it) + { + (*it)->approximate(d) ; + } + + // Get all approximated attributes + // New position + const VEC3& newPos = this->m_approx[m_approxindex_pos]->getApprox(d,m_attrindex_pos) ; // get newPos + + const Dart& v0 = dd ; + const Dart& v1 = d ; + + assert(newPos == this->m_position[v1]) ; + + // Compute errors + // Position + Utils::Quadric quadGeom ; + quadGeom += m_quadric[d] ; // compute the sum of the + quadGeom += m_quadric[dd] ; // two vertices quadrics + + // sum of QEM metric + const REAL& err = + quadGeom(newPos) + // geom + 100*computeLightfieldError(v0,v1) // lf + ; + //std::cout << computeLightfieldError(v0,v1) / quadGeom(newPos) << std::endl ; + + // Check if errated values appear + if (err < -1e-6) + heinfo.valid = false ; + else + { + heinfo.it = this->halfEdges.insert(std::make_pair(std::max(err,REAL(0)), d)) ; + heinfo.valid = true ; + } +} + +template +typename PFP::REAL +HalfEdgeSelector_LFexperimental::computeLightfieldError(const Dart& v0, const Dart& v1) +{ + MAP& m = this->m_map ; + + Traversor2VF tf(m,v0) ; + const unsigned int& v1_vertexId = m.template getEmbedding(v1) ; + + const VEC3& P0 = this->m_position[v0] ; + const VEC3& P1 = this->m_position[v1] ; + + REAL res = 0 ; + for (Dart fi = tf.begin() ; fi != tf.end() ; fi = tf.next()) + { + const Dart& vi = m.phi1(fi) ; + const Dart& vj = m.phi_1(fi) ; + + const VEC3& Pi = this->m_position[vi] ; + const VEC3& Pj = this->m_position[vj] ; + + const VEC3 PiP0 = P0 - Pi ; + const VEC3 PjP0 = P0 - Pj ; + const VEC3 PiPj = Pj - Pi ; + + const VEC3 P0prim = Pi + (PiP0 * PiPj)*PiPj ; + const VEC3 P0P0prim = P0prim - P0 ; + const REAL h0 = P0P0prim.norm() ; + + const REAL lfdiff01 = computeSquaredLightfieldDifference(v0,v1) ; + res += lfdiff01 * PiPj.norm() * h0 / 6. ; + + // test if v1 is adjacent to this face + bool adjacent = false ; + Traversor2FV tv(m,fi) ; + for (Dart v = tv.begin() ; v != tv.end() ; v = tv.next()) + { + if (v1_vertexId == m.template getEmbedding(v)) + adjacent = true ; + } + + // if v1 is not adjacent to this face + if (!adjacent) + { + const VEC3 PiP1 = P0 - Pi ; + const VEC3 PjP1 = P0 - Pj ; + + const VEC3 P1prim = Pi + (PiP1 * PiPj)*PiPj ; + const VEC3 P1P1prim = P1prim - P1 ; + const REAL h1 = P1P1prim.norm() ; + + const REAL lfdiff0i = computeSquaredLightfieldDifference(vi,v0) ; + const REAL lfdiff1i = computeSquaredLightfieldDifference(vi,v1) ; + const REAL lfdiff0j = computeSquaredLightfieldDifference(vj,v0) ; + const REAL lfdiff1j = computeSquaredLightfieldDifference(vj,v1) ; + + res += (lfdiff0i + lfdiff0j + lfdiff1i + lfdiff1j) * PiPj.norm() * std::fabs(h1 - h0) / 6. ; + } + } + + return res ; +} + +template +typename PFP::REAL HalfEdgeSelector_LFexperimental::computeSquaredLightfieldDifference(const Dart& d1, const Dart& d2) +{ + // get two frames + const VEC3& T1 = this->m_approx[m_approxindex_FT]->getAttr(m_attrindex_FT)[d1] ; + const VEC3& T2 = this->m_approx[m_approxindex_FT]->getAttr(m_attrindex_FT)[d2] ; + const VEC3& B1 = this->m_approx[m_approxindex_FB]->getAttr(m_attrindex_FB)[d1] ; + const VEC3& B2 = this->m_approx[m_approxindex_FB]->getAttr(m_attrindex_FB)[d2] ; + const VEC3& N1 = this->m_approx[m_approxindex_FN]->getAttr(m_attrindex_FN)[d1] ; + const VEC3& N2 = this->m_approx[m_approxindex_FN]->getAttr(m_attrindex_FN)[d2] ; + + // compute new frame + const VEC3& N = N1 ; + VEC3 T ; + if (N2 != N1) + T = N2 ^ N1 ; // i is perpendicular to newNormal + else + T = N1 ^ VEC3(1,2,3) ; // second random vector + T.normalize() ; + + // Compute D1' and D2' + VEC3 B1prime = N1 ^ T ; + B1prime.normalize() ; + VEC3 B2prime = N2 ^ T ; + B2prime.normalize() ; + + // Rotation dans sens trigo dans le plan tangent autour de N (T1 --> T) + const REAL gamma1 = ((B1 * T) > 0 ? 1 : -1) * acos( std::max(std::min(1.0f, T1 * T ), -1.0f)) ; // angle positif ssi + const REAL gamma2 = ((B2 * T) > 0 ? 1 : -1) * acos( std::max(std::min(1.0f, T2 * T ), -1.0f)) ; // -PI/2 < angle(i,j1) < PI/2 ssi i*j1 > 0 + // Rotation dans le sens trigo autour de l'axe T (N1 --> N) + const REAL alpha1 = ((N * B1prime) > 0 ? -1 : 1) * acos( std::max(std::min(1.0f, N * N1), -1.0f) ) ; // angle positif ssi + const REAL alpha2 = ((N * B2prime) > 0 ? -1 : 1) * acos( std::max(std::min(1.0f, N * N2), -1.0f) ) ; // PI/2 < angle(j1',n) < -PI/2 ssi j1'*n < 0 + + double alpha = fabs(alpha1 + alpha2) ; + + // get coefs of v1 and v2 + std::vector coefs1, coefs2, coefs ; + coefs1.resize(m_K) ; coefs2.resize(m_K) ; + for (unsigned int i = 0 ; i < m_K ; ++i) + { + coefs1[i] = this->m_approx[m_approxindex_HF[i]]->getAttr(m_attrindex_HF[i])[d1] ; + coefs2[i] = this->m_approx[m_approxindex_HF[i]]->getAttr(m_attrindex_HF[i])[d2] ; + } + + Utils::QuadricHF q(coefs1, gamma1, alpha1) ; + bool opt = q.findOptimizedCoefs(coefs) ; // coefs of d1's lightfield rotated around new local axis + q += Utils::QuadricHF(coefs2, gamma2, alpha2) ; + + if (!opt) + { + std::cerr << "HalfEdgeSelector_LightfieldKCL::Optimization failed (should never happen since no optim is done)" << std::endl ; + std::cout << alpha1 << std::endl ; + } + + const VEC3 avgColDiff = m_avgColor[d1] - m_avgColor[d2] ; + + REAL err = q(coefs) ; + if (fabs(err) < 1e-6) + err = 0 ; + + return (alpha / M_PI) * avgColDiff.norm2()/3. + err ; + +} + + +} // namespace Decimation + +} // namespace Surface + } // namespace Algo } // namespace CGoGN diff --git a/include/Algo/Decimation/selector.h b/include/Algo/Decimation/selector.h index ee3cf5bbdbcc4b7e5ad0039cdf669dcd229fbef9..393a1d56e5b614e1cfd9c839f9e67605780a1e3a 100644 --- a/include/Algo/Decimation/selector.h +++ b/include/Algo/Decimation/selector.h @@ -56,7 +56,9 @@ enum SelectorType S_hQEMml, S_hLightfield, S_hLightfieldExp, - S_hLightfieldKCL + S_hLightfieldKCL, + S_hColorExperimental, + S_hLFexperimental } ; template class ApproximatorGen ; diff --git a/include/Geometry/plane_3d.h b/include/Geometry/plane_3d.h index a496ce52bb5083a4a8ac13dc2df46497b45b293d..5806b920211b4e6fff94a069231831b800d288a6 100644 --- a/include/Geometry/plane_3d.h +++ b/include/Geometry/plane_3d.h @@ -25,6 +25,8 @@ #ifndef __PLANE_3D__ #define __PLANE_3D__ +#include "Geometry/vector_gen.h" + namespace CGoGN { diff --git a/include/Utils/colourConverter.h b/include/Utils/colourConverter.h index 8358ddb1300f5ad4c4bcb8cd42375c51455eeceb..fecfc8afb9fa9495cd51a439d73f7b6f1271b272 100644 --- a/include/Utils/colourConverter.h +++ b/include/Utils/colourConverter.h @@ -58,11 +58,12 @@ public: // types */ enum ColourEncoding { - C_RGB = 0, - C_XYZ = 1, - C_Luv = 2, - C_Lab = 3, - C_HSV = 4 + C_RGB = 0, /*!< R,G,B in [0,1] */ + C_XYZ = 1, /*!< X,Y,Z in [0,1] */ + C_Luv = 2, /*!< L in [0,100], u in [-83,175], v in [-134,108] */ + C_Lab = 3, /*!< L in [0,100], u in [-86,98], v in [-108,95] */ + C_HSV = 4, /*!< H,S,V in [0,1] */ + C_HSL = 5 /*!< H,S,L in [0,1] */ } ; typedef Geom::Vector<3,REAL> VEC3 ; /*!< Triplet for color encoding */ @@ -109,6 +110,11 @@ public: // methods * @return Lab value of provided colour */ VEC3 getHSV() ; + /** + * getR + * @return HSL value of provided colour + */ + VEC3 getHSL() ; /** * getR * @return XYZ value of provided colour @@ -124,6 +130,7 @@ private: // private members VEC3 *Lab ; VEC3 *HSV ; VEC3 *XYZ ; + VEC3 *HSL ; bool convert(enum ColourEncoding from, enum ColourEncoding to) ; void convertRGBtoXYZ() ; @@ -135,9 +142,30 @@ private: // private members void convertXYZtoLab() ; void convertLabToXYZ() ; + /** + * Converts RGB to HSV. All is normalized between 0 and 1. + * Conversion formula adapted from http://en.wikipedia.org/wiki/HSL_color_space. + */ void convertRGBtoHSV() ; + /** + * Converts HSV to RGB. All is normalized between 0 and 1. + * Conversion formula adapted from http://en.wikipedia.org/wiki/HSL_color_space. + */ void convertHSVtoRGB() ; + /** + * Converts RGB to HSL. All is normalized between 0 and 1. + * Conversion formula adapted from http://en.wikipedia.org/wiki/HSL_color_space. + */ + void convertRGBtoHSL() ; + /** + * Converts HSL to RGB. All is normalized between 0 and 1. + * Conversion formula adapted from http://en.wikipedia.org/wiki/HSL_color_space. + */ + void convertHSLtoRGB() ; + + static REAL hue2rgb(const REAL& p, const REAL& q, REAL t) ; + private: // private constants // D65 reference white static const REAL Xn = 0.950456 ; diff --git a/include/Utils/colourConverter.hpp b/include/Utils/colourConverter.hpp index b7370dcff82b0d197051b1487c91a693523d56a7..cf17bfcb3f297476b3b8d453a8ebc7cb48458935 100644 --- a/include/Utils/colourConverter.hpp +++ b/include/Utils/colourConverter.hpp @@ -32,7 +32,8 @@ ColourConverter::ColourConverter(const VEC3& col, const enum ColourEncodin Luv(NULL), Lab(NULL), HSV(NULL), - XYZ(NULL) + XYZ(NULL), + HSL(NULL) { originalEnc = enc ; @@ -70,12 +71,18 @@ ColourConverter::ColourConverter(const VEC3& col, const enum ColourEncodin break ; case (C_HSV) : #ifdef DEBUG - if (!(-0.001 < col[0] && col[0] < 360.001 && -0.001 < col[1] && col[1] < 1.001 && -0.001 < col[2] && col[2] < 1.001)) - std::cerr << "Warning : an unvalid Lab color was entered in ColourConverter constructor" << std::endl ; + if (!(-0.001 < col[0] && col[0] < 1.001 && -0.001 < col[1] && col[1] < 1.001 && -0.001 < col[2] && col[2] < 1.001)) + std::cerr << "Warning : an unvalid HSV color was entered in ColourConverter constructor" << std::endl ; #endif HSV = new VEC3(col) ; break ; - + case (C_HSL) : + #ifdef DEBUG + if (!(-0.001 < col[0] && col[0] < 1.001 && -0.001 < col[1] && col[1] < 1.001 && -0.001 < col[2] && col[2] < 1.001)) + std::cerr << "Warning : an unvalid HSL color was entered in ColourConverter constructor" << std::endl ; + #endif + HSL = new VEC3(col) ; + break ; } } @@ -87,6 +94,7 @@ ColourConverter::~ColourConverter() delete XYZ ; delete Lab ; delete HSV ; + delete HSL ; } template @@ -112,6 +120,10 @@ Geom::Vector<3,REAL> ColourConverter::getColour(enum ColourEncoding enc) { return getHSV() ; break ; + case (C_HSL) : + return getHSL() ; + break ; + default : assert(!"Should never arrive here : ColourConverter::getColour default case") ; return getOriginal() ; @@ -119,12 +131,16 @@ Geom::Vector<3,REAL> ColourConverter::getColour(enum ColourEncoding enc) { } template -Geom::Vector<3,REAL> ColourConverter::getOriginal() { +Geom::Vector<3,REAL> +ColourConverter::getOriginal() +{ return getColour(this->originalEnc) ; } template -Geom::Vector<3,REAL> ColourConverter::getRGB() { +Geom::Vector<3,REAL> +ColourConverter::getRGB() +{ if (RGB == NULL) convert(originalEnc,C_RGB) ; @@ -132,7 +148,9 @@ Geom::Vector<3,REAL> ColourConverter::getRGB() { } template -Geom::Vector<3,REAL> ColourConverter::getLuv() { +Geom::Vector<3,REAL> +ColourConverter::getLuv() +{ if (Luv == NULL) convert(originalEnc,C_Luv) ; @@ -140,7 +158,9 @@ Geom::Vector<3,REAL> ColourConverter::getLuv() { } template -Geom::Vector<3,REAL> ColourConverter::getLab() { +Geom::Vector<3,REAL> +ColourConverter::getLab() +{ if (Lab == NULL) convert(originalEnc,C_Lab) ; @@ -148,7 +168,9 @@ Geom::Vector<3,REAL> ColourConverter::getLab() { } template -Geom::Vector<3,REAL> ColourConverter::getXYZ() { +Geom::Vector<3,REAL> +ColourConverter::getXYZ() +{ if (XYZ == NULL) { convert(originalEnc,C_XYZ) ; } @@ -157,7 +179,9 @@ Geom::Vector<3,REAL> ColourConverter::getXYZ() { } template -Geom::Vector<3,REAL> ColourConverter::getHSV() { +Geom::Vector<3,REAL> +ColourConverter::getHSV() +{ if (HSV == NULL) convert(originalEnc,C_HSV) ; @@ -165,7 +189,20 @@ Geom::Vector<3,REAL> ColourConverter::getHSV() { } template -void ColourConverter::convertRGBtoXYZ() { +Geom::Vector<3,REAL> +ColourConverter::getHSL() +{ + if (HSL == NULL) + convert(originalEnc,C_HSL) ; + + return *HSL ; +} + + +template +void +ColourConverter::convertRGBtoXYZ() +{ Geom::Matrix<3,3,REAL> M ; M(0,0) = 0.412453 ; @@ -189,7 +226,9 @@ void ColourConverter::convertRGBtoXYZ() { } template -void ColourConverter::convertXYZtoRGB() { +void +ColourConverter::convertXYZtoRGB() +{ Geom::Matrix<3,3,REAL> M ; M(0,0) = 3.240479 ; @@ -213,7 +252,8 @@ void ColourConverter::convertXYZtoRGB() { } template -void ColourConverter::convertRGBtoHSV() +void +ColourConverter::convertRGBtoHSV() { const REAL& r = (*RGB)[0] ; const REAL& g = (*RGB)[1] ; @@ -221,27 +261,38 @@ void ColourConverter::convertRGBtoHSV() const REAL& max = std::max(std::max(r,g),b) ; const REAL& min = std::min(std::min(r,g),b) ; + VEC3 c ; + REAL& H = c[0] ; + REAL& S = c[1] ; + REAL& V = c[2] ; + + const REAL diff = max - min ; + + V = max ; + S = max == 0. ? 0 : diff / max ; + + if (max == min) { - c[0] = 0 ; - } - else if (max == r) - { - c[0] = 60 * (g - b) / (max - min) + 360 ; - c[0] = (unsigned int)(c[0]) % 360 ; + H = 0 ; } - else if (max == g) - { - c[0] = 60 * (b - r) / (max - min) + 120 ; - } - else if (max == b) + else { - c[0] = 60 * (r - g) / (max - min) + 240 ; + if (max == r) + { + H = (g - b) / diff + (g < b ? 6 : 0) ; + } + else if (max == g) + { + H = (b - r) / diff + 2 ; + } + else if (max == b) + { + H = (r - g) / diff + 4 ; + } } - - c[1] = (max == 0) ? 0 : 1 - min/max ; - c[2] = max ; + H /= 6. ; if (HSV != NULL) *HSV = c ; @@ -250,54 +301,134 @@ void ColourConverter::convertRGBtoHSV() } template -void ColourConverter::convertHSVtoRGB() +void +ColourConverter::convertHSVtoRGB() { const REAL& H = (*HSV)[0] ; - const REAL& s = (*HSV)[1] ; - const REAL& v = (*HSV)[2] ; + const REAL& S = (*HSV)[1] ; + const REAL& V = (*HSV)[2] ; - const unsigned int Hi = (unsigned int)(floor(H / 60)) % 6 ; - const REAL f = (H / 60) - Hi ; - const REAL l = v * (1 - s) ; - const REAL m = v * (1 - f * s) ; - const REAL n = v * (1 - (1 - f) * s) ; + const int i = std::floor(H * 6); + const REAL f = H * 6 - i; + const REAL p = V * (1 - S); + const REAL q = V * (1 - f * S); + const REAL t = V * (1 - (1 - f) * S); VEC3 c ; - switch(Hi) + REAL& r = c[0] ; + REAL& g = c[1] ; + REAL& b = c[2] ; + + switch(i % 6) { - case(0) : - { - c = VEC3(v,n,l) ; - } - break ; - case(1): - { - c = VEC3(m,v,l) ; - } - break ; - case(2): + case 0: r = V, g = t, b = p; break; + case 1: r = q, g = V, b = p; break; + case 2: r = p, g = V, b = t; break; + case 3: r = p, g = q, b = V; break; + case 4: r = t, g = p, b = V; break; + case 5: r = V, g = p, b = q; break; + } + + if (RGB != NULL) + *RGB = c ; + else + RGB = new VEC3(c) ; +} + +template +void +ColourConverter::convertRGBtoHSL() +{ + const REAL& r = (*RGB)[0] ; + const REAL& g = (*RGB)[1] ; + const REAL& b = (*RGB)[2] ; + const REAL& max = std::max(std::max(r,g),b) ; + const REAL& min = std::min(std::min(r,g),b) ; + + VEC3 c ; + REAL& H = c[0] ; + REAL& S = c[1] ; + REAL& L = c[2] ; + + const REAL sum = max + min ; + L = sum / 2. ; + + if (max == min) + { + H = 0 ; + S = 0 ; + } + else + { + const REAL diff = max - min ; + S = L > 0.5 ? diff / (2 - sum) : diff / sum ; + + if (max == r) { - c = VEC3(l,v,n) ; + H = (g - b) / diff + (g < b ? 6 : 0) ; } - break ; - case(3): + else if (max == g) { - c = VEC3(l,m,v) ; + H = (b - r) / diff + 2 ; } - break ; - case(4): + else if (max == b) { - c = VEC3(n,l,v) ; + H = (r - g) / diff + 4 ; } - break ; + } + H /= 6. ; - case(5): - { - c = VEC3(v,l,m) ; - } - break ; + if (HSL != NULL) + *HSL = c ; + else + HSL = new VEC3(c) ; +} - } +template +REAL +ColourConverter::hue2rgb(const REAL& p, const REAL& q, REAL t) +{ + if(t < 0) + t += 1 ; + if(t > 1) + t -= 1 ; + if(t < 1/6.) + return p + (q - p) * 6 * t ; + if(t < 1/2.) + return q ; + if(t < 2/3.) + return p + (q - p) * (2/3. - t) * 6 ; + + return p ; +} + +template +void +ColourConverter::convertHSLtoRGB() +{ + const REAL& H = (*HSL)[0] ; + const REAL& S = (*HSL)[1] ; + const REAL& L = (*HSL)[2] ; + + VEC3 c ; + REAL& r = c[0] ; + REAL& g = c[1] ; + REAL& b = c[2] ; + + if(S < 1e-8) + { + r = L ; + g = L ; + b = L ; + } + else + { + const REAL q = L < 0.5 ? L * (1 + S) : L + S - L * S; + const REAL p = 2 * L - q ; + r = hue2rgb(p, q, H + 1/3.) ; + g = hue2rgb(p, q, H) ; + b = hue2rgb(p, q, H - 1/3.) ; + } if (RGB != NULL) *RGB = c ; @@ -306,7 +437,8 @@ void ColourConverter::convertHSVtoRGB() } template -void ColourConverter::convertXYZtoLuv() +void +ColourConverter::convertXYZtoLuv() { REAL L,u,v ; @@ -333,7 +465,8 @@ void ColourConverter::convertXYZtoLuv() } template -void ColourConverter::convertLuvToXYZ() +void +ColourConverter::convertLuvToXYZ() { REAL X,Y,Z ; @@ -360,7 +493,9 @@ void ColourConverter::convertLuvToXYZ() } template -void ColourConverter::convertXYZtoLab() { +void +ColourConverter::convertXYZtoLab() +{ REAL L,a,b ; REAL &X = (*XYZ)[0] ; @@ -391,7 +526,9 @@ void ColourConverter::convertXYZtoLab() { } template -void ColourConverter::convertLabToXYZ() { +void +ColourConverter::convertLabToXYZ() +{ REAL X,Y,Z ; REAL &L = (*Lab)[0] ; @@ -423,7 +560,9 @@ void ColourConverter::convertLabToXYZ() { } template -bool ColourConverter::convert(enum ColourEncoding from, enum ColourEncoding to) { +bool +ColourConverter::convert(enum ColourEncoding from, enum ColourEncoding to) +{ if (to == from) { #ifdef DEBUG std::cout << "WARNING ColourConverter::convert(from,to) : conversion into same colour space" << std::endl ; @@ -458,6 +597,10 @@ bool ColourConverter::convert(enum ColourEncoding from, enum ColourEncodin if (HSV == NULL) convertRGBtoHSV() ; break ; + case(C_HSL) : + if (HSL == NULL) + convertRGBtoHSL() ; + break ; default : std::cerr << "Colour conversion not supported" << std::endl ; return false ; @@ -486,7 +629,7 @@ bool ColourConverter::convert(enum ColourEncoding from, enum ColourEncodin convertXYZtoLab() ; } break ; - case(C_HSV) : { + case(C_HSV) : if (HSV == NULL) { if (RGB == NULL) { if (XYZ == NULL) @@ -496,6 +639,16 @@ bool ColourConverter::convert(enum ColourEncoding from, enum ColourEncodin convertRGBtoHSV() ; } break ; + case(C_HSL) : { + if (HSL == NULL) { + if (RGB == NULL) { + if (XYZ == NULL) + convertLuvToXYZ() ; + convertXYZtoRGB() ; + } + convertRGBtoHSL() ; + } + break ; } default : std::cerr << "Colour conversion not supported" << std::endl ; @@ -524,6 +677,13 @@ bool ColourConverter::convert(enum ColourEncoding from, enum ColourEncodin convertRGBtoHSV() ; } break ; + case(C_HSL) : + if (HSL==NULL) { + if (RGB==NULL) + convertXYZtoRGB() ; + convertRGBtoHSL() ; + } + break ; default : std::cerr << "Colour conversion not supported" << std::endl ; return false ; @@ -563,7 +723,17 @@ bool ColourConverter::convert(enum ColourEncoding from, enum ColourEncodin } break ; } - + case(C_HSL) : { + if (HSL == NULL) { + if (RGB == NULL) { + if (XYZ == NULL) + convertLabToXYZ() ; + convertXYZtoRGB() ; + } + convertRGBtoHSL() ; + } + break ; + } default : std::cerr << "Colour conversion not supported" << std::endl ; return false ; @@ -613,12 +783,75 @@ bool ColourConverter::convert(enum ColourEncoding from, enum ColourEncodin convertXYZtoLuv() ; } break ; + case(C_HSL) : + if (HSL == NULL) { + if (RGB == NULL) + convertHSVtoRGB() ; + convertRGBtoHSL() ; + } + break ; default : std::cerr << "Colour conversion not supported" << std::endl ; return false ; } break ; + case(C_HSL) : + switch (to) { + case(C_RGB) : { + if (RGB == NULL) { + convertHSLtoRGB() ; + } + break ; + } + case(C_XYZ) : { + if (XYZ == NULL) + { + if (RGB == NULL) + convertHSLtoRGB() ; + convertRGBtoXYZ() ; + } + break ; + } + case(C_Lab) : + if (Lab == NULL) { + if (XYZ == NULL) + { + if (RGB == NULL) + { + convertHSLtoRGB() ; + } + convertRGBtoXYZ() ; + } + convertXYZtoLab() ; + } + break ; + case(C_Luv) : + if (Luv == NULL) { + if (XYZ == NULL) + { + if (RGB == NULL) + { + convertHSLtoRGB() ; + } + convertRGBtoXYZ() ; + } + convertXYZtoLuv() ; + } + break ; + case(C_HSL) : + if (HSL == NULL) { + if (RGB == NULL) + convertHSLtoRGB() ; + convertRGBtoHSV() ; + } + break ; + default : + std::cerr << "Colour conversion not supported" << std::endl ; + return false ; + } + break ; + default : std::cerr << "Colour conversion not supported" << std::endl ; return false ;