IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Ticket #386: pmObjects.c

File pmObjects.c, 75.5 KB (added by eugene, 21 years ago)

updated pmObjects.c

Line 
1/** @file pmObjects.c
2 *
3 * This file will ...
4 *
5 * @author GLG, MHPCC
6 *
7 * @version $Revision: $ $Name: $
8 * @date $Date: 2005/04/01 20:47:40 $
9 *
10 * Copyright 2004 Maui High Performance Computing Center, University of Hawaii
11 *
12 */
13
14#include<stdio.h>
15#include<math.h>
16#include "pslib.h"
17#include "psConstants.h"
18#include "pmObjects.h"
19
20/******************************************************************************
21pmPeakAlloc(): Allocate the psPeak data structure and set appropriate members.
22*****************************************************************************/
23psPeak *pmPeakAlloc(psS32 x,
24 psS32 y,
25 psF32 counts,
26 psPeakType class)
27{
28 psPeak *tmp = (psPeak *) psAlloc(sizeof(psPeak));
29 tmp->x = x;
30 tmp->y = y;
31 tmp->counts = counts;
32 tmp->class = class;
33
34 return(tmp);
35}
36
37/******************************************************************************
38pmMomentsAlloc(): Allocate the psMoments structure and initialize the members
39to zero.
40*****************************************************************************/
41psMoments *pmMomentsAlloc()
42{
43 psMoments *tmp = (psMoments *) psAlloc(sizeof(psMoments));
44 tmp->x = 0.0;
45 tmp->y = 0.0;
46 tmp->Sx = 0.0;
47 tmp->Sx = 0.0;
48 tmp->Sxy = 0.0;
49 tmp->Sum = 0.0;
50 tmp->Peak = 0.0;
51 tmp->Sky = 0.0;
52 tmp->nPixels = 0;
53
54 return(tmp);
55}
56
57static void p_psModelFree(psModel *tmp)
58{
59 psFree(tmp->params);
60 psFree(tmp->dparams);
61}
62
63/******************************************************************************
64pmModelAlloc(): Allocate the psModel structure, along with its parameters,
65and initialize the type member. Initialize the params to 0.0.
66XXX EAM: changing params and dparams to psVector
67*****************************************************************************/
68psModel *pmModelAlloc(psModelType type)
69{
70 psModel *tmp = (psModel *) psAlloc(sizeof(psModel));
71
72 tmp->type = type;
73 tmp->chisq = 0.0;
74 switch (type) {
75 case PS_MODEL_GAUSS:
76 tmp->params = psVectorAlloc(7, PS_TYPE_F32);
77 tmp->dparams = psVectorAlloc(7, PS_TYPE_F32);
78 break;
79 case PS_MODEL_PGAUSS:
80 tmp->params = psVectorAlloc(7, PS_TYPE_F32);
81 tmp->dparams = psVectorAlloc(7, PS_TYPE_F32);
82 break;
83 case PS_MODEL_TWIST_GAUSS:
84 tmp->params = psVectorAlloc(11, PS_TYPE_F32);
85 tmp->dparams = psVectorAlloc(11, PS_TYPE_F32);
86 break;
87 case PS_MODEL_WAUSS:
88 tmp->params = psVectorAlloc(9, PS_TYPE_F32);
89 tmp->dparams = psVectorAlloc(9, PS_TYPE_F32);
90 break;
91 case PS_MODEL_SERSIC:
92 tmp->params = psVectorAlloc(8, PS_TYPE_F32);
93 tmp->dparams = psVectorAlloc(8, PS_TYPE_F32);
94 break;
95 case PS_MODEL_SERSIC_CORE:
96 tmp->params = psVectorAlloc(12, PS_TYPE_F32);
97 tmp->dparams = psVectorAlloc(12, PS_TYPE_F32);
98 break;
99 default:
100 psError(PS_ERR_UNKNOWN, true, "Undefined psModelType");
101 return(NULL);
102 }
103
104 for (psS32 i = 0; i < tmp->params->n; i++) {
105 tmp->params->data.F32[i] = 0.0;
106 tmp->dparams->data.F32[i] = 0.0;
107 }
108
109 p_psMemSetDeallocator(tmp, (psFreeFcn) p_psModelFree);
110 return(tmp);
111}
112
113/******************************************************************************
114XXX: We don't free pixels and mask since that caused a memory error.
115We might need to increase the reference counter and decrease it here.
116*****************************************************************************/
117static void p_psSourceFree(psSource *tmp)
118{
119 psFree(tmp->peak);
120 // psFree(tmp->pixels);
121 // psFree(tmp->mask);
122 psFree(tmp->moments);
123 psFree(tmp->models);
124}
125
126/******************************************************************************
127pmSourceAlloc(): Allocate the psSource structure and initialize its members
128to NULL.
129*****************************************************************************/
130psSource *pmSourceAlloc()
131{
132 psSource *tmp = (psSource *) psAlloc(sizeof(psSource));
133 tmp->peak = NULL;
134 tmp->pixels = NULL;
135 tmp->mask = NULL;
136 tmp->moments = NULL;
137 tmp->models = NULL;
138 tmp->type = 0;
139 p_psMemSetDeallocator(tmp, (psFreeFcn) p_psSourceFree);
140
141 return(tmp);
142}
143
144/******************************************************************************
145pmFindVectorPeaks(vector, threshold): Find all local peaks in the given vector
146above the given threshold. Returns a vector of type PS_TYPE_U32 containing
147the location (x value) of all peaks.
148
149XXX: What types should be supported? Only F32 is implemented.
150
151XXX: We currently step through the input vector twice; once to determine the
152size of the output vector, then to set the values of the output vector.
153Depending upon actual use, this may need to be optimized.
154*****************************************************************************/
155psVector *pmFindVectorPeaks(const psVector *vector,
156 psF32 threshold)
157{
158 PS_VECTOR_CHECK_NULL(vector, NULL);
159 PS_VECTOR_CHECK_EMPTY(vector, NULL);
160 PS_VECTOR_CHECK_TYPE(vector, PS_TYPE_F32, NULL);
161 int count = 0;
162 int n = vector->n;
163
164 //
165 // Special case: the input vector has a single element.
166 //
167 if (n == 1) {
168 psVector *tmpVector = NULL;
169 ;
170 if (vector->data.F32[0] > threshold) {
171 tmpVector = psVectorAlloc(1, PS_TYPE_U32);
172 tmpVector->data.U32[0] = 0;
173 } else {
174 tmpVector = psVectorAlloc(0, PS_TYPE_U32);
175 }
176 return(tmpVector);
177 }
178
179 //
180 // Determine if first pixel is a peak
181 //
182 if ((vector->data.F32[0] > vector->data.F32[1]) &&
183 (vector->data.F32[0] > threshold)) {
184 count++;
185 }
186
187 //
188 // Determine if interior pixels are peaks
189 //
190 for (psU32 i = 1; i < n-1 ; i++) {
191 if ((vector->data.F32[i] > vector->data.F32[i-1]) &&
192 (vector->data.F32[i] > vector->data.F32[i+1]) &&
193 (vector->data.F32[i] > threshold)) {
194 count++;
195 }
196 }
197
198 //
199 // Determine if last pixel is a peak
200 //
201 if ((vector->data.F32[n-1] > vector->data.F32[n-2]) &&
202 (vector->data.F32[n-1] > threshold)) {
203 count++;
204 }
205
206 //
207 // We know how many peaks exist, so we now allocate a psVector to store
208 // those peaks.
209 //
210 psVector *tmpVector = psVectorAlloc(count, PS_TYPE_U32);
211 count = 0;
212
213 //
214 // Determine if first pixel is a peak
215 //
216 if ((vector->data.F32[0] > vector->data.F32[1]) &&
217 (vector->data.F32[0] > threshold)) {
218 tmpVector->data.U32[count++] = 0;
219 }
220
221 //
222 // Determine if interior pixels are peaks
223 //
224 for (psU32 i = 1; i < (n-1) ; i++) {
225 if ((vector->data.F32[i] > vector->data.F32[i-1]) &&
226 (vector->data.F32[i] > vector->data.F32[i+1]) &&
227 (vector->data.F32[i] > threshold)) {
228 tmpVector->data.U32[count++] = i;
229 }
230 }
231
232 //
233 // Determine if last pixel is a peak
234 //
235 if ((vector->data.F32[n-1] > vector->data.F32[n-2]) &&
236 (vector->data.F32[n-1] > threshold)) {
237 tmpVector->data.U32[count++] = n-1;
238 }
239
240 return(tmpVector);
241}
242
243/******************************************************************************
244p_psGetRowVectorFromImage(): a private function which simply returns a
245psVector containing the specified row of data from the psImage.
246
247XXX: Is there a better way to do this?
248*****************************************************************************/
249psVector *p_psGetRowVectorFromImage(psImage *image,
250 psU32 row)
251{
252 PS_IMAGE_CHECK_NULL(image, NULL);
253 PS_IMAGE_CHECK_TYPE(image, PS_TYPE_F32, NULL);
254
255 psVector *tmpVector = psVectorAlloc(image->numCols, PS_TYPE_F32);
256 for (psU32 col = 0; col < image->numCols ; col++) {
257 tmpVector->data.F32[col] = image->data.F32[row][col];
258 }
259 return(tmpVector);
260}
261
262/******************************************************************************
263MyListAddPeak(): A private function which allocates a psArray, if the list
264argument is NULL, otherwise it adds the peak to that list.
265XXX EAM : changed the output to psArray
266XXX EAM : Switched row, col args
267XXX EAM : NOTE: this was changed in the call, so the new code is consistent
268*****************************************************************************/
269psArray *MyListAddPeak(psArray *list,
270 psS32 row,
271 psS32 col,
272 psF32 counts,
273 psPeakType type)
274{
275 psPeak *tmpPeak = pmPeakAlloc(col, row, counts, type);
276
277 if (list == NULL) {
278 list = psArrayAlloc(100);
279 list->n = 0;
280 }
281 psArrayAdd(list, 100, tmpPeak);
282
283 return(list);
284}
285
286/******************************************************************************
287pmFindImagePeaks(image, threshold): Find all local peaks in the given psImage
288above the given threshold. Returns a psArray containing location (x/y value)
289of all peaks.
290
291XXX: I'm not convinced the peak type definition in the SDRS is mutually
292exclusive. Some peaks can have multiple types. Edges for sure. Also, a
293digonal line with the same value at each point will have a peak for every
294point on that line.
295
296XXX: This does not work if image has either a single row, or a single column.
297
298XXX: In the output psArray elements, should we use the image row/column offsets?
299 Currently, we do not.
300*****************************************************************************/
301psArray *pmFindImagePeaks(const psImage *image,
302 psF32 threshold)
303{
304 PS_IMAGE_CHECK_NULL(image, NULL);
305 PS_IMAGE_CHECK_TYPE(image, PS_TYPE_F32, NULL);
306 if ((image->numRows == 1) || (image->numCols == 1)) {
307 psError(PS_ERR_UNKNOWN, true, "Currently, input image must have at least 2 rows and 2 columns.");
308 }
309 psVector *tmpRow = NULL;
310 psU32 col = 0;
311 psU32 row = 0;
312 psArray *list = NULL;
313
314 //
315 // Find peaks in row 0 only.
316 //
317 row = 0;
318 tmpRow = p_psGetRowVectorFromImage((psImage *) image, row);
319 psVector *row1 = pmFindVectorPeaks(tmpRow, threshold);
320
321 for (psU32 i = 0 ; i < row1->n ; i++ ) {
322 col = row1->data.U32[i];
323
324 //
325 // Determine if pixel (0,0) is a peak.
326 //
327 if (col == 0) {
328 if ( (image->data.F32[row][col] > image->data.F32[row][col+1]) &&
329 (image->data.F32[row][col] > image->data.F32[row+1][col]) &&
330 (image->data.F32[row][col] >= image->data.F32[row+1][col+1])) {
331 if (image->data.F32[row][col] > threshold) {
332 list = MyListAddPeak(list, row, col, image->data.F32[row][col], PM_PEAK_EDGE);
333 }
334 }
335 } else if (col < (image->numCols - 1)) {
336 if ( (image->data.F32[row][col] >= image->data.F32[row][col-1]) &&
337 (image->data.F32[row][col] > image->data.F32[row][col+1]) &&
338 (image->data.F32[row][col] >= image->data.F32[row+1][col-1]) &&
339 (image->data.F32[row][col] > image->data.F32[row+1][col]) &&
340 (image->data.F32[row][col] >= image->data.F32[row+1][col+1])) {
341 if (image->data.F32[row][col] > threshold) {
342 list = MyListAddPeak(list, row, col, image->data.F32[row][col], PM_PEAK_EDGE);
343 }
344 }
345
346 } else if (col == (image->numCols - 1)) {
347 if ( (image->data.F32[row][col] >= image->data.F32[row][col-1]) &&
348 (image->data.F32[row][col] > image->data.F32[row+1][col]) &&
349 (image->data.F32[row][col] >= image->data.F32[row+1][col-1])) {
350 if (image->data.F32[row][col] > threshold) {
351 list = MyListAddPeak(list, row, col, image->data.F32[row][col], PM_PEAK_EDGE);
352 }
353 }
354
355 } else {
356 psError(PS_ERR_UNKNOWN, true, "peak specified valid column range.");
357 }
358 }
359
360 //
361 // Exit if this image has a single row.
362 //
363 if (image->numRows == 1) {
364 return(list);
365 }
366
367 //
368 // Find peaks in interior rows only.
369 //
370 for (row = 1 ; row < (image->numRows - 1) ; row++) {
371 tmpRow = p_psGetRowVectorFromImage((psImage *) image, row);
372 row1 = pmFindVectorPeaks(tmpRow, threshold);
373
374 // Step through all local peaks in this row.
375 for (psU32 i = 0 ; i < row1->n ; i++ ) {
376 psPeakType myType = PM_PEAK_UNDEF;
377 col = row1->data.U32[i];
378
379 if (col == 0) {
380 // If col==0, then we can not read col-1 pixels
381 if ((image->data.F32[row][col] > image->data.F32[row-1][col]) &&
382 (image->data.F32[row][col] >= image->data.F32[row-1][col+1]) &&
383 (image->data.F32[row][col] >= image->data.F32[row][col+1]) &&
384 (image->data.F32[row][col] >= image->data.F32[row+1][col]) &&
385 (image->data.F32[row][col] >= image->data.F32[row+1][col+1])) {
386 myType = PM_PEAK_EDGE;
387 list = MyListAddPeak(list, row, col, image->data.F32[row][col], myType);
388 }
389 } else if (col < (image->numCols - 1)) {
390 // This is an interior pixel
391 if ((image->data.F32[row][col] >= image->data.F32[row-1][col-1]) &&
392 (image->data.F32[row][col] > image->data.F32[row-1][col]) &&
393 (image->data.F32[row][col] >= image->data.F32[row-1][col+1]) &&
394 (image->data.F32[row][col] > image->data.F32[row][col-1]) &&
395 (image->data.F32[row][col] >= image->data.F32[row][col+1]) &&
396 (image->data.F32[row][col] >= image->data.F32[row+1][col-1]) &&
397 (image->data.F32[row][col] >= image->data.F32[row+1][col]) &&
398 (image->data.F32[row][col] >= image->data.F32[row+1][col+1])) {
399 if (image->data.F32[row][col] > threshold) {
400 if ((image->data.F32[row][col] > image->data.F32[row-1][col-1]) &&
401 (image->data.F32[row][col] > image->data.F32[row-1][col]) &&
402 (image->data.F32[row][col] > image->data.F32[row-1][col+1]) &&
403 (image->data.F32[row][col] > image->data.F32[row][col-1]) &&
404 (image->data.F32[row][col] > image->data.F32[row][col+1]) &&
405 (image->data.F32[row][col] > image->data.F32[row+1][col-1]) &&
406 (image->data.F32[row][col] > image->data.F32[row+1][col]) &&
407 (image->data.F32[row][col] > image->data.F32[row+1][col+1])) {
408 myType = PM_PEAK_LONE;
409 }
410
411 if ((image->data.F32[row][col] == image->data.F32[row-1][col-1]) ||
412 (image->data.F32[row][col] == image->data.F32[row-1][col]) ||
413 (image->data.F32[row][col] == image->data.F32[row-1][col+1]) ||
414 (image->data.F32[row][col] == image->data.F32[row][col-1]) ||
415 (image->data.F32[row][col] == image->data.F32[row][col+1]) ||
416 (image->data.F32[row][col] == image->data.F32[row+1][col-1]) ||
417 (image->data.F32[row][col] == image->data.F32[row+1][col]) ||
418 (image->data.F32[row][col] == image->data.F32[row+1][col+1])) {
419 myType = PM_PEAK_FLAT;
420 }
421
422 list = MyListAddPeak(list, row, col, image->data.F32[row][col], myType);
423 }
424 }
425 } else if (col == (image->numCols - 1)) {
426 // If col==numCols - 1, then we can not read col+1 pixels
427 if ((image->data.F32[row][col] >= image->data.F32[row-1][col-1]) &&
428 (image->data.F32[row][col] > image->data.F32[row-1][col]) &&
429 (image->data.F32[row][col] > image->data.F32[row][col-1]) &&
430 (image->data.F32[row][col] >= image->data.F32[row][col+1]) &&
431 (image->data.F32[row][col] >= image->data.F32[row+1][col-1]) &&
432 (image->data.F32[row][col] >= image->data.F32[row+1][col])) {
433 myType = PM_PEAK_EDGE;
434 list = MyListAddPeak(list, row, col, image->data.F32[row][col], myType);
435 }
436 } else {
437 psError(PS_ERR_UNKNOWN, true, "peak specified valid column range.");
438 }
439
440 }
441 }
442
443 //
444 // Find peaks in the last row only.
445 //
446 row = image->numRows - 1;
447 tmpRow = p_psGetRowVectorFromImage((psImage *) image, row);
448 row1 = pmFindVectorPeaks(tmpRow, threshold);
449 for (psU32 i = 0 ; i < row1->n ; i++ ) {
450 col = row1->data.U32[i];
451 if (col == 0) {
452 if ( (image->data.F32[row][col] > image->data.F32[row-1][col]) &&
453 (image->data.F32[row][col] >= image->data.F32[row-1][col+1]) &&
454 (image->data.F32[row][col] > image->data.F32[row][col+1])) {
455 if (image->data.F32[row][col] > threshold) {
456 list = MyListAddPeak(list, row, col, image->data.F32[row][col], PM_PEAK_EDGE);
457 }
458 }
459 } else if (col < (image->numCols - 1)) {
460 if ( (image->data.F32[row][col] >= image->data.F32[row-1][col-1]) &&
461 (image->data.F32[row][col] > image->data.F32[row-1][col]) &&
462 (image->data.F32[row][col] >= image->data.F32[row-1][col+1]) &&
463 (image->data.F32[row][col] > image->data.F32[row][col-1]) &&
464 (image->data.F32[row][col] >= image->data.F32[row][col+1])) {
465 if (image->data.F32[row][col] > threshold) {
466 list = MyListAddPeak(list, row, col, image->data.F32[row][col], PM_PEAK_EDGE);
467 }
468 }
469
470 } else if (col == (image->numCols - 1)) {
471 if ( (image->data.F32[row][col] >= image->data.F32[row-1][col-1]) &&
472 (image->data.F32[row][col] > image->data.F32[row-1][col]) &&
473 (image->data.F32[row][col] > image->data.F32[row][col-1])) {
474 if (image->data.F32[row][col] > threshold) {
475 list = MyListAddPeak(list, row, col, image->data.F32[row][col], PM_PEAK_EDGE);
476 }
477 }
478 } else {
479 psError(PS_ERR_UNKNOWN, true, "peak specified valid colum range.");
480 }
481 }
482 return(list);
483}
484
485// XXX: Macro this.
486bool IsItInThisRegion(const psRegion *valid,
487 psS32 x,
488 psS32 y)
489{
490
491 if ((x >= valid->x0) &&
492 (x <= valid->x1) &&
493 (y >= valid->y0) &&
494 (y <= valid->y1)) {
495 return(true);
496 }
497
498 return(false);
499}
500
501
502/******************************************************************************
503psCullPeaks(peaks, maxValue, valid): eliminate peaks from the psArray that have
504a peak value above the given maximum, or fall outside the valid region.
505
506XXX: Should the sky value be used when comparing the maximum?
507
508XXX: warning message if valid is NULL?
509
510XXX: changed API to create a NEW output psArray (should change name as well)
511*****************************************************************************/
512psList *pmCullPeaks(psList *peaks,
513 psF32 maxValue,
514 const psRegion *valid)
515{
516 PS_PTR_CHECK_NULL(peaks, NULL);
517 // PS_PTR_CHECK_NULL(valid, NULL);
518
519 psListElem *tmpListElem = (psListElem *) peaks->head;
520 psS32 indexNum = 0;
521
522 // printf("pmCullPeaks(): list size is %d\n", peaks->size);
523 while (tmpListElem != NULL) {
524 psPeak *tmpPeak = (psPeak *) tmpListElem->data;
525 if ((tmpPeak->counts > maxValue) ||
526 ((valid != NULL) &&
527 (true == IsItInThisRegion(valid, tmpPeak->x, tmpPeak->y)))) {
528 psListRemoveData(peaks, (psPtr) tmpPeak);
529 }
530
531 indexNum++;
532 tmpListElem = tmpListElem->next;
533 }
534
535 return(peaks);
536}
537
538// XXX EAM: I changed this to return a new, subset array
539// rather than alter the existing one
540psArray *pmPeaksSubset(psArray *peaks, psF32 maxValue, const psRegion *valid)
541{
542 PS_PTR_CHECK_NULL(peaks, NULL);
543
544 psArray *output = psArrayAlloc (200);
545 output->n = 0;
546
547 psTrace (".pmObjects.pmCullPeaks", 3, "list size is %d\n", peaks->n);
548
549 for (int i = 0; i < peaks->n; i++) {
550 psPeak *tmpPeak = (psPeak *) peaks->data[i];
551 if (tmpPeak->counts > maxValue) continue;
552 if (valid != NULL) {
553 if (IsItInThisRegion(valid, tmpPeak->x, tmpPeak->y)) continue;
554 }
555 psArrayAdd (output, 200, tmpPeak);
556 }
557 return(output);
558}
559
560/******************************************************************************
561psSource *pmSourceLocalSky(image, peak, innerRadius, outerRadius): this
562routine creates a new psSource data structure and sets the following members:
563 ->psPeak
564 ->psMoments->sky
565
566The sky value is set from the pixels in the square annulus surrounding the
567peak pixel.
568
569We simply create a subSet image and mask the inner pixels, then call
570psImageStats on that subImage+mask.
571
572XXX: The subImage has width of 1+2*outerRadius. Verify with IfA.
573
574XXX: Use static data structures for:
575 subImage
576 subImageMask
577 myStats
578
579XXX: ensure that the inner and out radius fit in the actual image. Should
580 we generate an error, or warning? Currently an error.
581
582XXX: Sync with IfA on whether the peak x/y coords are data structure coords,
583 or they use the image row/column offsets.
584
585XXX: Should we simply set psSource->peak = peak? If so, should we increase
586the reference counter? Or, should we copy the data structure?
587
588XXX: Currently the subimage always has an even number of rows/columns. Is
589 this correct? Since there is a center pixel, maybe it should have an
590 odd number of rows/columns.
591
592XXX: Use psTrace() for the print statements.
593
594XXX: Don't use separate structs for the subimage and mask. Use the source->
595 members.
596*****************************************************************************/
597psSource *pmSourceLocalSky(const psImage *image,
598 const psPeak *peak,
599 psStatsOptions statsOptions,
600 psF32 innerRadius,
601 psF32 outerRadius)
602{
603 PS_IMAGE_CHECK_NULL(image, NULL);
604 PS_IMAGE_CHECK_TYPE(image, PS_TYPE_F32, NULL);
605 PS_PTR_CHECK_NULL(peak, NULL);
606 PS_FLOAT_COMPARE(0.0, innerRadius, NULL);
607 PS_FLOAT_COMPARE(innerRadius, outerRadius, NULL);
608 psS32 innerRadiusS32 = (psS32) innerRadius;
609 psS32 outerRadiusS32 = (psS32) outerRadius;
610
611 //
612 // We define variables for code readability.
613 //
614 // XXX: Since the peak->xy coords are in image, not subImage coords,
615 // these variables should be renamed for clarity (imageCenterRow, etc).
616 //
617 // peak->x,y is guaranteed to be on image
618 psS32 SubImageCenterRow = peak->y;
619 psS32 SubImageCenterCol = peak->x;
620
621 // XXX EAM : I added this code to stay on the image. So did George
622 psS32 SubImageStartRow = PS_MAX (0, SubImageCenterRow - outerRadiusS32);
623 psS32 SubImageEndRow = PS_MIN (image->numRows - 1, SubImageCenterRow + outerRadiusS32);
624 psS32 SubImageStartCol = PS_MAX (0, SubImageCenterCol - outerRadiusS32);
625 psS32 SubImageEndCol = PS_MIN (image->numCols - 1, SubImageCenterCol + outerRadiusS32);
626 // AnulusWidth == number of pixels width in the annulus. We add one since
627 // the pixels at the inner AND outher radius are included.
628 psS32 AnulusWidth = 1 + (outerRadiusS32 - innerRadiusS32);
629 // Example: assume an outer/inner radius of 20/10. Then the subimage
630 // should have width/length of 40. An 18-by-18 interior region will
631 // be masked.
632 // printf("pmSourceLocalSky(): innerRadiusS32 is %d\n", innerRadiusS32);
633 // printf("pmSourceLocalSky(): outerRadiusS32 is %d\n", outerRadiusS32);
634 // printf("pmSourceLocalSky(): AnulusWidth is %d\n", AnulusWidth);
635
636 // XXX EAM : these tests should not be needed: we can never hit this error because of above
637# if (1)
638 if (SubImageStartRow < 0) {
639 psError(PS_ERR_UNKNOWN, true, "Sub image startRow is outside image boundaries (%d).\n",
640 SubImageStartRow);
641 return(NULL);
642 }
643 if (SubImageEndRow >= image->numRows) {
644 psError(PS_ERR_UNKNOWN, true, "Sub image endRow is outside image boundaries (%d).\n",
645 SubImageEndRow);
646 return(NULL);
647 }
648 if (SubImageStartCol < 0) {
649 psError(PS_ERR_UNKNOWN, true, "Sub image startCol is outside image boundaries (%d).\n",
650 SubImageStartCol);
651 return(NULL);
652 }
653 if (SubImageEndCol >= image->numCols) {
654 psError(PS_ERR_UNKNOWN, true, "Sub image endCol is outside image boundaries (%d).\n",
655 SubImageEndCol);
656 return(NULL);
657 }
658# endif
659
660 //
661 // Grab a subimage of the original image of size (2 * outerRadius).
662 //
663 psImage *subImage = psImageSubset((psImage *) image,
664 SubImageStartCol,
665 SubImageStartRow,
666 SubImageEndCol,
667 SubImageEndRow);
668 // printf("pmSourceLocalSky: subimage width/length is (%d, %d)\n", subImage->numCols, subImage->numRows);
669 psImage *subImageMask = psImageAlloc(subImage->numCols,
670 subImage->numRows,
671 PS_TYPE_U8);
672
673 //
674 // Loop through the subimage mask, initialize mask to 0.
675 //
676 for (psS32 row = 0 ; row < subImageMask->numRows; row++) {
677 for (psS32 col = 0 ; col < subImageMask->numCols; col++) {
678 subImageMask->data.U8[row][col] = 0;
679 }
680 }
681
682 //
683 // Loop through the subimage, mask off pixels in the inner square.
684 // XXX this uses a static mask value of 1
685 //
686 for (psS32 row = AnulusWidth; row <= (subImageMask->numRows - AnulusWidth) - 1; row++) {
687 for (psS32 col = AnulusWidth; col <= (subImageMask->numCols - AnulusWidth) - 1; col++) {
688 subImageMask->data.U8[row][col] = 1;
689 }
690 }
691
692
693 // for (psS32 row = 0 ; row < subImage->numRows; row++) {
694 // for (psS32 col = 0 ; col < subImage->numCols; col++) {
695 // printf("(%d) ", subImageMask->data.U8[row][col]);
696 // }
697 // printf("\n");
698 // }
699
700 //
701 // Allocate the myStats structure, then call psImageStats(), which will
702 // calculate the specified statistic.
703 //
704 psStats *myStats = psStatsAlloc(statsOptions);
705 myStats = psImageStats(myStats, subImage, subImageMask, 1);
706
707 //
708 // Create the output mySource, and set appropriate members.
709 //
710 psSource *mySource = pmSourceAlloc();
711 mySource->peak = (psPeak *) peak;
712 mySource->moments = pmMomentsAlloc();
713 psF64 tmpF64;
714 p_psGetStatValue(myStats, &tmpF64);
715 mySource->moments->Sky = (psF32) tmpF64;
716 mySource->pixels = subImage;
717 mySource->mask = subImageMask;
718
719 //
720 // Free things. XXX: This should be static memory.
721 //
722 psFree(myStats);
723
724 return(mySource);
725}
726
727/******************************************************************************
728bool CheckRadius(*peak, radius, x, y): private function which simply
729determines if the (x, y) point is within the radius of the specified peak.
730
731XXX: macro this for performance.
732*****************************************************************************/
733bool CheckRadius(psPeak *peak,
734 psF32 radius,
735 psS32 x,
736 psS32 y)
737{
738 if (PS_SQR(radius) >= (psF32) (PS_SQR(x - peak->x) + PS_SQR(y - peak->y))) {
739 return(true);
740 }
741
742 return(false);
743}
744
745/******************************************************************************
746bool CheckRadius2(): private function which simply determines if the (x, y)
747point is within the radius of the specified peak.
748
749XXX: macro this for performance.
750XXX: this is rather inefficient - at least compute and compare against radius^2
751*****************************************************************************/
752bool CheckRadius2(psF32 xCenter,
753 psF32 yCenter,
754 psF32 radius,
755 psF32 x,
756 psF32 y)
757{
758 /// XXX EAM should compare with hypot (x,y) for speed
759 if ((PS_SQR(x - xCenter) + PS_SQR(y - yCenter)) < PS_SQR(radius)) {
760 return(true);
761 }
762
763 return(false);
764}
765
766/******************************************************************************
767pmSourceMoments(source, radius): this function takes a subImage defined in the
768psSource data structure, along with the peak location, and determines the
769various moments associated with that peak.
770
771Requires the following to have been created:
772 psSource
773 psSource->peak
774 psSource->pixels
775
776XXX: The peak calculations are done in image coords, not subImage coords.
777
778XXX: mask values?
779*****************************************************************************/
780psSource *pmSourceMoments(psSource *source,
781 psF32 radius)
782{
783 PS_PTR_CHECK_NULL(source, NULL);
784 PS_PTR_CHECK_NULL(source->peak, NULL);
785 PS_PTR_CHECK_NULL(source->pixels, NULL);
786 PS_FLOAT_COMPARE(0.0, radius, NULL);
787
788 //
789 // XXX: Verify the setting for sky if source->moments == NULL.
790 //
791 psF32 sky = 0.0;
792 if (source->moments == NULL) {
793 source->moments = pmMomentsAlloc();
794 } else {
795 sky = source->moments->Sky;
796 }
797
798 //
799 // Sum = SUM (z - sky)
800 // X1 = SUM (x - xc)*(z - sky)
801 // X2 = SUM (x - xc)^2 * (z - sky)
802 // XY = SUM (x - xc)*(y - yc)*(z - sky)
803 //
804 psF32 Sum = 0.0;
805 psF32 peakPixel = -PS_MAX_F32;
806 psS32 numPixels = 0;
807 psF32 X1 = 0.0;
808 psF32 Y1 = 0.0;
809 psF32 X2 = 0.0;
810 psF32 Y2 = 0.0;
811 psF32 XY = 0.0;
812 psF32 x = 0;
813 psF32 y = 0;
814 //
815 // XXX why do I get different results for these two methods of finding Sx?
816 // XXX Sx, Sy would be better measured if we clip pixels close to sky
817 // XXX Sx, Sy can still be imaginary, so we probably need to keep Sx^2?
818 // We loop through all pixels in this subimage (source->pixels), and for each
819 // pixel that is not masked, AND within the radius of the peak pixel, we
820 // proceed with the moments calculation. need to do two loops for a
821 // numerically stable result. first loop: get the sums.
822 //
823 for (psS32 row = 0; row < source->pixels->numRows ; row++) {
824 for (psS32 col = 0; col < source->pixels->numCols ; col++) {
825 if ((source->mask != NULL) && (source->mask->data.U8[row][col] != 0)) {
826 psS32 imgColCoord = col + source->pixels->col0;
827 psS32 imgRowCoord = row + source->pixels->row0;
828 if (CheckRadius(source->peak,
829 radius,
830 imgColCoord,
831 imgRowCoord)) {
832 psF32 xDiff = (psF32) (imgColCoord - source->peak->x);
833 psF32 yDiff = (psF32) (imgRowCoord - source->peak->y);
834 psF32 pDiff = source->pixels->data.F32[row][col] - sky;
835
836 Sum+= pDiff;
837 X1+= xDiff * pDiff;
838 Y1+= yDiff * pDiff;
839 XY+= xDiff * yDiff * pDiff;
840
841 X2+= PS_SQR(xDiff) * pDiff;
842 Y2+= PS_SQR(yDiff) * pDiff;
843
844 if (source->pixels->data.F32[row][col] > peakPixel) {
845 peakPixel = source->pixels->data.F32[row][col];
846 }
847 numPixels++;
848 }
849 }
850 }
851 }
852
853 //
854 // first moment X = X1/Sum + xc
855 // second moment X = sqrt (X2/Sum - (X1/Sum)^2)
856 // Sxy = XY / Sum
857 //
858 x = X1/Sum;
859 y = Y1/Sum;
860 source->moments->x = x + ((psF32) source->peak->x);
861 source->moments->y = y + ((psF32) source->peak->y);
862
863 source->moments->Sxy = XY/Sum;
864 source->moments->Sum = Sum;
865 source->moments->Peak = peakPixel;
866 source->moments->nPixels = numPixels;
867
868 // XXX EAM : these values can be negative, so we need to limit the range
869 source->moments->Sx = sqrt(PS_MAX(X2/Sum - PS_SQR(x), 0));
870 source->moments->Sy = sqrt(PS_MAX(Y2/Sum - PS_SQR(y), 0));
871 return(source);
872
873// XXX EAM : the following code should be the same as above, but it is not very stable: ignore it
874# if (0)
875 //
876 // second loop: get the difference sums
877 //
878 X2 = Y2 = 0;
879 for (psS32 row = 0; row < source->pixels->numRows ; row++) {
880 for (psS32 col = 0; col < source->pixels->numCols ; col++) {
881 if ((source->mask != NULL) && (source->mask->data.U8[row][col] != 0)) {
882 psS32 imgColCoord = col + source->pixels->col0;
883 psS32 imgRowCoord = row + source->pixels->row0;
884 if (CheckRadius(source->peak,
885 radius,
886 imgColCoord,
887 imgRowCoord)) {
888 psF32 xDiff = (psF32) (imgColCoord - source->peak->x);
889 psF32 yDiff = (psF32) (imgRowCoord - source->peak->y);
890 psF32 pDiff = source->pixels->data.F32[row][col] - sky;
891
892 Sum+= pDiff;
893 X2+= PS_SQR(xDiff - x) * pDiff;
894 Y2+= PS_SQR(yDiff - y) * pDiff;
895 }
896 }
897 }
898 }
899
900 //
901 // second moment X = sqrt (X2/Sum)
902 //
903 source->moments->Sx = (X2/Sum);
904 source->moments->Sy = (Y2/Sum);
905 return(source);
906# endif
907}
908
909// XXX EAM : I used
910int pmComparePeakAscend (const void **a, const void **b)
911{
912 psPeak *A = *(psPeak **)a;
913 psPeak *B = *(psPeak **)b;
914
915 psF32 diff;
916
917 diff = A->counts - B->counts;
918 if (diff < FLT_EPSILON) return (-1);
919 if (diff > FLT_EPSILON) return (+1);
920 return (0);
921}
922
923int pmComparePeakDescend (const void **a, const void **b)
924{
925 psPeak *A = *(psPeak **)a;
926 psPeak *B = *(psPeak **)b;
927
928 psF32 diff;
929
930 diff = A->counts - B->counts;
931 if (diff < FLT_EPSILON) return (+1);
932 if (diff > FLT_EPSILON) return (-1);
933 return (0);
934}
935
936/******************************************************************************
937pmSourceRoughClass(source, metadata): make a guess at the source
938classification.
939
940XXX: This is not useable code, as of the release date. There remains a fair
941bit of coding to be completed.
942
943XXX: The sigX and sigY stuff in the SDRS is unclear.
944
945XXX: How can this function ever return FALSE?
946*****************************************************************************/
947
948# define NPIX 10
949# define SCALE 0.1
950
951// XXX I am ignore memory freeing issues (EAM)
952bool pmSourceRoughClass(psArray *sources, psMetadata *metadata)
953{
954 PS_PTR_CHECK_NULL(sources, false);
955 PS_PTR_CHECK_NULL(metadata, false);
956 psBool rc = true;
957 psArray *peaks = NULL;
958 psF32 clumpX = 0.0;
959 psF32 clumpDX = 0.0;
960 psF32 clumpY = 0.0;
961 psF32 clumpDY = 0.0;
962
963 // find the sigmaX, sigmaY clump
964 {
965 psStats *stats = NULL;
966 psImage *splane = NULL;
967 int binX, binY;
968
969 // construct a sigma-plane image
970 splane = psImageAlloc (NPIX/SCALE, NPIX/SCALE, PS_TYPE_F32);
971
972 // place the sources in the sigma-plane image (ignore 0,0 values?)
973 for (psS32 i = 0 ; i < sources->n ; i++) {
974 psSource *tmpSrc = (psSource *) sources->data[i];
975 PS_PTR_CHECK_NULL(tmpSrc, false); // just skip this one?
976 PS_PTR_CHECK_NULL(tmpSrc->moments, false); // just skip this one?
977
978 // Sx,Sy are limited at 0. a peak at 0,0 is artificial
979 if ((fabs(tmpSrc->moments->Sx) < FLT_EPSILON) && (fabs(tmpSrc->moments->Sy) < FLT_EPSILON)) {
980 continue;
981 }
982
983 // for the moment, force splane dimensions to be 10x10 image pix
984 binX = tmpSrc->moments->Sx/SCALE;
985 if (binX < 0) continue;
986 if (binX >= splane->numCols) continue;
987
988 binY = tmpSrc->moments->Sy/SCALE;
989 if (binY < 0) continue;
990 if (binY >= splane->numRows) continue;
991
992 splane->data.F32[binY][binX] += 1.0;
993 }
994
995 // find the peak in this image
996 stats = psStatsAlloc (PS_STAT_MAX);
997 stats = psImageStats (stats, splane, NULL, 0);
998 peaks = pmFindImagePeaks (splane, stats[0].max / 2);
999 psTrace (".pmObjects.pmSourceRoughClass", 2, "clump threshold is %f\n", stats[0].max/2);
1000
1001 }
1002
1003 // measure statistics on Sx, Sy if Sx, Sy within range of clump
1004 {
1005 psPeak *clump;
1006 psF32 minSx, maxSx;
1007 psF32 minSy, maxSy;
1008 psVector *tmpSx = NULL;
1009 psVector *tmpSy = NULL;
1010 psStats *stats = NULL;
1011
1012 // XXX EAM : this lets us takes the single highest peak
1013 psArraySort (peaks, pmComparePeakDescend);
1014 clump = peaks->data[0];
1015 psTrace (".pmObjects.pmSourceRoughClass", 2, "clump is at %d, %d\n", clump->x, clump->y);
1016
1017 // define section window for clump
1018 minSx = clump->x * SCALE - 0.2;
1019 maxSx = clump->x * SCALE + 0.2;
1020 minSy = clump->y * SCALE - 0.2;
1021 maxSy = clump->y * SCALE + 0.2;
1022
1023 tmpSx = psVectorAlloc (sources->n, PS_TYPE_F32);
1024 tmpSy = psVectorAlloc (sources->n, PS_TYPE_F32);
1025 tmpSx->n = 0;
1026 tmpSy->n = 0;
1027
1028 // XXX clip sources based on flux?
1029 // create vectors with Sx, Sy values in window
1030 for (psS32 i = 0 ; i < sources->n ; i++) {
1031 psSource *tmpSrc = (psSource *) sources->data[i];
1032
1033 if (tmpSrc->moments->Sx < minSx) continue;
1034 if (tmpSrc->moments->Sx > maxSx) continue;
1035 if (tmpSrc->moments->Sy < minSy) continue;
1036 if (tmpSrc->moments->Sy > maxSy) continue;
1037 tmpSx->data.F32[tmpSx->n] = tmpSrc->moments->Sx;
1038 tmpSy->data.F32[tmpSy->n] = tmpSrc->moments->Sy;
1039 tmpSx->n++;
1040 tmpSy->n++;
1041 if (tmpSx->n == tmpSx->nalloc) {
1042 psVectorRealloc (tmpSx, tmpSx->nalloc + 100);
1043 psVectorRealloc (tmpSy, tmpSy->nalloc + 100);
1044 }
1045 }
1046
1047 // measures stats of Sx, Sy
1048 stats = psStatsAlloc (PS_STAT_CLIPPED_MEAN | PS_STAT_CLIPPED_STDEV);
1049
1050 stats = psVectorStats (stats, tmpSx, NULL, NULL, 0);
1051 clumpX = stats->clippedMean;
1052 clumpDX = stats->clippedStdev;
1053
1054 stats = psVectorStats (stats, tmpSy, NULL, NULL, 0);
1055 clumpY = stats->clippedMean;
1056 clumpDY = stats->clippedStdev;
1057
1058 psTrace (".pmObjects.pmSourceRoughClass", 2, "clump X, Y: %f, %f\n", clumpX, clumpY);
1059 psTrace (".pmObjects.pmSourceRoughClass", 2, "clump DX, DY: %f, %f\n", clumpDX, clumpDY);
1060 // these values should be pushed on the metadata somewhere
1061 }
1062
1063 int Nsat = 0;
1064 int Ngal = 0;
1065 int Nfaint = 0;
1066 int Nstar = 0;
1067 int Npsf = 0;
1068 int Ncr = 0;
1069 psVector *starsn = psVectorAlloc (sources->n, PS_TYPE_F32);
1070 starsn->n = 0;
1071
1072 // XXX allow clump size to be scaled relative to sigmas?
1073 // make rough IDs based on clumpX,Y,DX,DY
1074 for (psS32 i = 0 ; i < sources->n ; i++) {
1075
1076 psSource *tmpSrc = (psSource *) sources->data[i];
1077
1078 tmpSrc->peak->class = 0;
1079
1080 psF32 sigX = tmpSrc->moments->Sx;
1081 psF32 sigY = tmpSrc->moments->Sy;
1082
1083 // check return status value (do these exist?)
1084 bool status;
1085 psF32 RDNOISE = psMetadataLookupF32 (&status, metadata, "RDNOISE");
1086 psF32 GAIN = psMetadataLookupF32 (&status, metadata, "GAIN");
1087 psF32 SATURATE = psMetadataLookupF32 (&status, metadata, "SATURATE");
1088
1089 psF32 PSF_SN_LIM = psMetadataLookupF32 (&status, metadata, "PSF_SN_LIM");
1090 psF32 FAINT_SN_LIM = psMetadataLookupF32 (&status, metadata, "FAINT_SN_LIM");
1091
1092 // saturated object (star or single pixel not distinguished)
1093 if (tmpSrc->moments->Peak > SATURATE) {
1094 tmpSrc->type |= PS_SOURCE_SATURATED;
1095 Nsat ++;
1096 continue;
1097 }
1098
1099 // too small to be stellar
1100 if ((sigX < (clumpX - clumpDX)) || (sigY < (clumpY - clumpDY))) {
1101 tmpSrc->type |= PS_SOURCE_DEFECT;
1102 Ncr ++;
1103 continue;
1104 }
1105
1106 // possible galaxy
1107 if ((sigX > (clumpX + clumpDX)) || (sigY > (clumpY + clumpDY))) {
1108 tmpSrc->type |= PS_SOURCE_GALAXY;
1109 Ngal ++;
1110 continue;
1111 }
1112
1113 // the rest are probable stellar objects
1114 psF32 S = tmpSrc->moments->Sum;
1115 psF32 A = PS_PI * sigX * sigY;
1116 psF32 B = tmpSrc->moments->Sky;
1117 psF32 RT = PS_SQRT_F32(S + (A * B) + (A * PS_SQR(RDNOISE) / PS_SQRT_F32(GAIN)));
1118 psF32 SN = (S * PS_SQRT_F32(GAIN) / RT);
1119
1120 starsn->data.F32[starsn->n] = SN;
1121 starsn->n ++;
1122 Nstar ++;
1123
1124 // faint star
1125 if (SN < FAINT_SN_LIM) {
1126 tmpSrc->type |= PS_SOURCE_FAINTSTAR;
1127 Nfaint ++;
1128 continue;
1129 }
1130
1131 // PSF star
1132 if (SN > PSF_SN_LIM) {
1133 tmpSrc->type |= PS_SOURCE_PSFSTAR;
1134 Npsf ++;
1135 continue;
1136 }
1137
1138 // random type of star
1139 tmpSrc->type |= PS_SOURCE_OTHER;
1140 }
1141
1142 {
1143 psStats *stats = NULL;
1144 stats = psStatsAlloc (PS_STAT_MIN | PS_STAT_MAX);
1145 stats = psVectorStats (stats, starsn, NULL, NULL, 0);
1146 psLogMsg ("pmObjects", 3, "SN range: %f - %f\n", stats[0].min, stats[0].max);
1147 }
1148
1149 psTrace (".pmObjects.pmSourceRoughClass", 2, "Nstar: %3d\n", Nstar);
1150 psTrace (".pmObjects.pmSourceRoughClass", 2, "Npsf: %3d\n", Npsf);
1151 psTrace (".pmObjects.pmSourceRoughClass", 2, "Nfaint: %3d\n", Nfaint);
1152 psTrace (".pmObjects.pmSourceRoughClass", 2, "Ngal: %3d\n", Ngal);
1153 psTrace (".pmObjects.pmSourceRoughClass", 2, "Nsat: %3d\n", Nsat);
1154 psTrace (".pmObjects.pmSourceRoughClass", 2, "Ncr: %3d\n", Ncr);
1155
1156 return(rc);
1157}
1158
1159/******************************************************************************
1160pmSourceSetPixelCircle(source, image, radius)
1161
1162XXX: Why boolean output?
1163
1164XXX: Why are we checking source->moments for NULL? Should the circle be
1165 centered on the centroid or the peak?
1166
1167XXX: The circle will have a diameter of (1+radius). This is different from
1168 the pmSourceSetLocal() function.
1169*****************************************************************************/
1170bool pmSourceSetPixelCircle(psSource *source,
1171 const psImage *image,
1172 psF32 radius)
1173{
1174 PS_IMAGE_CHECK_NULL(image, false);
1175 PS_IMAGE_CHECK_TYPE(image, PS_TYPE_F32, false);
1176 PS_PTR_CHECK_NULL(source, false);
1177 PS_PTR_CHECK_NULL(source->moments, false);
1178 // PS_PTR_CHECK_NULL(source->peak, false);
1179 PS_FLOAT_COMPARE(0.0, radius, false);
1180
1181 //
1182 // We define variables for code readability.
1183 //
1184 // XXX: Since the peak->xy coords are in image, not subImage coords,
1185 // these variables should be renamed for clarity (imageCenterRow, etc).
1186 //
1187 psS32 radiusS32 = (psS32) radius;
1188 psS32 SubImageCenterRow = source->peak->y;
1189 psS32 SubImageCenterCol = source->peak->x;
1190 // XXX EAM : for the circle to stay on the image
1191 psS32 SubImageStartRow = PS_MAX (0, SubImageCenterRow - radiusS32);
1192 psS32 SubImageEndRow = PS_MIN (image->numRows - 1, SubImageCenterRow + radiusS32);
1193 psS32 SubImageStartCol = PS_MAX (0, SubImageCenterCol - radiusS32);
1194 psS32 SubImageEndCol = PS_MIN (image->numCols - 1, SubImageCenterCol + radiusS32);
1195
1196 // XXX EAM : this should not be needed: we can never hit this error
1197# if (1)
1198 if (SubImageStartRow < 0) {
1199 psError(PS_ERR_UNKNOWN, true, "Sub image startRow is outside image boundaries (%d).\n",
1200 SubImageStartRow);
1201 return(false);
1202 }
1203 if (SubImageEndRow >= image->numRows) {
1204 psError(PS_ERR_UNKNOWN, true, "Sub image endRow is outside image boundaries (%d).\n",
1205 SubImageEndRow);
1206 return(false);
1207 }
1208 if (SubImageStartCol < 0) {
1209 psError(PS_ERR_UNKNOWN, true, "Sub image startCol is outside image boundaries (%d).\n",
1210 SubImageStartCol);
1211 return(false);
1212 }
1213 if (SubImageEndCol >= image->numCols) {
1214 psError(PS_ERR_UNKNOWN, true, "Sub image endCol is outside image boundaries (%d).\n",
1215 SubImageEndCol);
1216 return(false);
1217 }
1218# endif
1219
1220 // XXX: Must recycle image.
1221 // XXX EAM: this message reflects a programming error we know about.
1222 // i am setting it to a trace message which we can take out
1223 if (source->pixels != NULL) {
1224 psTrace (".psModule.pmObjects.pmSourceSetPixelCircle", 4,
1225 "WARNING: pmSourceSetPixelCircle(): image->pixels not NULL. Freeing and reallocating.\n");
1226 psFree(source->pixels);
1227 }
1228 source->pixels = psImageSubset((psImage *) image,
1229 SubImageStartCol,
1230 SubImageStartRow,
1231 SubImageEndCol,
1232 SubImageEndRow);
1233
1234 // XXX: Must recycle image.
1235 if (source->mask != NULL) {
1236 psFree(source->mask);
1237 }
1238 source->mask = psImageAlloc(source->pixels->numCols,
1239 source->pixels->numRows,
1240 PS_TYPE_F32);
1241
1242 //
1243 // Loop through the subimage mask, initialize mask to 0 or 1.
1244 // XXX EAM: valid pixels should have 0, not 1
1245 for (psS32 row = 0 ; row < source->mask->numRows; row++) {
1246 for (psS32 col = 0 ; col < source->mask->numCols; col++) {
1247
1248 if (CheckRadius2((psF32) radiusS32,
1249 (psF32) radiusS32,
1250 radius,
1251 (psF32) col,
1252 (psF32) row)) {
1253 source->mask->data.U8[row][col] = 0;
1254 } else {
1255 source->mask->data.U8[row][col] = 1;
1256 }
1257 }
1258 }
1259 return(true);
1260}
1261
1262/******************************************************************************
1263pmSourceModelGuess(source, image, model): This function allocates a new
1264psModel structure and stores it in the psSource data structure specified in
1265the argument list. The model type is specified in the argument list. The
1266params array in that psModel structure are allocated, and then set to the
1267appropriate values. This function returns true if everything was successful.
1268
1269XXX: Many of the initial parameters are set to 0.0 since I don't know what
1270the appropiate initial guesses are.
1271
1272XXX: The image argument is redundant.
1273
1274XXX: Many parameters are based on the src->moments structure, which is in
1275image, not subImage coords. Therefore, the calls to the model evaluation
1276functions will be in image, not subImage coords. Remember this.
1277*****************************************************************************/
1278bool pmSourceModelGuess(psSource *source,
1279 const psImage *image,
1280 psModelType model)
1281{
1282 PS_PTR_CHECK_NULL(source, false);
1283 PS_PTR_CHECK_NULL(source->moments, false);
1284 PS_PTR_CHECK_NULL(source->peak, false);
1285 PS_IMAGE_CHECK_NULL(image, false);
1286 PS_IMAGE_CHECK_TYPE(image, PS_TYPE_F32, false);
1287 if (source->models != NULL) {
1288 psLogMsg(__func__, PS_LOG_WARN, "WARNING: source->models was non-NULL; calling psFree(source->models).\n");
1289 psFree(source->models);
1290 }
1291 source->models = pmModelAlloc(model);
1292
1293 psVector *params = source->models->params;
1294
1295 switch (model) {
1296 case PS_MODEL_GAUSS:
1297 params->data.F32[0] = source->moments->Sky;
1298 params->data.F32[1] = source->peak->counts - source->moments->Sky;
1299 params->data.F32[2] = source->moments->x;
1300 params->data.F32[3] = source->moments->y;
1301 params->data.F32[4] = sqrt(2.0) / source->moments->Sx;
1302 params->data.F32[5] = sqrt(2.0) / source->moments->Sy;
1303 params->data.F32[6] = source->moments->Sxy;
1304 return(true);
1305
1306 case PS_MODEL_PGAUSS:
1307 params->data.F32[0] = source->moments->Sky;
1308 params->data.F32[1] = source->peak->counts - source->moments->Sky;
1309 params->data.F32[2] = source->moments->x;
1310 params->data.F32[3] = source->moments->y;
1311 params->data.F32[4] = sqrt(2.0) / source->moments->Sx;
1312 params->data.F32[5] = sqrt(2.0) / source->moments->Sy;
1313 params->data.F32[6] = source->moments->Sxy;
1314 return(true);
1315
1316 case PS_MODEL_WAUSS:
1317 params->data.F32[0] = source->moments->Sky;
1318 params->data.F32[1] = source->peak->counts - source->moments->Sky;
1319 params->data.F32[2] = source->moments->x;
1320 params->data.F32[3] = source->moments->y;
1321 params->data.F32[4] = sqrt(2.0) / source->moments->Sx;
1322 params->data.F32[5] = sqrt(2.0) / source->moments->Sy;
1323 params->data.F32[6] = source->moments->Sxy;
1324 // XXX: What are these?
1325 // source->models->params[7] = B2;
1326 // source->models->params[8] = B3;
1327 return(true);
1328
1329 // XXX EAM : I might drop this model (or rather, replace it)
1330 case PS_MODEL_TWIST_GAUSS:
1331 params->data.F32[0] = source->moments->Sky;
1332 params->data.F32[1] = source->peak->counts - source->moments->Sky;
1333 params->data.F32[2] = source->moments->x;
1334 params->data.F32[3] = source->moments->y;
1335 // XXX: What are these?
1336 // params->data.F32[4] = SxInner;
1337 // params->data.F32[5] = SyInner;
1338 // params->data.F32[6] = SxyInner;
1339 // params->data.F32[7] = SxOuter;
1340 // params->data.F32[8] = SyOuter;
1341 // params->data.F32[9] = SxyOuter;
1342 // params->data.F32[10] = N;
1343 return(true);
1344
1345 case PS_MODEL_SERSIC:
1346 params->data.F32[0] = source->moments->Sky;
1347 params->data.F32[1] = source->peak->counts - source->moments->Sky;
1348 params->data.F32[2] = source->moments->x;
1349 params->data.F32[3] = source->moments->y;
1350 params->data.F32[4] = sqrt(2.0) / source->moments->Sx;
1351 params->data.F32[5] = sqrt(2.0) / source->moments->Sy;
1352 params->data.F32[6] = source->moments->Sxy;
1353 // XXX: What are these?
1354 //params->data.F32[7] = Nexp;
1355 return(true);
1356
1357 case PS_MODEL_SERSIC_CORE:
1358 params->data.F32[0] = source->moments->Sky;
1359 params->data.F32[1] = source->peak->counts - source->moments->Sky;
1360 params->data.F32[2] = source->moments->x;
1361 params->data.F32[3] = source->moments->y;
1362 // XXX: What are these?
1363 // params->data.F32[4] SxInner;
1364 // params->data.F32[5] SyInner;
1365 // params->data.F32[6] SxyInner;
1366 // params->data.F32[7] Zd;
1367 // params->data.F32[8] SxOuter;
1368 // params->data.F32[9] SyOuter;
1369 // params->data.F32[10] = SxyOuter;
1370 // params->data.F32[11] = Nexp;
1371 return(true);
1372# endif
1373 default:
1374 psError(PS_ERR_UNKNOWN, true, "Undefined psModelType");
1375 return(false);
1376 }
1377}
1378
1379/******************************************************************************
1380evalModel(source, level, row): a private function which evaluates the
1381source->model function at the specified coords. The coords are subImage, not
1382image coords.
1383
1384NOTE: The coords are in subImage source->pixel coords, not image coords.
1385
1386XXX: reverse order of row,col args?
1387
1388XXX: rename all coords in this file such that their name defines whether
1389the coords is in subImage or image space.
1390
1391XXX: This should probably be a public psModules function.
1392
1393XXX: Use static vectors for x.
1394
1395XXX: Figure out if it's (row, col) or (col, row) for the model functions.
1396
1397XXX: For a while, the first psVectorAlloc() was generating a seg fault during
1398testing. Try to reproduce that and debug.
1399*****************************************************************************/
1400static psF32 evalModel(psSource *src,
1401 psU32 row,
1402 psU32 col)
1403{
1404 PS_PTR_CHECK_NULL(src, false);
1405 PS_PTR_CHECK_NULL(src->models, false);
1406 PS_PTR_CHECK_NULL(src->models->params, false);
1407
1408 // XXX: The following step will not be necessary if the models->params
1409 // member is a psVector. Suggest to IfA.
1410
1411 // XXX EAM: done: models->params is now a vector
1412 psVector *params = src->models->params;
1413
1414 //
1415 // Allocate the x coordinate structure and convert row/col to image space.
1416 //
1417 psVector *x = psVectorAlloc(2, PS_TYPE_F32);
1418 x->data.F32[0] = (psF32) (col + src->pixels->col0);
1419 x->data.F32[1] = (psF32) (row + src->pixels->row0);
1420 psF32 tmpF;
1421
1422 switch (src->models->type) {
1423 case PS_MODEL_GAUSS:
1424 tmpF = pmMinLM_Gauss2D(NULL, params, x);
1425 break;
1426 case PS_MODEL_PGAUSS:
1427 tmpF = pmMinLM_PsuedoGauss2D(NULL, params, x);
1428 break;
1429 case PS_MODEL_TWIST_GAUSS:
1430 tmpF = pmMinLM_TwistGauss2D(NULL, params, x);
1431 break;
1432 case PS_MODEL_WAUSS:
1433 tmpF = pmMinLM_Wauss2D(NULL, params, x);
1434 break;
1435 case PS_MODEL_SERSIC:
1436 tmpF = pmMinLM_Sersic(NULL, params, x);
1437 break;
1438 case PS_MODEL_SERSIC_CORE:
1439 tmpF = pmMinLM_SersicCore(NULL, params, x);
1440 break;
1441 default:
1442 psError(PS_ERR_UNKNOWN, true, "Undefined psModelType");
1443 return(NAN);
1444 }
1445 psFree(x);
1446 return(tmpF);
1447}
1448
1449/******************************************************************************
1450findValue(source, level, row, col, dir): a private function which determines
1451the column coordinate of the model function which has the value "level". If
1452dir equals 0, then you loop leftwards from the peak pixel, otherwise,
1453rightwards.
1454
1455XXX: reverse order of row,col args?
1456
1457XXX: Input row/col are in image coords.
1458
1459XXX: The result is returned in image coords.
1460*****************************************************************************/
1461static psF32 findValue(psSource *source,
1462 psF32 level,
1463 psU32 row,
1464 psU32 col,
1465 psU32 dir)
1466{
1467 //
1468 // Convert coords to subImage space.
1469 //
1470 psU32 subRow = row - source->pixels->row0;
1471 psU32 subCol = col - source->pixels->col0;
1472
1473 // Ensure that the starting column is allowable.
1474 if (!((0 <= subCol) && (subCol < source->pixels->numCols))) {
1475 psError(PS_ERR_UNKNOWN, true, "Starting column outside subImage range");
1476 return(NAN);
1477 }
1478 if (!((0 <= subRow) && (subRow < source->pixels->numRows))) {
1479 psError(PS_ERR_UNKNOWN, true, "Starting row outside subImage range");
1480 return(NAN);
1481 }
1482
1483 psF32 oldValue = evalModel(source, subRow, subCol);
1484 if (oldValue == level) {
1485 return(((psF32) (subCol + source->pixels->col0)));
1486 }
1487
1488 //
1489 // We define variables incr and lastColumn so that we can use the same loop
1490 // whether we are stepping leftwards, or rightwards.
1491 //
1492 psS32 incr;
1493 psS32 lastColumn;
1494 if (dir == 0) {
1495 incr = -1;
1496 lastColumn = -1;
1497 } else {
1498 incr = 1;
1499 lastColumn = source->pixels->numCols;
1500 }
1501 subCol+=incr;
1502
1503 while (subCol != lastColumn) {
1504 psF32 newValue = evalModel(source, subRow, subCol);
1505 if (oldValue == level) {
1506 return((psF32) (subCol + source->pixels->col0));
1507 }
1508
1509 if ((newValue <= level) && (level <= oldValue)) {
1510 // This is simple linear interpolation.
1511 return( ((psF32) (subCol + source->pixels->col0)) + ((psF32) incr) * ((level - newValue) / (oldValue - newValue)) );
1512 }
1513
1514 if ((oldValue <= level) && (level <= newValue)) {
1515 // This is simple linear interpolation.
1516 return( ((psF32) (subCol + source->pixels->col0)) + ((psF32) incr) * ((level - oldValue) / (newValue - oldValue)) );
1517 }
1518
1519 subCol+=incr;
1520 }
1521
1522 return(NAN);
1523}
1524
1525/******************************************************************************
1526pmSourceContour(src, img, level, mode): For an input subImage, and model, this
1527routine returns a psArray of coordinates that evaluate to the specified level.
1528
1529XXX: Probably should remove the "image" argument.
1530XXX: What type should the output coordinate vectors consist of? col,row?
1531XXX: Why a pmArray output?
1532XXX: doex x,y correspond with col,row or row/col?
1533XXX: What is momde?
1534XXX: The top, bottom of the contour is not correctly determined.
1535*****************************************************************************/
1536psArray *pmSourceContour(psSource *source,
1537 const psImage *image,
1538 psF32 level,
1539 pmContourType mode)
1540{
1541 PS_PTR_CHECK_NULL(source, false);
1542 PS_PTR_CHECK_NULL(image, false);
1543 PS_PTR_CHECK_NULL(source->moments, false);
1544 PS_PTR_CHECK_NULL(source->peak, false);
1545 PS_PTR_CHECK_NULL(source->pixels, false);
1546 PS_PTR_CHECK_NULL(source->models, false);
1547
1548 //
1549 // Allocate data for x/y pairs.
1550 //
1551 psVector *xVec = psVectorAlloc(2 * source->pixels->numRows, PS_TYPE_F32);
1552 psVector *yVec = psVectorAlloc(2 * source->pixels->numRows, PS_TYPE_F32);
1553
1554 //
1555 // Start at the row with peak pixel, then decrement.
1556 //
1557 psS32 col = source->peak->x;
1558 for (psS32 row = source->peak->y; row>= 0 ; row--) {
1559 // XXX: yVec contain no real information. Do we really need it?
1560 yVec->data.F32[row] = (psF32) (source->pixels->row0 + row);
1561 yVec->data.F32[row+yVec->n] = (psF32) (source->pixels->row0 + row);
1562
1563 // Starting at peak pixel, search leftwards for the column intercept.
1564 psF32 leftIntercept = findValue(source, level, row, col, 0);
1565 if (isnan(leftIntercept)) {
1566 psError(PS_ERR_UNKNOWN, true, "Could not find contour edge (NAN)");
1567 psFree(xVec);
1568 psFree(yVec);
1569 return(NULL);
1570 }
1571 xVec->data.F32[row] = ((psF32) source->pixels->col0) + leftIntercept;
1572
1573 // Starting at peak pixel, search rightwards for the column intercept.
1574
1575 psF32 rightIntercept = findValue(source, level, row, col, 1);
1576 if (isnan(rightIntercept)) {
1577 psError(PS_ERR_UNKNOWN, true, "Could not find contour edge (NAN)");
1578 psFree(xVec);
1579 psFree(yVec);
1580 return(NULL);
1581 }
1582 //printf("HERE the intercepts are (%.2f, %.2f)\n", leftIntercept, rightIntercept);
1583 xVec->data.F32[row+xVec->n] = ((psF32) source->pixels->col0) + rightIntercept;
1584
1585 // Set starting column for next row
1586 col = (psS32) ((leftIntercept + rightIntercept) / 2.0);
1587 }
1588 //
1589 // Start at the row (+1) with peak pixel, then increment.
1590 //
1591 col = source->peak->x;
1592 for (psS32 row = 1 + source->peak->y; row < source->pixels->numRows ; row++) {
1593 // XXX: yVec contain no real information. Do we really need it?
1594 yVec->data.F32[row] = (psF32) (source->pixels->row0 + row);
1595 yVec->data.F32[row+yVec->n] = (psF32) (source->pixels->row0 + row);
1596
1597 // Starting at peak pixel, search leftwards for the column intercept.
1598 psF32 leftIntercept = findValue(source, level, row, col, 0);
1599 if (isnan(leftIntercept)) {
1600 psError(PS_ERR_UNKNOWN, true, "Could not find contour edge (NAN)");
1601 psFree(xVec);
1602 psFree(yVec);
1603 return(NULL);
1604 }
1605 xVec->data.F32[row] = ((psF32) source->pixels->col0) + leftIntercept;
1606
1607 // Starting at peak pixel, search rightwards for the column intercept.
1608 psF32 rightIntercept = findValue(source, level, row, col, 1);
1609 if (isnan(rightIntercept)) {
1610 psError(PS_ERR_UNKNOWN, true, "Could not find contour edge (NAN)");
1611 psFree(xVec);
1612 psFree(yVec);
1613 return(NULL);
1614 }
1615 xVec->data.F32[row+xVec->n] = ((psF32) source->pixels->col0) + rightIntercept;
1616
1617 // Set starting column for next row
1618 col = (psS32) ((leftIntercept + rightIntercept) / 2.0);
1619 }
1620
1621 //
1622 // Allocate an array for result, store coord vectors there.
1623 //
1624 psArray *tmpArray = psArrayAlloc(2);
1625 tmpArray->data[0] = (psPtr *) yVec;
1626 tmpArray->data[1] = (psPtr *) xVec;
1627 return(tmpArray);
1628}
1629
1630psVector *p_pmMinLM_Gauss2D_Vec(psImage *deriv, psVector *params, psArray *x);
1631psVector *p_pmMinLM_PsuedoGauss2D_Vec(psImage *deriv, psVector *params, psArray *x);
1632psVector *p_pmMinLM_Wauss2D_Vec(psImage *deriv, psVector *params, psArray *x);
1633psVector *p_pmMinLM_TwistGauss2D_Vec(psImage *deriv, psVector *params, psArray *x);
1634psVector *p_pmMinLM_Sersic_Vec(psImage *deriv, psVector *params, psArray *x);
1635psVector *p_pmMinLM_SersicCore_Vec(psImage *deriv, psVector *params, psArray *x);
1636
1637// XXX EAM : these are better starting values, but should be available from metadata?
1638#define PM_SOURCE_FIT_MODEL_NUM_ITERATIONS 20
1639#define PM_SOURCE_FIT_MODEL_TOLERANCE 0.1
1640/******************************************************************************
1641pmSourceFitModel(source, image): must create the appropiate arguments to the
1642LM minimization routines for the various p_pmMinLM_XXXXXX_Vec() functions.
1643
1644XXX: should there be a mask value?
1645XXX: Probably should remove the "image" argument.
1646*****************************************************************************/
1647bool pmSourceFitModel(psSource *source,
1648 const psImage *image)
1649{
1650 PS_PTR_CHECK_NULL(source, false);
1651 PS_PTR_CHECK_NULL(source->moments, false);
1652 PS_PTR_CHECK_NULL(source->peak, false);
1653 PS_PTR_CHECK_NULL(source->pixels, false);
1654 PS_PTR_CHECK_NULL(source->models, false);
1655 PS_IMAGE_CHECK_NULL(image, false);
1656 PS_IMAGE_CHECK_TYPE(image, PS_TYPE_F32, false);
1657 psBool rc;
1658
1659 // find the number of valid pixels
1660 // XXX EAM : this loop and the loop below could just be one pass
1661 // using the psArrayAdd and psVectorExtend functions
1662 psS32 count = 0;
1663 for (psS32 i = 0; i < source->pixels->numRows; i++) {
1664 for (psS32 j = 0; j < source->pixels->numCols; j++) {
1665 if (source->mask->data.U8[i][j] == 0) {
1666 count++;
1667 }
1668 }
1669 }
1670
1671 // construct the coordinate and value entries
1672 psArray *x = psArrayAlloc(count);
1673 psVector *y = psVectorAlloc(count, PS_TYPE_F32);
1674 psVector *yErr = psVectorAlloc(count, PS_TYPE_F32);
1675 psS32 tmpCnt = 0;
1676 for (psS32 i = 0; i < source->pixels->numRows; i++) {
1677 for (psS32 j = 0; j < source->pixels->numCols; j++) {
1678 if (source->mask->data.U8[i][j] == 0) {
1679 psVector *coord = psVectorAlloc(2, PS_TYPE_F32);
1680 // XXX: Convert i/j to image space:
1681 // XXX EAM: coord order is (x,y) == (col,row)
1682 coord->data.F32[0] = (psF32) (j + source->pixels->col0);
1683 coord->data.F32[1] = (psF32) (i + source->pixels->row0);
1684 x->data[tmpCnt] = (psPtr *) coord;
1685 y->data.F32[tmpCnt] = source->pixels->data.F32[i][j];
1686
1687 // XXX EAM : this is approximate: need to apply the gain and rdnoise
1688 yErr->data.F32[tmpCnt] = sqrt(PS_MAX(1, source->pixels->data.F32[i][j]));
1689 tmpCnt++;
1690 }
1691 }
1692 }
1693
1694 psMinimization *myMin = psMinimizationAlloc(PM_SOURCE_FIT_MODEL_NUM_ITERATIONS,
1695 PM_SOURCE_FIT_MODEL_TOLERANCE);
1696
1697 psVector *params = source->models->params;
1698
1699 switch (source->models->type) {
1700 case PS_MODEL_GAUSS:
1701 rc = psMinimizeLMChi2(myMin, NULL, params, NULL, x, y, yErr, pmMinLM_Gauss2D);
1702 break;
1703 case PS_MODEL_PGAUSS:
1704 rc = psMinimizeLMChi2(myMin, NULL, params, NULL, x, y, yErr, pmMinLM_PsuedoGauss2D);
1705 break;
1706 case PS_MODEL_TWIST_GAUSS:
1707 rc = psMinimizeLMChi2(myMin, NULL, params, NULL, x, y, yErr, pmMinLM_Wauss2D);
1708 break;
1709 case PS_MODEL_WAUSS:
1710 rc = psMinimizeLMChi2(myMin, NULL, params, NULL, x, y, yErr, pmMinLM_TwistGauss2D);
1711 break;
1712 case PS_MODEL_SERSIC:
1713 rc = psMinimizeLMChi2(myMin, NULL, params, NULL, x, y, yErr, pmMinLM_Sersic);
1714 break;
1715 case PS_MODEL_SERSIC_CORE:
1716 rc = psMinimizeLMChi2(myMin, NULL, params, NULL, x, y, yErr, pmMinLM_SersicCore);
1717 break;
1718 default:
1719 psError(PS_ERR_UNKNOWN, true, "Undefined psModelType");
1720 rc = false;
1721 }
1722 // XXX EAM: we need to do something (give an error?) if rc is false
1723 // XXX EAM: save the resulting chisq, nDOF, nIter
1724 source->models->chisq = myMin->value;
1725 source->models->nDOF = y->n - params->n;
1726 source->models->nIter = myMin->iter;
1727
1728 psFree(x);
1729 psFree(y);
1730 psFree(myMin);
1731 return(rc);
1732}
1733
1734bool p_pmSourceAddOrSubModel(psImage *image,
1735 psSource *src,
1736 bool center,
1737 psS32 flag)
1738{
1739 PS_PTR_CHECK_NULL(src, false);
1740 PS_PTR_CHECK_NULL(src->moments, false);
1741 PS_PTR_CHECK_NULL(src->peak, false);
1742 PS_PTR_CHECK_NULL(src->pixels, false);
1743 PS_PTR_CHECK_NULL(src->models, false);
1744 PS_IMAGE_CHECK_NULL(image, false);
1745 PS_IMAGE_CHECK_TYPE(image, PS_TYPE_F32, false);
1746
1747 psVector *x = psVectorAlloc(2, PS_TYPE_F32);
1748 psVector *params = src->models->params;
1749
1750 for (psS32 i = 0; i < src->pixels->numRows; i++) {
1751 for (psS32 j = 0; j < src->pixels->numCols; j++) {
1752 psF32 pixelValue;
1753 // XXX: Should you be adding the pixels for the entire subImage,
1754 // or a radius of pixels around it?
1755
1756 // Convert i/j to imace coord space:
1757 // XXX: Make sure you have col/row order correct.
1758 psS32 imageRow = i + src->pixels->row0;
1759 psS32 imageCol = j + src->pixels->col0;
1760
1761 x->data.F32[0] = (float) imageCol;
1762 x->data.F32[1] = (float) imageRow;
1763 switch (src->models->type) {
1764 case PS_MODEL_GAUSS:
1765 pixelValue = pmMinLM_Gauss2D(NULL, params, x);
1766 break;
1767 case PS_MODEL_PGAUSS:
1768 pixelValue = pmMinLM_PsuedoGauss2D(NULL, params, x);
1769 break;
1770 case PS_MODEL_TWIST_GAUSS:
1771 pixelValue = pmMinLM_TwistGauss2D(NULL, params, x);
1772 break;
1773 case PS_MODEL_WAUSS:
1774 pixelValue = pmMinLM_Wauss2D(NULL, params, x);
1775 break;
1776 case PS_MODEL_SERSIC:
1777 pixelValue = pmMinLM_Sersic(NULL, params, x);
1778 break;
1779 case PS_MODEL_SERSIC_CORE:
1780 pixelValue = pmMinLM_SersicCore(NULL, params, x);
1781 break;
1782 default:
1783 psError(PS_ERR_UNKNOWN, true, "Undefined psModelType");
1784 psFree(x);
1785 return(false);
1786 }
1787 if (flag == 1) {
1788 pixelValue = -pixelValue;
1789 }
1790
1791 // XXX: Must figure out how to calculate the image coordinates and
1792 // how to use the boolean "center" flag.
1793
1794 image->data.F32[imageRow][imageCol]+= pixelValue;
1795 }
1796 }
1797 psFree(x);
1798 return(true);
1799}
1800
1801
1802
1803/******************************************************************************
1804 *****************************************************************************/
1805bool pmSourceAddModel(psImage *image,
1806 psSource *src,
1807 bool center)
1808{
1809 return(p_pmSourceAddOrSubModel(image, src, center, 0));
1810}
1811
1812/******************************************************************************
1813 *****************************************************************************/
1814bool pmSourceSubModel(psImage *image,
1815 psSource *src,
1816 bool center)
1817{
1818 return(p_pmSourceAddOrSubModel(image, src, center, 1));
1819}
1820
1821
1822// XXX: Put this is psConstants.h
1823#define PS_VECTOR_CHECK_SIZE(VEC1, N, RVAL) \
1824if (VEC1->n != N) { \
1825 psError(PS_ERR_BAD_PARAMETER_SIZE, true, \
1826 "psVector %s has size %d, should be %d.", \
1827 #VEC1, VEC1->n, N); \
1828 return(RVAL); \
1829}
1830
1831
1832/**
1833 all of these object representation functions have the same form : func(*deriv, *params, *x)
1834
1835 the argument "x" contains a single "x,y" coordinate pair. The function computes the object
1836 model, based on the parameters in "params" at the x,y point specified by *x, and returns the value.
1837 The derivatives are also caculated and returned in the "deriv" argument. parameter error checking is
1838 skipped because speed is most important.
1839**/
1840
1841/******************************************************************************
1842 params->data.F32[0] = So;
1843 params->data.F32[1] = Zo;
1844 params->data.F32[2] = Xo;
1845 params->data.F32[3] = Yo;
1846 params->data.F32[4] = sqrt(2.0) / SigmaX;
1847 params->data.F32[5] = sqrt(2.0) / SigmaY;
1848 params->data.F32[6] = Sxy;
1849*****************************************************************************/
1850psF64 pmMinLM_Gauss2D(psVector *deriv,
1851 psVector *params,
1852 psVector *x)
1853{
1854 psF32 X = x->data.F32[0] - params->data.F32[2];
1855 psF32 Y = x->data.F32[1] - params->data.F32[3];
1856 psF32 px = params->data.F32[4]*X;
1857 psF32 py = params->data.F32[5]*Y;
1858 psF32 z = 0.5*PS_SQR(px) + 0.5*PS_SQR(py) + params->data.F32[6]*X*Y;
1859 psF32 r = exp(-z);
1860 psF32 q = params->data.F32[1]*r;
1861 psF32 f = q + params->data.F32[0];
1862
1863 if (deriv != NULL) {
1864 deriv->data.F32[0] = +1.0;
1865 deriv->data.F32[1] = +r;
1866 deriv->data.F32[2] = q*(2*px*params->data.F32[4] + params->data.F32[6]*Y);
1867 deriv->data.F32[3] = q*(2*py*params->data.F32[5] + params->data.F32[6]*X);
1868 deriv->data.F32[4] = -2.0*q*px*X;
1869 deriv->data.F32[5] = -2.0*q*py*Y;
1870 deriv->data.F32[6] = -q*X*Y;
1871 }
1872 return(f);
1873}
1874
1875/******************************************************************************
1876 params->data.F32[0] = So;
1877 params->data.F32[1] = Zo;
1878 params->data.F32[2] = Xo;
1879 params->data.F32[3] = Yo;
1880 params->data.F32[4] = sqrt(2) / SigmaX;
1881 params->data.F32[5] = sqrt(2) / SigmaY;
1882 params->data.F32[6] = Sxy;
1883*****************************************************************************/
1884psF64 pmMinLM_PsuedoGauss2D(psVector *deriv,
1885 psVector *params,
1886 psVector *x)
1887{
1888 psF32 X = x->data.F32[0] - params->data.F32[2];
1889 psF32 Y = x->data.F32[1] - params->data.F32[3];
1890 psF32 px = params->data.F32[4]*X;
1891 psF32 py = params->data.F32[5]*Y;
1892 psF32 z = 0.5*PS_SQR(px) + 0.5*PS_SQR(py) + params->data.F32[6]*X*Y;
1893 psF32 t = 1 + z + 0.5*z*z;
1894 psF32 r = 1.0 / (t*(1 + z/3)); /* exp (-Z) */
1895 psF32 f = params->data.F32[1]*r + params->data.F32[0];
1896
1897 if (deriv != NULL) {
1898 // note difference from a pure gaussian: q = params->data.F32[1]*r
1899 psF32 q = params->data.F32[1]*r*r*t;
1900 deriv->data.F32[0] = +1.0;
1901 deriv->data.F32[1] = +r;
1902 deriv->data.F32[2] = q*(2.0*px*params->data.F32[4] + params->data.F32[6]*Y);
1903 deriv->data.F32[3] = q*(2.0*py*params->data.F32[5] + params->data.F32[6]*X);
1904 deriv->data.F32[4] = -2.0*q*px*X;
1905 deriv->data.F32[5] = -2.0*q*py*Y;
1906 deriv->data.F32[6] = -q*X*Y;
1907 }
1908 return(f);
1909}
1910
1911/******************************************************************************
1912 params->data.F32[0] = So;
1913 params->data.F32[1] = Zo;
1914 params->data.F32[2] = Xo;
1915 params->data.F32[3] = Yo;
1916 params->data.F32[4] = Sx;
1917 params->data.F32[5] = Sy;
1918 params->data.F32[6] = Sxy;
1919 params->data.F32[7] = B2;
1920 params->data.F32[8] = B3;
1921*****************************************************************************/
1922psF64 pmMinLM_Wauss2D(psVector *deriv,
1923 psVector *params,
1924 psVector *x)
1925{
1926 psF32 X = x->data.F32[0] - params->data.F32[2];
1927 psF32 Y = x->data.F32[1] - params->data.F32[2];
1928 psF32 px = params->data.F32[4]*X;
1929 psF32 py = params->data.F32[5]*Y;
1930 psF32 z = 0.5*PS_SQR(px) + 0.5*PS_SQR(py) + params->data.F32[6]*X*Y;
1931 psF32 t = 0.5*z*z*(1.0 + params->data.F32[8]*z/3.0);
1932 psF32 r = 1.0 / (1.0 + z + params->data.F32[7]*t); /* exp (-Z) */
1933 psF32 f = params->data.F32[1]*r + params->data.F32[0];
1934
1935 if (deriv != NULL) {
1936 // note difference from gaussian: q = params->data.F32[1]*r
1937 psF32 q = params->data.F32[1]*r*r*(1.0 + params->data.F32[7]*z*(1.0 + params->data.F32[8]*z/2.0));
1938 deriv->data.F32[0] = +1.0;
1939 deriv->data.F32[1] = +r;
1940 deriv->data.F32[2] = q*(2.0*px*params->data.F32[4] + params->data.F32[6]*Y);
1941 deriv->data.F32[3] = q*(2.0*py*params->data.F32[5] + params->data.F32[6]*X);
1942 deriv->data.F32[4] = -2.0*q*px*X;
1943 deriv->data.F32[5] = -2.0*q*py*Y;
1944 deriv->data.F32[6] = -q*X*Y;
1945 deriv->data.F32[7] = -100.0*params->data.F32[1]*r*r*t;
1946 deriv->data.F32[8] = -100.0*params->data.F32[1]*r*r*params->data.F32[7]*(z*z*z)/6.0;
1947 // The values of 100 dampen the swing of params->data.F32[7,8] */
1948 }
1949 return(f);
1950}
1951
1952// XXX: What should these be?
1953#define FFACTOR 1.0
1954#define FSCALE 1.0
1955/******************************************************************************
1956 params->data.F32[0] = So;
1957 params->data.F32[1] = Zo;
1958 params->data.F32[2] = Xo;
1959 params->data.F32[3] = Yo;
1960 params->data.F32[4] = SxInner;
1961 params->data.F32[5] = SyInner;
1962 params->data.F32[6] = SxyInner;
1963 params->data.F32[7] = SxOuter;
1964 params->data.F32[8] = SyOuter;
1965 params->data.F32[9] = SxyOuter;
1966 params->data.F32[10] = N;
1967*****************************************************************************/
1968psF64 pmMinLM_TwistGauss2D(psVector *deriv,
1969 psVector *params,
1970 psVector *x)
1971{
1972 psF32 X = x->data.F32[0] - params->data.F32[2];
1973 psF32 Y = x->data.F32[1] - params->data.F32[3];
1974 psF32 px1 = params->data.F32[4]*X;
1975 psF32 py1 = params->data.F32[5]*Y;
1976 psF32 px2 = params->data.F32[7]*X;
1977 psF32 py2 = params->data.F32[8]*Y;
1978 psF32 z1 = 0.5*PS_SQR(px1) + 0.5*PS_SQR(py1) + params->data.F32[4]*X*Y;
1979 psF32 z2 = 0.5*PS_SQR(px2) + 0.5*PS_SQR(py2) + params->data.F32[9]*X*Y;
1980 psF32 r = 1.0 / (1.0 + z1 + pow(z2,params->data.F32[10]));
1981
1982 psF32 f = params->data.F32[5]*r + params->data.F32[6];
1983
1984 if (deriv != NULL) {
1985 psF32 q1 = params->data.F32[5]*PS_SQR(r);
1986 psF32 q2 = params->data.F32[5]*PS_SQR(r)*params->data.F32[10]*pow(z2,(params->data.F32[10]-1.0));
1987 deriv->data.F32[0] = +1.0;
1988 deriv->data.F32[1] = +r;
1989 deriv->data.F32[2] = q1*(2.0*px1*params->data.F32[4] + params->data.F32[6]*Y) + q2*(2*px2*params->data.F32[7] + params->data.F32[9]*Y);
1990 deriv->data.F32[3] = q1*(2.0*py1*params->data.F32[5] + params->data.F32[6]*X) + q2*(2*py2*params->data.F32[8] + params->data.F32[9]*X);
1991
1992 // These fudge factors impede the growth of params->data.F32[4] beyond
1993 // params->data.F32[7].
1994 psF32 f1 = fabs(params->data.F32[7]) / fabs(params->data.F32[4]);
1995 psF32 f2 = (f1 < FSCALE) ? 1.0 : FFACTOR*(f1 - FSCALE) + 1.0;
1996 deriv->data.F32[4] = -2.0*q1*px1*X*f2;
1997
1998 // These fudge factors impede the growth of params->data.F32[5] beyond
1999 // params->data.F32[8].
2000 f1 = fabs(params->data.F32[8]) / fabs(params->data.F32[5]);
2001 f2 = (f1 < FSCALE) ? 1.0 : FFACTOR*(f1 - FSCALE) + 1.0;
2002 deriv->data.F32[5] = -2.0*q1*py1*Y*f2;
2003 deriv->data.F32[6] = -q1*X*Y;
2004 deriv->data.F32[7] = -2.0*q2*px2*X;
2005 deriv->data.F32[8] = -2.0*q2*py2*Y;
2006 deriv->data.F32[9] = -q2*X*Y;
2007 deriv->data.F32[10] = -q1*log(z2);
2008 }
2009
2010 return(f);
2011}
2012
2013/******************************************************************************
2014 float Sersic()
2015 params->data.F32[0] = So;
2016 params->data.F32[1] = Zo;
2017 params->data.F32[2] = Xo;
2018 params->data.F32[3] = Yo;
2019 params->data.F32[4] = Sx;
2020 params->data.F32[5] = Sy;
2021 params->data.F32[6] = Sxy;
2022 params->data.F32[7] = Nexp;
2023*****************************************************************************/
2024psF64 pmMinLM_Sersic(psVector *deriv,
2025 psVector *params,
2026 psVector *x)
2027{
2028 psError(PS_ERR_UNKNOWN, true, "This function is not implemented yet.");
2029 return(0.0);
2030}
2031
2032/******************************************************************************
2033 float SersicBulge()
2034 params->data.F32[0] So;
2035 params->data.F32[1] Zo;
2036 params->data.F32[2] Xo;
2037 params->data.F32[3] Yo;
2038 params->data.F32[4] SxInner;
2039 params->data.F32[5] SyInner;
2040 params->data.F32[6] SxyInner;
2041 params->data.F32[7] Zd;
2042 params->data.F32[8] SxOuter;
2043 params->data.F32[9] SyOuter;
2044 params->data.F32[10] = SxyOuter;
2045 params->data.F32[11] = Nexp;
2046*****************************************************************************/
2047psF64 pmMinLM_SersicCore(psVector *deriv,
2048 psVector *params,
2049 psVector *x)
2050{
2051 psError(PS_ERR_UNKNOWN, true, "This function is not implemented yet.");
2052 return(0.0);
2053}