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