source: trunk/src/Detection/lutz_detect.cc @ 1008

Last change on this file since 1008 was 1008, checked in by MatthewWhiting, 12 years ago

Trying to solve #153 for giant - still had some longs & ints that really should be size_t. In lutz_detect, doing an on-the-fly cast from size_t to long for addPixel, and had to change the Object2D::addPixel to remove the
references and pass by value instead.

File size: 8.9 KB
RevLine 
[301]1// -----------------------------------------------------------------------
2// lutz_detect.cc: Search a 2D Image for objects.
3// -----------------------------------------------------------------------
4// Copyright (C) 2006, Matthew Whiting, ATNF
5//
6// This program is free software; you can redistribute it and/or modify it
7// under the terms of the GNU General Public License as published by the
8// Free Software Foundation; either version 2 of the License, or (at your
9// option) any later version.
10//
11// Duchamp is distributed in the hope that it will be useful, but WITHOUT
12// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14// for more details.
15//
16// You should have received a copy of the GNU General Public License
17// along with Duchamp; if not, write to the Free Software Foundation,
18// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
19//
20// Correspondence concerning Duchamp may be directed to:
21//    Internet email: Matthew.Whiting [at] atnf.csiro.au
22//    Postal address: Dr. Matthew Whiting
23//                    Australia Telescope National Facility, CSIRO
24//                    PO Box 76
25//                    Epping NSW 1710
26//                    AUSTRALIA
27// -----------------------------------------------------------------------
[582]28#include <duchamp/Detection/finders.hh>
[393]29#include <duchamp/PixelMap/Voxel.hh>
30#include <duchamp/PixelMap/Object2D.hh>
[3]31#include <vector>
32
[258]33using namespace PixelInfo;
34
[528]35/// @brief Enumeration to describe status of a pixel or a detected object
[290]36enum STATUS { NONOBJECT, ///< Pixel not above the threshold.
37              OBJECT,    ///< Pixel above the threshold.
38              COMPLETE,  ///< Object is complete
39              INCOMPLETE ///< Object not yet complete
40};
41
[528]42/// @brief Simple enumeration to enable obvious reference to current or prior row.
[3]43enum ROW { PRIOR=0, CURRENT};
44
[528]45/// @brief A couple of null values: the default starting value for markers, and one used for debugging.
[290]46enum NULLS { NULLSTART=-1, ///< Default start/end value, obviously
47                           ///   outside valid range.
48             NULLMARKER=45 ///< ASCII 45 = '-', which eases printing
49                           ///   for debugging purposes
50};
51
[258]52//---------------------------
[528]53/// @brief
[582]54/// A simple class local to @file to help manage detected
[528]55/// objects.
56///
57/// @details Keeps a track of a detection, as well as the start and finish
58/// locations of the detection on the current row.
59///
[258]60class FoundObject{
[3]61public:
[528]62  /// @brief Basic constructor, setting the start & end to NULL values.
[258]63  FoundObject(){start=NULLSTART; end=NULLSTART;};
[222]64  int start; ///< Pixel on the current row where the detection starts.
65  int end;   ///< Pixel on the current row where the detection finishes.
[258]66  Object2D info; ///< Collection of detected pixels.
[3]67};
[258]68//---------------------------
[3]69
[378]70namespace duchamp
[3]71{
[222]72
[1008]73  std::vector<Object2D> lutz_detect(std::vector<bool> &array, size_t xdim, size_t ydim, unsigned int minSize)
[378]74  {
[528]75    /// @details
76    ///  A detection algorithm for 2-dimensional images based on that of
77    ///  Lutz (1980).
78    /// 
79    ///  The image is raster-scanned, and searched row-by-row. Objects
80    ///  detected in each row are compared to objects in subsequent rows,
81    ///  and combined if they are connected (in an 8-fold sense).
82    ///
[3]83
[378]84    // Allocate necessary arrays.
85    std::vector<Object2D> outputlist;
86    STATUS *status  = new STATUS[2];
[582]87    Object2D *store = new Object2D[xdim+1];
[627]88    char *marker    = new char[xdim+1];
[1008]89    for(size_t i=0; i<(xdim+1); i++) marker[i] = NULLMARKER;
[378]90    std::vector<FoundObject> oS;
91    std::vector<STATUS>      psS;
[3]92
[378]93    Pixel pix;
[655]94    size_t loc=0;
[378]95
[1008]96    for(size_t posY=0;posY<(ydim+1);posY++){
[378]97      // Loop over each row -- consider rows one at a time
[3]98   
[378]99      status[PRIOR] = COMPLETE;
100      status[CURRENT] = NONOBJECT;
[3]101
[1008]102      for(size_t posX=0;posX<(xdim+1);posX++){
[378]103        // Now the loop for a given row, looking at each column individually.
[3]104
[378]105        char currentMarker = marker[posX];
106        marker[posX] = NULLMARKER;
[3]107
[378]108        bool isObject;
[582]109        if((posX<xdim)&&(posY<ydim)){
[378]110          // if we are in the original image
[655]111          isObject = array[loc++];
[378]112        }
113        else isObject = false;
114        // else we're in the padding row/col and isObject=FALSE;
[3]115
[378]116        //
117        // ------------------------------ START SEGMENT ------------------------
118        // If the current pixel is object and the previous pixel is not, then
119        // start a new segment.
120        // If the pixel touches an object on the prior row, the marker is either
121        // an S or an s, depending on whether the object has started yet.
122        // If it doesn't touch a prior object, this is the start of a completly
123        // new object on this row.
124        //
125        if ( (isObject) && (status[CURRENT] != OBJECT) ){
[3]126
[378]127          status[CURRENT] = OBJECT;
128          if(status[PRIOR] == OBJECT){
[3]129         
[378]130            if(oS.back().start==NULLSTART){
131              marker[posX] = 'S';
132              oS.back().start = posX;
133            }
134            else  marker[posX] = 's';
135          }
136          else{
137            psS.push_back(status[PRIOR]);  //PUSH PS onto PSSTACK;
[3]138            marker[posX] = 'S';
[378]139            oS.resize(oS.size()+1);        //PUSH OBSTACK;
[258]140            oS.back().start = posX;
[378]141
142            status[PRIOR] = COMPLETE;
[3]143          }
144        }
145
[378]146        //
147        // ------------------------------ PROCESS MARKER -----------------------
148        // If the current marker is not blank, then we need to deal with it.
149        // Four cases:
150        //   S --> start of object on prior row. Push priorStatus onto PSSTACK
151        //         and set priorStatus to OBJECT
152        //   s --> start of a secondary segment of object on prior row.
153        //         If current object joined, pop PSSTACK and join the objects.
154        //       Set priorStatus to OBJECT.
155        //   f --> end of a secondary segment of object on prior row.
156        //         Set priorStatus to INCOMPLETE.
157        //   F --> end of object on prior row. If no more of the object is to
158        //          come (priorStatus=COMPLETE), then finish it and output data.
159        //         Add to list, but only if it has more than the minimum number
160        //           of pixels.
161        //
162        if(currentMarker != NULLMARKER){
[3]163
[378]164          if(currentMarker == 'S'){
165            psS.push_back(status[PRIOR]);      // PUSH PS onto PSSTACK
166            if(status[CURRENT] == NONOBJECT){
167              psS.push_back(COMPLETE);         // PUSH COMPLETE ONTO PSSTACK;
168              oS.resize(oS.size()+1);          // PUSH OBSTACK;
169              oS.back().info = store[posX];
170            }
171            else oS.back().info = oS.back().info + store[posX];
172         
173            status[PRIOR] = OBJECT;
[3]174          }
175
[378]176          /*---------*/
177          if(currentMarker == 's'){
[3]178
[378]179            if( (status[CURRENT] == OBJECT) && (status[PRIOR] == COMPLETE) ){
180              status[PRIOR] = psS.back();
181              psS.pop_back();                   //POP PSSTACK ONTO PS
[3]182
[378]183              //            oS.at(oS.size()-2).info.addAnObject( oS.back().info );
184              //            if(oS.at(oS.size()-2).start == NULLSTART)
185              //              oS.at(oS.size()-2).start = oS.back().start;
186              //            else marker[oS.back().start] = 's';
[3]187
[378]188              oS[oS.size()-2].info = oS[oS.size()-2].info + oS.back().info;
189              if(oS[oS.size()-2].start == NULLSTART)
190                oS[oS.size()-2].start = oS.back().start;
191              else marker[oS.back().start] = 's';
[258]192
[378]193              oS.pop_back();
194            }
195
196            status[PRIOR] = OBJECT;
[3]197          }
198
[378]199          /*---------*/
200          if(currentMarker == 'f') status[PRIOR] = INCOMPLETE;
[3]201
[378]202          /*---------*/
203          if(currentMarker == 'F') {
[3]204
[378]205            status[PRIOR] = psS.back();
206            psS.pop_back();                    //POP PSSTACK ONTO PS
[3]207
[378]208            if( (status[CURRENT] == NONOBJECT) && (status[PRIOR] == COMPLETE) ){
[3]209
[378]210              if(oS.back().start == NULLSTART){
211                // The object is completed. If it is big enough, add to
212                // the end of the output list.       
[582]213                if(oS.back().info.getSize() >= minSize){
[378]214                  //oS.back().info.calcParams(); // work out midpoints, fluxes etc
215                  outputlist.push_back(oS.back().info);
216                }
217              }
218              else{
219                marker[ oS.back().end ] = 'F';
220                store[ oS.back().start ] = oS.back().info;
221              }
[3]222
[378]223              oS.pop_back();
224
225              status[PRIOR] = psS.back();
226              psS.pop_back();
[3]227            }
[378]228          }
[3]229
[378]230        } // end of PROCESSMARKER section ( if(currentMarker!=NULLMARKER) )
[3]231
[378]232        if (isObject){
[1008]233          oS.back().info.addPixel(long(posX),long(posY));
[3]234        }
[378]235        else{
236          //
237          // ----------------------------- END SEGMENT -------------------------
238          // If the current pixel is background and the previous pixel was an
239          // object, then finish the segment.
240          // If the prior status is COMPLETE, it's the end of the final segment
241          // of the object section.
242          // If not, it's end of the segment, but not necessarily the section.
243          //
244          if ( status[CURRENT] == OBJECT) {
[3]245
[378]246            status[CURRENT] = NONOBJECT;
[3]247
[378]248            if(status[PRIOR] != COMPLETE){
249              marker[posX] = 'f';
250              oS.back().end = posX;
251            }
252            else{
253              status[PRIOR] = psS.back();
254              psS.pop_back();                   //POP PSSTACK onto PS;
255              marker[posX] = 'F';
256              store[ oS.back().start ] = oS.back().info;
257              oS.pop_back();
258            }
[3]259          }
260       
[378]261        } //-> end of END SEGMENT else{ clause
[3]262
[378]263      }//-> end of loop over posX
[3]264
[378]265    }//-> end of loop over posY
[3]266
[378]267    // clean up and remove declared arrays
268    delete [] marker;
269    delete [] store;
270    delete [] status;
[3]271
[378]272    return outputlist;
[258]273
[378]274  }
275
[3]276}
Note: See TracBrowser for help on using the repository browser.