File indexing completed on 2024-04-28 15:10:11

0001 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
0002 
0003     This file is part of SEP
0004 
0005     SPDX-FileCopyrightText: 1993-2011 Emmanuel Bertin -- IAP /CNRS/UPMC
0006     SPDX-FileCopyrightText: 2014 SEP developers
0007 
0008     SPDX-License-Identifier: LGPL-3.0-or-later
0009 */
0010 
0011 /* Note: was scan.c in SExtractor. */
0012 
0013 #include <math.h>
0014 #include <stdio.h>
0015 #include <stdlib.h>
0016 #include <string.h>
0017 #include "sep.h"
0018 #include "sepcore.h"
0019 #include "extract.h"
0020 
0021 #define DETECT_MAXAREA 0             /* replaces prefs.ext_maxarea */
0022 #define WTHRESH_CONVFAC 1e-4         /* Factor to apply to weights when */
0023                          /* thresholding filtered weight-maps */
0024 
0025 /* globals */
0026 int plistexist_cdvalue, plistexist_thresh, plistexist_var;
0027 int plistoff_value, plistoff_cdvalue, plistoff_thresh, plistoff_var;
0028 int plistsize;
0029 size_t extract_pixstack = 1000000;
0030 
0031 /* get and set pixstack */
0032 void sep_set_extract_pixstack(size_t val)
0033 {
0034   extract_pixstack = val;
0035 }
0036 
0037 size_t sep_get_extract_pixstack()
0038 {
0039   return extract_pixstack;
0040 }
0041 
0042 int sortit(infostruct *info, objliststruct *objlist, int minarea,
0043        objliststruct *finalobjlist,
0044        int deblend_nthresh, double deblend_mincont, double gain);
0045 void plistinit(int hasconv, int hasvar);
0046 void clean(objliststruct *objlist, double clean_param, int *survives);
0047 int convert_to_catalog(objliststruct *objlist, int *survives,
0048                        sep_catalog *cat, int w, int include_pixels);
0049 
0050 int arraybuffer_init(arraybuffer *buf, void *arr, int dtype, int w, int h,
0051                      int bufw, int bufh);
0052 void arraybuffer_readline(arraybuffer *buf);
0053 void arraybuffer_free(arraybuffer *buf);
0054 
0055 /********************* array buffer functions ********************************/
0056 
0057 /* initialize buffer */
0058 /* bufw must be less than or equal to w */
0059 int arraybuffer_init(arraybuffer *buf, void *arr, int dtype, int w, int h,
0060                      int bufw, int bufh)
0061 {
0062   int status, yl;
0063   status = RETURN_OK;
0064 
0065   /* data info */
0066   buf->dptr = arr;
0067   buf->dw = w;
0068   buf->dh = h;
0069 
0070   /* buffer array info */
0071   buf->bptr = NULL;
0072   QMALLOC(buf->bptr, PIXTYPE, bufw*bufh, status);
0073   buf->bw = bufw;
0074   buf->bh = bufh;
0075 
0076   /* pointers to within buffer */
0077   buf->midline = buf->bptr + bufw*(bufh/2);  /* ptr to middle buffer line */
0078   buf->lastline = buf->bptr + bufw*(bufh-1);  /* ptr to last buffer line */
0079 
0080   status = get_array_converter(dtype, &(buf->readline), &(buf->elsize));
0081   if (status != RETURN_OK)
0082     goto exit;
0083 
0084   /* initialize yoff */
0085   buf->yoff = -bufh;
0086 
0087   /* read in lines until the first data line is one line above midline */
0088   for (yl=0; yl < bufh - bufh/2 - 1; yl++)
0089     arraybuffer_readline(buf);
0090 
0091   return status;
0092 
0093  exit:
0094   free(buf->bptr);
0095   buf->bptr = NULL;
0096   return status;
0097 }
0098 
0099 /* read a line into the buffer at the top, shifting all lines down one */
0100 void arraybuffer_readline(arraybuffer *buf)
0101 {
0102   PIXTYPE *line;
0103   int y;
0104 
0105   /* shift all lines down one */
0106   for (line = buf->bptr; line < buf->lastline; line += buf->bw)
0107     memcpy(line, line + buf->bw, sizeof(PIXTYPE) * buf->bw);
0108 
0109   /* which image line now corresponds to the last line in buffer? */
0110   buf->yoff++;
0111   y = buf->yoff + buf->bh - 1;
0112 
0113   if (y < buf->dh)
0114     buf->readline(buf->dptr + buf->elsize * buf->dw * y, buf->dw,
0115                   buf->lastline);
0116 
0117   return;
0118 }
0119 
0120 void arraybuffer_free(arraybuffer *buf)
0121 {
0122   free(buf->bptr);
0123   buf->bptr = NULL;
0124 }
0125 
0126 /* apply_mask_line: Apply the mask to the image and noise buffers.
0127  *
0128  * If convolution is off, masked values should simply be not
0129  * detected. For this, would be sufficient to either set data to zero or
0130  * set noise (if present) to infinity.
0131  *
0132  * If convolution is on, strictly speaking, a masked (unknown) pixel
0133  * should "poison" the convolved value whenever it is present in the
0134  * convolution kernel (e.g., NaN behavior). However, practically we'd
0135  * rather use a "best guess" for the value. Without doing
0136  * interpolation from neighbors, 0 is the best guess (assuming image
0137  * is background subtracted).
0138  *
0139  * For the purpose of the full matched filter, we should set noise = infinity.
0140  *
0141  * So, this routine sets masked pixels to zero in the image buffer and
0142  * infinity in the noise buffer (if present). It affects the first 
0143  */
0144 void apply_mask_line(arraybuffer *mbuf, arraybuffer *imbuf, arraybuffer *nbuf)
0145 {
0146   int i;
0147   
0148   for (i=0; i<mbuf->bw; i++)
0149     {
0150       if (mbuf->lastline[i] > 0.0)
0151         {
0152           imbuf->lastline[i] = 0.0;
0153           if (nbuf)
0154             nbuf->lastline[i] = BIG;
0155         }
0156     }
0157 }
0158 
0159 /****************************** extract **************************************/
0160 int sep_extract(sep_image *image, float thresh, int thresh_type,
0161                 int minarea, float *conv, int convw, int convh,
0162         int filter_type, int deblend_nthresh, double deblend_cont,
0163         int clean_flag, double clean_param,
0164         sep_catalog **catalog)
0165 {
0166   arraybuffer       dbuf, nbuf, mbuf;
0167   infostruct        curpixinfo, initinfo, freeinfo;
0168   objliststruct     objlist;
0169   char              newmarker;
0170   size_t            mem_pixstack;
0171   int               nposize, oldnposize;
0172   int               w, h;
0173   int               co, i, luflag, pstop, xl, xl2, yl, cn;
0174   int               stacksize, convn, status;
0175   int               bufh;
0176   int               isvarthresh, isvarnoise;
0177   short             trunflag;
0178   PIXTYPE           relthresh, cdnewsymbol, pixvar, pixsig;
0179   float             sum;
0180   pixstatus         cs, ps;
0181 
0182   infostruct        *info, *store;
0183   objliststruct     *finalobjlist;
0184   pliststruct       *pixel, *pixt;
0185   char              *marker;
0186   PIXTYPE           *scan, *cdscan, *wscan, *dummyscan;
0187   PIXTYPE           *sigscan, *workscan;
0188   float             *convnorm;
0189   int               *start, *end, *survives;
0190   pixstatus         *psstack;
0191   char              errtext[512];
0192   sep_catalog       *cat;
0193 
0194   status = RETURN_OK;
0195   pixel = NULL;
0196   convnorm = NULL;
0197   scan = wscan = cdscan = dummyscan = NULL;
0198   sigscan = workscan = NULL;
0199   info = NULL;
0200   store = NULL;
0201   marker = NULL;
0202   psstack = NULL;
0203   start = end = NULL;
0204   finalobjlist = NULL;
0205   survives = NULL;
0206   cat = NULL;
0207   convn = 0;
0208   sum = 0.0;
0209   w = image->w;
0210   h = image->h;
0211   isvarthresh = 0;
0212   relthresh = 0.0;
0213   pixvar = 0.0;
0214   pixsig = 0.0;
0215   isvarnoise = 0;
0216   
0217   mem_pixstack = sep_get_extract_pixstack();
0218 
0219   /* seed the random number generator consistently on each call to get
0220    * consistent results. rand() is used in deblending. */
0221   srand(1);
0222 
0223   /* Noise characteristics of the image: None, scalar or variable? */
0224   if (image->noise_type == SEP_NOISE_NONE) { } /* nothing to do */
0225   else if (image->noise == NULL) {
0226     /* noise is constant; we can set pixel noise now. */
0227     if (image->noise_type == SEP_NOISE_STDDEV) {
0228       pixsig = image->noiseval;
0229       pixvar = pixsig * pixsig;
0230     }
0231     else if (image->noise_type == SEP_NOISE_VAR) {
0232       pixvar = image->noiseval;
0233       pixsig = sqrt(pixvar);
0234     }
0235     else {
0236       return UNKNOWN_NOISE_TYPE;
0237     }
0238   }
0239   else {
0240     /* noise is variable; we deal with setting pixvar and pixsig at each
0241      * pixel. */
0242     isvarnoise = 1;
0243   }
0244 
0245   /* Deal with relative thresholding. (For an absolute threshold
0246   *  nothing needs to be done, as `thresh` should already contain the constant
0247   *  threshold, and `isvarthresh` is already 0.) */
0248   if (thresh_type == SEP_THRESH_REL) {
0249 
0250     /* The image must have noise information. */
0251     if (image->noise_type == SEP_NOISE_NONE) return RELTHRESH_NO_NOISE;
0252 
0253     isvarthresh = isvarnoise;  /* threshold is variable if noise is */
0254     if (isvarthresh) {
0255       relthresh = thresh; /* used below to set `thresh` for each pixel. */
0256     }
0257     else {
0258       /* thresh is constant; convert relative threshold to absolute */
0259       thresh *= pixsig;
0260     }
0261   }
0262 
0263   /* this is input `thresh` regardless of thresh_type. */
0264   objlist.thresh = thresh;
0265 
0266   /*Allocate memory for buffers */
0267   stacksize = w+1;
0268   QMALLOC(info, infostruct, stacksize, status);
0269   QCALLOC(store, infostruct, stacksize, status);
0270   QMALLOC(marker, char, stacksize, status);
0271   QMALLOC(dummyscan, PIXTYPE, stacksize, status);
0272   QMALLOC(psstack, pixstatus, stacksize, status);
0273   QCALLOC(start, int, stacksize, status);
0274   QMALLOC(end, int, stacksize, status);
0275   if ((status = lutzalloc(w, h)) != RETURN_OK)
0276     goto exit;
0277   if ((status = allocdeblend(deblend_nthresh)) != RETURN_OK)
0278     goto exit;
0279 
0280   /* Initialize buffers for input array(s).
0281    * The buffer size depends on whether or not convolution is active.
0282    * If not convolving, the buffer size is just a single line. If convolving,
0283    * the buffer height equals the height of the convolution kernel.
0284    */
0285   bufh = conv ? convh : 1;
0286   status = arraybuffer_init(&dbuf, image->data, image->dtype, w, h, stacksize,
0287                             bufh);
0288   if (status != RETURN_OK) goto exit;
0289   if (isvarnoise) {
0290       status = arraybuffer_init(&nbuf, image->noise, image->ndtype, w, h,
0291                                 stacksize, bufh);
0292       if (status != RETURN_OK) goto exit;
0293     }
0294   if (image->mask) {
0295       status = arraybuffer_init(&mbuf, image->mask, image->mdtype, w, h,
0296                                 stacksize, bufh);
0297       if (status != RETURN_OK) goto exit;
0298     }
0299 
0300   /* `scan` (or `wscan`) is always a pointer to the current line being
0301    * processed. It might be the only line in the buffer, or it might be the 
0302    * middle line. */
0303   scan = dbuf.midline;
0304   if (isvarnoise)
0305     wscan = nbuf.midline;
0306 
0307   /* More initializations */
0308   initinfo.pixnb = 0;
0309   initinfo.flag = 0;
0310   initinfo.firstpix = initinfo.lastpix = -1;
0311 
0312   for (xl=0; xl<stacksize; xl++)
0313     {
0314       marker[xl]  = 0;
0315       dummyscan[xl] = -BIG;
0316     }
0317 
0318   co = pstop = 0;
0319   objlist.nobj = 1;
0320   curpixinfo.pixnb = 1;
0321 
0322   /* Init finalobjlist */
0323   QMALLOC(finalobjlist, objliststruct, 1, status);
0324   finalobjlist->obj = NULL;
0325   finalobjlist->plist = NULL;
0326   finalobjlist->nobj = finalobjlist->npix = 0;
0327 
0328 
0329   /* Allocate memory for the pixel list */
0330   plistinit((conv != NULL), (image->noise_type != SEP_NOISE_NONE));
0331   if (!(pixel = objlist.plist = malloc(nposize=mem_pixstack*plistsize)))
0332     {
0333       status = MEMORY_ALLOC_ERROR;
0334       goto exit;
0335     }
0336 
0337   /*----- at the beginning, "free" object fills the whole pixel list */
0338   freeinfo.firstpix = 0;
0339   freeinfo.lastpix = nposize-plistsize;
0340   pixt = pixel;
0341   for (i=plistsize; i<nposize; i += plistsize, pixt += plistsize)
0342     PLIST(pixt, nextpix) = i;
0343   PLIST(pixt, nextpix) = -1;
0344 
0345   /* can only use a matched filter when convolving and when there is a noise
0346    * array */
0347   if (!(conv && isvarnoise))
0348     filter_type = SEP_FILTER_CONV;
0349 
0350   if (conv)
0351     {
0352       /* allocate memory for convolved buffers */
0353       QMALLOC(cdscan, PIXTYPE, stacksize, status);
0354       if (filter_type == SEP_FILTER_MATCHED)
0355         {
0356           QMALLOC(sigscan, PIXTYPE, stacksize, status);
0357           QMALLOC(workscan, PIXTYPE, stacksize, status);
0358         }
0359 
0360       /* normalize the filter */
0361       convn = convw * convh;
0362       QMALLOC(convnorm, PIXTYPE, convn, status);
0363       for (i=0; i<convn; i++)
0364     sum += fabs(conv[i]);
0365       for (i=0; i<convn; i++)
0366     convnorm[i] = conv[i] / sum;
0367     }
0368 
0369   /*----- MAIN LOOP ------ */
0370   for (yl=0; yl<=h; yl++)
0371     {
0372 
0373       ps = COMPLETE;
0374       cs = NONOBJECT;
0375     
0376       /* Need an empty line for Lutz' algorithm to end gracely */
0377       if (yl==h)
0378     {
0379       if (conv)
0380         {
0381           free(cdscan);
0382           cdscan = NULL;
0383               if (filter_type == SEP_FILTER_MATCHED)
0384               {
0385                   for (xl=0; xl<stacksize; xl++)
0386                   {
0387                       sigscan[xl] = -BIG;
0388                   }
0389               }
0390         }
0391       cdscan = dummyscan;
0392     }
0393 
0394       else
0395     {
0396           arraybuffer_readline(&dbuf);
0397           if (isvarnoise)
0398             arraybuffer_readline(&nbuf);
0399           if (image->mask)
0400             {
0401               arraybuffer_readline(&mbuf);
0402               apply_mask_line(&mbuf, &dbuf, (isvarnoise? &nbuf: NULL));
0403             }
0404 
0405       /* filter the lines */
0406       if (conv)
0407         {
0408               status = convolve(&dbuf, yl, convnorm, convw, convh, cdscan);
0409               if (status != RETURN_OK)
0410                 goto exit;
0411 
0412           if (filter_type == SEP_FILTER_MATCHED)
0413                 {
0414                   status = matched_filter(&dbuf, &nbuf, yl, convnorm, convw,
0415                                           convh, workscan, sigscan,
0416                                           image->noise_type);
0417 
0418                   if (status != RETURN_OK)
0419                     goto exit;
0420                 }
0421             }
0422       else
0423         {
0424           cdscan = scan;
0425         }     
0426     }
0427       
0428       trunflag = (yl==0 || yl==h-1)? SEP_OBJ_TRUNC: 0;
0429       
0430       for (xl=0; xl<=w; xl++)
0431     {
0432       if (xl == w)
0433         cdnewsymbol = -BIG;
0434       else
0435         cdnewsymbol = cdscan[xl];
0436 
0437       newmarker = marker[xl];  /* marker at this pixel */
0438       marker[xl] = 0;
0439 
0440       curpixinfo.flag = trunflag;
0441 
0442           /* set pixel variance/noise based on noise array */
0443           if (isvarthresh) {
0444             if (xl == w || yl == h) {
0445               pixsig = pixvar = 0.0;
0446             }
0447             else if (image->noise_type == SEP_NOISE_VAR) {
0448               pixvar = wscan[xl];
0449               pixsig = sqrt(pixvar);
0450             }
0451             else if (image->noise_type == SEP_NOISE_STDDEV) {
0452               pixsig = wscan[xl];
0453               pixvar = pixsig * pixsig;
0454             }
0455             else {
0456               status = UNKNOWN_NOISE_TYPE;
0457               goto exit;
0458             }
0459             
0460             /* set `thresh` (This is needed later, even
0461              * if filter_type is SEP_FILTER_MATCHED */
0462             thresh = relthresh * pixsig;
0463           }
0464 
0465           /* luflag: is pixel above thresh (Y/N)? */
0466           if (filter_type == SEP_FILTER_MATCHED)
0467             luflag = ((xl != w) && (sigscan[xl] > relthresh))? 1: 0;
0468           else
0469             luflag = cdnewsymbol > thresh? 1: 0;
0470 
0471       if (luflag)
0472         {
0473           /* flag the current object if we're near the image bounds */
0474           if (xl==0 || xl==w-1)
0475         curpixinfo.flag |= SEP_OBJ_TRUNC;
0476           
0477           /* point pixt to first free pixel in pixel list */
0478           /* and increment the "first free pixel" */
0479           pixt = pixel + (cn=freeinfo.firstpix);
0480           freeinfo.firstpix = PLIST(pixt, nextpix);
0481           curpixinfo.lastpix = curpixinfo.firstpix = cn;
0482 
0483           /* set values for the new pixel */ 
0484           PLIST(pixt, nextpix) = -1;
0485           PLIST(pixt, x) = xl;
0486           PLIST(pixt, y) = yl;
0487           PLIST(pixt, value) = scan[xl];
0488           if (PLISTEXIST(cdvalue))
0489         PLISTPIX(pixt, cdvalue) = cdnewsymbol;
0490           if (PLISTEXIST(var))
0491         PLISTPIX(pixt, var) = pixvar;
0492           if (PLISTEXIST(thresh))
0493         PLISTPIX(pixt, thresh) = thresh;
0494 
0495           /* Check if we have run out of free pixels in objlist.plist */
0496           if (freeinfo.firstpix==freeinfo.lastpix)
0497         {
0498           status = PIXSTACK_FULL;
0499           sprintf(errtext,
0500               "The limit of %d active object pixels over the "
0501               "detection threshold was reached. Check that "
0502               "the image is background subtracted and the "
0503               "detection threshold is not too low. If you "
0504                           "need to increase the limit, use "
0505                           "set_extract_pixstack.",
0506               (int)mem_pixstack);
0507           put_errdetail(errtext);
0508           goto exit;
0509 
0510           /* The code in the rest of this block increases the
0511            * stack size as needed. Currently, it is never
0512            * executed. This is because it isn't clear that
0513            * this is a good idea: most times when the stack
0514            * overflows it is due to user error: too-low
0515            * threshold or image not background subtracted. */
0516 
0517           /* increase the stack size */
0518           oldnposize = nposize;
0519           mem_pixstack = (int)(mem_pixstack * 2);
0520           nposize = mem_pixstack * plistsize;
0521           pixel = (pliststruct *)realloc(pixel, nposize);
0522           objlist.plist = pixel;
0523           if (!pixel)
0524             {
0525               status = MEMORY_ALLOC_ERROR;
0526               goto exit;
0527             }
0528 
0529           /* set next free pixel to the start of the new block 
0530            * and link up all the pixels in the new block */
0531           PLIST(pixel+freeinfo.firstpix, nextpix) = oldnposize;
0532           pixt = pixel + oldnposize;
0533           for (i=oldnposize + plistsize; i<nposize;
0534                i += plistsize, pixt += plistsize)
0535             PLIST(pixt, nextpix) = i;
0536           PLIST(pixt, nextpix) = -1;
0537 
0538           /* last free pixel is now at the end of the new block */
0539           freeinfo.lastpix = nposize - plistsize;
0540         }
0541           /*------------------------------------------------------------*/
0542 
0543           /* if the current status on this line is not already OBJECT... */
0544           /* start segment */
0545           if (cs != OBJECT)
0546         {
0547           cs = OBJECT;
0548           if (ps == OBJECT)
0549             {
0550               if (start[co] == UNKNOWN)
0551             {
0552               marker[xl] = 'S';
0553               start[co] = xl;
0554             }
0555               else
0556             marker[xl] = 's';
0557             }
0558           else
0559             {
0560               psstack[pstop++] = ps;
0561               marker[xl] = 'S';
0562               start[++co] = xl;
0563               ps = COMPLETE;
0564               info[co] = initinfo;
0565             }
0566         }
0567 
0568         } /* closes if pixel above threshold */
0569 
0570       /* process new marker ---------------------------------------------*/
0571       /* newmarker is marker[ ] at this pixel position before we got to
0572          it. We'll only enter this if marker[ ] was set on a previous
0573          loop iteration.   */
0574       if (newmarker)
0575         {
0576           if (newmarker == 'S')
0577         {
0578           psstack[pstop++] = ps;
0579           if (cs == NONOBJECT)
0580             {
0581               psstack[pstop++] = COMPLETE;
0582               info[++co] = store[xl];
0583               start[co] = UNKNOWN;
0584             }
0585           else
0586             update(&info[co], &store[xl], pixel);
0587           ps = OBJECT;
0588         }
0589 
0590           else if (newmarker == 's')
0591         {
0592           if ((cs == OBJECT) && (ps == COMPLETE))
0593             {
0594               pstop--;
0595               xl2 = start[co];
0596               update (&info[co-1],&info[co], pixel);
0597               if (start[--co] == UNKNOWN)
0598             start[co] = xl2;
0599               else
0600             marker[xl2] = 's';
0601             }
0602           ps = OBJECT;
0603         }
0604 
0605           else if (newmarker == 'f')
0606         ps = INCOMPLETE;
0607 
0608           else if (newmarker == 'F')
0609         {
0610           ps = psstack[--pstop];
0611           if ((cs == NONOBJECT) && (ps == COMPLETE))
0612             {
0613               if (start[co] == UNKNOWN)
0614             {
0615               if ((int)info[co].pixnb >= minarea)
0616                 {
0617                   /* update threshold before object is processed */
0618                   objlist.thresh = thresh;
0619 
0620                   status = sortit(&info[co], &objlist, minarea,
0621                           finalobjlist,
0622                           deblend_nthresh,deblend_cont,
0623                                               image->gain);
0624                   if (status != RETURN_OK)
0625                 goto exit;
0626                 }
0627 
0628               /* free the chain-list */
0629               PLIST(pixel+info[co].lastpix, nextpix) =
0630                 freeinfo.firstpix;
0631               freeinfo.firstpix = info[co].firstpix;
0632             }
0633               else
0634             {
0635               marker[end[co]] = 'F';
0636               store[start[co]] = info[co];
0637             }
0638               co--;
0639               ps = psstack[--pstop];
0640             }
0641         }
0642         }
0643       /* end of if (newmarker) ------------------------------------------*/
0644 
0645       /* update the info or end segment */
0646       if (luflag)
0647         {
0648           update(&info[co], &curpixinfo, pixel);
0649         }
0650       else if (cs == OBJECT)
0651         {
0652           cs = NONOBJECT;
0653           if (ps != COMPLETE)
0654         {
0655           marker[xl] = 'f';
0656           end[co] = xl;
0657         }
0658           else
0659         {
0660           ps = psstack[--pstop];
0661           marker[xl] = 'F';
0662           store[start[co]] = info[co];
0663           co--;
0664         }
0665         }
0666 
0667     } /*------------ End of the loop over the x's -----------------------*/
0668 
0669     } /*---------------- End of the loop over the y's -----------------------*/
0670 
0671   /* convert `finalobjlist` to an array of `sepobj` structs */
0672   /* if cleaning, see which objects "survive" cleaning. */
0673   if (clean_flag)
0674     {
0675       /* Calculate mthresh for all objects in the list (needed for cleaning) */
0676       for (i=0; i<finalobjlist->nobj; i++)
0677     {
0678       status = analysemthresh(i, finalobjlist, minarea, thresh);
0679       if (status != RETURN_OK)
0680         goto exit;
0681     }
0682 
0683       QMALLOC(survives, int, finalobjlist->nobj, status);
0684       clean(finalobjlist, clean_param, survives);
0685     }
0686 
0687   /* convert to output catalog */
0688   QCALLOC(cat, sep_catalog, 1, status);
0689   status = convert_to_catalog(finalobjlist, survives, cat, w, 1);
0690   if (status != RETURN_OK) goto exit;
0691 
0692  exit:
0693   free(finalobjlist->obj);
0694   free(finalobjlist->plist);
0695   free(finalobjlist);
0696   freedeblend();
0697   free(pixel);
0698   lutzfree();
0699   free(info);
0700   free(store);
0701   free(marker);
0702   free(dummyscan);
0703   free(psstack);
0704   free(start);
0705   free(end);
0706   free(survives);
0707   arraybuffer_free(&dbuf);
0708   if (image->noise)
0709     arraybuffer_free(&nbuf);
0710   if (image->mask)
0711     arraybuffer_free(&mbuf);
0712   if (conv)
0713     free(convnorm);
0714   if (filter_type == SEP_FILTER_MATCHED)
0715     {
0716       free(sigscan);
0717       free(workscan);
0718     }
0719 
0720   if (status != RETURN_OK)
0721     {
0722       /* free cdscan if we didn't do it on the last `yl` line */
0723       if ((cdscan != NULL) && (cdscan != dummyscan))
0724         free(cdscan);
0725       /* clean up catalog if it was allocated */
0726       sep_catalog_free(cat);
0727       cat = NULL;
0728     }
0729 
0730   *catalog = cat;
0731   return status;
0732 }
0733 
0734 
0735 /********************************* sortit ************************************/
0736 /*
0737 build the object structure.
0738 */
0739 int sortit(infostruct *info, objliststruct *objlist, int minarea,
0740        objliststruct *finalobjlist,
0741        int deblend_nthresh, double deblend_mincont, double gain)
0742 {
0743   objliststruct         objlistout, *objlist2;
0744   static objstruct  obj;
0745   int           i, status;
0746 
0747   status=RETURN_OK;  
0748   objlistout.obj = NULL;
0749   objlistout.plist = NULL;
0750   objlistout.nobj = objlistout.npix = 0;
0751 
0752   /*----- Allocate memory to store object data */
0753   objlist->obj = &obj;
0754   objlist->nobj = 1;
0755 
0756   memset(&obj, 0, (size_t)sizeof(objstruct));
0757   objlist->npix = info->pixnb;
0758   obj.firstpix = info->firstpix;
0759   obj.lastpix = info->lastpix;
0760   obj.flag = info->flag;
0761   obj.thresh = objlist->thresh;
0762 
0763   preanalyse(0, objlist);
0764 
0765   status = deblend(objlist, 0, &objlistout, deblend_nthresh, deblend_mincont,
0766            minarea);
0767   if (status)
0768     {
0769       /* formerly, this wasn't a fatal error, so a flag was set for
0770        * the object and we continued. I'm leaving the flag-setting
0771        * here in case we want to change this to a non-fatal error in
0772        * the future, but currently the flag setting is irrelevant. */
0773       objlist2 = objlist;
0774       for (i=0; i<objlist2->nobj; i++)
0775     objlist2->obj[i].flag |= SEP_OBJ_DOVERFLOW;
0776       goto exit;
0777     }
0778   else
0779     objlist2 = &objlistout;
0780 
0781   /* Analyze the deblended objects and add to the final list */
0782   for (i=0; i<objlist2->nobj; i++)
0783     {
0784       analyse(i, objlist2, 1, gain);
0785 
0786       /* this does nothing if DETECT_MAXAREA is 0 (and it currently is) */
0787       if (DETECT_MAXAREA && objlist2->obj[i].fdnpix > DETECT_MAXAREA)
0788     continue;
0789 
0790       /* add the object to the final list */
0791       status = addobjdeep(i, objlist2, finalobjlist);
0792       if (status != RETURN_OK)
0793     goto exit;
0794     }
0795 
0796  exit:
0797   free(objlistout.plist);
0798   free(objlistout.obj);
0799   return status;
0800 }
0801 
0802 
0803 /********** addobjdeep (originally in manobjlist.c) **************************/
0804 /*
0805 Add object number `objnb` from list `objl1` to list `objl2`.
0806 Unlike `addobjshallow` this also copies plist pixels to the second list.
0807 */
0808 
0809 int addobjdeep(int objnb, objliststruct *objl1, objliststruct *objl2)
0810 {
0811   objstruct *objl2obj;
0812   pliststruct   *plist1 = objl1->plist, *plist2 = objl2->plist;
0813   int       fp, i, j, npx, objnb2;
0814   
0815   fp = objl2->npix;      /* 2nd list's plist size in pixels */
0816   j = fp*plistsize;      /* 2nd list's plist size in bytes */
0817   objnb2 = objl2->nobj;  /* # of objects currently in 2nd list*/
0818 
0819   /* Allocate space in `objl2` for the new object */
0820   if (objnb2)
0821     objl2obj = (objstruct *)realloc(objl2->obj,
0822                     (++objl2->nobj)*sizeof(objstruct));
0823   else
0824     objl2obj = (objstruct *)malloc((++objl2->nobj)*sizeof(objstruct));
0825 
0826   if (!objl2obj)
0827     goto earlyexit;
0828   objl2->obj = objl2obj;
0829 
0830   /* Allocate space for the new object's pixels in 2nd list's plist */
0831   npx = objl1->obj[objnb].fdnpix;
0832   if (fp)
0833     plist2 = (pliststruct *)realloc(plist2, (objl2->npix+=npx)*plistsize);
0834   else
0835     plist2 = (pliststruct *)malloc((objl2->npix=npx)*plistsize);
0836 
0837   if (!plist2)
0838     goto earlyexit;
0839   objl2->plist = plist2;
0840   
0841   /* copy the plist */
0842   plist2 += j;
0843   for(i=objl1->obj[objnb].firstpix; i!=-1; i=PLIST(plist1+i,nextpix))
0844     {
0845       memcpy(plist2, plist1+i, (size_t)plistsize);
0846       PLIST(plist2,nextpix) = (j+=plistsize);
0847       plist2 += plistsize;
0848     }
0849   PLIST(plist2-=plistsize, nextpix) = -1;
0850   
0851   /* copy the object itself */
0852   objl2->obj[objnb2] = objl1->obj[objnb];
0853   objl2->obj[objnb2].firstpix = fp*plistsize;
0854   objl2->obj[objnb2].lastpix = j-plistsize;
0855 
0856   return RETURN_OK;
0857   
0858   /* if early exit, reset 2nd list */
0859  earlyexit:
0860   objl2->nobj--;
0861   objl2->npix = fp;
0862   return MEMORY_ALLOC_ERROR;
0863 }
0864 
0865 
0866 
0867 /****************************** plistinit ************************************
0868  * (originally init_plist() in sextractor)
0869 PURPOSE initialize a pixel-list and its components.
0870  ***/
0871 void plistinit(int hasconv, int hasvar)
0872 {
0873   pbliststruct  *pbdum = NULL;
0874 
0875   plistsize = sizeof(pbliststruct);
0876   plistoff_value = (char *)&pbdum->value - (char *)pbdum;
0877 
0878   if (hasconv)
0879     {
0880       plistexist_cdvalue = 1;
0881       plistoff_cdvalue = plistsize;
0882       plistsize += sizeof(PIXTYPE);
0883     }
0884   else
0885     {
0886       plistexist_cdvalue = 0;
0887       plistoff_cdvalue = plistoff_value;
0888     }
0889 
0890   if (hasvar)
0891     {
0892       plistexist_var = 1;
0893       plistoff_var = plistsize;
0894       plistsize += sizeof(PIXTYPE);
0895 
0896       plistexist_thresh = 1;
0897       plistoff_thresh = plistsize;
0898       plistsize += sizeof(PIXTYPE);
0899     }
0900   else
0901     {
0902       plistexist_var = 0;
0903       plistexist_thresh = 0;
0904     }
0905 
0906   return;
0907 
0908 }
0909 
0910 
0911 /************************** clean an objliststruct ***************************/
0912 /*
0913 Fill a list with whether each object in the list survived the cleaning 
0914 (assumes that mthresh has already been calculated for all objects in the list)
0915 */
0916 
0917 void clean(objliststruct *objlist, double clean_param, int *survives)
0918 {
0919   objstruct     *obj1, *obj2;
0920   int           i,j;
0921   double        amp,ampin,alpha,alphain, unitarea,unitareain,beta,val;
0922   float         dx,dy,rlim;
0923 
0924   beta = clean_param;
0925 
0926   /* initialize to all surviving */
0927   for (i=0; i<objlist->nobj; i++)
0928     survives[i] = 1;
0929 
0930   obj1 = objlist->obj;
0931   for (i=0; i<objlist->nobj; i++, obj1++)
0932     {
0933       if (!survives[i])
0934     continue;
0935 
0936       /* parameters for test object */
0937       unitareain = PI*obj1->a*obj1->b;
0938       ampin = obj1->fdflux/(2*unitareain*obj1->abcor);
0939       alphain = (pow(ampin/obj1->thresh, 1.0/beta)-1)*unitareain/obj1->fdnpix;
0940 
0941       /* loop over remaining objects in list*/
0942       obj2 = obj1 + 1;
0943       for (j=i+1; j<objlist->nobj; j++, obj2++)
0944     {
0945       if (!survives[j])
0946         continue;
0947 
0948       dx = obj1->mx - obj2->mx;
0949       dy = obj1->my - obj2->my;
0950       rlim = obj1->a + obj2->a;
0951       rlim *= rlim;
0952       if (dx*dx + dy*dy > rlim*CLEAN_ZONE*CLEAN_ZONE)
0953         continue;
0954 
0955       /* if obj1 is bigger, see if it eats obj2 */
0956       if (obj2->fdflux < obj1->fdflux)
0957         {
0958           val = 1 + alphain*(obj1->cxx*dx*dx + obj1->cyy*dy*dy +
0959                  obj1->cxy*dx*dy);
0960           if (val>1.0 && ((float)(val<1e10?ampin*pow(val,-beta):0.0) >
0961                   obj2->mthresh))
0962           survives[j] = 0; /* the test object eats this one */
0963         }
0964 
0965       /* if obj2 is bigger, see if it eats obj1 */
0966       else
0967         {
0968           unitarea = PI*obj2->a*obj2->b;
0969           amp = obj2->fdflux/(2*unitarea*obj2->abcor);
0970           alpha = (pow(amp/obj2->thresh, 1.0/beta) - 1) *
0971         unitarea/obj2->fdnpix;
0972           val = 1 + alpha*(obj2->cxx*dx*dx + obj2->cyy*dy*dy +
0973                    obj2->cxy*dx*dy);
0974           if (val>1.0 && ((float)(val<1e10?amp*pow(val,-beta):0.0) >
0975                   obj1->mthresh))
0976         survives[i] = 0;  /* this object eats the test object */
0977         }
0978 
0979     } /* inner loop over objlist (obj2) */
0980     } /* outer loop of objlist (obj1) */
0981 }
0982 
0983 
0984 /*****************************************************************************/
0985 /* sep_catalog manipulations */
0986 
0987 void free_catalog_fields(sep_catalog *catalog)
0988 {
0989   free(catalog->thresh);
0990   free(catalog->npix);
0991   free(catalog->tnpix);
0992   free(catalog->xmin);
0993   free(catalog->xmax);
0994   free(catalog->ymin);
0995   free(catalog->ymax);
0996   free(catalog->x);
0997   free(catalog->y);
0998   free(catalog->x2);
0999   free(catalog->y2);
1000   free(catalog->xy);
1001   free(catalog->errx2);
1002   free(catalog->erry2);
1003   free(catalog->errxy);
1004   free(catalog->a);
1005   free(catalog->b);
1006   free(catalog->theta);
1007   free(catalog->cxx);
1008   free(catalog->cyy);
1009   free(catalog->cxy);
1010   free(catalog->cflux);
1011   free(catalog->flux);
1012   free(catalog->cpeak);
1013   free(catalog->peak);
1014   free(catalog->xcpeak);
1015   free(catalog->ycpeak);
1016   free(catalog->xpeak);
1017   free(catalog->ypeak);
1018   free(catalog->flag);
1019 
1020   free(catalog->pix);
1021   free(catalog->objectspix);
1022 
1023   memset(catalog, 0, sizeof(sep_catalog));
1024 }
1025 
1026 
1027 /* convert_to_catalog()
1028  *
1029  * Convert the final object list to an output catalog.
1030  *
1031  * `survives`: array of 0 or 1 indicating whether or not to output each object
1032  *             (ignored if NULL)
1033  * `cat`:      catalog object to be filled.
1034  * `w`:        width of image (used to calculate linear indicies).
1035  */
1036 int convert_to_catalog(objliststruct *objlist, int *survives,
1037                        sep_catalog *cat, int w, int include_pixels) 
1038 {
1039   int i, j, k;
1040   int totnpix;
1041   int nobj = 0;
1042   int status = RETURN_OK;
1043   objstruct *obj;
1044   pliststruct *pixt, *pixel;
1045 
1046   /* Set struct to zero in case the caller didn't.
1047    * This is important if there is a memory error and we have to call
1048    * free_catalog_fields() to recover at exit */
1049   memset(cat, 0, sizeof(sep_catalog));
1050 
1051   /* Count number of surviving objects so that we can allocate the
1052      appropriate amount of space in the output catalog. */
1053   if (survives)
1054     for (i=0; i<objlist->nobj; i++) nobj += survives[i];
1055   else 
1056     nobj = objlist->nobj;
1057 
1058   /* allocate catalog fields */
1059   cat->nobj = nobj;
1060   QMALLOC(cat->thresh, float, nobj, status);
1061   QMALLOC(cat->npix, int, nobj, status);
1062   QMALLOC(cat->tnpix, int, nobj, status);
1063   QMALLOC(cat->xmin, int, nobj, status);
1064   QMALLOC(cat->xmax, int, nobj, status);
1065   QMALLOC(cat->ymin, int, nobj, status);
1066   QMALLOC(cat->ymax, int, nobj, status);
1067   QMALLOC(cat->x, double, nobj, status);
1068   QMALLOC(cat->y, double, nobj, status);
1069   QMALLOC(cat->x2, double, nobj, status);
1070   QMALLOC(cat->y2, double, nobj, status);
1071   QMALLOC(cat->xy, double, nobj, status);
1072   QMALLOC(cat->errx2, double, nobj, status);
1073   QMALLOC(cat->erry2, double, nobj, status);
1074   QMALLOC(cat->errxy, double, nobj, status);
1075   QMALLOC(cat->a, float, nobj, status);
1076   QMALLOC(cat->b, float, nobj, status);
1077   QMALLOC(cat->theta, float, nobj, status);
1078   QMALLOC(cat->cxx, float, nobj, status);
1079   QMALLOC(cat->cyy, float, nobj, status);
1080   QMALLOC(cat->cxy, float, nobj, status);
1081   QMALLOC(cat->cflux, float, nobj, status);
1082   QMALLOC(cat->flux, float, nobj, status);
1083   QMALLOC(cat->cpeak, float, nobj, status);
1084   QMALLOC(cat->peak, float, nobj, status);
1085   QMALLOC(cat->xcpeak, int, nobj, status);
1086   QMALLOC(cat->ycpeak, int, nobj, status);
1087   QMALLOC(cat->xpeak, int, nobj, status);
1088   QMALLOC(cat->ypeak, int, nobj, status);
1089   QMALLOC(cat->cflux, float, nobj, status);
1090   QMALLOC(cat->flux, float, nobj, status);
1091   QMALLOC(cat->flag, short, nobj, status);
1092 
1093   /* fill output arrays */
1094   j = 0;  /* running index in output array */
1095   for (i=0; i<objlist->nobj; i++)
1096     {
1097       if ((survives == NULL) || survives[i])
1098         {
1099           obj = objlist->obj + i;
1100           cat->thresh[j] = obj->thresh;
1101           cat->npix[j] = obj->fdnpix;
1102           cat->tnpix[j] = obj->dnpix;
1103           cat->xmin[j] = obj->xmin;
1104           cat->xmax[j] = obj->xmax;
1105           cat->ymin[j] = obj->ymin;
1106           cat->ymax[j] = obj->ymax;
1107           cat->x[j] = obj->mx;
1108           cat->y[j] = obj->my;
1109           cat->x2[j] = obj->mx2;
1110           cat->y2[j] = obj->my2;
1111           cat->xy[j] = obj->mxy;
1112           cat->errx2[j] = obj->errx2;
1113           cat->erry2[j] = obj->erry2;
1114           cat->errxy[j] = obj->errxy;
1115 
1116           cat->a[j] = obj->a;
1117           cat->b[j] = obj->b;
1118           cat->theta[j] = obj->theta;
1119 
1120           cat->cxx[j] = obj->cxx;
1121           cat->cyy[j] = obj->cyy;
1122           cat->cxy[j] = obj->cxy;
1123 
1124           cat->cflux[j] = obj->fdflux; /* these change names */
1125           cat->flux[j] = obj->dflux;
1126           cat->cpeak[j] = obj->fdpeak;
1127           cat->peak[j] = obj->dpeak;
1128 
1129           cat->xpeak[j] = obj->xpeak;
1130           cat->ypeak[j] = obj->ypeak;
1131           cat->xcpeak[j] = obj->xcpeak;
1132           cat->ycpeak[j] = obj->ycpeak;
1133 
1134           cat->flag[j] = obj->flag;
1135 
1136           j++;
1137         }
1138     }
1139 
1140   if (include_pixels)
1141     {
1142       /* count the total number of pixels */
1143       totnpix = 0;
1144       for (i=0; i<cat->nobj; i++) totnpix += cat->npix[i];
1145       
1146       /* allocate buffer for all objects' pixels */
1147       QMALLOC(cat->objectspix, int, totnpix, status);
1148 
1149       /* allocate array of pointers into the above buffer */
1150       QMALLOC(cat->pix, int*, nobj, status);
1151 
1152       pixel = objlist->plist;
1153 
1154       /* for each object, fill buffer and direct object's to it */
1155       k = 0; /* current position in `objectspix` buffer */
1156       j = 0; /* output object index */
1157       for (i=0; i<objlist->nobj; i++)
1158         {
1159           obj = objlist->obj + i; /* input object */
1160           if ((survives == NULL) || survives[i])
1161             {
1162               /* point this object's pixel list into the buffer. */
1163               cat->pix[j] = cat->objectspix + k;
1164 
1165               /* fill the actual pixel values */
1166               for (pixt=pixel+obj->firstpix; pixt>=pixel;
1167                    pixt=pixel+PLIST(pixt,nextpix), k++)
1168                 {
1169                   cat->objectspix[k] = PLIST(pixt,x) + w*PLIST(pixt,y);
1170                 }
1171               j++;
1172             }
1173         }
1174     }
1175 
1176  exit:
1177   if (status != RETURN_OK)
1178     free_catalog_fields(cat);
1179 
1180   return status;
1181 }
1182 
1183 
1184 void sep_catalog_free(sep_catalog *catalog)
1185 {
1186   if (catalog != NULL)
1187     free_catalog_fields(catalog);
1188   free(catalog);
1189 }