source: tags/release-1.1.9/src/Detection/lutz_detect.cc @ 1441

Last change on this file since 1441 was 655, checked in by MatthewWhiting, 14 years ago

Minor change to the lutz detect, changing an integer multiplication to an incrementing counter.

File size: 8.8 KB
Line 
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// -----------------------------------------------------------------------
28#include <duchamp/Detection/finders.hh>
29#include <duchamp/PixelMap/Voxel.hh>
30#include <duchamp/PixelMap/Object2D.hh>
31#include <vector>
32
33using namespace PixelInfo;
34
35/// @brief Enumeration to describe status of a pixel or a detected object
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
42/// @brief Simple enumeration to enable obvious reference to current or prior row.
43enum ROW { PRIOR=0, CURRENT};
44
45/// @brief A couple of null values: the default starting value for markers, and one used for debugging.
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
52//---------------------------
53/// @brief
54/// A simple class local to @file to help manage detected
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///
60class FoundObject{
61public:
62  /// @brief Basic constructor, setting the start & end to NULL values.
63  FoundObject(){start=NULLSTART; end=NULLSTART;};
64  int start; ///< Pixel on the current row where the detection starts.
65  int end;   ///< Pixel on the current row where the detection finishes.
66  Object2D info; ///< Collection of detected pixels.
67};
68//---------------------------
69
70namespace duchamp
71{
72
73  std::vector<Object2D> lutz_detect(std::vector<bool> &array, long xdim, long ydim, unsigned int minSize)
74  {
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    ///
83
84    // Allocate necessary arrays.
85    std::vector<Object2D> outputlist;
86    STATUS *status  = new STATUS[2];
87    Object2D *store = new Object2D[xdim+1];
88    char *marker    = new char[xdim+1];
89    for(int i=0; i<(xdim+1); i++) marker[i] = NULLMARKER;
90    std::vector<FoundObject> oS;
91    std::vector<STATUS>      psS;
92
93    Pixel pix;
94    size_t loc=0;
95
96    for(int posY=0;posY<(ydim+1);posY++){
97      // Loop over each row -- consider rows one at a time
98   
99      status[PRIOR] = COMPLETE;
100      status[CURRENT] = NONOBJECT;
101
102      for(int posX=0;posX<(xdim+1);posX++){
103        // Now the loop for a given row, looking at each column individually.
104
105        char currentMarker = marker[posX];
106        marker[posX] = NULLMARKER;
107
108        bool isObject;
109        if((posX<xdim)&&(posY<ydim)){
110          // if we are in the original image
111          isObject = array[loc++];
112        }
113        else isObject = false;
114        // else we're in the padding row/col and isObject=FALSE;
115
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) ){
126
127          status[CURRENT] = OBJECT;
128          if(status[PRIOR] == OBJECT){
129         
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;
138            marker[posX] = 'S';
139            oS.resize(oS.size()+1);        //PUSH OBSTACK;
140            oS.back().start = posX;
141
142            status[PRIOR] = COMPLETE;
143          }
144        }
145
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){
163
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;
174          }
175
176          /*---------*/
177          if(currentMarker == 's'){
178
179            if( (status[CURRENT] == OBJECT) && (status[PRIOR] == COMPLETE) ){
180              status[PRIOR] = psS.back();
181              psS.pop_back();                   //POP PSSTACK ONTO PS
182
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';
187
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';
192
193              oS.pop_back();
194            }
195
196            status[PRIOR] = OBJECT;
197          }
198
199          /*---------*/
200          if(currentMarker == 'f') status[PRIOR] = INCOMPLETE;
201
202          /*---------*/
203          if(currentMarker == 'F') {
204
205            status[PRIOR] = psS.back();
206            psS.pop_back();                    //POP PSSTACK ONTO PS
207
208            if( (status[CURRENT] == NONOBJECT) && (status[PRIOR] == COMPLETE) ){
209
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.       
213                if(oS.back().info.getSize() >= minSize){
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              }
222
223              oS.pop_back();
224
225              status[PRIOR] = psS.back();
226              psS.pop_back();
227            }
228          }
229
230        } // end of PROCESSMARKER section ( if(currentMarker!=NULLMARKER) )
231
232        if (isObject){
233          oS.back().info.addPixel(posX,posY);
234        }
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) {
245
246            status[CURRENT] = NONOBJECT;
247
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            }
259          }
260       
261        } //-> end of END SEGMENT else{ clause
262
263      }//-> end of loop over posX
264
265    }//-> end of loop over posY
266
267    // clean up and remove declared arrays
268    delete [] marker;
269    delete [] store;
270    delete [] status;
271
272    return outputlist;
273
274  }
275
276}
Note: See TracBrowser for help on using the repository browser.