1364 lines
45 KiB
C++
1364 lines
45 KiB
C++
/****************************************************************************
|
|
* VCGLib o o *
|
|
* Visual and Computer Graphics Library o o *
|
|
* _ O _ *
|
|
* Copyright(C) 2004-2016 \/)\/ *
|
|
* 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. *
|
|
* *
|
|
****************************************************************************/
|
|
|
|
#ifndef __VOLUME_H__
|
|
#define __VOLUME_H__
|
|
|
|
#include "voxel.h"
|
|
#include <vcg/space/index/grid_static_ptr.h>
|
|
|
|
namespace vcg {
|
|
|
|
// forward definition
|
|
template < class VOL >
|
|
class VolumeIterator;
|
|
|
|
//******************************************
|
|
//******************************************
|
|
//typedef Voxel<float> Voxelf;
|
|
|
|
const char *SFormat( const char * f, ... )
|
|
{
|
|
static char buf[4096];
|
|
va_list marker;
|
|
va_start( marker, f );
|
|
vsprintf(buf,f,marker);
|
|
va_end( marker );
|
|
return buf;
|
|
}
|
|
|
|
|
|
template<class VOX_TYPE, class SCALAR_TYPE=float>
|
|
class Volume {
|
|
public:
|
|
typedef SCALAR_TYPE scalar;
|
|
typedef Point3<scalar> Point3x;
|
|
typedef Box3<scalar> Box3x;
|
|
|
|
typedef VOX_TYPE voxel_type;
|
|
|
|
static int BLOCKSIDE() { return 8;}
|
|
Volume(){
|
|
SetDefaultParam();
|
|
}
|
|
// I dati veri e propri
|
|
// Sono contenuti in un vettore di blocchi.
|
|
std::vector< std::vector<VOX_TYPE> > rv;
|
|
Box3x bbox;
|
|
|
|
_int64 AskedCells;
|
|
Point3x dim; /// Dimensione spaziale (lunghezza lati) del bbox
|
|
|
|
Point3i sz; /// Dimensioni griglia come numero di celle per lato
|
|
|
|
Point3i ssz; /// Dimensioni sottoblocco in esame come numero di celle per lato
|
|
|
|
Point3i rsz; /// Dimensioni macro griglia dei blocchi in cui e' suddiviso il volume (ogni blocco e' BLOCKSIDE()^3 celle)
|
|
|
|
Point3i asz; /// Dimensioni macro griglia dei blocchi relativa al sottoblocco in questione (quello allocato davvero!)
|
|
|
|
|
|
Point3x voxel; /// Dimensioni di una cella
|
|
|
|
|
|
int WN,WP; // di quanti vox mi devo allargare per settare la manhattan distance in neg e pos
|
|
int DeltaVoxelSafe; // di quanti vox mi devo allargare per stare sicuro nel fare solo una sottoparte.
|
|
|
|
const Point3i ISize() { return sz; }
|
|
private :
|
|
// offset e distanze varie precalcolate una volta per tutte
|
|
Point3f nnf[26];
|
|
Point3i nni[26];
|
|
float len[26];
|
|
float slen[26];
|
|
|
|
/// Gestione sottoparte
|
|
Point3i div;
|
|
Point3i pos;
|
|
public:
|
|
Box3i SubPart; // Sottoparte del volume da considerare ufficialmente
|
|
Box3x SubBox; // BBox della sottoparte del volume da considerare in coord assolute
|
|
Box3i SubPartSafe; // come sopra ma aumentati per sicurezza.
|
|
Box3x SubBoxSafe;
|
|
|
|
FILE *LogFP;
|
|
bool Verbose; // se true stampa un sacco di info in piu su logfp;
|
|
|
|
void SetDefaultParam(){
|
|
WN=0;
|
|
WP=1;
|
|
//WN=-2;//
|
|
//WP=3;
|
|
DeltaVoxelSafe=BLOCKSIDE();
|
|
Verbose=true;
|
|
LogFP=stderr;
|
|
}
|
|
|
|
void Init(const Volume &VV)
|
|
{
|
|
SetDefaultParam();
|
|
WN=VV.WN;
|
|
WP=VV.WP;
|
|
DeltaVoxelSafe=VV.DeltaVoxelSafe;
|
|
Init(VV.AskedCells,VV.bbox,VV.div,VV.pos);
|
|
}
|
|
|
|
void Init(_int64 cells, Box3x bb, Point3i _div=Point3i(1,1,1), Point3i _pos=Point3i(0,0,0))
|
|
{
|
|
Point3d voxdim;voxdim.Import(bb.max-bb.min);
|
|
AskedCells=cells;
|
|
vcg::BestDim<double>( cells, voxdim, sz );
|
|
bbox=bb;
|
|
/*
|
|
printf("grid of ~%i kcells: %d x %d x %d \n",int(cells/1000),sz[0],sz[1],sz[2]);
|
|
printf("grid voxel size of %f %f %f\n",voxdim[0]/sz[0],voxdim[1]/sz[1],voxdim[2]/sz[2]);
|
|
*/
|
|
// il box deve essere multipli di BLOCKSIDE()
|
|
sz=((sz/BLOCKSIDE())+Point3i(1,1,1))*BLOCKSIDE();
|
|
|
|
|
|
rsz=sz/BLOCKSIDE();
|
|
if(sz!=rsz*BLOCKSIDE()) {
|
|
assert(0); // il box deve essere multipli di BLOCKSIDE()
|
|
exit(-1);
|
|
}
|
|
|
|
dim=bbox.max-bbox.min;
|
|
voxel[0]=dim[0]/sz[0];
|
|
voxel[1]=dim[1]/sz[1];
|
|
voxel[2]=dim[2]/sz[2];
|
|
|
|
SetSubPart(_div,_pos);
|
|
ssz=SubPartSafe.max-SubPartSafe.min;
|
|
asz=ssz/BLOCKSIDE() + Point3i(1,1,1);
|
|
rv.clear();
|
|
rv.resize(asz[0]*asz[1]*asz[2]);
|
|
for(size_t i=0;i<rv.size();++i)
|
|
rv[i].resize(0,VOX_TYPE::Zero());
|
|
SetDim(bb);
|
|
}
|
|
|
|
private:
|
|
|
|
// Da chiamare sempre DOPO la resize...
|
|
void SetDim(const Box3x & /*bb*/)
|
|
{
|
|
|
|
// Setup the precomputed offsets and offset normals
|
|
int cnt=0,x,y,z;
|
|
for(z=-1;z<=1;++z){
|
|
for(y=-1;y<=1;++y){
|
|
for(x=-1;x<=1;++x)
|
|
if(x!=0 || y!=0 || z!=0)
|
|
{
|
|
nnf[cnt]=Point3f(x,y,z);
|
|
len[cnt]=nnf[cnt].Norm();
|
|
slen[cnt]=nnf[cnt].SquaredNorm();
|
|
nnf[cnt].Normalize();
|
|
nni[cnt]=Point3i(x,y,z);
|
|
cnt++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
Parametri
|
|
div indica il numero di blocchi da fare lungo i vari assi (sempre >=1)
|
|
pos indicano la coord del subbox da prendere in considerazione (sempre >=0 && < xdiv,ydiv,zdiv)
|
|
*/
|
|
|
|
void SetSubPart(Point3i _div, Point3i _pos)
|
|
{
|
|
int k;
|
|
// Controllo correttezza parametri.
|
|
for(k=0;k<3;++k)
|
|
{
|
|
assert(_div[k]>0);
|
|
if(_div[k]==0){
|
|
printf("Error in subbox definition:\n the subdiv settings must be greater than 0; it was %i %i %i\n",_div[0],_div[1],_div[2]);
|
|
exit(-1);
|
|
}
|
|
if(_pos[k]<0 || _pos[k]>=_div[k]){
|
|
printf("Error in subbox definition:\n the Position of the subbox must be between (0,0,0) and (%i,%i,%i); it was %i %i %i\n",_div[0],_div[1],_div[2],_pos[0],_pos[1],_pos[2]);
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
div=_div;
|
|
pos=_pos;
|
|
|
|
// Setting the subpart under analisys
|
|
for(k=0;k<3;++k)
|
|
{
|
|
SubPart.min[k]= pos[k]*sz[k]/div[k];
|
|
SubPart.max[k]=(pos[k]+1)*sz[k]/div[k];
|
|
SubBox.min[k]= bbox.min[k]+SubPart.min[k]*voxel[k];
|
|
SubBox.max[k]= bbox.min[k]+SubPart.max[k]*voxel[k];
|
|
}
|
|
|
|
// Setting the Safe Subpart under analisys
|
|
SubPartSafe=SubPart;
|
|
for(k=0;k<3;++k)
|
|
{
|
|
SubPartSafe.min[k] -= DeltaVoxelSafe;
|
|
SubPartSafe.max[k] += DeltaVoxelSafe;
|
|
|
|
if( SubPartSafe.min[k]< 0 ) SubPartSafe.min[k] = 0;
|
|
if( SubPartSafe.max[k]> sz[k] ) SubPartSafe.max[k] = sz[k];
|
|
SubBoxSafe.min[k]= bbox.min[k]+SubPartSafe.min[k]*voxel[k];
|
|
SubBoxSafe.max[k]= bbox.min[k]+SubPartSafe.max[k]*voxel[k];
|
|
}
|
|
/*
|
|
printf(" Computing only subvolume: (%d x %d x %d)= %dk cells \n"
|
|
" %d,%d,%d -> %d,%d,%d\n"
|
|
,SubPart.DimX(),SubPart.DimY(),SubPart.DimZ(),(int)(((__int64)SubPart.DimX()*(__int64)SubPart.DimY()*(__int64)SubPart.DimZ())/1000)
|
|
,SubPart.min[0] ,SubPart.min[1] ,SubPart.min[2]
|
|
,SubPart.max[0] ,SubPart.max[1] ,SubPart.max[2] );
|
|
*/
|
|
}
|
|
|
|
public:
|
|
|
|
// Sa
|
|
/*bool Write(string filename, const float &minv, const float &maxv )
|
|
{
|
|
FILE *fp;
|
|
if(div!=Point3i(1,1,1)) {
|
|
string subvoltag;
|
|
GetSubVolumeTag(subvoltag);
|
|
filename+=subvoltag;
|
|
}
|
|
string datname=filename;
|
|
string rawname=filename;
|
|
datname+=".dat";
|
|
rawname+=".raw";
|
|
|
|
fp=fopen(datname,"w");
|
|
|
|
fprintf(fp,"ObjectFileName: %s\n",rawname.c_str());
|
|
fprintf(fp,"TaggedFileName: ---\n");
|
|
fprintf(fp,"Resolution: %i %i %i\n",SubPart.max[0]-SubPart.min[0],SubPart.max[1]-SubPart.min[1],SubPart.max[2]-SubPart.min[2]);
|
|
fprintf(fp,"SliceThickness: %f %f %f\n",voxel[2]/voxel[0],voxel[2]/voxel[1],voxel[2]/voxel[2]);
|
|
fprintf(fp,"Format: UCHAR\n");
|
|
fprintf(fp,"NbrTags: 0\n");
|
|
fprintf(fp,"ObjectType: TEXTURE_VOLUME_OBJECT\n");
|
|
fprintf(fp,"ObjectModel: RGBA\n");
|
|
fprintf(fp,"GridType: EQUIDISTANT\n");
|
|
|
|
fclose(fp);
|
|
fp=fopen(rawname,"wb");
|
|
if(!fp)
|
|
{
|
|
printf("Error: unable ro open output volume file '%s'\n",filename);
|
|
return false;
|
|
}
|
|
|
|
int i,j,k;
|
|
for(k=SubPart.min[2];k<SubPart.max[2];++k)
|
|
for(j=SubPart.min[1];j<SubPart.max[1];++j)
|
|
for(i=SubPart.min[0];i<SubPart.max[0];++i)
|
|
{
|
|
float fv=V(i,j,k).V();
|
|
fv=(fv-minv)/(maxv-minv);
|
|
if(fv<0) fv=0;
|
|
else if(fv>1) fv=1;
|
|
fv=((fv*2.0f)-1.0f)*127;
|
|
char fs= (char) fv;
|
|
fwrite(&fs,sizeof(char),1,fp);
|
|
}
|
|
fclose(fp);
|
|
return true;
|
|
}*/
|
|
void AbsPos(Point3i pi, Point3x &p)
|
|
{
|
|
p[0]=bbox.min[0]+pi[0]*voxel[0];
|
|
p[1]=bbox.min[1]+pi[1]*voxel[1];
|
|
p[2]=bbox.min[2]+pi[2]*voxel[2];
|
|
}
|
|
|
|
|
|
void GetSubVolumeTag(std::string &subtag)
|
|
{
|
|
char buf[32];
|
|
if (div[0]<= 10 && div[1]<= 10 && div[2]<= 10 ) sprintf(buf,"_%01d%01d%01d",pos[0],pos[1],pos[2]);
|
|
else if(div[0]<= 100 && div[1]<= 100 && div[2]<= 100 ) sprintf(buf,"_%02d%02d%02d",pos[0],pos[1],pos[2]);
|
|
else sprintf(buf,"_%03d%03d%03d",pos[0],pos[1],pos[2]);
|
|
subtag=buf;
|
|
}
|
|
|
|
/*
|
|
* Compute the offset <lpos> inside the subblock <rpos> of voxel (x,y,z).
|
|
* return true if the subblock is allocated.
|
|
*/
|
|
bool Pos(const int &_x,const int &_y,const int &_z, int & rpos,int &lpos) const
|
|
{
|
|
int x=_x-SubPartSafe.min[0]; int y=_y-SubPartSafe.min[1]; int z=_z-SubPartSafe.min[2];
|
|
|
|
assert(_x>=SubPartSafe.min[0] && _x<SubPartSafe.max[0] &&
|
|
_y>=SubPartSafe.min[1] && _y<SubPartSafe.max[1] &&
|
|
_z>=SubPartSafe.min[2] && _z<SubPartSafe.max[2]);
|
|
|
|
// assert(x>=0 && x<sz[0] && y>=0 && y<sz[1] && z>=0 && z<sz[2]);
|
|
|
|
int rx=x/BLOCKSIDE(); int ry=y/BLOCKSIDE(); int rz=z/BLOCKSIDE();
|
|
assert(rx>=0 && rx<asz[0] && ry>=0 && ry<asz[1] && rz>=0 && rz<asz[2]);
|
|
rpos = rz*asz[0]*asz[1]+ry*asz[0]+rx;
|
|
assert(rpos < int(rv.size()));
|
|
int lx = x%BLOCKSIDE(); int ly = y%BLOCKSIDE(); int lz = z % BLOCKSIDE();
|
|
lpos = lz*BLOCKSIDE()*BLOCKSIDE()+ly*BLOCKSIDE()+lx;
|
|
if(rv[rpos].empty()) return false;
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
Funzione inversa della precedente
|
|
Date due posizioni rpos e lpos restituisce x,y,z assoluti
|
|
*/
|
|
bool IPos(int &x,int &y,int &z, const int & rpos, const int &lpos) const
|
|
{
|
|
assert (rpos>=0 && lpos >=0);
|
|
|
|
int rz = rpos / (asz[0]*asz[1]); int remainder = rpos % (asz[0]*asz[1]);
|
|
int ry = ( remainder ) / asz[0] ;
|
|
int rx = remainder % asz[0];
|
|
|
|
assert(rx>=0 && rx<asz[0] && ry>=0 && ry<asz[1] && rz>=0 && rz<asz[2]);
|
|
|
|
int lz = lpos / (BLOCKSIDE()*BLOCKSIDE()); int lemaindel = lpos % (BLOCKSIDE()*BLOCKSIDE());
|
|
int ly = ( lemaindel ) / BLOCKSIDE();
|
|
int lx = lemaindel % BLOCKSIDE();
|
|
|
|
x = rx*BLOCKSIDE()+lx;
|
|
y = ry*BLOCKSIDE()+ly;
|
|
z = rz*BLOCKSIDE()+lz;
|
|
|
|
x+=SubPartSafe.min[0];
|
|
y+=SubPartSafe.min[1];
|
|
z+=SubPartSafe.min[2];
|
|
|
|
assert(x>=0 && x<sz[0] && y>=0 && y<sz[1] && z>=0 && z<sz[2]);
|
|
//int trpos,tlpos;
|
|
//assert(rv[rpos].size()>0);
|
|
//Pos(x,y,z,trpos,tlpos);
|
|
//assert(trpos==rpos && tlpos == lpos);
|
|
return true;
|
|
}
|
|
|
|
void Alloc(int rpos, const VOX_TYPE &zeroval)
|
|
{
|
|
rv[rpos].resize(BLOCKSIDE()*BLOCKSIDE()*BLOCKSIDE(),zeroval);
|
|
}
|
|
/************************************/
|
|
// Funzioni di accesso ai dati
|
|
bool ValidCell(const Point3i &p1, const Point3i &p2) const
|
|
{
|
|
if(!cV(p1.X(),p1.Y(),p1.Z()).B() ) return false;
|
|
if(!cV(p2.X(),p1.Y(),p1.Z()).B() ) return false;
|
|
if(!cV(p1.X(),p2.Y(),p1.Z()).B() ) return false;
|
|
if(!cV(p2.X(),p2.Y(),p1.Z()).B() ) return false;
|
|
if(!cV(p1.X(),p1.Y(),p2.Z()).B() ) return false;
|
|
if(!cV(p2.X(),p1.Y(),p2.Z()).B() ) return false;
|
|
if(!cV(p1.X(),p2.Y(),p2.Z()).B() ) return false;
|
|
if(!cV(p2.X(),p2.Y(),p2.Z()).B() ) return false;
|
|
return true;
|
|
}
|
|
|
|
float Val(const int &x,const int &y,const int &z) const {
|
|
if(!cV(x,y,z).B()) return 1000;
|
|
return cV(x,y,z).V();
|
|
//else return numeric_limits<float>::quiet_NaN( );
|
|
}
|
|
|
|
VOX_TYPE &V(const int &x,const int &y,const int &z) {
|
|
int rpos,lpos;
|
|
if(!Pos(x,y,z,rpos,lpos)) Alloc(rpos,VOX_TYPE::Zero());
|
|
return rv[rpos][lpos];
|
|
}
|
|
|
|
const VOX_TYPE &cV(const int &x,const int &y,const int &z) const
|
|
{
|
|
int rpos,lpos;
|
|
if(!Pos(x,y,z,rpos,lpos)) return VOX_TYPE::Zero();
|
|
else return rv[rpos][lpos];
|
|
}
|
|
const VOX_TYPE &V(const int &x,const int &y,const int &z) const
|
|
{
|
|
int rpos,lpos;
|
|
if(!Pos(x,y,z,rpos,lpos)) return VOX_TYPE::Zero();
|
|
else return rv[rpos][lpos];
|
|
}
|
|
/************************************/
|
|
void Fill(VOX_TYPE (__cdecl *func)(const Point3i &p) )
|
|
{
|
|
int x,y,z;
|
|
for(z=0;z<sz[2];++z)
|
|
for(y=0;y<sz[1];++y)
|
|
for(x=0;x<sz[0];++x)
|
|
{
|
|
Point3i p(x,y,z);
|
|
V(x,y,z)=func(p);
|
|
}
|
|
}
|
|
|
|
void Fill(VOX_TYPE const p)
|
|
{
|
|
int x,y,z;
|
|
for(z=0;z<sz[2];++z)
|
|
for(y=0;y<sz[1];++y)
|
|
for(x=0;x<sz[0];++x)
|
|
{
|
|
V(x,y,z)=p;
|
|
}
|
|
}
|
|
|
|
|
|
// Copia sul volume corrente una versione smoothed del volume in ingresso S.
|
|
// il parametro serve a specificare il range di valori di campo vicini allo zero che non vanno mediati!
|
|
// questo perche se si smootha anche sullo zero si smoota anche dove e' bene allineato
|
|
|
|
void CopySmooth( Volume<VOX_TYPE> &S, scalar SafeZone=1, scalar SafeQuality=0)
|
|
{
|
|
if(sz!=S.sz)
|
|
{
|
|
printf("Error");
|
|
exit(-1);
|
|
}
|
|
int lcnt=0;
|
|
VolumeIterator< Volume > vi(S);
|
|
vi.Restart();
|
|
vi.FirstNotEmpty();
|
|
// const Voxelf *VC;
|
|
while(vi.IsValid())
|
|
// scandisci il volume in ingresso, per ogni voxel non vuoto del volume
|
|
// in ingresso calcola la media con gli adiacenti
|
|
{
|
|
if((*vi).B())
|
|
{
|
|
int x,y,z;
|
|
IPos(x,y,z,vi.rpos,vi.lpos);
|
|
if(Bound1(x,y,z))
|
|
{
|
|
VOX_TYPE &VC = V(x,y,z);
|
|
for(int i=0;i<26;++i)
|
|
{
|
|
VOX_TYPE &VV= S.V(x+nni[i][0],y+nni[i][1],z+nni[i][2]);
|
|
if(VV.B()) VC+=VV;
|
|
}
|
|
lcnt++;
|
|
|
|
/*
|
|
Voxelf &VV=V(x,y,z);
|
|
//Voxelf &VV=rv[vi.rpos][vi.lpos];
|
|
for(int i=0;i<26;++i)
|
|
{
|
|
VC = &(S.V(x+nni[i][0],y+nni[i][1],z+nni[i][2]));
|
|
if(VC->b)
|
|
{
|
|
VV+=*VC;
|
|
lcnt++;
|
|
}
|
|
}*/
|
|
}
|
|
}
|
|
vi.Next();
|
|
if(vi.IsValid()) vi.FirstNotEmpty();
|
|
//if((lcnt%100)==0) vi.Dump();
|
|
|
|
}
|
|
// Step 2,
|
|
// dopo aver calcolato la media,
|
|
|
|
VolumeIterator< Volume > svi(*this);
|
|
svi.Restart();
|
|
svi.FirstNotEmpty();
|
|
int smoothcnt=0;
|
|
int preservedcnt=0;
|
|
int blendedcnt=0;
|
|
const float FieldBorder = 1; // dove finisce la transizione tra la zona safe e quella smoothed
|
|
const float EndFBorderZone = SafeZone+FieldBorder;
|
|
const float EndQBorderZone = SafeQuality*1.5;
|
|
const float QBorder = EndQBorderZone-SafeQuality; // dove finisce la transizione tra la zona safe e quella smoothed
|
|
while(svi.IsValid())
|
|
{
|
|
if((*svi).Cnt()>0)
|
|
{
|
|
VOX_TYPE &sv=S.rv[svi.rpos][svi.lpos];
|
|
(*svi).Normalize(1); // contiene il valore mediato
|
|
float SafeThr = fabs(sv.V());
|
|
|
|
// Se la qualita' e' bassa o se siamo distanti si smootha sempre
|
|
// se siamo vicini allo zero e con buona qualita' si deve fare attenzione
|
|
if(SafeThr<EndFBorderZone && sv.Q() > EndQBorderZone)
|
|
{ // se il voxel corrente aveva un valore < safezone E qualita' > SafeQuality
|
|
// allora si copia il valore originale di S
|
|
if((SafeThr <= SafeZone) && sv.Q() > SafeQuality )
|
|
{
|
|
(*svi)=sv;
|
|
(*svi).SetB(true);
|
|
++preservedcnt;
|
|
}
|
|
else
|
|
{ // Siamo nella zona di transizione o per field o per quality
|
|
float blendq= std::max(0.0f,std::min(1.0f,(EndQBorderZone-sv.Q())/QBorder));
|
|
float blendf= std::max(0.0f,std::min(1.0f,(EndFBorderZone-SafeThr)/FieldBorder));
|
|
float BlendFactor = 1.0-std::max(blendf,blendq); // quanto del voxel originale <sv> si prende;
|
|
(*svi).Blend(sv,BlendFactor);
|
|
++blendedcnt;
|
|
}
|
|
}
|
|
++smoothcnt;
|
|
}
|
|
svi.Next();
|
|
if(svi.IsValid()) svi.FirstNotEmpty();
|
|
}
|
|
|
|
if(Verbose) fprintf(LogFP,"CopySmooth %i voxels, %i preserved, %i blended\n",smoothcnt,preservedcnt,blendedcnt);
|
|
}
|
|
|
|
void Merge(Volume<VOX_TYPE> &S)
|
|
{
|
|
VolumeIterator< Volume > svi(S);
|
|
svi.Restart();
|
|
svi.FirstNotEmpty();
|
|
int loccnt=0;
|
|
|
|
while(svi.IsValid())
|
|
{
|
|
if((*svi).B())
|
|
{
|
|
int x,y,z;
|
|
IPos(x,y,z,svi.rpos,svi.lpos);
|
|
if(cV(x,y,z).B()) V(x,y,z).Merge( (*svi));
|
|
else {
|
|
V(x,y,z).Set((*svi));
|
|
V(x,y,z).SetB(true);
|
|
}
|
|
++loccnt;
|
|
}
|
|
svi.Next();
|
|
if(svi.IsValid()) svi.FirstNotEmpty();
|
|
}
|
|
|
|
printf("Merge2 %i voxels\n",loccnt);
|
|
|
|
}
|
|
|
|
|
|
void Interize( Point3x & vert ) const // OK
|
|
{
|
|
for(int j=0;j<3;++j)
|
|
{
|
|
assert(vert[j]>=bbox.min[j]);
|
|
assert(vert[j]<=bbox.max[j]);
|
|
vert[j] = (vert[j] - bbox.min[j]) * sz[j] / (bbox.max[j] - bbox.min[j]);
|
|
}
|
|
}
|
|
|
|
|
|
void DeInterize( Point3x & vert ) const // OK
|
|
{
|
|
for(int j=0;j<3;++j)
|
|
vert[j] = vert[j] * (bbox.max[j] - bbox.min[j]) / sz[j] + bbox.min[j];
|
|
}
|
|
|
|
bool SplatVert( const Point3x & v0, double quality, const Point3x & nn, Color4b c)
|
|
{
|
|
Box3i ibox;
|
|
|
|
assert(math::Abs(SquaredNorm(nn) - 1.0) < 0.0001); // Just a safety check that the vertex normals are NORMALIZED!
|
|
ibox.min=Point3i(floor(v0[0]),floor(v0[1]),floor(v0[2]));
|
|
ibox.max=Point3i( ceil(v0[0]), ceil(v0[1]), ceil(v0[2]));
|
|
ibox.Intersect(SubPartSafe);
|
|
|
|
ibox.max[0] = std::min(SubPartSafe.max[0]-1,ibox.max[0]);
|
|
ibox.max[1] = std::min(SubPartSafe.max[1]-1,ibox.max[1]);
|
|
ibox.max[2] = std::min(SubPartSafe.max[2]-1,ibox.max[2]);
|
|
|
|
|
|
// Skip faces not colliding current subvolume.
|
|
if(ibox.IsEmpty())
|
|
{
|
|
// point outside the box do nothing
|
|
return false;
|
|
}
|
|
|
|
Point3x iV, deltaIV;
|
|
|
|
// Now scan the eight voxel surrounding the splat
|
|
// and fill them with the distance from the plane
|
|
for(iV[0]=ibox.min[0]; iV[0]<=ibox.max[0]; ++iV[0])
|
|
for(iV[1]=ibox.min[1]; iV[1]<=ibox.max[1]; ++iV[1])
|
|
for(iV[2]=ibox.min[2]; iV[2]<=ibox.max[2]; ++iV[2])
|
|
{
|
|
deltaIV = v0-iV;
|
|
scalar offset = deltaIV * nn;
|
|
VOX_TYPE &VV=V(iV[0],iV[1],iV[2]);
|
|
VV=VOX_TYPE(offset,nn,quality,c);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
template <const int CoordZ>
|
|
void RasterFace(const int sx, const int ex, const int sy, const int ey,
|
|
scalar dist, const Point3x &norm, scalar quality,
|
|
const Point3x &v0, const Point3x &v1, const Point3x &v2,
|
|
const Point3x &d10, const Point3x &d21, const Point3x &d02)
|
|
{
|
|
const scalar EPS = scalar(1e-12);
|
|
const int crd0 = CoordZ;
|
|
const int crd1 = (CoordZ+1)%3;
|
|
const int crd2 = (CoordZ+2)%3;
|
|
assert(fabs(norm[crd0])+0.001 > fabs(norm[crd1]));
|
|
assert(fabs(norm[crd0])+0.001 > fabs(norm[crd2]));
|
|
scalar x,y;
|
|
for(x=sx;x<=ex;++x)
|
|
for(y=sy;y<=ey;++y)
|
|
{
|
|
scalar n0 = (x-v0[crd1])*d10[crd2] - (y-v0[crd2])*d10[crd1];
|
|
scalar n1 = (x-v1[crd1])*d21[crd2] - (y-v1[crd2])*d21[crd1];
|
|
scalar n2 = (x-v2[crd1])*d02[crd2] - (y-v2[crd2])*d02[crd1];
|
|
|
|
if( (n0>-EPS && n1>-EPS && n2>-EPS) ||
|
|
(n0< EPS && n1< EPS && n2< EPS ) )
|
|
{
|
|
scalar iz = ( dist - x*norm[crd1] - y*norm[crd2] ) / norm[crd0];
|
|
//assert(iz>=fbox.min[2] && iz<=fbox.max[2]);
|
|
AddIntercept<CoordZ>(x,y,iz, quality, norm );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Si sa che la faccia ha una intercetta sull'asse z-dir di coord xy alla posizione z;
|
|
// quindi si setta nei 2 vertici prima e 2 dopo la distanza corrispondente.
|
|
template<int CoordZ>
|
|
void AddIntercept( const int x, const int y, const scalar z, const scalar q, const Point3f &n )
|
|
{
|
|
scalar esgn = (n[CoordZ] > 0 ? -1 : 1);
|
|
int zint = floor(z);
|
|
scalar dist=z-zint; // sempre positivo e compreso tra zero e uno
|
|
|
|
for(int k=WN;k<=WP;k++)
|
|
{
|
|
if(zint+k >= SubPartSafe.min[CoordZ] && zint+k < SubPartSafe.max[CoordZ])
|
|
{
|
|
VOX_TYPE *VV;
|
|
if(CoordZ==2) VV=&V(x,y,zint+k);
|
|
if(CoordZ==1) VV=&V(y,zint+k,x);
|
|
if(CoordZ==0) VV=&V(zint+k,x,y);
|
|
scalar nvv= esgn*( k-dist);
|
|
if(!VV->B() || fabs(VV->V()) > fabs(nvv)) {
|
|
*VV=VOX_TYPE(nvv,n,q);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// assume che i punti della faccia in ingresso siano stati interized
|
|
bool ScanFace2( const Point3x & v0, const Point3x & v1, const Point3x & v2,
|
|
scalar quality, const Point3x & norm)//, const int name ) // OK
|
|
{
|
|
|
|
Box3x fbox; // Bounding Box della faccia (double)
|
|
Box3i ibox; // Bounding Box della faccia (int)
|
|
int sx,sy,sz; // Bounding Box intero
|
|
int ex,ey,ez;
|
|
|
|
// Calcolo bbox della faccia
|
|
fbox.Set(v0);
|
|
fbox.Add(v1);
|
|
fbox.Add(v2);
|
|
|
|
// BBox intero (nota che il cast a int fa truncation (funge solo perche' v0,v1,v2 sono positivi)
|
|
|
|
ibox.min[0] =sx = floor(fbox.min[0]); if( ((scalar)sx)!=fbox.min[0] ) ++sx; // necessario se il punto e'approx a .9999
|
|
ibox.min[1] =sy = floor(fbox.min[1]); if( ((scalar)sy)!=fbox.min[1] ) ++sy;
|
|
ibox.min[2] =sz = floor(fbox.min[2]); if( ((scalar)sz)!=fbox.min[2] ) ++sz;
|
|
ibox.max[0] =ex = floor(fbox.max[0]);
|
|
ibox.max[1] =ey = floor(fbox.max[1]);
|
|
ibox.max[2] =ez = floor(fbox.max[2]);
|
|
// Skip faces not colliding current subvolume.
|
|
if(!ibox.Collide(SubPartSafe)) return false;
|
|
|
|
Point3x d10 = v1 - v0;
|
|
Point3x d21 = v2 - v1;
|
|
Point3x d02 = v0 - v2;
|
|
|
|
assert(norm.Norm() >0.999f && norm.Norm()<1.001f);
|
|
// assert(0);
|
|
scalar dist = norm * v0;
|
|
|
|
|
|
/**** Rasterizzazione bbox ****/
|
|
|
|
// Clamping dei valori di rasterizzazione al subbox corrente
|
|
sx = std::max(SubPartSafe.min[0],sx); ex = std::min(SubPartSafe.max[0]-1,ex);
|
|
sy = std::max(SubPartSafe.min[1],sy); ey = std::min(SubPartSafe.max[1]-1,ey);
|
|
sz = std::max(SubPartSafe.min[2],sz); ez = std::min(SubPartSafe.max[2]-1,ez);
|
|
|
|
if(fabs(norm[0]) > fabs(norm[1]) && fabs(norm[0])>fabs(norm[2])) RasterFace<0>(sy,ey,sz,ez,dist,norm,quality,v0,v1,v2,d10,d21,d02);
|
|
else if(fabs(norm[1]) > fabs(norm[0]) && fabs(norm[1])>fabs(norm[2])) RasterFace<1>(sz,ez,sx,ex,dist,norm,quality,v0,v1,v2,d10,d21,d02);
|
|
else RasterFace<2>(sx,ex,sy,ey,dist,norm,quality,v0,v1,v2,d10,d21,d02);
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
// assume che i punti della faccia in ingresso siano stati interized
|
|
bool ScanFace( const Point3x & v0, const Point3x & v1, const Point3x & v2,
|
|
double quality, const Point3x & nn)//, const int name ) // OK
|
|
{
|
|
const scalar EPS = scalar(1e-12);
|
|
// const scalar EPS_INT = scalar(1e-20);
|
|
const scalar EPS_INT = .3f;//scalar(1e-20);
|
|
|
|
#ifndef _NDEBUG
|
|
if(quality==0.0)
|
|
{
|
|
printf("Zero quality face are not allowed!\n");
|
|
exit(-1);
|
|
}
|
|
#endif
|
|
|
|
// ++nfaces;
|
|
|
|
Box3x fbox; // Bounding Box della faccia (double)
|
|
Box3i ibox; // Bounding Box della faccia (int)
|
|
int sx,sy,sz; // Bounding Box intero
|
|
int ex,ey,ez;
|
|
|
|
// Calcolo bbox della faccia
|
|
fbox.Set(v0);
|
|
fbox.Add(v1);
|
|
fbox.Add(v2);
|
|
|
|
// BBox intero (nota che il cast a int fa truncation (funge solo perche' v0,v1,v2 sono positivi)
|
|
|
|
ibox.min[0] =sx = floor(fbox.min[0]); if( ((scalar)sx)!=fbox.min[0] ) ++sx; // necessario se il punto e'approx a .9999
|
|
ibox.min[1] =sy = floor(fbox.min[1]); if( ((scalar)sy)!=fbox.min[1] ) ++sy;
|
|
ibox.min[2] =sz = floor(fbox.min[2]); if( ((scalar)sz)!=fbox.min[2] ) ++sz;
|
|
ibox.max[0] =ex = floor(fbox.max[0]);
|
|
ibox.max[1] =ey = floor(fbox.max[1]);
|
|
ibox.max[2] =ez = floor(fbox.max[2]);
|
|
// Skip faces not colliding current subvolume.
|
|
if(!ibox.Collide(SubPartSafe)) return false;
|
|
|
|
/**** Dati per controllo intersezione ****/
|
|
|
|
// Versori degli spigoli della faccia
|
|
|
|
Point3x d10 = v1 - v0;
|
|
Point3x d21 = v2 - v1;
|
|
Point3x d02 = v0 - v2;
|
|
|
|
// Normale al piano della faccia e distanza origine
|
|
|
|
Point3x norm = d10 ^ d21;
|
|
norm.Normalize();
|
|
double dist = norm * v0;
|
|
|
|
/**** Rasterizzazione bbox ****/
|
|
|
|
int x,y,z;
|
|
|
|
|
|
// Clamping dei valori di rasterizzazione al subbox corrente
|
|
sx = std::max(SubPartSafe.min[0],sx); ex = std::min(SubPartSafe.max[0]-1,ex);
|
|
sy = std::max(SubPartSafe.min[1],sy); ey = std::min(SubPartSafe.max[1]-1,ey);
|
|
sz = std::max(SubPartSafe.min[2],sz); ez = std::min(SubPartSafe.max[2]-1,ez);
|
|
|
|
// Rasterizzazione xy
|
|
|
|
if(fabs(norm[2])>EPS_INT)
|
|
for(x=sx;x<=ex;++x)
|
|
for(y=sy;y<=ey;++y)
|
|
{
|
|
double n0 = ((double)x-v0[0])*d10[1] - ((double)y-v0[1])*d10[0];
|
|
double n1 = ((double)x-v1[0])*d21[1] - ((double)y-v1[1])*d21[0];
|
|
double n2 = ((double)x-v2[0])*d02[1] - ((double)y-v2[1])*d02[0];
|
|
|
|
if( (n0>-EPS && n1>-EPS && n2>-EPS) ||
|
|
(n0< EPS && n1< EPS && n2< EPS ))
|
|
{
|
|
double iz = ( dist - double(x)*norm[0] - double(y)*norm[1] ) / norm[2];
|
|
//assert(iz>=fbox.min[2] && iz<=fbox.max[2]);
|
|
AddXYInt(x,y,iz,-norm[2], quality, nn );
|
|
}
|
|
}
|
|
|
|
// Rasterizzazione xz
|
|
|
|
if(fabs(norm[1])>EPS_INT)
|
|
for(x=sx;x<=ex;++x)
|
|
for(z=sz;z<=ez;++z)
|
|
{
|
|
double n0 = ((double)x-v0[0])*d10[2] - ((double)z-v0[2])*d10[0];
|
|
double n1 = ((double)x-v1[0])*d21[2] - ((double)z-v1[2])*d21[0];
|
|
double n2 = ((double)x-v2[0])*d02[2] - ((double)z-v2[2])*d02[0];
|
|
|
|
if( (n0>-EPS && n1>-EPS && n2>-EPS) ||
|
|
(n0< EPS && n1< EPS && n2< EPS ))
|
|
{
|
|
double iy = ( dist - double(x)*norm[0] - double(z)*norm[2] ) / norm[1];
|
|
//assert(iy>=fbox.min[1] && iy<=fbox.max[1]);
|
|
AddXZInt(x,z,iy,-norm[1], quality,nn );
|
|
}
|
|
}
|
|
|
|
// Rasterizzazione yz
|
|
|
|
if(fabs(norm[0])>EPS_INT)
|
|
for(y=sy;y<=ey;++y)
|
|
for(z=sz;z<=ez;++z)
|
|
{
|
|
double n0 = ((double)y-v0[1])*d10[2] - ((double)z-v0[2])*d10[1];
|
|
double n1 = ((double)y-v1[1])*d21[2] - ((double)z-v1[2])*d21[1];
|
|
double n2 = ((double)y-v2[1])*d02[2] - ((double)z-v2[2])*d02[1];
|
|
|
|
if( (n0>-EPS && n1>-EPS && n2>-EPS) ||
|
|
(n0< EPS && n1< EPS && n2< EPS ) )
|
|
{
|
|
double ix = ( dist - double(y)*norm[1] - double(z)*norm[2] ) / norm[0];
|
|
//assert(ix>=fbox.min[0] && ix<=fbox.max[0]);
|
|
AddYZInt(y,z,ix,-norm[0], quality, nn );
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
// Si sa che la faccia ha una intercetta sull'asse z-dir di coord xy alla posizione z;
|
|
// quindi si setta nei 2 vertici prima e 2 dopo la distanza corrispondente.
|
|
|
|
void AddXYInt( const int x, const int y, const double z, const double sgn, const double q, const Point3f &n )
|
|
{ double esgn = (sgn<0 ? -1 : 1);//*max(fabs(sgn),0.001);
|
|
double dist=z-floor(z); // sempre positivo e compreso tra zero e uno
|
|
int zint = floor(z);
|
|
for(int k=WN;k<=WP;k++)
|
|
if(zint+k >= SubPartSafe.min[2] && zint+k < SubPartSafe.max[2])
|
|
{
|
|
VOX_TYPE &VV=V(x,y,zint+k);
|
|
double nvv= esgn*( k-dist);
|
|
if(!VV.B() || fabs(VV.V()) > fabs(nvv)) {
|
|
VV=VOX_TYPE(nvv,n,q);
|
|
}
|
|
}
|
|
}
|
|
void AddYZInt( const int y, const int z, const double x, const double sgn, const double q, const Point3f &n )
|
|
{ double esgn = (sgn<0 ? -1 : 1);//*max(fabs(sgn),0.001);
|
|
double dist=x-floor(x); // sempre positivo e compreso tra zero e uno
|
|
int xint = int(floor(x));
|
|
for(int k=WN;k<=WP;k++)
|
|
if(xint+k >= SubPartSafe.min[0] && xint+k < SubPartSafe.max[0])
|
|
{
|
|
VOX_TYPE &VV=V(xint+k,y,z);
|
|
double nvv= esgn*( k-dist);
|
|
if(!VV.B() || fabs(VV.V()) > fabs(nvv)) {
|
|
VV=VOX_TYPE(nvv,n,q);
|
|
}
|
|
}
|
|
}
|
|
void AddXZInt( const int x, const int z, const double y, const double sgn, const double q, const Point3f &n )
|
|
{ double esgn = (sgn<0 ? -1 : 1);//*max(fabs(sgn),0.001);
|
|
double dist=y-scalar(floor(y)); // sempre positivo e compreso tra zero e uno
|
|
int yint = floor(y);
|
|
for(int k=WN;k<=WP;k++)
|
|
if(yint+k >= SubPartSafe.min[1] && yint+k < SubPartSafe.max[1])
|
|
{
|
|
VOX_TYPE &VV=V(x,yint+k,z);
|
|
double nvv= esgn*( k-dist);
|
|
if(!VV.B() || fabs(VV.V()) > fabs(nvv)) {
|
|
VV=VOX_TYPE(nvv,n,q);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void Dump(FILE *fp)
|
|
{
|
|
fprintf(fp,"Volume Info:\n");
|
|
fprintf(fp," BBbox %7.4f %7.4f %7.4f - %7.4f %7.4f %7.4f:\n",bbox.min[0],bbox.min[1],bbox.min[2],bbox.max[0],bbox.max[1],bbox.max[2]);
|
|
fprintf(fp," Size in voxels %7i %7i %7i (tot: %7.3f M):\n",sz[0],sz[1],sz[2],(double(sz[0]*sz[1])/1000000.0)*sz[2]);
|
|
fprintf(fp," Voxel dimension %7.4f %7.4f %7.4f \n",voxel[0],voxel[1],voxel[2]);
|
|
|
|
fprintf(fp," Size in MacroCell %7i %7i %7i (tot: %7.3f M):\n",rsz[0],rsz[1],rsz[2],double(rsz[0]*rsz[1]*rsz[2])/1000000.0);
|
|
fprintf(fp," Memory Info: \n Voxel Size %8li b Virtually needed mem %8i Mb\n",
|
|
sizeof(VOX_TYPE),int(sizeof(VOX_TYPE)*(_int64)(sz[0])*(_int64)(sz[1])*(_int64)(sz[2])/(1024*1024)));
|
|
if(div!=Point3i(1,1,1))
|
|
{
|
|
fprintf(fp," Subdivided in %6i %6i %6i (tot: %12i block):\n",div[0],div[1],div[2],div[0]*div[1]*div[2]);
|
|
fprintf(fp," Computing subblock %6i %6i %6i :\n",pos[0],pos[1],pos[2]);
|
|
fprintf(fp," %6i %6i %6i - %6i %6i %6i :\n",SubPart.min[0],SubPart.min[1],SubPart.min[2],SubPart.max[0],SubPart.max[1],SubPart.max[2]);
|
|
fprintf(fp," Safe %6i %6i %6i - %6i %6i %6i :\n",SubPartSafe.min[0],SubPartSafe.min[1],SubPartSafe.min[2],SubPartSafe.max[0],SubPartSafe.max[1],SubPartSafe.max[2]);
|
|
|
|
}
|
|
fprintf(fp,"\n");
|
|
}
|
|
|
|
int Allocated()
|
|
{int cnt=0;
|
|
for(size_t i=0;i<rv.size();++i)
|
|
if(!rv[i].empty()) cnt++;
|
|
return cnt;
|
|
}
|
|
|
|
bool Bound1(const int x, const int y, const int z)
|
|
{
|
|
return (x>SubPartSafe.min[0] && x < SubPartSafe.max[0]-1 ) &&
|
|
(y>SubPartSafe.min[1] && y < SubPartSafe.max[1]-1 ) &&
|
|
(z>SubPartSafe.min[2] && z < SubPartSafe.max[2]-1 ) ;
|
|
}
|
|
|
|
/*
|
|
Note sull'algoritmo di espansione:
|
|
|
|
Si riempie i voxel vuoti
|
|
|
|
Se il volume e' inizialmente riempito con ii valori delle intercette alla superficie
|
|
nei 2 vertici immediatamente adiacenti all'edge intersecato dalla superficie
|
|
si deve espadnere tale coampo in maniera sensata.
|
|
|
|
Notare che e' importante che non tutto il campo sia riempito con un approx ella distanza di hausdorf:
|
|
il campo deve essere "tagliato" sui bordi della superficie per evitare pasticci. Levoy riempie il campo
|
|
solo lungo la direzione dello scanner, io invece riempio lungo la normale alla superficie. In questo modo
|
|
si evita il problema che l'espansione e' legata all'acquisizione iniziale
|
|
|
|
*/
|
|
|
|
|
|
void Expand(scalar AngleThrRad)
|
|
{
|
|
int i;
|
|
VolumeIterator< Volume > vi(*this);
|
|
|
|
float CosThr=math::Cos(AngleThrRad);
|
|
// printf("Expand2 angle %f, %f\n",AngleThrRad,CosThr);
|
|
int loccnt=0;
|
|
|
|
vi.Restart();
|
|
vi.FirstNotEmpty();
|
|
while(vi.IsValid())
|
|
{
|
|
if((*vi).B()) // si espande solo i voxel con valori "validi"
|
|
{
|
|
int x,y,z;
|
|
IPos(x,y,z,vi.rpos,vi.lpos);
|
|
Point3f n=(*vi).N();
|
|
VOX_TYPE vtmp = (*vi);
|
|
if(Bound1(x,y,z))
|
|
for(i=0;i<26;++i)
|
|
{
|
|
float angle = -(nnf[i]*n); // cos angolo tra la normale alla superficie e la direzione di espansione
|
|
if( fabs(angle)> CosThr )
|
|
{
|
|
//bbfloat tt=(*vi).V();
|
|
vtmp.SetV((*vi).V()+len[i]*angle); // la nuova distanza e' la distanza rispetto al piano passante per il punto a cui si riferisce VV;
|
|
VOX_TYPE &VV= V(x+nni[i][0],y+nni[i][1],z+nni[i][2]);
|
|
if(!VV.B()){
|
|
VV+=vtmp;
|
|
loccnt++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
vi.Next();
|
|
if(vi.IsValid()) vi.FirstNotEmpty();
|
|
}
|
|
printf("Expand %8i ",loccnt);
|
|
Normalize(1);
|
|
}
|
|
|
|
// filla i buchi vuoti di un volume;
|
|
// scorre tutti i voxel pieni e aggiunge agli adiacenti vuoti tale valore;
|
|
// si riscorre tutti i voxel vuoti e se si sono sommati almeno thr valori sitiene.
|
|
// in pratica maggiore il valore di thr meno buchi si riempiono.
|
|
|
|
void Refill(const int thr,float maxdistance = std::numeric_limits<float>::max() )
|
|
{
|
|
int lcnt=0;
|
|
VolumeIterator< Volume > vi(*this);
|
|
vi.Restart();
|
|
vi.FirstNotEmpty();
|
|
|
|
while(vi.IsValid())
|
|
{
|
|
if((*vi).B())
|
|
{
|
|
int x,y,z;
|
|
IPos(x,y,z,vi.rpos,vi.lpos);
|
|
if(Bound1(x,y,z))
|
|
{
|
|
for(int i=0;i<26;++i)
|
|
{
|
|
VOX_TYPE &VC= V(x+nni[i][0],y+nni[i][1],z+nni[i][2]);
|
|
if(!VC.B()){
|
|
if(VC.Cnt()==0) lcnt++;
|
|
VC+=(*vi);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
vi.Next();
|
|
|
|
if(vi.IsValid()) vi.FirstNotEmpty();
|
|
|
|
|
|
}
|
|
printf("ReFill %8i ",lcnt);
|
|
Normalize(thr,maxdistance);
|
|
}
|
|
|
|
/*
|
|
Attraversa il volume e modifica il valore di campo in modo da creare una offset surface che si connetta
|
|
bene con la superficie originale
|
|
il parametro specifica dove dovrebbe passare l'offset surface
|
|
|
|
L'idea e' quella di fare un altro zero del distance field al threshold specificato
|
|
|
|
La cosa deve essere smooth
|
|
quindi scelgo una funzione che abbia 2 zeri (in zero e in thr) e
|
|
*/
|
|
void Offset(const float thr)
|
|
{
|
|
int lcnt=0;
|
|
VolumeIterator< Volume > vi(*this);
|
|
vi.Restart();
|
|
vi.FirstNotEmpty();
|
|
float thr2=thr/2.0;
|
|
while(vi.IsValid())
|
|
{
|
|
if((*vi).B())
|
|
{
|
|
float vv=(*vi).V();
|
|
if(thr<0) if(vv<thr2) vv=thr-vv;
|
|
if(thr>0) if(vv>thr2) vv=thr-vv;
|
|
|
|
(*vi).SetV(vv);
|
|
}
|
|
|
|
vi.Next();
|
|
|
|
if(vi.IsValid()) vi.FirstNotEmpty();
|
|
|
|
|
|
}
|
|
printf("ReFill %8i ",lcnt);
|
|
Normalize(thr);
|
|
}
|
|
|
|
// prende un volume e mette a true il campo b di tutti i voxel che hanno un valore significativo.
|
|
// thr indica il numero minimo di valori che si devono essere sommati sul voxel;
|
|
// di solito e' uguale a 1;
|
|
int Normalize(int thr, float maxdistance=std::numeric_limits<float>::max() )
|
|
{
|
|
VolumeIterator< Volume > vi(*this);
|
|
vi.Restart();
|
|
vi.FirstNotEmpty();
|
|
int loccnt=0;
|
|
while(vi.IsValid())
|
|
{
|
|
if(!(*vi).B())
|
|
{
|
|
if((*vi).Normalize(thr))
|
|
++loccnt;
|
|
if(math::Abs((*vi).V())>maxdistance) *vi=VOX_TYPE::Zero();
|
|
}
|
|
vi.Next();
|
|
if(vi.IsValid()) vi.FirstNotEmpty();
|
|
}
|
|
printf("Normalize(%i) %8i voxels\n",thr,loccnt);
|
|
return loccnt;
|
|
}
|
|
|
|
|
|
|
|
// Salva
|
|
void SlicedPPMQ( const char * filename,const char *tag,int SliceNum)
|
|
{
|
|
std::string basename=filename;
|
|
std::string name;
|
|
int ix,iy,iz;
|
|
Color4b Tab[100];
|
|
for(int ii=1;ii<100;++ii)
|
|
Tab[ii].SetColorRamp(0,100,ii);
|
|
Tab[0]=Color4b::Gray;
|
|
|
|
int ZStep=sz[2]/(SliceNum+1);
|
|
for(iz=ZStep;iz<sz[2];iz+=ZStep)
|
|
if(iz>=SubPartSafe.min[2] && iz<SubPartSafe.max[2])
|
|
{
|
|
name=SFormat("%s%03i%s_q.ppm",filename,iz,tag);
|
|
FILE * fp = fopen(name.c_str(),"wb");
|
|
fprintf(fp,
|
|
"P6\n"
|
|
"%d %d\n"
|
|
"255\n"
|
|
,sz[1]
|
|
,sz[0]
|
|
);
|
|
unsigned char rgb[3];
|
|
for(ix=0;ix<sz[0];++ix)
|
|
{
|
|
for(iy=0;iy<sz[1];++iy)
|
|
{
|
|
if( ix>=SubPartSafe.min[0] && ix<SubPartSafe.max[0] &&
|
|
iy>=SubPartSafe.min[1] && iy<SubPartSafe.max[1])
|
|
{
|
|
if(!V(ix,iy,iz).B()) {
|
|
rgb[0]=rgb[1]=rgb[2]=64;
|
|
}
|
|
else
|
|
{
|
|
float vv=V(ix,iy,iz).Q();
|
|
int qi=std::min(V(ix,iy,iz).Q()*100.0f,100.0f);
|
|
|
|
if( vv>0) {
|
|
rgb[0]=Tab[qi][0];
|
|
rgb[1]=Tab[qi][1];
|
|
rgb[2]=Tab[qi][2];
|
|
}
|
|
else if(vv<0)
|
|
{
|
|
rgb[0]=128;
|
|
rgb[1]=255+32*vv;
|
|
rgb[2]=0;//V(ix,iy,iz).Q()*256;
|
|
}
|
|
else {
|
|
rgb[0]=255; rgb[1]=255; rgb[2]=255;
|
|
}
|
|
}
|
|
}
|
|
else{
|
|
rgb[0]=rgb[1]=rgb[2]=64;
|
|
}
|
|
fwrite(rgb,3,1,fp);
|
|
}
|
|
}
|
|
fclose(fp);
|
|
}
|
|
}
|
|
|
|
void SlicedPPM( const char * filename,const char *tag,int SliceNum=1)
|
|
{
|
|
std::string basename=filename;
|
|
std::string name;
|
|
int ix,iy,iz;
|
|
int ZStep=sz[2]/(SliceNum+1);
|
|
for(iz=ZStep;iz<sz[2];iz+=ZStep)
|
|
if(iz>=SubPartSafe.min[2] && iz<SubPartSafe.max[2])
|
|
{
|
|
name=SFormat("%s_%03i_%s.ppm",filename,iz,tag);
|
|
printf("Saving slice '%s'",name.c_str());
|
|
FILE * fp = fopen(name.c_str(),"wb");
|
|
if(!fp) return;
|
|
fprintf(fp,
|
|
"P6\n"
|
|
"%d %d\n"
|
|
"255\n"
|
|
,sz[1]
|
|
,sz[0]
|
|
);
|
|
unsigned char rgb[3];
|
|
for(ix=0;ix<sz[0];++ix)
|
|
{
|
|
for(iy=0;iy<sz[1];++iy)
|
|
{
|
|
if( ix>=SubPartSafe.min[0] && ix<SubPartSafe.max[0] &&
|
|
iy>=SubPartSafe.min[1] && iy<SubPartSafe.max[1])
|
|
{
|
|
if(!V(ix,iy,iz).B()) {
|
|
rgb[0]=rgb[1]=rgb[2]=64;
|
|
}
|
|
else
|
|
{
|
|
float vv=V(ix,iy,iz).V();
|
|
if( vv>0) {
|
|
rgb[0]=255-32*vv;
|
|
rgb[1]=128;
|
|
rgb[2]=0;//V(ix,iy,iz).Q()*256;
|
|
}
|
|
else if(vv<0)
|
|
{
|
|
rgb[0]=128;
|
|
rgb[1]=255+32*vv;
|
|
rgb[2]=0;//V(ix,iy,iz).Q()*256;
|
|
}
|
|
else {
|
|
rgb[0]=255; rgb[1]=255; rgb[2]=255;
|
|
}
|
|
}
|
|
}
|
|
else{
|
|
rgb[0]=rgb[1]=rgb[2]=64;
|
|
}
|
|
fwrite(rgb,3,1,fp);
|
|
}
|
|
}
|
|
fclose(fp);
|
|
}
|
|
}
|
|
template < class VertexPointerType >
|
|
void GetXIntercept(const vcg::Point3i &p1, const vcg::Point3i &p2, VertexPointerType &v, float /*thr*/)
|
|
{
|
|
float f1 = Val(p1.X(), p1.Y(), p1.Z());
|
|
float f2 = Val(p2.X(), p2.Y(), p2.Z());
|
|
float u = (float) f1/(f1-f2);
|
|
v->P().X() = (float) p1.X()*(1-u) + u*p2.X();
|
|
v->P().Y() = (float) p1.Y();
|
|
v->P().Z() = (float) p1.Z();
|
|
v->Q()=cV(p1.X(), p1.Y(), p1.Z()).Q();
|
|
v->C()=cV(p1.X(), p1.Y(), p1.Z()).C4b();
|
|
}
|
|
|
|
template < class VertexPointerType >
|
|
void GetYIntercept(const vcg::Point3i &p1, const vcg::Point3i &p2, VertexPointerType &v, float /*thr*/)
|
|
{
|
|
float f1 = Val(p1.X(), p1.Y(), p1.Z());
|
|
float f2 = Val(p2.X(), p2.Y(), p2.Z());
|
|
float u = (float) f1/(f1-f2);
|
|
v->P().X() = (float) p1.X();
|
|
v->P().Y() = (float) p1.Y()*(1-u) + u*p2.Y();
|
|
v->P().Z() = (float) p1.Z();
|
|
v->Q()=cV(p1.X(), p1.Y(), p1.Z()).Q();
|
|
v->C()=cV(p1.X(), p1.Y(), p1.Z()).C4b();
|
|
}
|
|
|
|
template < class VertexPointerType>
|
|
void GetZIntercept(const vcg::Point3i &p1, const vcg::Point3i &p2, VertexPointerType &v, float /*thr*/)
|
|
{
|
|
float f1 = Val(p1.X(), p1.Y(), p1.Z());
|
|
float f2 = Val(p2.X(), p2.Y(), p2.Z());
|
|
float u = (float) f1/(f1-f2);
|
|
v->P().X() = (float) p1.X();
|
|
v->P().Y() = (float) p1.Y();
|
|
v->P().Z() = (float) p1.Z()*(1-u) + u*p2.Z();
|
|
v->Q()=cV(p1.X(), p1.Y(), p1.Z()).Q();
|
|
v->C()=cV(p1.X(), p1.Y(), p1.Z()).C4b();
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template < class VOL >
|
|
class VolumeIterator
|
|
{
|
|
public:
|
|
VOL &V;
|
|
//vector<VOL::voxel_type> vi;
|
|
VolumeIterator(VOL &_VV):V(_VV) {}
|
|
|
|
//Point3i curPos;
|
|
int rpos;
|
|
int lpos;
|
|
|
|
void Restart(){rpos=0;lpos=0;}
|
|
private :
|
|
public:
|
|
|
|
|
|
void Set(const Point3i &p)
|
|
{
|
|
//curPos=p;
|
|
V.Pos(p[0],p[1],p[2],rpos,lpos);
|
|
}
|
|
bool FirstNotEmpty()
|
|
{
|
|
|
|
//Dump();
|
|
typename std::vector<std::vector<typename VOL::voxel_type> >::iterator rvi=V.rv.begin()+rpos;
|
|
do
|
|
{
|
|
if((*rvi).empty())
|
|
{
|
|
while(rvi!=V.rv.end() && (*rvi).empty()) ++rvi;
|
|
if(rvi==V.rv.end())
|
|
{
|
|
rpos=-1;
|
|
return false;
|
|
}
|
|
rpos= rvi-V.rv.begin();
|
|
lpos=0;
|
|
}
|
|
typename std::vector<typename VOL::voxel_type>::iterator lvi= (*rvi).begin()+lpos;
|
|
// un voxel e' non vuoto se ha b!=0;
|
|
while(lvi!=(*rvi).end() && !((*lvi).B() || (*lvi).Cnt()>0)) {
|
|
++lvi;
|
|
}
|
|
if(lvi!=(*rvi).end())
|
|
{
|
|
lpos= lvi-(*rvi).begin();
|
|
//V.IPos(p[0],p[1],p[2],rpos,lpos);
|
|
//Dump();
|
|
return true;
|
|
}
|
|
else lpos=0;
|
|
++rvi;
|
|
rpos= rvi-V.rv.begin();
|
|
|
|
} while (rvi!=V.rv.end());
|
|
rpos=-1;
|
|
return false;
|
|
}
|
|
|
|
typename VOL::voxel_type &operator *()
|
|
{
|
|
assert(rpos>=0 && lpos >=0);
|
|
return V.rv[rpos][lpos];
|
|
}
|
|
bool Next()
|
|
{
|
|
assert(IsValid());
|
|
if(lpos< VOL::BLOCKSIDE() * VOL::BLOCKSIDE() * VOL::BLOCKSIDE() -1)
|
|
{
|
|
++lpos;
|
|
//V.IPos(p[0],p[1],p[2],rpos,lpos);
|
|
return true;
|
|
}
|
|
if(rpos < int(V.rv.size()-1))
|
|
{
|
|
lpos=0;
|
|
++rpos;
|
|
//V.IPos(p[0],p[1],p[2],rpos,lpos);
|
|
return true;
|
|
}
|
|
rpos=-1;
|
|
lpos=-1;
|
|
return false;
|
|
}
|
|
|
|
bool IsValid() {
|
|
return rpos>=0;
|
|
}
|
|
void Dump()
|
|
{
|
|
int x,y,z;
|
|
V.IPos(x,y,z,rpos,lpos);
|
|
printf("Iterator r %4i l %4i (%3i %3i %3i)\n",rpos,lpos,x,y,z);
|
|
}
|
|
|
|
|
|
};
|
|
}
|
|
#endif
|