Isis 3 Programmer Reference
Selection.cpp
1 
20 //Isis.h and IsisDebug.h if needed
21 
22 //C++ standard libraries if needed
23 #include <math.h>
24 #include <time.h>
25 
26 //QT libraries if needed if needed
27 
28 //third party libraries if needed
29 #include <gsl/gsl_linalg.h>
30 #include <gsl/gsl_rng.h>
31 #include <GSLUtility.h>
32 
33 
34 //Isis Headers if needed
35 #include "Ransac.h"
36 #include "Selection.h"
37 
38 
39 using namespace std;
40 namespace Isis {
41  Selection::Selection() {};
42  Selection::~Selection() {};
43 
44 
45  //Reduction methods--methods used to trim off outliers in the selection based on aprior knowledge of the expected shape
46  int Selection::elipticalReduction(Chip *selectionChip, double percent_selected, double play, int patience_limit) {
47  /* This method searchs for the largest elipsoid that has at least percent_selected of the pixels within it selected, and lies entirely within the given chip
48  * The purpose of this is to trim off 'hairs' and 'danglies' and thus reduce the data to just what is close to being within the range of the ellipse
49  * The method is general (meaning it works for any orientation of any ellipse within the chip) provided enough of the edge of the ellipse is preserved to define it.
50  *
51  * Algorithim:
52  * 1 Compile an array of all points on the border of the selected area
53  * 2 Pick Five points at random from the border points and use them to define a general conic
54  * 3 if the general conics is an elipse do a least squares generization. Any points within a distance of play pixels to the edge of the ellipse are included in the gernalization. the distance check is repeated for every iteration so the ellipse can effective grow to include more points.
55  * 4 If the generization is successfully check to see if the area is at least as great as the current best.
56  * 5 If the area is great enough check that the percent sellected is at least percent_selected.
57  * 6 If all above tests are passed then we have a new Best ellipse and the number of consecutive emptySets is zeroed. Otherwise emptySets++
58  * 7 repeat steps 2 through 6 until pacience_limit consecquitive failures to find a better (larger area) elipse have occured
59  */
60 
61  int i, j, //=0,k,l,
62  samples, //samples in selectionChip
63  lines; //lines in selectionChip
64 
65  std::vector < std::vector<int> > pts; //vector of pixels on the border of the slection
66 
67  double dpt[2], random5[5][2];
68 
69  unsigned long int randPt;
70 
71  int emptySets; //consecutive sets of five points that haven't given a new best ellipse
72 
73  gsl_rng *rand_gen = gsl_rng_alloc(gsl_rng_taus);
74  gsl_rng_set( rand_gen, time(NULL) ); //using the current time as a seed value
75 
76 
77  samples = selectionChip->Samples();
78  lines = selectionChip->Lines();
79 
80  //STEP 1: finding points along the boundary of the selection
81  selectionEdge(selectionChip,&pts);
82 
83  Ellipse ellNew,ellBest; //new and best ellipses
84  emptySets =0;
85  ellBest.area = 0.0; //initialize the current best ellipse to zero area
86 
87  while (emptySets < patience_limit) {
88  //STEP 2: selecting edge points at random to define ellipses to define a general conic
89  for (i=0;i<5;i++) {
90  randPt = gsl_rng_uniform_int( rand_gen ,pts.size());
91  random5[i][0] = pts[randPt][0];
92  random5[i][1] = pts[randPt][1];
93  }
94 
95  //STEP 3: Testing to see if it's an ellipse
96  if (!ellipseFrom5Pts(&ellNew,random5)) { //if it's not a real ellipse
97  emptySets++;
98  continue;
99  }
100 
101  //if the center isn't within the chip continue
102  if ( ellNew.cen[0] < 1 || ellNew.cen[0] > samples || ellNew.cen[1] < 1 || ellNew.cen[1] > lines ) {
103  emptySets++;
104  continue;
105  }
106 
107  //STEP 3 continued: attempt to generalize the ellipse to a least squares fit of all the edge points near it
108  if (!bestFitEllipse(&ellNew, &pts, play, 50)) {
109  emptySets++;
110  continue; //if optimization fails go on to the next five point set
111  }
112 
113  //STEP 4 If the generization is successfully check to see if the area is at least as great as the current best.
114  if (ellNew.area < ellBest.area) { //if the new elipse isn't at least as big as the current best ellipse there's no point in continueing
115  emptySets++;
116  continue;
117  }
118 
119 
120  if (!ellipseInChip(&ellNew,selectionChip)) { //if the ellipse isn't entirely contained in the chip
121  emptySets++;
122  continue;
123  }
124 
125 
126  //STEP 5 is there a sufficient potion of the ellipse selected?
127  if (elipsePercentSelected(selectionChip,&ellNew) < percent_selected) {
128  emptySets++;
129  continue;
130  }
131 
132  //STEP 6 saving the Best ellipse so far
133  //printf("Debug: Best area: %lf emptySets: %d \n",ellNew.area,emptySets);
134  //we have a new best
135  emptySets=0;
136  ellBest.area = ellNew.area;
137  ellBest.A[0] = ellNew.A[0];
138  ellBest.A[1] = ellNew.A[1];
139  ellBest.A[2] = ellNew.A[2];
140 
141  ellBest.cen[0] = ellNew.cen[0];
142  ellBest.cen[1] = ellNew.cen[1];
143 
144  ellBest.semiMajor = ellNew.semiMajor;
145  ellBest.semiMinor = ellNew.semiMinor;
146  ellBest.majorAxis[0] = ellNew.majorAxis[0];
147  ellBest.majorAxis[1] = ellNew.majorAxis[1];
148  ellBest.minorAxis[0] = ellNew.minorAxis[0];
149  ellBest.minorAxis[1] = ellNew.minorAxis[1];
150 
151  }//end emptySets while loop
152 
153  if (ellBest.area == 0)
154  return 0; //no ellipse meeting the selection criteria was found
155 
156  //go through and unselect the points outside the trimming ellipse
157  for (i=1;i<=samples;i++) {
158  for (j=1;j<=lines;j++) {
159  dpt[0] = double(i) ;
160  dpt[1] = double(j) ;
161 
162  if (!pointInEllipse(&ellBest,dpt,play)) { //if the point isn't within play pixles of being within the elipse
163  //selectionChip->setValue(i,j,0.0);
164  selectionChip->SetValue(i,j,0.0);
165  }
166 
167  }
168  }
169 
170  return 1;
171  }
172 
173 
174 
175 
176  //Observation Methods--methods used to reduce a selection to a single sub-pixel observation
177 
178  int Selection::centerOfMass(Chip *selectionChip, double *sample, double *line) {
179  //calculates the unweighted center of mass of all the selected pixels
180  unsigned int samples, lines, i, j, n=0;
181  *sample=0.0;
182  *line=0.0;
183  samples = selectionChip->Samples();
184  lines = selectionChip->Lines();
185 
186  for (i=1;i<=samples;i++) {
187  for (j=1;j<=lines;j++) {
188  if (selectionChip->GetValue(i,j) == 1) {
189  *sample += i;
190  *line += j;
191  n++;
192  }
193  }
194  }
195 
196  *sample /= double(n);
197  *line /= double(n);
198  return 1;
199  }
200 
201  int Selection::centerOfMassWeighted(Chip *inputChip, Chip *selectionChip, double *sample, double *line) {
202  //this function computes a center of mass, as the average of the coordiantes of the selected pixels in the selectionChip weighted by the DN in the inputChip
203 
204  int samples, lines,i, j;
205  //make sure the two chips are the same size
206  samples = selectionChip->Samples();
207  if (inputChip->Samples() != samples) {
208  //todo messege
209  return 0;
210  }
211 
212  lines = selectionChip->Lines();
213  if (inputChip->Lines() != lines) {
214  //todo messege
215  return 0;
216  }
217 
218 
219  *sample=0.0;
220  *line=0.0;
221  double temp,sumDN=0;
222 
223  for (i=1;i<=samples;i++) {
224  for (j=1;j<=lines;j++) {
225  if (selectionChip->GetValue(i,j) == 1) {
226  temp = inputChip->GetValue(i,j);
227  *sample += double(i)*temp;
228  *line += double(j)*temp;
229  sumDN += temp;
230  }
231  }
232  }
233 
234  *sample /= sumDN;
235  *line /= sumDN;
236  return 1;
237  }
238 
239 
240 
241  //basic math methods
242  std::vector<double> Selection::minimumBoundingElipse(std::vector< std::vector<int> > pts,Ellipse *ell) {
243  /*Function finds the minimum bounding elipsoid for pts
244  input: pts vector of points
245  output: definition of elipse (pt-center)'A(pt-center) = 2 for all points on the elipse ( <2 for all points within the elipse )
246  Algorithim taken from "Estimation of Correlation Coefficients by Ellipsoidal Trimming". D. M. Titterington. Journal of the Royal Statistical Society, Series C (Applied Statistics), Vol. 27, No. 3
247  */
248 
249  std::vector<double> lamda(pts.size(),1.0/double(pts.size())); //vector of weights for the individual pts
250 
251 
252  double delta,temp,ptc[2];
253 
254  unsigned int niter=0;
255 
256  unsigned int i;
257 
258  do {
259  //find the center
260  ell->cen[0] = ell->cen[1] = 0.0;
261  for (i=0; i<pts.size(); i++) {
262  ell->cen[0] += pts[i][0]*lamda[i];
263  ell->cen[1] += pts[i][1]*lamda[i];
264  }
265 
266  //calc A sum( transpose(pt)*pt*lamda )
267  ell->A[0]=ell->A[1]=ell->A[2]=0.0;
268  for (i=0;i<pts.size();i++) {
269  ell->A[0] += pts[i][0] * pts[i][0] * lamda[i];
270  ell->A[1] += pts[i][0] * pts[i][1] * lamda[i];
271  ell->A[2] += pts[i][1] * pts[i][1] * lamda[i];
272  }
273  //symetric 2x2 inverse
274  temp = ell->A[0];
275  ell->A[0] = ell->A[2];
276  ell->A[2] = temp;
277  temp = ell->A[0]*ell->A[2] - ell->A[1]*ell->A[1];
278  ell->A[0] /= temp;
279  ell->A[2] /= temp;
280  ell->A[1] /= -temp;
281 
282 
283  //find the updated weights
284  delta=0;
285  for (i=0; i<pts.size(); i++) {
286  temp = lamda[i];
287  ptc[0] = pts[i][0] - ell->cen[0];
288  ptc[1] = pts[i][1] - ell->cen[1];
289  lamda[i] = ( (ptc[0]*ell->A[0] + ptc[1]*ell->A[1] )*ptc[0] + ( ptc[0]*ell->A[1] + ptc[1]*ell->A[2] )*ptc[1] )*lamda[i]/2.0; // transpose(pt - center)*A*(pt-center)*lamda/2.0
290  delta += ( lamda[i] - temp )*( lamda[i] - temp );
291  }
292 
293  niter++;
294  } while (delta > 1e-10 && niter < 500);
295 
296  ell->A[0] /= 2.0;
297  ell->A[1] /= 2.0;
298  ell->A[1] /= 2.0;
299  ell->A[2] /= 2.0;
300 
301  ellipseAxesAreaFromMatrix(ell);
302 
303  return lamda; //return the relative weights--this is the relative importance of each point in determining the center of the MBE
304  }
305 
306  double Selection::elipsePercentSelected(Chip *selectionChip,Ellipse *ell) {
307  //Given and elipse definition and a selectionChip find the percentage of the elipse that is selected assuming the whole ellipse is within the chip
308  unsigned int lines, samples, i,j,
309  ellipsePixels=0,
310  ellipsePixelsSelected=0,
311  outsideEllipsePixelsSelected=0;
312 
313  double ptc[2];
314  lines = selectionChip->Lines();
315  samples = selectionChip->Samples();
316 
317  //Chip temp(*selectionChip);//debug
318 
319  for (i=1; i<=samples; i++) {
320  for (j=1; j<=lines; j++) {
321  ptc[0] =i;
322  ptc[1] =j;
323  if (pointInEllipse(ell, ptc, 0.0)) { //if the point is within the elipse
324  ellipsePixels++; //increment the number of points
325  if (selectionChip->GetValue(i,j) == 1) ellipsePixelsSelected++; //increment the number selected within the ellipse;
326  //else temp.setValue(i,j,3.0);//debug
327  }
328  else
329  if (selectionChip->GetValue(i,j) == 1) outsideEllipsePixelsSelected++; //increment the number selected outside the ellipse;
330  }
331  }
332 
333  //temp.Write("solutionTemp.cub");//debug
334 
335 
336  //printf("DEBUG ellipse pixels: %d selected inside: %d selected outside: %d ratio of outside selected %lf\n",ellipsePixels,ellipsePixelsSelected,outsideEllipsePixelsSelected,double(outsideEllipsePixelsSelected)/( double(samples*lines) - double(ellipsePixels) ));
337  //if (double(outsideEllipsePixelsSelected)/( double(samples*lines) - double(ellipsePixels) ) > 0.2) return 0; //if more than 20% of the pixels outside the ellipse are selected return 0 (this is an unexceptable solution)
338  if (double(outsideEllipsePixelsSelected)/double(outsideEllipsePixelsSelected+ellipsePixelsSelected) > 0.33) return 0;//if more than a third of the total selected pixels are outside the ellipse return 0 (this avoids returning questionable solutions)'
339  //printf("DEBUG: outsideEllipsePixelsSelected: %d ellipsePixelsSelected: %d\n",outsideEllipsePixelsSelected,ellipsePixelsSelected);
340  //printf("DEBUG Percent of ellipse selected: %lf percent of selected pixels outside ellipse: %lf\n",double(ellipsePixelsSelected)/double(ellipsePixels)*100.0,double(outsideEllipsePixelsSelected)/double(outsideEllipsePixelsSelected+ellipsePixelsSelected))*100.0;getchar();
341  return double(ellipsePixelsSelected)/double(ellipsePixels)*100.0; //return the percent selected
342  }
343 
344 
345  bool Selection::ellipseFrom5Pts(Ellipse *ell,double pts[5][2]) {
346  //this function fits a general conic to five points using a singular value decompisition
347  double svdu[6][6];
348  svdu[5][0]=svdu[5][1]=svdu[5][2]=svdu[5][3]=svdu[5][4]=svdu[5][5]=0.0; //extra line to make gsl library happy
349 
350  double svdv[6][6],cubic[6],discriminant,delta, svdd[6];
351 
352  int i;
353 
354  gsl_matrix SVDVT,SVDU;
355  gsl_vector SVDD;
356 
357  SVDU.size1 = 6;
358  SVDU.size2 = 6;
359  SVDU.data = &svdu[0][0];
360  SVDU.tda = 6;
361  SVDU.owner = 0;
362 
363  SVDVT.size1 = 6;
364  SVDVT.size2 = 6;
365  SVDVT.data = &svdv[0][0];
366  SVDVT.tda = 6;
367  SVDVT.owner = 0;
368 
369  SVDD.size = 6;
370  SVDD.stride = 1;
371  SVDD.data = svdd;
372  SVDD.owner =0;
373 
374  for (i=0;i<5;i++) {
375  svdu[i][3] = pts[i][0];
376  svdu[i][4] = pts[i][1];
377  svdu[i][5] = 1.0;
378  svdu[i][0] = svdu[i][3]*svdu[i][3];
379  svdu[i][1] = svdu[i][3]*svdu[i][4];
380  svdu[i][2] = svdu[i][4]*svdu[i][4];
381  }
382 
383  gsl_linalg_SV_decomp_jacobi(&SVDU,&SVDVT,&SVDD);
384 
385  //save the general cubic coefficients
386  for (i=0;i<6;i++) cubic[i] = svdv[i][5];
387 
388  //check to see if it is a real ellipse see: http://www.geom.uiuc.edu/docs/reference/CRC-formulas/node28.html and: http://en.wikipedia.org/wiki/Ellipse
389  discriminant = cubic[1]*cubic[1]-4*cubic[0]*cubic[2];
390  delta = (cubic[0]*cubic[2]-cubic[1]*cubic[1]/4.0)*cubic[5] + cubic[1]*cubic[4]*cubic[3]/4.0 - cubic[2]*cubic[3]*cubic[3]/4.0 - cubic[0]*cubic[4]*cubic[4]/4.0;
391  delta *= cubic[2];
392  if( discriminant < 0 && delta < 0) //then it's a real ellipse
393  return this->ellipseFromCubic(ell,cubic);
394 
395  else return false; //not a real ellipse
396 
397  }
398 
399  bool Selection::ellipseFromCubic(Ellipse *ell, double cubic[6]) {
400  //This function tests a general cubic to see if it's an ellipse and if it is caculates all members of Ellipse structure
401  //check to see if it is a real ellipse see: http://www.geom.uiuc.edu/docs/reference/CRC-formulas/node28.html and: http://en.wikipedia.org/wiki/Ellipse
402  double discriminant,delta;
403  discriminant = cubic[1]*cubic[1]-4*cubic[0]*cubic[2];
404  delta = (cubic[0]*cubic[2]-cubic[1]*cubic[1]/4.0)*cubic[5] + cubic[1]*cubic[4]*cubic[3]/4.0 - cubic[2]*cubic[3]*cubic[3]/4.0 - cubic[0]*cubic[4]*cubic[4]/4.0;
405  delta *= cubic[2];
406  if (discriminant > 0 || delta > 0) return false; //it's not a real ellipse
407  double temp;
408 
409  ell->cen[0] = (-cubic[4]*cubic[1]+2*cubic[2]*cubic[3])/(cubic[1]*cubic[1] - 4*cubic[0]*cubic[2]);
410  ell->cen[1] = (cubic[4] + cubic[1]*ell->cen[0])/(-2*cubic[2]);
411 
412  //finish converting new ellipse to general matrix form
413  ell->A[0] = cubic[0];
414  ell->A[1] = cubic[1]/2.0;
415  ell->A[2] = cubic[2];
416 
417  //it sounds weird but ell->cen will always be correctly scaled after this calculation but the ellements of A will not... so to correct the scale of A
418  temp = (ell->A[0]*ell->cen[0]*ell->cen[0] + 2.0*ell->A[1]*ell->cen[0]*ell->cen[1] + ell->A[2]*ell->cen[1]*ell->cen[1] - cubic[5]);
419 
420  ell->A[0] /= temp;
421  ell->A[1] /= temp;
422  ell->A[2] /= temp;
423 
424  ellipseAxesAreaFromMatrix(ell);
425 
426  return true;
427  }
428 
429 
430  bool Selection::bestFitEllipse(Ellipse *ell, std::vector < std::vector<int> > *pts,double play,unsigned int max_iter) {
431  /*Given an initial Ellipse ell (A and cen must be defined) and an array of pts (integers because this was desinged for imagery)
432  find an outlier resistent best fit ellipse
433 
434  Input:
435  ell initial ellipse defintion
436  pts points to be fit--gernerally edge points of a conitinuous selection see Selection::SelectionEdge
437  play any point further than play pixels from the ellipse are ignored. This is rechecked for every iteration so the ellipse can grow to include more points during the processing
438  max_iter maximum number of iterations before the software gives up
439 
440  Output:
441  ell refined ellipse values. NOTE: all members of Ellipse will be recalcuate whether the optimization succeeds or not so check the return value
442 
443 
444  Return:
445  1 -> success
446  0 -> failure
447 
448  This is a Gauss-Helemert non-linear least squares adjustment
449 
450  Matrices
451  a partials wrt unknowns
452  b partials wrt to measured quantities
453  w constant portion of linearized equations evaluate using estimates of unknown parameters
454  p p covariance matrix of measured quantities
455  m b*p*transpose(b) which is the propogated covariance of the design equations
456  delta is vector of corrections to estimated unknowns
457  v residual vector
458 
459  linearized math model:
460  a*delta + b*v = w
461 
462  normal equation:
463  transpose(a)*inverse(m)*a*delta = transpose(a)*inverse(m)*w
464 
465  Solution:
466  delta = minverse(transpose(a)*inverse(m)*a)*transpose(a)*inverse(m)*w iterate until corrections in delta are insignificant
467 
468  In this case to keep all resdiuals in pixel units and weight each observation identically p is modeled as the identity matrix thus the solution can be built using a, b, m, and w submatrices (indicated with a dot suffix) as follows
469 
470  Normal equation:
471  sum(transpose(adot)*inverse(mdot)*adot)*delta = sum(transpose(adot)*inverse(mdot)*wdot) or ata*delta = atw
472 
473  solution:
474  delta = inverse(ata)*atw iterate until corrections in delta are insignificant
475  */
476 
477  double adot[5],bdot[2],mdot,wdot,ata[15],atf[5],dpt[2]; //variable definitions given in introductory comments above
478  unsigned int ni;
479  int iterFlag,i,j,k,l;
480  //attempt to generalize the ellipse to a least squares fit of all the edge points near it
481  ni=0;
482  iterFlag=1;
483  l=0;
484  while (iterFlag && ni<max_iter) {
485  //intialize the normal equation
486  for(i=0;i<5;i++)
487  atf[i] =0.0;
488  for(i=0;i<15;i++)
489  ata[i] =0.0;
490  //printf("Debug a: %lf b: %lf theta: %lf center: %lf %lf\n",ell->semiMajor,ell->semiMinor,ell->majorAxis[0],ell->cen[0],ell->cen[1]);getchar();
491  for (i=0,l=0; i<int((*pts).size()); i++) {
492  dpt[0] = (*pts)[i][0]; //integer pixel locations coverted to double for calcualations
493  dpt[1] = (*pts)[i][1];
494  if (dpt[0]>150) continue; //debug
495 
496  //partials wrt measured quantities
497  bdot[0] = 2*ell->A[0]*dpt[0] + 2*ell->A[1]*dpt[1] + -2.0*(ell->A[0]*ell->cen[0] + ell->A[1]*ell->cen[1]); //wrt sample
498  bdot[1] = 2*ell->A[2]*dpt[1] + 2*ell->A[1]*dpt[0] + -2.0*(ell->A[1]*ell->cen[0] + ell->A[2]*ell->cen[1]); //wrt line
499 
500  mdot = 1.0 / (bdot[0]*bdot[0] + bdot[1]*bdot[1]); //note this is actually mdot invserse
501 
502  wdot = -( ell->A[0]*dpt[0]*dpt[0] +
503  2.0*ell->A[1]*dpt[0]*dpt[1] +
504  ell->A[2]*dpt[1]*dpt[1] +
505  -2.0*(ell->A[0]*ell->cen[0] + ell->A[1]*ell->cen[1])*dpt[0] +
506  -2.0*(ell->A[1]*ell->cen[0] + ell->A[2]*ell->cen[1])*dpt[1] +
507  (ell->A[0]*ell->cen[0]*ell->cen[0] + 2.0*ell->A[1]*ell->cen[0]*ell->cen[1] + ell->A[2]*ell->cen[1]*ell->cen[1] -1.0) ); //linearized objective equation evaluated with estimates of unknown parameters
508 
509  if (fabs(wdot*sqrt(mdot)) > play) continue; //if the point is more than play pixels (approximately) away from the ellipse then don't include it in the best fit ellipse calculation
510  l++; //counter for number of points included in the best fit ellipse
511 
512  //partials wrt unknowns
513  adot[0] = dpt[0]*dpt[0] + -2.0*ell->cen[0]*dpt[0] + ell->cen[0]*ell->cen[0]; //wrt A[0] ellipse matrix ellement
514  adot[1] = 2.0*dpt[0]*dpt[1] - 2.0*ell->cen[1]*dpt[0] -2.0*ell->cen[0]*dpt[1] + 2.0*ell->cen[0]*ell->cen[1]; //wrt A[1] ellipse matrix ellement
515  adot[2] = dpt[1]*dpt[1] + -2.0*ell->cen[1]*dpt[1] + ell->cen[1]*ell->cen[1]; //wrt A[2] ellipse matrix ellement
516  adot[3] = -bdot[0]; //wrt to center sample coordinate, ellipse.cen[0]
517  adot[4] = -bdot[1]; //wrt to center line coordinate, ellipse.cen[1]
518 
519  //summing sum(transpose(adot)*inverse(mdot)*adot)
520  for (j=0;j<5;j++)
521  for (k=0;k<=j;k++) //because ata is a memory optimized symetric matrix it is stored as a one deminsional array and only 1 of each pair of symetric ellements is calculated, hence k<=j rather than k<= 5
522  ata[isymp(j,k)] += adot[j]*mdot*adot[k]; //isymp(j,k) converst the 2d matrix location j,k to the memory optimized 1d location
523 
524  //summing sum(transpose(adot)*inverse(mdot)*wdot)
525  for (j=0;j<5;j++)
526  atf[j] += mdot*adot[j]*wdot;
527  }
528  //printf("Debug pts in caculation: %d\n",l);getchar();
529  if (l<5)return false;
530 
531  //solve for delat
532  if (choleski_solve(ata,atf,5,2) != 1) return false; //calculation is done in place as delta is returned in atf
533 
534  //add corections
535  ell->A[0] += atf[0];
536  ell->A[1] += atf[1];
537  ell->A[2] += atf[2];
538 
539  ell->cen[0] += atf[3];
540  ell->cen[1] += atf[4];
541 
542  iterFlag = 0; //assume iterations are complete and then verify
543  for (i=0;i<5;i++) {
544  if (fabs(atf[i]) > 0.001) {
545  iterFlag = 1;
546  break;
547  }
548  }
549  ni++;
550 
551 
552  } //end of iteration loop for best fit ellipse*/
553 
554  //if( fabs( double(l) / double((*pts).size()) ) < .33 ) return false; //insisting that at least a third of the edge points are involved in the solution
555 
556  for (i=0;i<5;i++)
557  if (atf[i] != atf[i]) return false; //nan check, I've never had to do this check before... linux?
558 
559 
560  if (ni > max_iter)
561  return false;
562 
563  return ellipseAxesAreaFromMatrix(ell); //if the matrix form can't be decomposed as an ellipse return false
564 
565  }
566 
567  void Selection::selectionEdge(Chip *selectionChip, std::vector < std::vector <int> > *pts) {
568  //Populates a vector of 2D points that are on the edge of the selection
569  //any selected point with at least one unsellected edge pixel is included
570  //It only makes since to use this method for a continuous selection eg a centroid
571  //algorithim: If the center pixel is selected and at least 1 neighboring pixel is not add it to the array.
572  std::vector <int> pt(2);
573  int i,j,k,l,nUS,lines,samples;
574  (*pts).clear();
575  samples = selectionChip->Samples();
576  lines = selectionChip->Lines();
577  for (i=2;i<samples;i++) {
578  for (j=2;j<lines;j++) {
579  //calculate nUS (number of unselected) for the square
580  for (k=i-1,nUS=0;k<=i+1;k++)
581  for (l=j-1;l<=j+1;l++)
582  if (selectionChip->GetValue(k,l) == 0 && k != l) nUS++;
583 
584  if (nUS > 0 && selectionChip->GetValue(i,j) == 1) { //add it to the array of border pixels
585  pt[0] = i;
586  pt[1] = j;
587  (*pts).push_back(pt);
588  }
589  }
590  }
591 
592  }
593 
594 
595  bool Selection::ellipseAxesAreaFromMatrix(Ellipse *ell) {
596  //several methods solve directly for the ellipse in center matrix form--this function populates the rest of the ellipse structure, axes, area, etc
597  // if the matrix form given isn't actually an ellipse it will return false
598  double temp, Ai[3];
599 
600  //invert A
601  temp = ell->A[0]*ell->A[2] - ell->A[1]*ell->A[1];
602 
603  Ai[0] = ell->A[2]/temp;
604  Ai[1] = -ell->A[1]/temp;
605  Ai[2] = ell->A[0]/temp;
606 
607  //find the eigen values of the Ai matrix-this can be done simply using the quadratic formula becuase A is 2x2--the sqaure roots of these eigen values are the lengths of the semi axes
608  //the numerically stable quadratic equation formula proposed in Numerical Recipies will be used
609  temp = -(Ai[0] + Ai[2]);
610  temp = -0.5*(temp + ((temp>=0) - (temp<0))*sqrt(temp*temp - 4.0*(Ai[0]*Ai[2]-Ai[1]*Ai[1])));
611 
612  ell->semiMajor = temp; //sqrt delayed until the end.. so these are currently the actual eigen values
613  ell->semiMinor = (Ai[0]*Ai[2]-Ai[1]*Ai[1])/temp;
614 
615  if (ell->semiMajor < 0 || ell->semiMinor < 0) return false; //if the matrix equation is actually and ellipse, ell->A and el->A inverse will be positive definite, and positive definite matrices have postive eigen values thus if one is negative this isn't an ellipse
616 
617  if (ell->semiMajor < ell->semiMinor) { //if need be, switch them
618  temp= ell->semiMajor;
619  ell->semiMajor = ell->semiMinor;
620  ell->semiMinor = temp;
621  }
622 
623  //now find the eigen vectors associated with these values for the axis directions
624  //the idea used to solve for these vectors quickly is as follows: Ai*Vector = eigen_value*vector
625  // Vector can be any length, and for the system above the length must be held for it to be solvable
626  // in 2D this is easily accomplished--without loss of generality--by letting vector = transpose(cos(theta) sin(theta)) where theta is the righthanded angle of the eigen vector wrt to the positve x axis
627  // understanding this substitution reduces the eigen vector computation to simply...
628  temp = atan2(Ai[0]-ell->semiMajor,-Ai[1]);
629  ell->majorAxis[0] = cos(temp);
630  ell->majorAxis[1] = sin(temp);
631 
632  temp = atan2(Ai[0]-ell->semiMinor, -Ai[1]);
633  ell->minorAxis[0] = cos(temp);
634  ell->minorAxis[1] = sin(temp);
635 
636  ell->semiMajor = sqrt(ell->semiMajor); //sqrt reduces the eigen values to semi axis lengths
637  ell->semiMinor = sqrt(ell->semiMinor);
638 
639  //the area of the ellipse is proportional to the product of the semi axes-as
640  ell->area = ell->semiMajor*ell->semiMinor*acos(-1.0);
641 
642  return true;
643 
644  }
645 
646  bool Selection::ellipseInChip(Ellipse *ell, Chip *chip) {
647  //Function determines whether the ellipse is entirely contained within the chip
648  // The axes of the ellipse-not just the Matrix and center-must be defined to use this method see Selection::ellipseAxesAreaFromMatrix
649  double pt[4][2],vec[2],temp;
650  int i,samples,lines;
651 
652 
653  samples = chip->Samples();
654  lines = chip->Lines();
655 
656  if (ell->cen[0] < 1 || ell->cen[0] > samples || ell->cen[1] < 1 || ell->cen[1] > lines) return false;
657 
658  //four corner points of the chip-translated so the the center of the ellipse is (0,0)
659  pt[0][0] = 1-ell->cen[0];
660  pt[0][1] = 1-ell->cen[1];
661 
662  pt[1][0] = 1-ell->cen[0];
663  pt[1][1] = double(lines)-ell->cen[1];
664 
665  pt[2][0] = double(samples)-ell->cen[0];
666  pt[2][1] = double(lines)-ell->cen[1];
667 
668  pt[3][0] = double(samples)-ell->cen[0];
669  pt[3][1] = 1-ell->cen[1];
670 
671 
672  //four corners rotated into a system where the ellipse major axis is parrallel to the x axis
673  // note: theta -> the right handed angle from the positive x axis to the semi major axis
674  // cos(theta) = ell->semiMajor[0]
675  // sin(theta) = ell->semiMajor[1]
676  // thus rotations are-
677  temp = pt[0][0];
678  pt[0][0] = ell->majorAxis[0] * temp + ell->majorAxis[1] * pt[0][1];
679  pt[0][1] = -ell->majorAxis[1] * temp + ell->majorAxis[0] * pt[0][1];
680  temp = pt[1][0];
681  pt[1][0] = ell->majorAxis[0] * temp + ell->majorAxis[1] * pt[1][1];
682  pt[1][1] = -ell->majorAxis[1] * temp + ell->majorAxis[0] * pt[1][1];
683 
684  temp = pt[2][0];
685  pt[2][0] = ell->majorAxis[0] * temp + ell->majorAxis[1] * pt[2][1];
686  pt[2][1] = -ell->majorAxis[1] * temp + ell->majorAxis[0] * pt[2][1];
687 
688  temp = pt[3][0];
689  pt[3][0] = ell->majorAxis[0] * temp + ell->majorAxis[1] * pt[3][1];
690  pt[3][1] = -ell->majorAxis[1] * temp + ell->majorAxis[0] * pt[3][1];
691 
692  //coordinates scaled so as to be in a system where the ellipse is a unit circle
693  for (i=0;i<4;i++) {
694  pt[i][0] /= ell->semiMajor;
695  pt[i][1] /= ell->semiMinor;
696  }
697 
698  //now check the distance between the four lines around the edge of the chip and the center of the ellipse
699  //line from pt[0] to pt[1]
700  vec[0] = pt[1][0] - pt[0][0];
701  vec[1] = pt[1][1] - pt[0][1];
702  temp = sqrt(vec[0]*vec[0] + vec[1]*vec[1]);
703  vec[0] /= temp;
704  vec[1] /= temp;
705 
706  temp = fabs(vec[0]*pt[0][1] - vec[1]*pt[0][0]); //length of vec cross pt
707  if (temp < 1.0) return false;
708 
709  //line from pt[1 to pt[2]
710  vec[0] = pt[2][0] - pt[1][0];
711  vec[1] = pt[2][1] - pt[1][1];
712  temp = sqrt(vec[0]*vec[0] + vec[1]*vec[1]);
713  vec[0] /= temp;
714  vec[1] /= temp;
715 
716  temp = fabs(vec[0]*pt[1][1] - vec[1]*pt[1][0]); //length of vec cross pt
717 
718  if (temp < 1.0) return false;
719 
720  //line from pt[2] to pt[3]
721  vec[0] = pt[3][0] - pt[2][0];
722  vec[1] = pt[3][1] - pt[2][1];
723  temp = sqrt(vec[0]*vec[0] + vec[1]*vec[1]);
724  vec[0] /= temp;
725  vec[1] /= temp;
726 
727  temp = fabs(vec[0]*pt[2][1] - vec[1]*pt[2][0]); //length of vec cross pt
728 
729  if (temp < 1.0) return false;
730 
731  //line from pt[3] to pt[0]
732  vec[0] = pt[0][0] - pt[3][0];
733  vec[1] = pt[0][1] - pt[3][1];
734  temp = sqrt(vec[0]*vec[0] + vec[1]*vec[1]);
735  vec[0] /= temp;
736  vec[1] /= temp;
737 
738  temp = fabs(vec[0]*pt[3][1] - vec[1]*pt[3][0]); //length of vec cross pt
739  if (temp < 1.0) return false;
740 
741  return true;
742  }
743 
744  bool Selection::pointInEllipse(Ellipse *ell, double pt[2], double play) {
745  //is a point withing a distance of play of being inside an ellipse
746  // ellipse matrix (A) and center (cen) must be defined
747  // not linear approximates of the ellipse function are used... thus the play is approximate
748  double bdot[2],mdot,wdot;
749 
750  bdot[0] = 2*ell->A[0]*pt[0] + 2*ell->A[1]*pt[1] + -2.0*(ell->A[0]*ell->cen[0] + ell->A[1]*ell->cen[1]);
751  bdot[1] = 2*ell->A[2]*pt[1] + 2*ell->A[1]*pt[0] + -2.0*(ell->A[1]*ell->cen[0] + ell->A[2]*ell->cen[1]);
752  mdot = 1.0 / (bdot[0]*bdot[0] + bdot[1]*bdot[1]); //note this is actually mdot invserse
753 
754  wdot = ( ell->A[0]*pt[0]*pt[0] +
755  2.0*ell->A[1]*pt[0]*pt[1] +
756  ell->A[2]*pt[1]*pt[1] +
757  -2.0*(ell->A[0]*ell->cen[0] + ell->A[1]*ell->cen[1])*pt[0] +
758  -2.0*(ell->A[1]*ell->cen[0] + ell->A[2]*ell->cen[1])*pt[1] +
759  (ell->A[0]*ell->cen[0]*ell->cen[0] + 2.0*ell->A[1]*ell->cen[0]*ell->cen[1] + ell->A[2]*ell->cen[1]*ell->cen[1] -1.0) );
760 
761  if (wdot*sqrt(mdot) > play) return false; //if the point isn't within play pixles of being within the elipse
762 
763  return true;
764 
765  }
766 
767  bool Selection::ellipseFromCenterAxesAngle(Ellipse *ell, double centerSample, double centerLine, double semiMajor, double semiMinor, double theta) {
768  /*defines an ellipse from basic descritors
769 
770  Input
771  centerSample center sample (x) coordinate
772  centerLine center line (y) coordinate
773  semiMajor length of semiMajor axis
774  semiMinor length of semiMinor axis
775  theta right handed angle between the positive x-axis and the semiMajor axis
776  */
777 
778  if (semiMajor < semiMinor) return false;
779 
780  double Ai[3],temp; //inverse of ell->A
781 
782  ell->semiMajor = semiMajor;
783  ell->semiMinor = semiMinor;
784  ell->majorAxis[0] = cos(theta);
785  ell->majorAxis[1] = sin(theta);
786  ell->minorAxis[0] = -ell->majorAxis[1];
787  ell->minorAxis[1] = -ell->majorAxis[0];
788  ell->cen[0] = centerSample;
789  ell->cen[1] = centerLine;
790 
791  Ai[0] = ell->majorAxis[0]*ell->majorAxis[0]*ell->semiMajor*ell->semiMajor + ell->majorAxis[1]*ell->majorAxis[1]*ell->semiMinor*ell->semiMinor;
792  Ai[2] = ell->majorAxis[1]*ell->majorAxis[1]*ell->semiMajor*ell->semiMajor + ell->majorAxis[0]*ell->majorAxis[0]*ell->semiMinor*ell->semiMinor;
793 
794  Ai[1] = ell->majorAxis[0]*ell->majorAxis[1]*(ell->semiMajor*ell->semiMajor - ell->semiMinor*ell->semiMinor);
795 
796  temp = Ai[0]*Ai[2] + Ai[1]*Ai[1];
797 
798  ell->A[0] = Ai[2]/temp;
799  ell->A[1] = -Ai[1]/temp;
800  ell->A[2] = Ai[0]/temp;
801 
802  ell->area = semiMajor*semiMinor*acos(-1.0);
803 
804  return true;
805  }
806 
807 }
Namespace for the standard library.
Namespace for ISIS/Bullet specific routines.
Definition: Apollo.h:31