You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1363 lines
45 KiB
1363 lines
45 KiB
/**************************************************************************** |
|
* 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 ); |
|
vsnprintf(buf,4096,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 ) snprintf(buf,32,"_%01d%01d%01d",pos[0],pos[1],pos[2]); |
|
else if(div[0]<= 100 && div[1]<= 100 && div[2]<= 100 ) snprintf(buf,32,"_%02d%02d%02d",pos[0],pos[1],pos[2]); |
|
else snprintf(buf,32,"_%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
|
|
|