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