vcglib/apps/nexus/remapping.cpp

521 lines
15 KiB
C++

/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2004 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
/****************************************************************************
History
$Log: not supported by cvs2svn $
Revision 1.11 2005/02/19 14:00:43 ponchio
Small opt.
Revision 1.10 2005/02/19 10:45:05 ponchio
Patch generalized and small fixes.
Revision 1.9 2005/02/08 12:43:03 ponchio
Added copyright
****************************************************************************/
#include <iostream>
#include "remapping.h"
#include "watch.h"
using namespace std;
using namespace vcg;
using namespace nxs;
bool BlockIndex::Save(const string &file) {
FILE *fp = fopen(file.c_str(), "wb+");
if(!fp) {
cerr << "Could not save: " << file << endl;
return false;
}
unsigned int nsize = size();
fwrite(&nsize, sizeof(unsigned int), 1, fp);
fwrite(&*begin(), sizeof(BlockEntry), nsize, fp);
fclose(fp);
return true;
}
bool BlockIndex::Load(const string &file) {
FILE *fp = fopen(file.c_str(), "rb");
if(!fp) {
cerr << "Could not load: " << file << endl;
return false;
}
unsigned int nsize;
fread(&nsize, sizeof(unsigned int), 1, fp);
resize(nsize);
fread(&*begin(), sizeof(BlockEntry), nsize, fp);
fclose(fp);
return true;
}
void nxs::Remap(VChain &chain,
VFile<vcg::Point3f> &points,
VFile<unsigned int> &remap,
BlockIndex &index,
unsigned int target_size,
unsigned int min_size,
unsigned int max_size,
float scaling,
int steps) {
VPartition *finepart = new VPartition;
chain.push_back(finepart);
BuildPartition(*finepart, points, target_size, min_size, max_size, steps);
VPartition *coarsepart = new VPartition;
chain.push_back(coarsepart);
BuildPartition(*coarsepart, points,
(int)(target_size/scaling), min_size, max_size, steps);
cerr << "Fine size: " << finepart->size() << endl;
cerr << "Coarse size: " << coarsepart->size() << endl;
// typedef map<pair<unsigned int, unsigned int>, unsigned int> FragIndex;
typedef map<unsigned int, map<unsigned int, unsigned int> > FragIndex;
FragIndex patches;
unsigned int totpatches = 0;
vector<unsigned int> count;
Point3f bari;
for(unsigned int i = 0; i < points.Size(); i++) {
bari = points[i];
unsigned int fine = finepart->Locate(bari);
unsigned int coarse = coarsepart->Locate(bari);
unsigned int patch;
if(!patches.count(coarse) || !patches[coarse].count(fine)) {
patch = totpatches;
patches[coarse][fine] = totpatches++;
} else
patch = patches[coarse][fine];
remap[i] = patch;
while(count.size() <= patch)
count.push_back(0);
count[patch]++;
}
for(unsigned int i = 0; i < totpatches; i++) {
if(count[i] > 32000) {
//TODO do something to reduce patch size... :P
cerr << "Found a patch too big... sorry\n";
exit(0);
}
}
unsigned int mean = 0;
for(unsigned int i = 0; i < count.size(); i++)
mean += count[i];
mean /= count.size();
min_size /= 4;
cerr << "Pruning small patches... < " << min_size << " mean: " << mean << endl;
//prune small patches
vector<int> patch_map;
patch_map.resize(totpatches);
for(unsigned int i = 0; i < totpatches; i++)
patch_map[i] = i;
for(FragIndex::iterator s = patches.begin(); s != patches.end(); s++) {
map<unsigned int, unsigned int> &fines = (*s).second;
while(1) {
if(fines.size() <= 1) break;
unsigned int inf_fine = 0xffffffff;
unsigned int inf_count, min_count;
unsigned int min_fine = 0xffffffff;
unsigned int min_patch, inf_patch;
map<unsigned int, unsigned int>::iterator t;
for(t = fines.begin(); t != fines.end(); t++) {
unsigned int c = count[(*t).second];
if(inf_fine == 0xffffffff || c < inf_count) {
if(inf_fine != 0xffffffff) {
min_fine = inf_fine;
min_count = inf_count;
min_patch = inf_patch;
}
inf_fine = (*t).first;
inf_count = c;
inf_patch = (*t).second;
} else if(min_fine == 0xffffffff || c < min_count) {
min_fine = (*t).first;
min_count = c;
min_patch = (*t).second;
}
}
if(inf_count >= min_size ||
inf_count + min_count > max_size) break;
count[min_patch] += count[inf_patch];
patch_map[inf_patch] = min_patch;
fines.erase(inf_fine);
}
}
for(unsigned int i = 0; i < totpatches; i++)
while(patch_map[patch_map[i]] != patch_map[i])
patch_map[i] = patch_map[patch_map[i]];
//now we remap remaining patches into 0 - n.
unsigned int new_totpatches = 0;
vector<int> patch_remap;
patch_remap.resize(totpatches, -1);
for(unsigned int i = 0; i < totpatches; i++) {
unsigned int p = patch_map[i];
if(patch_remap[p] == -1)
patch_remap[p] = new_totpatches++;
patch_remap[i] = patch_remap[p];
}
cerr << "Building fragments\n";
//building fragments
for(FragIndex::iterator s = patches.begin(); s != patches.end(); s++) {
unsigned int coarse = (*s).first;
map<unsigned int, unsigned int> &fines = (*s).second;
map<unsigned int, unsigned int>::iterator t;
for(t = fines.begin(); t != fines.end(); t++) {
unsigned int fine = (*t).first;
unsigned int oldpatch = (*t).second;
assert(oldpatch < patch_remap.size());
unsigned int patch = patch_remap[oldpatch];
if(patch != -1) //not deleted...
chain.oldfragments[coarse].insert(patch);
}
}
cerr << "remapping faces again\n";
//remapping faces
index.resize(new_totpatches);
for(unsigned int i = 0; i < remap.Size(); i++) {
unsigned int patch = remap[i];
#ifdef CONTROLS
if(patch == 0xffffffff) {
cerr << "RESIGH\n";
exit(0);
}
if(patch_remap[patch] == -1) {//must relocate this thing....
//TODO
cerr << "Could not do this\n";
exit(0);
}
#endif
unsigned int newpatch = patch_remap[patch];
assert(newpatch < index.size());
remap[i] = newpatch;
BlockEntry &entry = index[newpatch];
entry.size++;
}
cerr << "fixing offsets in index\n";
//Fixing offset
int64 offset = 0;
for(unsigned int i = 0; i < index.size(); i++) {
assert(index[i].size < 65000);
index[i].offset = offset;
offset += index[i].size;
}
}
void nxs::BuildPartition(VPartition &part,
VFile<vcg::Point3f> &points,
unsigned int target_size,
unsigned int min_size,
unsigned int max_size,
int steps) {
//TODO: improve quality of patches and implement threshold.
unsigned int ncells = points.Size()/target_size;
cerr << "Target partition size: " << ncells
<< " mean: " << points.Size()/ncells << endl;
srand(0);
for(unsigned int i = 0; i < points.Size(); i++) {
int f = (int)(target_size * (float)rand()/(RAND_MAX + 1.0));
if(f == 2) {
Point3f &point = points[i];
part.push_back(point);
}
}
//TODO! Check for duplicates (use the closest :P)
part.Init();
vector<Point3f> centroids;
vector<unsigned int> counts;
for(int step = 0; step < steps; step++) {
cerr << "Optimization step: " << step+1 << "/" << steps << endl;
centroids.clear();
counts.clear();
centroids.resize(part.size(), Point3f(0, 0, 0));
counts.resize(part.size(), 0);
Report report(points.Size());
for(unsigned int v = 0; v < points.Size(); v++) {
if(v & 0xffff == 0xffff)
report.Step(v);
unsigned int target = part.Locate(points[v]);
centroids[target] += points[v];
counts[target]++;
}
for(unsigned int v = 0; v < centroids.size(); v++)
if(counts[v] != 0)
centroids[v]/= counts[v];
double quality = 0;
for(int i = 0; i < part.size(); i++)
quality += (counts[i] - target_size) * (counts[i] - target_size);
cerr << "Quality: " << quality << endl;
if(step == steps-1) {
if(!Optimize(part, ncells, target_size, min_size, max_size,
centroids, counts, false))
step--;
} else
Optimize(part, ncells, target_size, min_size, max_size,
centroids, counts, true);
}
cerr << "Partition size: " << part.size()
<< " mean: " << (float)(points.Size()/part.size()) << endl << endl;
}
void nxs::BuildLevel(VChain &chain,
Nexus &nexus,
unsigned int offset,
float scaling,
unsigned int target_size,
unsigned int min_size,
unsigned int max_size,
int steps) {
unsigned int totface = 0;
unsigned int totvert = 0;
for(unsigned int idx = offset; idx < nexus.size(); idx++) {
totface += nexus[idx].nface;
totvert += nexus[idx].nvert;
}
VPartition *fine = chain[chain.size()-1];
fine->Init();
VPartition *coarse = new VPartition;
chain.push_back(coarse);
//unsigned int ncells = (unsigned int)(fine.size() * scaling);
unsigned int ncells = (unsigned int)(scaling * totface/target_size);
//TODO this method for selecting the seeds is ugly!
float ratio = ncells/(float)(nexus.size() - offset);
float cratio = 0;
for(unsigned int idx = offset; idx < nexus.size(); idx++) {
cratio += ratio;
if(cratio > 1) {
Patch patch = nexus.GetPatch(idx);
Point3f &v = patch.Vert3f(0);
coarse->push_back(v);
cratio -= 1;
}
}
if(coarse->size() == 0) {
Patch patch = nexus.GetPatch(0);
coarse->push_back(patch.Vert3f(0));
}
float coarse_vmean = totface/(float)coarse->size();
coarse->Init();
cerr << "Ncells: " << ncells << endl;
cerr << "Coarse size: " << coarse->size() << endl;
cerr << "Coarse mean: " << coarse_vmean << " mean_size: " << target_size << endl;
//here goes some optimization pass.
//Coarse optimization.
vector<Point3f> centroids;
vector<unsigned int> counts;
for(int step = 0; step < steps; step++) {
cerr << "Optimization step: " << step+1 << "/" << steps << endl;
centroids.clear();
counts.clear();
centroids.resize(coarse->size(), Point3f(0, 0, 0));
counts.resize(coarse->size(), 0);
Report report(nexus.size());
for(unsigned int idx = offset; idx < nexus.size(); idx++) {
report.Step(idx);
Patch patch = nexus.GetPatch(idx);
for(unsigned int i = 0; i < patch.nf; i++) {
unsigned short *face = patch.Face(i);
Point3f bari = (patch.Vert3f(face[0]) +
patch.Vert3f(face[1]) +
patch.Vert3f(face[2]))/3;
assert(coarse->size() > 0);
unsigned int target = coarse->Locate(bari);
assert(target < coarse->size());
centroids[target] += bari;
counts[target]++;
}
}
for(unsigned int v = 0; v < centroids.size(); v++)
if(counts[v] != 0)
centroids[v]/= counts[v];
if(step == steps-1) {
if(!Optimize(*coarse, ncells, (int)coarse_vmean, min_size, max_size,
centroids, counts, false))
step--;
} else
Optimize(*coarse, ncells, (int)coarse_vmean, min_size, max_size,
centroids, counts, true);
}
chain.newfragments.clear();
}
int nxs::GetBest(VPartition &part, unsigned int seed,
vector<bool> &mark,
vector<unsigned int> &counts) {
vector<int> nears;
vector<float> dists;
int nnear = 7;
if(part.size() < 7) nnear = part.size()/2;
if(!nnear) return -1;
part.Closest(part[seed], nnear, nears, dists);
int best = -1;
int bestcount = -1;
int bestdist = -1;
for(int k = 0; k < nnear; k++) {
int c = nears[k];
if(c == seed) continue;
assert(c >= 0);
assert(c < part.size());
if(mark[c]) continue;
if(bestcount < 0 ||
(counts[c] < bestcount)) {
best = c;
bestcount = counts[c];
}
}
return best;
}
bool nxs::Optimize(VPartition &part,
unsigned int target_cells,
unsigned int target_size,
unsigned int min_size,
unsigned int max_size,
vector<Point3f> &centroids,
vector<unsigned int> &counts,
bool join) {
if(max_size > target_size *3)
max_size = target_size * 3;
min_size = (unsigned int)(target_size * 0.3f);
unsigned int toobig = 0;
unsigned int toosmall = 0;
for(unsigned int i = 0; i < part.size(); i++) {
if(counts[i] > max_size) toobig++;
if(counts[i] < min_size) toosmall--;
}
unsigned int close = part.size()/2;
if(close < 1) close = 1;
if(close > 10) close = 10;
unsigned int failed = 0;
vector<Point3f> seeds;
vector<bool> mark;
mark.resize(part.size(), false);
vector<int> nears;
vector<float> dists;
//removing small ones.
for(unsigned int i = 0; i < part.size(); i++) {
if(counts[i] > max_size) {
float radius;
if(part.size() == 1)
radius = 0.00001;
else
radius = part.Radius(i)/4;
seeds.push_back(centroids[i] + Point3f(1, -1, 1) * radius);
seeds.push_back(centroids[i] + Point3f(-1, 1, 1) * radius);
seeds.push_back(centroids[i] + Point3f(-1, -1, -1) * radius);
seeds.push_back(centroids[i] + Point3f(1, 1, -1) * radius);
continue;
}
if(counts[i] < min_size)
continue;
part.Closest(part[i], close, nears, dists);
Point3f dir(0,0,0);
for(unsigned int k = 0; k < close; k++) {
unsigned int n = nears[k];
float c = (target_size - (float)counts[n])/
((float)target_size * close);
dir += (centroids[i] - part[n]) * c;
}
seeds.push_back(centroids[i] + dir);
}
part.clear();
for(unsigned int i = 0; i < seeds.size(); i++)
part.push_back(seeds[i]);
if(part.size() == 0) {
cerr << "OOOPS i accidentally deleted all seeds... backup :P\n";
part.push_back(Point3f(0,0,0));
}
part.Init();
return true;
}