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