CR2RE Pipeline Reference Manual 1.6.7
hdrl_cat_overlp.c
1/*
2 * This file is part of the HDRL
3 * Copyright (C) 2017 European Southern Observatory
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20#include "hdrl_cat_overlp.h"
21
22#include "hdrl_cat_apio.h"
23#include "hdrl_cat_apclust.h"
24#include "hdrl_cat_polynm.h"
25#include "hdrl_cat_terminate.h"
26#include "hdrl_cat_utils_sort.h"
27
28
29/*** Defines ***/
30
31#define IDBLIM 10000 /* Maximum number of pixels to use in deblending */
32#define NITER 6 /* Number of iterations */
33
34
35/*** Internal global variables ***/
36
37static double oldthr;
38static double curthr;
39static double nexthr;
40static double lasthr;
41static double xbar_start;
42static double ybar_start;
43
44
45/*** Prototypes ***/
46
47static void moments_thr(ap_t *ap, double results[NPAR+1], cpl_size ipk[2]);
48static void update_ov( double iap[NAREAL], double t, double thresh, double fconst, double offset);
49static void check_term( ap_t *ap, cpl_size *nobj, double parm[IMNUM][NPAR+1],
50 cpl_size peaks[IMNUM][2], cpl_size *toomany);
51
52static int cmp_plstruct(const void *a, const void *b);
53
54
55/*---------------------------------------------------------------------------*/
63/*----------------------------------------------------------------------------*/
64
67/* ---------------------------------------------------------------------------*/
87/* ---------------------------------------------------------------------------*/
88cpl_error_code hdrl_overlp(
89 ap_t *ap, double parm[IMNUM][NPAR], cpl_size *nbit,
90 double xbar, double ybar, double total, cpl_size npix, double tmax)
91{
92 /* Initialize a few variables */
93 plstruct *pl = ap->plarray;
94 cpl_size npl = ap->npl_pix;
95 cpl_size ipix = ap->ipnop;
96 double fconst = ap->fconst;
97 double offset = ap->areal_offset;
98
99 oldthr = ap->thresh;
100 xbar_start = xbar;
101 ybar_start = ybar;
102
103 /* Initialize some constants that you might need later */
104 double tmul = 1.2589678; /* 1/4 mag deblending contour increment */
105 double smul = 2.5; /* starting contour increment */
106 cpl_size ipixo2 = CPL_MAX(2, (ipix + 1) / 2); /* ipix is the minimum image pixel size */
107 double xintmn = oldthr * ipixo2; /* minimum intensity for fragments */
108 double itmaxlim = 0.9 * tmax; /* upper limit deblending 90% of peak */
109
110 lasthr = itmaxlim;
111 curthr = smul * oldthr;
112
113 /* Get a maximum of IDBLIM points brighter than the new detection threshold
114 * by reverse sorting the input array. If there are still more than IDBLIM
115 * above the threshold, then revise the threshold until there aren't. Then
116 * use the variable npl2 to stop the rest of the routine accessing any of
117 * the fainter pixels in the list. This is to restrict processing time
118 * for large extended objects */
119
120 sort_array_f(pl, npl, sizeof(*pl), &cmp_plstruct);
121
122 cpl_size npl2;
123 while (1) {
124
125 npl2 = 0;
126 while (npl2 < npl - 1 && pl[npl2].zsm > curthr) {
127 npl2++;
128 }
129
130 if (npl2 > IDBLIM) curthr += oldthr;
131 else break;
132 }
133
134 /* If there are fewer pixels above the new threshold than the minimum
135 * specified in the input parameters, then you have no reason to be here */
136 if (npl2 < ipix) {
137 *nbit = 1;
138 return CPL_ERROR_NONE;
139 }
140
141 /* Get a new ap structure */
142 ap_t ap2;
143 ap2.lsiz = ap->lsiz;
144 ap2.csiz = ap->csiz;
145 ap2.multiply = 1;
146 ap2.ipnop = ipixo2;
147 ap2.areal_offset = offset;
148 ap2.fconst = fconst;
149 ap2.mflag = cpl_calloc((ap2.lsiz) * (ap2.csiz), sizeof(*ap2.mflag));
150
151 hdrl_apinit(&ap2);
152
153 /* Main analysis loop at new thresholds */
154 *nbit = 0;
155
156 /* TODO: it could be problems???, it's checked (in the while) before initialize */
157 cpl_size ibitx[IMNUM];
158 cpl_size ibity[IMNUM];
159
160 cpl_size iupdate[IMNUM];
161 double parmnew[IMNUM][NPAR];
162
163 cpl_size nbitprev = 0;
164 while (1) {
165
166 nexthr = CPL_MAX(curthr + oldthr, curthr * tmul);
167
168 /* Locate objects in this cluster */
169 ap2.thresh = curthr;
170 hdrl_apclust(&ap2, npl2, pl);
171
172 cpl_size nobj;
173 double results[IMNUM][NPAR+1];
174 cpl_size ipks[ IMNUM][2];
175 cpl_size toomany;
176 check_term(&ap2, &nobj, results, ipks, &toomany);
177
178 hdrl_apreinit(&ap2);
179
180 if (nobj == 0) {
181 break;
182 }
183
184 /* For each image check the number of points above the next threshold
185 * and flag. Do a moments analysis of each object */
186 for (cpl_size i = 0; i < nobj; i++) {
187
188 /* Ok, has this object already been detected? If so, then
189 * load the new results into the parmnew array. We'll check
190 * whether it's worthwhile updating the master parameters
191 * list later */
192
193 cpl_size isnew = 1;
194
195 double xb = results[i][1];
196 double yb = results[i][2];
197 double sxx = CPL_MAX(1., results[i][4]);
198 double syy = CPL_MAX(1., results[i][6]);
199
200 for (cpl_size k = 0; k < nbitprev; k++) {
201
202 double dx = xb - parm[k][1];
203 double dy = yb - parm[k][2];
204
205 double radius2 = dx * dx / sxx + dy * dy / syy;
206
207 if ( (ibitx[k] == ipks[i][0] && ibity[k] == ipks[i][1]) || radius2 < 1.) {
208
209 isnew = 0;
210
211 for (cpl_size kk = 0; kk < NPAR; kk++) {
212 parmnew[k][kk] = results[i][kk];
213 }
214
215 break;
216 }
217 }
218
219 /* If this is a new one and it's above the minimum threshold
220 then check to make sure you don't already have too many.
221 If you do, then flag this and break out to the next iteration.
222 If there is room for another, then store the moments analysis
223 profile */
224
225 if (isnew && results[i][0] > xintmn) {
226
227 if (*nbit >= IMNUM) {
228
229 *nbit = IMNUM;
230 toomany = 1;
231
232 break;
233 }
234
235 ibitx[*nbit] = ipks[i][0];
236 ibity[*nbit] = ipks[i][1];
237
238 for (cpl_size kk = 0; kk < NPAR; kk++) {
239 parm[*nbit][kk] = results[i][kk];
240 }
241
242 (*nbit)++;
243 }
244
245 } /* End of object loop */
246
247
248 /* If too many objects were found, then skip the next bit...otherwise
249 go through and update parameters if necessary. This block of
250 code is a bit of a place holder waiting for something better to
251 be worked out...*/
252 if (!toomany) {
253
254 if (*nbit > nbitprev && nbitprev > 0) {
255
256 for (cpl_size i = 0; i < nbitprev; i++) iupdate[i] = 0;
257
258 for (cpl_size j = nbitprev; j < *nbit; j++) {
259
260 double distmax = 0.;
261 cpl_size iwas = 0;
262
263 for (cpl_size i = 0; i < nbitprev; i++) {
264
265 if (parmnew[i][0] > 0.) {
266
267 double radius2 = pow(parmnew[i][1] - parm[i][1], 2.) + pow(parmnew[i][2] - parm[i][2], 2.);
268
269 if (radius2 > distmax) {
270 iwas = i;
271 distmax = radius2;
272 }
273 }
274 }
275 iupdate[iwas] = 1;
276 }
277
278 for (cpl_size i = 0; i < nbitprev; i++) {
279 if (iupdate[i] == 1 && parmnew[i][0] > 0.) {
280 for (cpl_size j = 0; j < NPAR; j++) parm[i][j] = parmnew[i][j];
281 }
282 }
283 }
284
285 /* Reset the update flag and prepare for next iteration*/
286 for (cpl_size i = 0; i <= *nbit; i++) {
287 parmnew[i][0] = -1.;
288 }
289 nbitprev = *nbit;
290 }
291
292 /* Where do we cut in the list now? */
293 cpl_size npl3 = 0;
294 while (npl3 < npl2-1 && pl[npl3].zsm > nexthr) {
295 npl3++;
296 }
297 npl2 = npl3;
298
299 /* Now, do we need to move onto the next threshold? */
300 if (npl2 == 0 || toomany || nexthr >= itmaxlim) {
301 break;
302 }
303
304 /* If so, then reset some variables and continue */
305
306 curthr = nexthr;
307
308 } /* End of main analysis loop */
309
310
311 /* Free up some workspace */
312 cpl_free(ap2.mflag);
313 hdrl_apclose(&ap2);
314
315 /* If there isn't only one --> continue program, in other case we can exit */
316 if (*nbit != 1) return hdrl_overlp_2orMore( ap, parm, nbit, xbar, ybar, total,
317 npix, curthr, nexthr, lasthr);
318
319 return CPL_ERROR_NONE;
320}
321
322/* ---------------------------------------------------------------------------*/
345/* ---------------------------------------------------------------------------*/
346cpl_error_code hdrl_overlp_2orMore(
347 ap_t *ap, double parm[IMNUM][NPAR], cpl_size *nbit,
348 double xbar, double ybar, double total, cpl_size npix,
349 double curthr_prev, double nexthr_prev, double lasthr_prev)
350{
351 /* Initialize a few variables */
352 cpl_size ipix = ap->ipnop;
353 double offset = ap->areal_offset;
354
355 oldthr = ap->thresh;
356 curthr = curthr_prev;
357 nexthr = nexthr_prev;
358 lasthr = lasthr_prev;
359 xbar_start = xbar;
360 ybar_start = ybar;
361
362 /* Initialize some constants that you might need later */
363 cpl_size ipixo2 = CPL_MAX(2, (ipix + 1) / 2); /* ipix is the minimum image pixel size */
364 double xintmn = oldthr * ipixo2; /* minimum intensity for fragments */
365 double algthr = log(oldthr); /* Convenient bit of shorthand */
366 double radmax = sqrt((double)npix / CPL_MATH_PI); /* Isophotal size of input data array */
367
368 /* Find out which images terminated properly and remove those that didn't */
369 cpl_size j = -1;
370 for (cpl_size k = 0; k < *nbit; k++) {
371
372 /* Commented this out as checking for terminations seems to miss some
373 * if the total flux above the threshold for an object is negative */
374 if (parm[k][0] > xintmn) {
375
376 j++;
377 if (j != k) {
378 for (cpl_size i = 0; i < NPAR; i++) parm[j][i] = parm[k][i];
379 }
380 }
381 }
382
383 double bitx[IMNUM];
384 double bitl[IMNUM];
385 *nbit = j + 1;
386 for (cpl_size jj = 0; jj < *nbit; jj++) {
387 bitx[jj] = 0.;
388 bitl[jj] = 0.;
389 }
390
391 /* For each image find true areal profile levels and iterate to find local continuum */
392 double xdat[NAREAL + 1];
393 double xcor[NAREAL + 1];
394 cpl_size lastone = 0;
395 cpl_size iter = 0;
396 double sumint = 0.;
397 while (iter < NITER) {
398
399 iter++;
400
401 /* Loop for each of the objects and create a level vs radius array */
402 for (cpl_size k = 0; k < *nbit; k++) {
403
404 if (parm[k][0] >= 0.) {
405
406 /* Pk + newthresh - cont */
407 double xlevol = log(parm[k][7] + parm[k][3] - bitl[k]);
408 double xlevel = xlevol;
409
410 double radold = 0.;
411 double radius = radold;
412 double slope = 1.;
413 cpl_size ic = 0;
414
415 for (cpl_size i = 1; i <= NAREAL; i++) {
416
417 cpl_size jj = NPAR - i;
418 cpl_size ii = NAREAL - i;
419
420 double xx = (double)ii + offset;
421
422 if (parm[k][jj] > 0.5) {
423
424 if (ii == 0) {
425 xlevel = log(parm[k][3] - bitl[k] + 0.5);
426 } else {
427 xlevel = log(pow(2., xx) - oldthr + parm[k][3] - bitl[k] - 0.5);
428 }
429
430 radius = sqrt(parm[k][jj] / CPL_MATH_PI);
431 xdat[ic] = xlevel;
432 xcor[ic++] = radius;
433
434 double dlbydr = (xlevol - xlevel) / CPL_MAX(0.01, radius - radold);
435
436 double wt = CPL_MIN(1., CPL_MAX((radius - radold) * 5., 0.1));
437
438 slope = (1. - 0.5 * wt) * slope + 0.5 * wt * CPL_MIN(5., dlbydr);
439
440 radold = radius;
441 xlevol = xlevel;
442 }
443 }
444
445 /* If this is not the last iteration then work out the effect on the local continuum from each image */
446 if (!lastone) {
447
448 for (cpl_size i = 0; i < *nbit; i++) {
449
450 if (parm[i][0] >= 0.0 && i != k) {
451
452 double dist = sqrt(pow(parm[k][1] - parm[i][1], 2.) + pow(parm[k][2] - parm[i][2], 2.));
453
454 double xeff = xlevel - CPL_MAX(0., CPL_MIN(50., slope * (dist - radius)));
455
456 bitx[i] += exp(xeff);
457 }
458 }
459
460 } else {
461
462 /* If this is the last iteration loop, then update the parameters before exiting*/
463 double ttt;
464 if (ic > 2) {
465 double polycf[3];
466 hdrl_polynm(xdat, xcor, ic, polycf, 3, 0);
467 ttt = polycf[1] + 2. * polycf[2] * radius;
468 } else {
469 ttt = 0.;
470 }
471
472 slope = CPL_MAX(0.1, CPL_MAX(-ttt, slope));
473
474 double radthr = radius + (xlevel - algthr) / slope;
475 if (radthr > radmax) {
476 slope = 1.;
477 radthr = radmax;
478 }
479
480 /* Pixel area */
481 double delb = parm[k][8] * (parm[k][3] - bitl[k]);
482 parm[k][8] = CPL_MATH_PI * radthr * radthr;
483
484 /* Peak height */
485 parm[k][7] += (parm[k][3] - bitl[k]);
486
487 /* Intensity */
488 double deli = 2. * CPL_MATH_PI * ( (parm[k][3] - bitl[k]) * (1. + slope * radius)
489 - oldthr * (1. + slope*radthr)
490 ) / (slope * slope);
491
492 parm[k][0] += delb + CPL_MAX(0., deli);
493
494 for (cpl_size i = 0; i < 7; i++) {
495 parm[k][i+9] = -1.;
496 }
497
498 if (parm[k][0] > xintmn) {
499 sumint += parm[k][0];
500 }
501 }
502 }
503 }
504
505 /* If this is not the last iteration then check and see how the
506 * continuum estimates are converging. If they appear to be converging
507 * then let the next iteration be the last one. */
508 if (!lastone) {
509
510 cpl_size conv = 1;
511
512 for (cpl_size i = 0; i < *nbit; i++) {
513
514 if (parm[i][0] >= 0.) {
515
516 if (fabs(bitx[i] - bitl[i]) > 3.) conv = 0;
517
518 bitl[i] = bitx[i];
519 bitx[i] = 0;
520
521 /* FIXME Possible font of problems in the cast. Is it necessary ? */
522 double a = parm[i][3] - oldthr;
523 bitl[i] = CPL_MIN(bitl[i], (cpl_size)(a + (a < 0 ? -0.5 : 0.5)));
524
525 //bitl[i] = CPL_MIN(bitl[i], parm[i][3] - oldthr);
526 }
527 }
528
529 lastone = (conv || (iter == NITER - 1));
530
531 } else {
532 break;
533 }
534 }
535
536 /* Find the scaling if needed */
537 if (sumint == 0.) *nbit = 1;
538 else {
539 double ratio = total / sumint;
540 for (cpl_size i = 0; i < *nbit; i++) {
541 parm[i][0] = ratio * parm[i][0];
542 }
543 }
544
545 return CPL_ERROR_NONE;
546}
547
550/* ---------------------------------------------------------------------------*/
562/* ---------------------------------------------------------------------------*/
563static void moments_thr(ap_t *ap, double results[NPAR + 1], cpl_size ipk[2])
564{
565 /* Copy some stuff to local variables */
566 double fconst = ap->fconst;
567 double offset = ap->areal_offset;
568 plstruct *plarray = ap->plarray;
569 cpl_size np = ap->npl_pix;
570
571 /* Initialize a few things */
572 double xoff = xbar_start;
573 double yoff = ybar_start;
574 double xsum = 0.;
575 double ysum = 0.;
576 double xsum_w = 0.;
577 double ysum_w = 0.;
578 double wsum = 0.;
579 double xsumsq = 0.;
580 double ysumsq = 0.;
581 double tsum = 0.;
582 double xysum = 0.;
583 double tmax = plarray[0].z - curthr;
584
585 ipk[0] = plarray[0].x;
586 ipk[1] = plarray[0].y;
587
588 for (cpl_size i = 8; i < NPAR; i++) {
589 results[i] = 0.;
590 }
591
592 /* Do a moments analysis on an object */
593 cpl_size nnext = 0;
594 for (cpl_size i = 0; i < np; i++) {
595
596 double x = (double)(plarray[i].x) - xoff;
597 double y = (double)(plarray[i].y) - yoff;
598
599 double t = plarray[i].z - curthr;
600 double w = plarray[i].zsm - curthr;
601
602 if (w > nexthr) {
603 nnext++;
604 }
605
606 xsum += t * x;
607 ysum += t * y;
608 tsum += t;
609
610 xsum_w += w * t * x;
611 ysum_w += w * t * y;
612
613 wsum += w * t;
614
615 xsumsq += (x * x) * t;
616 ysumsq += (y * y) * t;
617
618 xysum += x * y * t;
619
620 update_ov(results + 8, t, oldthr, fconst, offset);
621
622 if (t > tmax) {
623
624 ipk[0] = plarray[i].x;
625 ipk[1] = plarray[i].y;
626
627 tmax = t;
628 }
629 }
630
631 /* Check that the total intensity is enough and if it is, then do
632 * the final results. Use negative total counts to signal an error */
633 if (tsum > 0.) {
634 results[0] = tsum;
635 } else {
636 results[0] = -1.;
637 tsum = 1.;
638 }
639
640 double xbar = xsum / tsum;
641 double ybar = ysum / tsum;
642
643 double sxx = CPL_MAX(0., (xsumsq / tsum - xbar * xbar));
644 double syy = CPL_MAX(0., (ysumsq / tsum - ybar * ybar));
645 double sxy = xysum / tsum - xbar * ybar;
646
647 wsum = CPL_MAX(1., wsum);
648
649 xbar = xsum_w / wsum;
650 ybar = ysum_w / wsum;
651
652 xbar += xoff;
653 ybar += yoff;
654
655 xbar = CPL_MAX(1., CPL_MIN(xbar, ap->lsiz));
656 ybar = CPL_MAX(1., CPL_MIN(ybar, ap->csiz));
657
658 /* Store the results now */
659 results[1] = xbar;
660 results[2] = ybar;
661 results[3] = curthr;
662 results[4] = sxx;
663 results[5] = sxy;
664 results[6] = syy;
665 results[7] = tmax;
666 results[NPAR] = ((nnext > ap->ipnop && nexthr < lasthr) ? 0 : 1);
667}
668
669/* ---------------------------------------------------------------------------*/
682/* ---------------------------------------------------------------------------*/
683static void update_ov(double iap[NAREAL], double t, double thresh, double fconst, double offset)
684{
685 /* if the intensity is positive */
686 if (t > 0.) {
687
688 /* update the relevant profile counts */
689 cpl_size nup = CPL_MAX(1, CPL_MIN(NAREAL, (cpl_size)(log(t + thresh) * fconst - offset) + 1));
690
691 for (cpl_size i = 0; i < nup; i++) {
692 iap[i] += 1.;
693 }
694 }
695}
696
697/* ---------------------------------------------------------------------------*/
710/* ---------------------------------------------------------------------------*/
711static void check_term( ap_t *ap, cpl_size *nobj, double parm[IMNUM][NPAR+1],
712 cpl_size peaks[IMNUM][2], cpl_size *toomany)
713{
714 /* Search through all possible parents */
715 *nobj = 0;
716 *toomany = 0;
717
718 double momresults[NPAR + 1];
719 cpl_size ipks[2];
720
721 for (cpl_size ip = 1; ip <= ap->maxip; ip++) {
722
723 if (ap->parent[ip].pnop != -1) {
724
725 if ( ap->parent[ip].pnop >= ap->ipnop
726 && ap->parent[ip].touch == 0)
727 {
728 hdrl_extract_data(ap, ip);
729
730 moments_thr(ap, momresults, ipks);
731 if (momresults[0] > 0.) {
732
733 if (*nobj == IMNUM - 1) {
734 *toomany = 1;
735 break;
736 }
737
738 for (cpl_size i = 0; i <= NPAR; i++) {
739 parm[*nobj][i] = momresults[i];
740 }
741
742 for (cpl_size i = 0; i < 2; i++) {
743 peaks[*nobj][i] = ipks[i];
744 }
745
746 (*nobj)++;
747 }
748 }
749
750 hdrl_restack(ap, ip);
751 }
752 }
753}
754
755static int cmp_plstruct(const void *a, const void *b) {
756
757 const plstruct *a_d = (const plstruct*)a;
758 const plstruct *b_d = (const plstruct*)b;
759
760 /* Descending order */
761 return -(a_d->zsm < b_d->zsm ? -1 : (a_d->zsm > b_d->zsm ? 1 : 0));
762}
void hdrl_apclust(ap_t *ap, cpl_size np, plstruct *plstr)
Detect multiple objects from a given Plessey array.
void hdrl_apinit(ap_t *ap)
Initialize the ap structure.
Definition: hdrl_cat_apio.c:54
void hdrl_apreinit(ap_t *ap)
Re-initialize the ap structure.
void hdrl_apclose(ap_t *ap)
Close ap structure.
cpl_error_code hdrl_overlp(ap_t *ap, double parm[IMNUM][NPAR], cpl_size *nbit, double xbar, double ybar, double total, cpl_size npix, double tmax)
Deblend overlapping images.
cpl_error_code hdrl_overlp_2orMore(ap_t *ap, double parm[IMNUM][NPAR], cpl_size *nbit, double xbar, double ybar, double total, cpl_size npix, double curthr_prev, double nexthr_prev, double lasthr_prev)
Deblend overlapping images 2 or more. If you know that exist more than 1 (come to the previous versio...
cpl_error_code hdrl_polynm(double xdat[], double xcor[], cpl_size n, double polycf[], cpl_size m, cpl_size ilim)
Work out the median seeing.
void hdrl_restack(ap_t *ap, cpl_size ip)
Free information for an object from the ap structure.
void hdrl_extract_data(ap_t *ap, cpl_size ip)
Put data into the Plessey array for an object.
cpl_error_code sort_array_f(void *a, cpl_size nE, cpl_size sE, f_compare f)
sort_array_f Core sort algorith that it's called with the other sort function. If you need to changed...