VIRCAM Pipeline 2.3.12
imcore_overlp.c
1/* $Id: imcore_overlp.c,v 1.3 2015/08/12 11:16:55 jim Exp $
2 *
3 * This file is part of the CASU Pipeline utilities
4 * Copyright (C) 2015 European Southern Observatory
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21/*
22 * $Author: jim $
23 * $Date: 2015/08/12 11:16:55 $
24 * $Revision: 1.3 $
25 * $Name: $
26 */
27
28#include <stdio.h>
29#include <stdlib.h>
30#include <math.h>
31
32#include <cpl.h>
33
34#include "ap.h"
35#include "util.h"
36#include "imcore.h"
37#include "floatmath.h"
38
39#define NITER 6
40
41static void moments_thr(ap_t *ap, float results[NPAR+1], int ipk[2]);
42static void sort_on_zsm_rev(int, plstruct *);
43static void update_ov(float iap[NAREAL], float t, float thresh, float fconst,
44 float offset);
45static void check_term(ap_t *, int *, float [IMNUM][NPAR+1], int [IMNUM][2],
46 int *);
47
48static float oldthr;
49static float curthr;
50static float nexthr;
51static float lasthr;
52static float xbar_start;
53static float ybar_start;
54
57/*---------------------------------------------------------------------------*/
97/*---------------------------------------------------------------------------*/
98
99extern void imcore_overlp(ap_t *ap, float parm[IMNUM][NPAR], int *nbit,
100 float xbar, float ybar, float total, int npix,
101 float tmax) {
102 plstruct *pl;
103 int npl,ipix,ipixo2,npl2,nbitprev,nobj,toomany,i,isnew,k,kk,j,ibitx[IMNUM];
104 int ibity[IMNUM],iwas,iupdate[IMNUM],npl3,iter,lastone,ic,jj;
105 int ii,conv,ipks[IMNUM][2];
106 float fconst,offset,tmul,smul,xintmn,itmaxlim,algthr,radmax,xb,yb,radius2;
107 float results[IMNUM][NPAR+1],distmax,dx,dy,parmnew[IMNUM][NPAR],sumint;
108 float xlevol,radold,slope,xx,xlevel,radius,xdat[NAREAL+1],xcor[NAREAL+1];
109 float dlbydr,wt,dist,xeff,polycf[3],ttt,radthr,delb,deli,ratio;
110 float bitx[IMNUM],bitl[IMNUM],sxx,syy;
111 ap_t ap2;
112
113 /* Initialise a few variables */
114
115 pl = ap->plarray;
116 npl = ap->npl_pix;
117 ipix = ap->ipnop;
118 oldthr = ap->thresh;
119 fconst = ap->fconst;
120 offset = ap->areal_offset;
121 xbar_start = xbar;
122 ybar_start = ybar;
123
124 /* Initialise some constants that you might need later */
125
126 tmul = 1.2589678; /* 1/4 mag deblending contour increment */
127 smul = 2.5; /* starting contour increment */
128 ipixo2 = MAX(2,(ipix + 1)/2); /* ipix is the minimum image pixel size */
129 xintmn = oldthr*ipixo2; /* minimum intensity for fragments */
130 itmaxlim = 0.9*tmax; /* upper limit deblending 90% of peak */
131 lasthr = itmaxlim;
132 algthr = logf(oldthr); /* Convenient bit of shorthand */
133 radmax = sqrtf(((float)npix)/CPL_MATH_PI); /* Isophotal size of input data array */
134
135 /* Get a maximum of IDBLIM points brighter than the new detection threshold
136 by reverse sorting the input array. If there are still more than IDBLIM
137 above the threshold, then revise the thresold until there aren't. Then
138 use the variable npl2 to stop the rest of the routine accessing any of
139 the fainter pixels in the list. This is to restrict processing time
140 for large extended objects */
141
142 curthr = smul*oldthr;
143 sort_on_zsm_rev(npl,pl);
144 while (1) {
145 npl2 = 0;
146 while (pl[npl2].zsm > curthr && npl2 < npl-1)
147 npl2++;
148 if (npl2 > IDBLIM)
149 curthr += oldthr;
150 else
151 break;
152 }
153
154 /* If there are fewer pixels above the new threshold than the minimum
155 specified in the input parameters, then you have no reason to be here */
156
157 if (npl2 < ipix) {
158 *nbit = 1;
159 return;
160 }
161
162 /* Get a new ap structure */
163
164 ap2.lsiz = ap->lsiz;
165 ap2.csiz = ap->csiz;
166 ap2.multiply = 1;
167 ap2.ipnop = ipixo2;
168 ap2.areal_offset = offset;
169 ap2.fconst = fconst;
170 ap2.mflag = cpl_calloc((ap2.lsiz)*(ap2.csiz),sizeof(unsigned char*));
171
172 /* Main analysis loop at new thresholds */
173
174 *nbit = 0;
175 nbitprev = 0;
176 imcore_apinit(&ap2);
177 while (1) {
178 nexthr = MAX(curthr+oldthr,curthr*tmul);
179
180 /* Locate objects in this cluster */
181
182 ap2.thresh = curthr;
183 imcore_apclust(&ap2,npl2,pl);
184 check_term(&ap2,&nobj,results,ipks,&toomany);
185 imcore_apreinit(&ap2);
186 if (nobj == 0)
187 break;
188
189 /* For each image check the number of points above the next threshold
190 and flag. Do a moments analysis of each object */
191
192 for (i = 0; i < nobj; i++) {
193
194 /* Ok, has this object already been detected? If so, then
195 load the new results into the parmnew array. We'll check
196 whether it's worthwhile updating the master parameters
197 list later */
198
199 isnew = 1;
200 xb = results[i][1];
201 yb = results[i][2];
202 sxx = MAX(1.0,results[i][4]);
203 syy = MAX(1.0,results[i][6]);
204 for (k = 0; k < nbitprev; k++) {
205 dx = xb - parm[k][1];
206 dy = yb - parm[k][2];
207 radius2 = dx*dx/sxx + dy*dy/syy;
208 if ((ibitx[k] == ipks[i][0] && ibity[k] == ipks[i][1]) ||
209 radius2 < 1.0) {
210 isnew = 0;
211 for (kk = 0; kk < NPAR; kk++)
212 parmnew[k][kk] = results[i][kk];
213 break;
214 }
215 }
216
217 /* If this is a new one and it's above the minimum threshold
218 then check to make sure you don't already have too many.
219 If you do, then flag this and break out to the next iteration.
220 If there is room for another, then store the moments analysis
221 profile */
222
223 if (isnew && results[i][0] > xintmn) {
224 if (*nbit >= IMNUM) {
225 *nbit = IMNUM;
226 toomany = 1;
227 break;
228 }
229 ibitx[*nbit] = ipks[i][0];
230 ibity[*nbit] = ipks[i][1];
231 for (kk = 0; kk < NPAR; kk++)
232 parm[*nbit][kk] = results[i][kk];
233 (*nbit)++;
234 }
235 } /* End of object loop */
236
237 /* If too many objects were found, then skip the next bit...otherwise
238 go through and update parameters if necessary. This block of
239 code is a bit of a place holder waiting for something better to
240 be worked out...*/
241
242 if (! toomany) {
243 if (*nbit > nbitprev && nbitprev > 0) {
244 for (i = 0; i < nbitprev; i++)
245 iupdate[i] = 0;
246 for (j = nbitprev; j < *nbit; j++) {
247 distmax = 0.0;
248 iwas = 0;
249 for (i = 0; i < nbitprev; i++) {
250 if (parmnew[i][0] > 0.0) {
251 dx = parmnew[i][1] - parm[i][1];
252 dy = parmnew[i][2] - parm[i][2];
253 radius2 = dx*dx + dy*dy;
254 if (radius2 > distmax) {
255 iwas = i;
256 distmax = radius2;
257 }
258 }
259 }
260 iupdate[iwas] = 1;
261 }
262 for (i = 0; i < nbitprev; i++)
263 if (iupdate[i] == 1 && parmnew[i][0] > 0.0)
264 for (j = 0; j < NPAR; j++)
265 parm[i][j] = parmnew[i][j];
266 }
267
268 /* Reset the update flag and prepare for next iteration*/
269
270 for (i = 0; i <= *nbit; i++)
271 parmnew[i][0] = -1.0;
272 nbitprev = *nbit;
273 }
274
275 /* Where do we cut in the list now? */
276
277
278 npl3 = 0;
279 while (pl[npl3].zsm > nexthr && npl3 < npl2-1)
280 npl3++;
281 npl2 = npl3;
282
283 /* Now, do we need to move onto the next threshold? */
284
285 if (npl2 == 0 || toomany || nexthr >= itmaxlim)
286 break;
287
288 /* If so, then reset some variables and continue */
289
290 curthr = nexthr;
291
292 } /* End of main analysis loop */
293
294 /* Free up some workspace */
295
296 cpl_free(ap2.mflag);
297 imcore_apclose(&ap2);
298
299 /* If there is only one then we can exit now */
300
301 if (*nbit == 1)
302 return;
303
304 /* Find out which images terminated properly and remove those that didn't */
305 j = -1;
306 for (k = 0; k < *nbit; k++) {
307 /* Commented this out as checking for terminations seems to miss some
308 if the total flux above the threshold for an object is negative */
309
310/* if (ibitl[k] == 1 && parm[k][0] > xintmn) { */
311 if (parm[k][0] > xintmn) {
312 j++;
313 if (j != k)
314 for (i = 0; i < NPAR; i++)
315 parm[j][i] = parm[k][i];
316 }
317 }
318 *nbit = j + 1;
319 for (j = 0; j < *nbit; j++) {
320 bitx[j] = 0.0;
321 bitl[j] = 0.0;
322 }
323
324 /* For each image find true areal profile levels and iterate to find
325 local continuum */
326
327 iter = 0;
328 sumint = 0.0;
329 lastone = 0;
330 while (iter < NITER) {
331 iter++;
332
333 /* Loop for each of the objects and create a level vs radius array */
334
335 for (k = 0; k < *nbit; k++) {
336 if (parm[k][0] < 0.0)
337 continue;
338 xlevol = logf(parm[k][7] + parm[k][3] - bitl[k]); /* Pk + newthresh - cont */
339 xlevel = xlevol;
340 radold = 0.0;
341 radius = radold;
342 slope = 1.0;
343 ic = 0;
344 for (i = 1; i <= NAREAL; i++) {
345 jj = NPAR - i;
346 ii = NAREAL - i;
347 xx = (float)ii + offset;
348 if (parm[k][jj] > 0.5) {
349 if (ii == 0)
350 xlevel = logf(parm[k][3] - bitl[k] + 0.5);
351 else
352 xlevel = logf(powf(2.0,xx) - oldthr + parm[k][3] -
353 bitl[k] - 0.5);
354 radius = sqrt(parm[k][jj]/CPL_MATH_PI);
355 xdat[ic] = xlevel;
356 xcor[ic++] = radius;
357 dlbydr = (xlevol - xlevel)/MAX(0.01,radius-radold);
358 wt = MIN(1.0,MAX((radius-radold)*5.0,0.1));
359 slope = (1.0 - 0.5*wt)*slope + 0.5*wt*MIN(5.0,dlbydr);
360 radold = radius;
361 xlevol = xlevel;
362 }
363 }
364
365 /* If this is not the last iteration then work out the effect
366 on the local continuum from each image */
367
368 if (! lastone) {
369 for (i = 0; i < *nbit; i++) {
370 if (parm[i][0] >= 0.0 && i != k) {
371 dx = parm[k][1] - parm[i][1];
372 dy = parm[k][2] - parm[i][2];
373 dist = sqrtf(dx*dx + dy*dy);
374 xeff = xlevel - MAX(0.0,MIN(50.0,slope*(dist-radius)));
375 bitx[i] += expf(xeff);
376 }
377 }
378
379 /* If this is the last iteration loop, then update the parameters
380 before exiting*/
381
382 } else {
383 if (ic > 2) {
384 imcore_polynm(xdat,xcor,ic,polycf,3,0);
385 ttt = polycf[1] + 2.0*polycf[2]*radius;
386 } else
387 ttt = 0.0;
388 slope = MAX(0.1,MAX(-ttt,slope));
389 radthr = radius + (xlevel - algthr)/slope;
390 if (radthr > radmax) {
391 slope = 1.0;
392 radthr = radmax;
393 }
394
395 /* Pixel area */
396
397 delb = parm[k][8]*(parm[k][3] - bitl[k]);
398 parm[k][8] = CPL_MATH_PI*radthr*radthr;
399
400 /* Peak height */
401
402 parm[k][7] += (parm[k][3] - bitl[k]);
403
404 /* Intensity */
405
406 deli = 2.0*CPL_MATH_PI*((parm[k][3] - bitl[k])*(1.0 + slope*radius) -
407 oldthr*(1.0 + slope*radthr))/(slope*slope);
408 parm[k][0] += delb + MAX(0.0,deli);
409 for (i = 0; i < 7; i++)
410 parm[k][i+9] = -1.0;
411 if (parm[k][0] > xintmn)
412 sumint += parm[k][0];
413 }
414 }
415
416 /* If this is not the last iteration then check and see how the
417 continuum estimates are converging. If they appear to be converging
418 then let the next iteration be the last one. */
419
420 if (! lastone) {
421 conv = 1;
422 for (i = 0; i < *nbit; i++) {
423 if (parm[i][0] >= 0.0) {
424 if (fabs(bitx[i] - bitl[i]) > 3.0)
425 conv = 0;
426 bitl[i] = bitx[i];
427 bitx[i] = 0;
428 bitl[i] = MIN(bitl[i],NINT(parm[i][3]-oldthr));
429 }
430 }
431 lastone = (conv || (iter == NITER-1));
432 } else {
433 break;
434 }
435 }
436
437 /* Find the scaling if needed */
438
439 if (sumint == 0.0) {
440 *nbit = 1;
441 return;
442 } else
443 ratio = total/sumint;
444 for (i = 0; i < *nbit; i++)
445 parm[i][0] = ratio*parm[i][0];
446}
447
448/*---------------------------------------------------------------------------*/
468/*---------------------------------------------------------------------------*/
469
470static void sort_on_zsm_rev(int npts, plstruct *pts) {
471 int i,j,ii,jj,ifin;
472 plstruct tmp;
473
474 jj = 4;
475 while (jj < npts)
476 jj = 2*jj;
477 jj = MIN(npts,(3*jj)/4-1);
478 while (jj > 1) {
479 jj = jj/2;
480 ifin = npts - jj;
481 for (ii = 0; ii < ifin; ii++) {
482 i = ii;
483 j = i + jj;
484 if (pts[i].zsm > pts[j].zsm)
485 continue;
486 tmp = pts[j];
487 do {
488 pts[j] = pts[i];
489 j = i;
490 i = i - jj;
491 if (i < 0)
492 break;
493 } while (pts[i].zsm <= tmp.zsm);
494 pts[j] = tmp;
495 }
496 }
497}
498
499/*---------------------------------------------------------------------------*/
521/*---------------------------------------------------------------------------*/
522
523static void moments_thr(ap_t *ap, float results[NPAR+1], int ipk[2]) {
524 int i,np,nnext;
525 float x,y,xoff,yoff,xsum,ysum,xsumsq,ysumsq,tsum,xysum,t,tmax,twelfth;
526 float xbar,ybar,sxx,syy,sxy,fconst,offset,xsum_w,ysum_w,wsum,w;
527 plstruct *plarray;
528
529 /* Copy some stuff to local variables */
530
531 fconst = ap->fconst;
532 offset = ap->areal_offset;
533 plarray = ap->plarray;
534 np = ap->npl_pix;
535
536 /* Initialise a few things */
537
538 xoff = xbar_start;
539 yoff = ybar_start;
540 xsum = 0.0;
541 ysum = 0.0;
542 xsum_w = 0.0;
543 ysum_w = 0.0;
544 wsum = 0.0;
545 xsumsq = 0.0;
546 ysumsq = 0.0;
547 tsum = 0.0;
548 xysum = 0.0;
549 tmax = plarray[0].z - curthr;
550 ipk[0] = plarray[0].x;
551 ipk[1] = plarray[0].y;
552 twelfth = 1.0/12.0;
553 for (i = 8; i < NPAR; i++)
554 results[i] = 0.0;
555
556 /* Do a moments analysis on an object */
557
558 nnext = 0;
559 for (i = 0; i < np; i++) {
560 x = (float)plarray[i].x - xoff;
561 y = (float)plarray[i].y - yoff;
562 t = plarray[i].z - curthr;
563 w = plarray[i].zsm - curthr;
564 if (w > nexthr)
565 nnext++;
566 xsum += t*x;
567 ysum += t*y;
568 tsum += t;
569 xsum_w += w*t*x;
570 ysum_w += w*t*y;
571 wsum += w*t;
572 xsumsq += (x*x + twelfth)*t;
573 ysumsq += (y*y + twelfth)*t;
574 xysum += x*y*t;
575 update_ov(results+8,t,oldthr,fconst,offset);
576 if (t > tmax) {
577 ipk[0] = plarray[i].x;
578 ipk[1] = plarray[i].y;
579 tmax = t;
580 }
581 }
582
583 /* Check that the total intensity is enough and if it is, then do
584 the final results. Use negative total counts to signal an error */
585
586 if (tsum > 0.0) {
587 results[0] = tsum;
588 } else {
589 results[0] = -1.0;
590 tsum = 1.0;
591 }
592 xbar = xsum/tsum;
593 ybar = ysum/tsum;
594 sxx = MAX(0.0,(xsumsq/tsum-xbar*xbar));
595 syy = MAX(0.0,(ysumsq/tsum-ybar*ybar));
596 sxy = xysum/tsum - xbar*ybar;
597 wsum = MAX(1.0,wsum);
598 xbar = xsum_w/wsum;
599 ybar = ysum_w/wsum;
600 xbar += xoff;
601 ybar += yoff;
602 xbar = MAX(1.0,MIN(xbar,ap->lsiz));
603 ybar = MAX(1.0,MIN(ybar,ap->csiz));
604
605 /* Store the results now */
606
607 results[1] = xbar;
608 results[2] = ybar;
609 results[3] = curthr;
610 results[4] = sxx;
611 results[5] = sxy;
612 results[6] = syy;
613 results[7] = tmax;
614 results[NPAR] = ((nnext > ap->ipnop && nexthr < lasthr) ? 0 : 1);
615}
616
617/*---------------------------------------------------------------------------*/
642/*---------------------------------------------------------------------------*/
643
644static void update_ov(float iap[NAREAL], float t, float thresh, float fconst,
645 float offset) {
646 int nup,i;
647
648 /* Get out of here if the intensity is too small */
649
650 if (t <= 0.0)
651 return;
652
653 /* Otherwise update the relevant profile counts */
654
655 nup = MAX(1,MIN(NAREAL,(int)(logf(t+thresh)*fconst-offset)+1));
656 for (i = 0; i < nup; i++)
657 iap[i] += 1.0;
658}
659
660/*---------------------------------------------------------------------------*/
686/*---------------------------------------------------------------------------*/
687
688static void check_term(ap_t *ap, int *nobj, float parm[IMNUM][NPAR+1],
689 int peaks[IMNUM][2], int *toomany) {
690 int ip,i,ipks[2];
691 float momresults[NPAR+1];
692
693 /* Search through all possible parents */
694
695 *nobj = 0;
696 *toomany = 0;
697 for (ip = 1; ip <= ap->maxip; ip++) {
698 if (ap->parent[ip].pnop != -1) {
699/* if (ap->parent[ip].pnop == ap->parent[ip].growing) { */
700
701 /* That's a termination: */
702
703 if ((ap->parent[ip].pnop >= ap->ipnop &&
704 ap->parent[ip].touch == 0)) {
705 imcore_extract_data(ap,ip);
706 moments_thr(ap,momresults,ipks);
707 if (momresults[0] > 0.0) {
708 if (*nobj == IMNUM-1) {
709 *toomany = 1;
710 break;
711 }
712 for (i = 0; i <= NPAR; i++)
713 parm[*nobj][i] = momresults[i];
714 for (i = 0; i < 2; i++)
715 peaks[*nobj][i] = ipks[i];
716 (*nobj)++;
717 }
718 }
719 imcore_restack(ap,ip);
720/* } else { */
721
722/* /\* This parent still active: *\/ */
723
724/* ap->parent[ip].growing = ap->parent[ip].pnop; */
725/* } */
726 }
727 }
728}
729
732/*
733
734$Log: imcore_overlp.c,v $
735Revision 1.3 2015/08/12 11:16:55 jim
736Modified procedure names to protect namespace
737
738Revision 1.2 2015/08/07 13:06:54 jim
739Fixed copyright to ESO
740
741Revision 1.1.1.1 2015/06/12 10:44:32 jim
742Initial import
743
744Revision 1.2 2014/04/09 09:09:51 jim
745Detabbed
746
747Revision 1.1.1.1 2013/08/27 12:07:48 jim
748Imported
749
750
751*/
void imcore_apclust(ap_t *ap, int np, plstruct *plstr)
Detect multiple objects from a given Plessey array.
Definition: apclust.c:71
void imcore_extract_data(ap_t *ap, int ip)
Put data into the Plessey array for an object.
Definition: terminate.c:250
void imcore_apclose(ap_t *ap)
Close ap structure.
Definition: apinit.c:186
void imcore_restack(ap_t *ap, int ip)
Free information for an object from the ap structure.
Definition: terminate.c:64
void imcore_apreinit(ap_t *ap)
Re-initialise the ap structure.
Definition: apinit.c:142
void imcore_overlp(ap_t *ap, float parm[IMNUM][NPAR], int *nbit, float xbar, float ybar, float total, int npix, float tmax)
Deblend overlapping images.
Definition: imcore_overlp.c:99
void imcore_apinit(ap_t *ap)
Initialise the ap structure.
Definition: apinit.c:65