// ******************************************************* // * Calculation of skull-shape features * // * based on higher order spherical moments, * // * with the use of C0.lib for numerical linear algebra * // * www.vector-space.com/c0.html * // *-----------------------------------------------------* // * C.Dullin 2007 * // * University Medicine Goettingen * // * Dept. Diagnosty Radiology * // * 37099 Geottingen * // * Germany * // ******************************************************* #include "c0_init.h" #include "matrix.h" #include class _voxel { public: Vector r; unsigned short v; Scalar weight; _voxel(){double dt[3]={0.0,0.0,0.0};r=C0(3,dt);r[0]=r[1]=r[2]=0.0;v=0;weight=1.0;} _voxel(const float& xx,const float& yy,const float& zz,const unsigned short& vv,const double& gg=1.0) { double dt[3]={0.0,0.0,0.0};r=C0(3,dt); r[0]=xx; r[1]=yy; r[2]=zz; v=vv; weight=gg; }; _voxel(const _voxel& other) { double dt[3]={0.0,0.0,0.0};r=C0(3,dt); r=other.r; v=other.v; weight=other.weight; }; }; inline Matrix dyad(Vector& v1,Vector& v2) { double dt[3][3]={{0.0,0.0,0.0},{0.0,0.0,0.0},{0.0,0.0,0.0}}; C0 erg(3,3,dt[0]); erg[0][0]=v1[0]*v2[0]; erg[0][1]=v1[0]*v2[1]; erg[0][2]=v1[0]*v2[2]; erg[1][0]=v1[1]*v2[0]; erg[1][1]=v1[1]*v2[1]; erg[1][2]=v1[1]*v2[2]; erg[2][0]=v1[2]*v2[0]; erg[2][1]=v1[2]*v2[1]; erg[2][2]=v1[2]*v2[2]; return erg; } inline void minVec(Vector& v1,Vector& v2) { for (int i=0;i<3;++i) v1[i]=__min(v1[i],v2[i]); } inline void maxVec(Vector& v1,Vector& v2) { for (int i=0;i<3;++i) v1[i]=__max(v1[i],v2[i]); } inline void polar(Vector& v,_voxel& center=_voxel(0,0,0,0,0)) { double sqrX=pow(v[0]-center.r[0],2); double sqrY=pow(v[1]-center.r[1],2); double sqrZ=pow(v[2]-center.r[2],2); double r=sqrt(sqrX+sqrY+sqrZ); // radius double z=atan(sqrt(sqrX+sqrY)/(v[2]-center.r[2])); // zenit double a=atan((v[1]-center.r[1])/(v[0]-center.r[0])); // azimut v[0]=r;v[1]=z;v[2]=a; } void GenerateSkullShapeFeatures( unsigned short *volumeData, int dimX,int dimY,int dimZ, unsigned short isoLevel, double vsX,double vsY,double vsZ) { //1.) Build Feature Space unsigned long pointArray_size=1000; _voxel **pointArray=(_voxel**)malloc(pointArray_size*sizeof(_voxel*)); unsigned long count=0; unsigned long pos=0; double meanValue=0; double sumBuffer=0; unsigned short minValue=65535; unsigned short maxValue=0; for (unsigned short lz=0;lz=isoLevel) { if (count>=pointArray_size) { pointArray_size+=1000; pointArray=(_voxel**)realloc(pointArray,pointArray_size*sizeof(_voxel*)); } pointArray[count]=new _voxel(); pointArray[count]->r[0]=lx*vsX; pointArray[count]->r[1]=ly*vsY; pointArray[count]->r[2]=lz*vsZ; pointArray[count]->v=volumeData[pos]; ++count; sumBuffer+=(double)volumeData[pos]; minValue=__min(minValue,volumeData[pos]); maxValue=__min(maxValue,volumeData[pos]); } ++pos; } if (count>0) meanValue=sumBuffer/(double)count; pointArray_size=count; pointArray=(_voxel**)realloc(pointArray,pointArray_size*sizeof(_voxel*)); //2.) Create Mean and Covarianzmatrix double dt[3]={0.0,0.0,0.0}; double dt2[3][3]={{0.0,0.0,0.0},{0.0,0.0,0.0},{0.0,0.0,0.0}}; C0 mean(3,dt); C0 covMat(3,3,dt2[0]); double dIsoLevel=(double)isoLevel; double dminValue=(double)minValue; double dmaxValue=(double)maxValue; double norm=pow(__max(fabs(dIsoLevel-dminValue),fabs(dmaxValue-dIsoLevel)),2); double weight=0; double weightSum=0; unsigned long i; for (i=0;iv,2)/norm; weightSum+=weight; pointArray[i]->weight=weight; mean[0]+=pointArray[i]->r[0]*weight; mean[1]+=pointArray[i]->r[1]*weight; mean[2]+=pointArray[i]->r[2]*weight; } mean=mean/weightSum; double diff[3]={0.0,0.0,0.0}; Vector diffVec=C0(3,diff); for (i=0;ir-mean; covMat+=dyad(diffVec,diffVec)*pointArray[i]->weight; } covMat=covMat/weightSum; // 3.) perform Kahunen Love Transformation // 3.1) solve eigenvalue problem Eigen a(covMat); Vector lambda = a.Eigenvalues(); Matrix M = a.Eigenvectors(); double **ew=lambda.rep_ptr2(); double **ev=M.rep_ptr(); // 3.2) Apply transformation Vector tmp=pointArray[0]->r-mean; tmp=tmp*M; double mid[3]={0.0,0.0,0.0}; double mad[3]={0.0,0.0,0.0}; Vector minDim=C0(3,mid); Vector maxDim=C0(3,mad); minDim[0]=maxDim[0]=tmp[0]; minDim[1]=maxDim[1]=tmp[1]; minDim[2]=maxDim[2]=tmp[2]; for (i=0;ir=(pointArray[i]->r-mean)*M; minVec(minDim,pointArray[i]->r); maxVec(maxDim,pointArray[i]->r); } // 4.) scale isotropically to [-1 .. 1] diffVec=maxDim-minDim; double maxD=__max(diffVec[0],__max(diffVec[1],diffVec[2])); double scaleFac=1.0/maxD; for (i=0;ir=pointArray[i]->r*scaleFac; // 5.) generate spherical moments up to an order of 4 // 5.1) generate indexfield double m[4][4][4]; double pd[64][3]; Matrix permVec=C0(64,3,pd[0]); unsigned long permCount=0; for (i=0;i<=4;++i) for (unsigned int j=0;j<=4;++j) for (unsigned int k=0;k<=4;++k) if (i+j+k<=4 && i+j+k>0) // 0te order out of interest { m[i][j][k]=0.0; permVec[permCount][0]=i; permVec[permCount][1]=j; permVec[permCount][2]=k; ++permCount; } // 5.2) perform calculations for (unsigned long int p=0;pr); for (unsigned int perm=0;perm<64;++perm) m[(int)permVec[perm][0]] [(int)permVec[perm][1]] [(int)permVec[perm][2]]+= pow(pointArray[p]->r[0],permVec[perm][0])* pow(pointArray[p]->r[1],permVec[perm][1])* pow(pointArray[p]->r[2],permVec[perm][2])* (double)pointArray[p]->weight; } // 5.3) normalize features for (unsigned int perm=0;perm<64;++perm) m[(int)permVec[perm][0]] [(int)permVec[perm][1]] [(int)permVec[perm][2]]/= weightSum* pow(10,sqrt((int)permVec[perm][0]+(int)permVec[perm][1]+(int)permVec[perm][2])-1); // 6.) output example for (perm=0;perm<4;++perm) { cout << 'm' << (int)permVec[perm][0] << (int)permVec[perm][1] << (int)permVec[perm][0] << " = " << m[(int)permVec[perm][0]][(int)permVec[perm][1]][(int)permVec[perm][2]]; } }