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 
41 static void moments_thr(ap_t *ap, float results[NPAR+1], int ipk[2]);
42 static void sort_on_zsm_rev(int, plstruct *);
43 static void update_ov(float iap[NAREAL], float t, float thresh, float fconst,
44  float offset);
45 static void check_term(ap_t *, int *, float [IMNUM][NPAR+1], int [IMNUM][2],
46  int *);
47 
48 static float oldthr;
49 static float curthr;
50 static float nexthr;
51 static float lasthr;
52 static float xbar_start;
53 static float ybar_start;
54 
57 /*---------------------------------------------------------------------------*/
97 /*---------------------------------------------------------------------------*/
98 
99 extern 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 
470 static 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 
523 static 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 
644 static 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 
688 static 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 $
735 Revision 1.3 2015/08/12 11:16:55 jim
736 Modified procedure names to protect namespace
737 
738 Revision 1.2 2015/08/07 13:06:54 jim
739 Fixed copyright to ESO
740 
741 Revision 1.1.1.1 2015/06/12 10:44:32 jim
742 Initial import
743 
744 Revision 1.2 2014/04/09 09:09:51 jim
745 Detabbed
746 
747 Revision 1.1.1.1 2013/08/27 12:07:48 jim
748 Imported
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