Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members

ImageStats.cpp

Go to the documentation of this file.
00001 /* ImLib3D
00002  * Copyright (c) 2001, ULP-IPB Strasbourg.
00003  *
00004  * This program is free software; you can redistribute it and/or modify
00005  * it under the terms of the GNU General Public License as published by
00006  * the Free Software Foundation; either version 2 of the License, or (at
00007  * your option) any later version.
00008  * 
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  * 
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00017  */
00020 #include<ImLib3D/ImageStats.hpp>
00021 #include<ImLib3D/TestPatterns.hpp>
00022 #include<ImLib3D/Arithmetic.hpp>
00023 #include<ImLib3D/ConvenienceProcessors.hpp>
00024 
00025 namespace IP3D
00026 {
00027 void 
00028 RobustAverageAndVariance(const Image3Df &src,double &average,double &variance)
00029 {
00030     // compute median as robust estimator of average
00031     IP3D::Median(src,average);
00032     // compute median absolute deviation (MAD) as estimator of std dev
00033     // MAD =1.4826 * median |xi- avg|
00034     Image3Df diff=src;
00035     diff-=average;
00036     IP3D::Abs(diff,diff);
00037     IP3D::Median(diff,variance);
00038     variance=1.4826*variance;
00039     // variance = stddev^2
00040     variance=variance*variance;
00041 }
00042 }
00043 
00044 void 
00045 ProbabilityDistribution::SetRange(const vector<Value> &samples)
00046 {
00047     vmin=*min_element(samples.begin(),samples.end());
00048     vmax=*max_element(samples.begin(),samples.end());
00049     double grow=(vmax-vmin)*.0001;
00050     if(vmax==vmin){mprintf("ProbabilityDistribution::SetRange: WARNING vmax==vmin==%f\n",vmax);grow=.0001;}
00051     vmin-=grow;
00052     vmax+=grow;
00053 }
00054 
00055 
00056 // compute statistics for this class
00057 // This just computes the first three moments of the distribution for pixels in class classNum
00058 // Hiwever, it keeps memory of previously computed class stats so it doen't need to
00059 // recompute everything each time
00060 ProbabilityDistribution::OtsuClass
00061 ProbabilityDistribution::ComputeClassStats(vector<OtsuClass> &classes,int classNum,vector<int> &indexes)
00062 {
00063     double moment1=0,moment2=0,moment0=0;
00064     int j0=(classNum>0                   ? indexes[classNum-1] : 0               );
00065     int j1=(classNum<(int)indexes.size() ? indexes[classNum  ] : (int)bins.size());
00066 
00067 //      if(dotalk)mprintf("class %d: from %f to %f\n",
00068 //                       classNum,vmin+j0*((vmax-vmin)/bins.size()),vmin+j1*((vmax-vmin)/bins.size())   );
00069 
00070     OtsuClass &old=classes[classNum];
00071     // try using last statistics to avoid recomputing 
00072     if(old.id==classNum && old.j0==j0   && old.j1==j1)
00073     {
00074         moment0=old.moment0;
00075         moment1=old.moment1;
00076         moment2=old.moment2;                    
00077     }
00078     else
00079     if(old.id==classNum && old.j0==j0-1 && old.j1==j1)
00080     {
00081         int j=j0-1;
00082         double v=vmin+j*((vmax-vmin)/bins.size());
00083         moment0=old.moment0 -     bins[j];
00084         moment1=old.moment1 - v*  bins[j];
00085         moment2=old.moment2 - v*v*bins[j];                  
00086     }
00087     else
00088     if(old.id==classNum && old.j0==j0   && old.j1==j1-1)
00089     {
00090         int j=j1-1;
00091         double v=vmin+j*((vmax-vmin)/bins.size());
00092         moment0=old.moment0 +     bins[j];
00093         moment1=old.moment1 + v*  bins[j];
00094         moment2=old.moment2 + v*v*bins[j];                  
00095     }
00096     else
00097     {
00098         // *** we must recompute!****
00099 
00100 //                  mprintf("recomp for (class %d):%d %d (old %d %d)  : indexes:",i,j0,j1,old.j0,old.j1);
00101 //                  for(int zz=0;zz<indexes.size();zz++){mprintf("%d:%d:%f  ",zz,indexes[zz],thresholds[zz]);}
00102 //                  mprintf("\n");
00103 
00104         for(int j=j0;j<j1;j++)
00105         {
00106             double v=vmin+j*((vmax-vmin)/bins.size());
00107             moment0+=    bins[j];
00108             moment1+=v*  bins[j];
00109             moment2+=v*v*bins[j];
00110         }
00111     }
00112     
00113 
00114     // save stats for avoiding recomputing 
00115     old.id=classNum;
00116     old.j0=j0;
00117     old.j1=j1;
00118     old.moment0=moment0;
00119     old.moment1=moment1;
00120     old.moment2=moment2;
00121     OtsuClass res;
00122     res.id=classNum;
00123     
00124     res.id=classNum;
00125     res.moment0=moment0;
00126     res.moment1=moment1;
00127     res.moment2=moment2;
00128     res.j0=j0;
00129     res.j1=j1;
00130     return res;
00131 }
00132 
00133 
00134 void 
00135 ProbabilityDistribution::OtsuThreshholds(int nbClasses,vector<double> &res)
00136 {
00137     uint i;
00138     res.assign(nbClasses-1,0);
00139     vector<double> thresholds;
00140     vector<int> indexes;
00141     indexes.assign(nbClasses-1,0);
00142     thresholds.assign(nbClasses-1,0);
00143     double minS2=-1;
00144     Periodic talk(.5);
00145     vector<OtsuClass> classes;
00146     OtsuClass dud;
00147     classes.assign(nbClasses,dud);
00148 
00149     // try out all possible threshold combinations
00150     while(NextIndex(indexes))
00151     {
00152         // check if thresholds are in order t1<t2<t3...
00153         for(i=1;i<indexes.size();i++){if(indexes[i]<=indexes[i-1]) break;}
00154         if(i<indexes.size()){continue;}// bad order, try again!
00155 
00156         int dotalk=talk();
00157         // compute thresholds from indexes
00158         for(i=0;i<indexes.size();i++){thresholds[i]=vmin+indexes[i]*((vmax-vmin)/bins.size());}
00159         if(dotalk)
00160         {
00161             mprintf("considering thresholds:");
00162             for(i=0;i<indexes.size();i++){mprintf("%d:%f  ",i,thresholds[i]);}
00163             mprintf("\n");
00164         }
00165 
00166         // now compute the within - class variance using theese thresholds
00167         double s2=0;
00168         for(int classNum=0;classNum<nbClasses;classNum++)
00169         {
00170             // compute statistics for this class
00171             OtsuClass c=ComputeClassStats(classes,classNum,indexes);
00172             // empty class... ignore this segmentation
00173             if(c.moment0==0){s2=2*minS2+1;break;}
00174 
00175             double classProb=c.moment0;
00176             double average  =c.moment1/classProb;
00177             // variance within this class
00178             double variance =c.moment2/classProb-average*average;
00179 
00180             // compute s2
00181             s2+=variance*classProb;
00182             if(dotalk)mprintf("class %d: from %f to %f: classProb:%f average:%f variance:%f contr:%f\n",
00183                              classNum,vmin+c.j0*((vmax-vmin)/bins.size()),vmin+c.j1*((vmax-vmin)/bins.size()),
00184                              classProb,average,variance,variance*classProb);
00185         }
00186         // check if this threshold combination is optimal
00187         if(s2<minS2 || minS2<0)
00188         {// it is!
00189             minS2=s2;
00190             res=thresholds;
00191         }
00192         if(dotalk)
00193         {
00194             mprintf("TOT S2:%f BEST:%f  : ",s2,minS2);
00195             for(uint i=0;i<res.size();i++){mprintf("%d:%f  ",i,res[i]);}
00196             mprintf("\n");
00197         }
00198     }
00199     mprintf("OtsuThresholds result:");
00200     for(i=0;i<res.size();i++){mprintf("%d:%f  ",i,res[i]);}
00201     mprintf("\n");
00202 }
00203 void 
00204 ProbabilityDistribution::ShowWithGnuPlot(const string &label) const
00205 {
00206     vector<double> values(bins.size());
00207     for(uint i=0;i<bins.size();i++)
00208     {
00209         values[i]=(i*(vmax-vmin)/bins.size())+vmin;
00210     }
00211     GnuPlot(values,bins,label,"histogram");
00212 }
00213 
00214 void 
00215 ProbabilityDistribution::SetWithSamples(const vector<Value> &samples,int nbBins)
00216 {
00217     bins.assign(nbBins,0);
00218     SetRange(samples);
00219     accumulatedSegmentCount = 0;
00220 
00221     AccumulateWithSamples(samples);
00222 }
00223 
00224 void 
00225 ProbabilityDistribution::AccumulateWithSamples(const vector<Value> &samples)
00226 {
00227     // We first make histogram, then accumulate before we again normalize to make probability
00228     for(uint tt=0;tt<bins.size();tt++)
00229     {
00230         bins[tt]*=accumulatedSegmentCount;
00231     }
00232 
00233     for(uint i=0;i<samples.size();i++)
00234     {
00235         bins[BinNum(samples[i])]+=1.0;
00236         accumulatedSegmentCount++;
00237     }
00238     for(uint tt=0;tt<bins.size();tt++)
00239     {
00240         bins[tt]/=accumulatedSegmentCount;
00241     }
00242 
00243 //      double totproba=0;
00244 //      for(int tt=0;tt<bins.size();tt++)
00245 //      {
00246 //          totproba+=bins[tt];
00247 //      }
00248 //  mprintf("totproba:%f\n",totproba);
00249 }
00250 
00252 void 
00253 ProbabilityDistribution::SetWithSignal(const vector<Value> &samples,int nbBins)
00254 {
00255     bins.assign(nbBins,0);
00256     SetRange(samples);
00257     accumulatedSegmentCount = 0;
00258 
00259     AccumulateWithSignal(samples);
00260 }
00261 
00262 void 
00263 ProbabilityDistribution::AccumulateWithSignal(const vector<Value> &samples)
00264 {
00265     // We first make histogram, then accumulate before we again normalize to make probability
00266     for(uint tt=0;tt<bins.size();tt++)
00267     {
00268         bins[tt]*=accumulatedSegmentCount;
00269     }
00270 
00271     // for each sample pair fill all the bins whose values span both samples
00272     for(uint i=0;i<samples.size()-1;i++)
00273     {
00274         Value v0=samples[i];
00275         Value v1=samples[i+1];
00276         Value t=max(v0,v1);
00277         v0=min(v0,v1);
00278         v1=t;
00279 
00280         if( BinNum(v0)==BinNum(v1))
00281         {// just one bin:
00282             bins[BinNum(v0)]+=1.0;
00283         }
00284         else
00285         {
00286             // more than one bin:
00287             double j0=BinPos(v0);
00288             double j1=BinPos(v1);
00289             //          double dprob=1.0/(samples.size()*(j1-j0));
00290             double dweight=1.0/(j1-j0);
00291             int cj0=(int)ceil(j0);
00292             int fj1=(int)floor(j1);
00293                 
00294             double dy;
00295             // first bin
00296             dy=cj0-j0;
00297             bins[(int)j0]+=dy*dweight;
00298             // middle bins
00299             for(int j=cj0;j<fj1;j++)
00300             {
00301                 bins[j]+=dweight;
00302             }
00303             // last bin
00304             dy=j1-fj1;
00305             bins[(int)j1]+=dy*dweight;
00306         }
00307         accumulatedSegmentCount++;
00308     }
00309     // renormalize histogram to obtain proba
00310     for(uint tt=0;tt<bins.size();tt++)
00311     {
00312         bins[tt]/=accumulatedSegmentCount;
00313     }
00314     // display totproba
00315     double totproba=0;
00316     for(uint tt=0;tt<bins.size();tt++)
00317     {
00318         totproba+=bins[tt];
00319     }
00320     mprintf("totproba:%f bins:%d\n",totproba,bins.size());
00321 }
00322 
00324 void 
00325 ProbabilityDistribution::SetWithImage3D(const Image3Df &image,SetWithImage3DType type,int nbBins)
00326 {
00327     if(type==LINEAR3){SetWithImage3Dlinear3(image,nbBins);return;}
00328     vector<float> samples;
00329     samples.reserve(image.GetNVoxels());
00330     for(Image3Df::const_iteratorFastMasked p=image.begin();p!=image.end();++p)
00331     {
00332         samples.push_back(*p);
00333     }
00334     if(type==LINEAR1){SetWithSignal(samples,nbBins);return;}
00335     if(type==SAMPLES){SetWithSamples(samples,nbBins);return;}
00336     ThrowError("ProbabilityDistribution::SetWithImage3D unknown type:%d\n",type);
00337 }
00338 
00340 void 
00341 ProbabilityDistribution::AccumulateWithImage3D(const Image3Df &image,SetWithImage3DType type)
00342 {
00343     if(type==LINEAR3){AccumulateImage3Dlinear3(image);return;}
00344     vector<float> samples;
00345     samples.reserve(image.GetNVoxels());
00346     for(Image3Df::const_iteratorFastMasked p=image.begin();p!=image.end();++p)
00347     {
00348         samples.push_back(*p);
00349     }
00350     if(type==LINEAR1){AccumulateWithSignal(samples);return;}
00351     if(type==SAMPLES){AccumulateWithSamples(samples);return;}
00352     ThrowError("ProbabilityDistribution::SetWithImage3D unknown type:%d\n",type);
00353 }
00354 
00355 
00357 void 
00358 ProbabilityDistribution::SetWithImage3Dlinear3(const Image3Df &src,int nbBins)
00359 {
00360     Vect3Di srcSize=src.SizeV();
00361     IP3D::FindMinMax(src,vmin,vmax);
00362     double grow=(vmax-vmin)*.0001;
00363     if(vmax==vmin){mprintf("ProbabilityDistribution::SetWithImage3D: WARNING vmax==vmin==%f\n",vmax);grow=.0001;}
00364     vmin-=grow;
00365     vmax+=grow;
00366     bins.assign(nbBins,0);
00367     accumulatedSegmentCount = 0;
00368 
00369     AccumulateImage3Dlinear3(src);
00370 }
00371 
00372 void 
00373 ProbabilityDistribution::AccumulateImage3Dlinear3(const Image3Df &src)
00374 {
00375     if (bins.size() == 0)
00376         ThrowError("Histogram must be set before accumulating");
00377     
00378     Vect3Di srcSize=src.SizeV();
00379     int nbBins = bins.size();
00380 
00381     // We first make histogram, then accumulate before we again normalize to make probability
00382     for(uint tt=0;tt<bins.size();tt++)
00383     {
00384         bins[tt]*=accumulatedSegmentCount;
00385     }
00386 
00387     int segmentCount=0;
00388     for(int dir=0;dir<3;dir++)
00389     {
00390         int d0=(dir+1)%3;
00391         int d1=(dir+2)%3;
00392         int d2=dir;
00393         float minDSrc=.001*(vmax-vmin)/(double)nbBins;
00394         Vect3Di dpos(0,0,0);dpos(d2)=1;
00395         Vect3Di pos(0,0,0);
00396         for(int x0=0;x0<srcSize(d0);x0++)
00397         {
00398             pos(d0)=x0;
00399             for(int x1=0;x1<srcSize(d1);x1++)
00400             {
00401                 pos(d1)=x1;
00402                 for(int x2=0;x2<srcSize(d2)-1;x2++)
00403                 {
00404                     pos(d2)=x2;
00405                     Vect3Di pos0=pos;
00406                     Vect3Di pos1=pos+dpos;
00407                     // skip if we're on unmasked
00408                     if(src.properties.mask && !src.Mask()(pos0)){continue;}
00409                     if(src.properties.mask && !src.Mask()(pos1)){continue;}
00410                     segmentCount++;
00411                     float src0=src(pos0);
00412                     float src1=src(pos1);
00413 //                  pos0.Show("pos0:","   ");
00414 //                  pos1.Show("pos1:","\n");
00415 //                  printf("src0:%f     src1:%f\n",src0,src1);
00416                                                     
00417                     // swap so that src1 > src0
00418                     if(src1<src0){swap(src0,src1);}
00419                     if(src1-src0<minDSrc){src1=src0+minDSrc;}// make sure dsrc!=0
00420                     int srcBin0=BinNum(src0);
00421                     int srcBin1=BinNum(src1);
00422 //                  printf("src0:%f     src1:%f srcBin0:%d srcBin1:%d\n",src0,src1,srcBin0,srcBin1);
00423                     for(int s=srcBin0;s<=srcBin1;s++)
00424                     {
00425 //                      float srcvb0=max(src0,(float)BinValue(s));
00426 //                      float srcvb1=min(src1,(float)BinValue(s+1));
00427 //                      float srcBinProba=(srcvb1-srcvb0)/(src1-src0);
00428 //                      float srcBinProba=srcBin1-srcBin0;
00429                         bins[s]+=1.0/(1+srcBin1-srcBin0);
00430                     }
00431                 }
00432             }
00433         }
00434     }
00435     accumulatedSegmentCount += segmentCount;
00436     for(uint tt=0;tt<bins.size();tt++)
00437     {
00438         bins[tt]/=accumulatedSegmentCount;
00439     }
00440     // display totproba
00441     double totproba=0;
00442     for(uint tt=0;tt<bins.size();tt++)
00443     {
00444         totproba+=bins[tt];
00445     }
00446     mprintf("totproba2:%f bins:%d\n",totproba,bins.size());
00447 
00448 }
00449 
00450 
00452 int 
00453 ProbabilityDistribution::ScottsRule(const vector<float>& vals)
00454 {
00455     float N=vals.size();
00456     double av=0;
00457     double var=0;
00458     for (int i=0;i<N;i++)
00459     {
00460         av+=vals[i]; 
00461         var+=vals[i]*vals[i];
00462     }
00463     av/=N;
00464     var/=(N-1);
00465     var-=N/(N-1)*av*av;
00466 
00467     double s=sqrt(var);
00468     double w=3.5*s/pow((double)N, 1/3.0);
00469     float maximum = *max_element(vals.begin(), vals.end());
00470     float minimum = *min_element(vals.begin(), vals.end());
00471     int nBins=(int)((maximum-minimum)/w);
00472     return nBins;
00473 }
00474 
00475 void 
00476 ProbabilityDistribution::SetBins(float _vmin, float _vmax, int nbBins)
00477 {
00478     if (bins.size() > 0) 
00479         bins.clear();
00480     vmin=_vmin; 
00481     vmax=_vmax;
00482     bins.assign(nbBins,0);
00483     accumulatedSegmentCount = 0; 
00484 }

Generated on Fri Jun 17 13:36:04 2005 for ImLib3D by  doxygen 1.4.2