source: branches/pixelmap-refactor-branch/src/Detection/lutz_detect.cc

Last change on this file was 528, checked in by MatthewWhiting, 15 years ago

Changing the documentation comments to match the askapsoft style. Also have split ChanMap? and Object3D into separate files.

File size: 9.1 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/Cubes/cubes.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 lutz_detect.cc 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> Image::lutz_detect()
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    ///  Note that "detected" here means according to the
84    ///  Image::isDetection(long,long) function.
85    ///
86    ///  Upon return, the detected objects are stored in the
87    ///  Image::objectList vector.
88
89    // Allocate necessary arrays.
90    std::vector<Object2D> outputlist;
91    STATUS *status  = new STATUS[2];
92    Object2D *store = new Object2D[this->axisDim[0]+1];
93    char *marker    = new char[this->axisDim[0]+1];
94    for(int i=0; i<(this->axisDim[0]+1); i++) marker[i] = NULLMARKER;
95    std::vector<FoundObject> oS;
96    std::vector<STATUS>      psS;
97
98    Pixel pix;
99
100    for(int posY=0;posY<(this->axisDim[1]+1);posY++){
101      // Loop over each row -- consider rows one at a time
102   
103      status[PRIOR] = COMPLETE;
104      status[CURRENT] = NONOBJECT;
105
106      for(int posX=0;posX<(this->axisDim[0]+1);posX++){
107        // Now the loop for a given row, looking at each column individually.
108
109        char currentMarker = marker[posX];
110        marker[posX] = NULLMARKER;
111
112        bool isObject;
113        if((posX<this->axisDim[0])&&(posY<this->axisDim[1])){
114          // if we are in the original image
115          isObject = this->isDetection(posX,posY);
116        }
117        else isObject = false;
118        // else we're in the padding row/col and isObject=FALSE;
119
120        //
121        // ------------------------------ START SEGMENT ------------------------
122        // If the current pixel is object and the previous pixel is not, then
123        // start a new segment.
124        // If the pixel touches an object on the prior row, the marker is either
125        // an S or an s, depending on whether the object has started yet.
126        // If it doesn't touch a prior object, this is the start of a completly
127        // new object on this row.
128        //
129        if ( (isObject) && (status[CURRENT] != OBJECT) ){
130
131          status[CURRENT] = OBJECT;
132          if(status[PRIOR] == OBJECT){
133         
134            if(oS.back().start==NULLSTART){
135              marker[posX] = 'S';
136              oS.back().start = posX;
137            }
138            else  marker[posX] = 's';
139          }
140          else{
141            psS.push_back(status[PRIOR]);  //PUSH PS onto PSSTACK;
142            marker[posX] = 'S';
143            oS.resize(oS.size()+1);        //PUSH OBSTACK;
144            oS.back().start = posX;
145
146            status[PRIOR] = COMPLETE;
147          }
148        }
149
150        //
151        // ------------------------------ PROCESS MARKER -----------------------
152        // If the current marker is not blank, then we need to deal with it.
153        // Four cases:
154        //   S --> start of object on prior row. Push priorStatus onto PSSTACK
155        //         and set priorStatus to OBJECT
156        //   s --> start of a secondary segment of object on prior row.
157        //         If current object joined, pop PSSTACK and join the objects.
158        //       Set priorStatus to OBJECT.
159        //   f --> end of a secondary segment of object on prior row.
160        //         Set priorStatus to INCOMPLETE.
161        //   F --> end of object on prior row. If no more of the object is to
162        //          come (priorStatus=COMPLETE), then finish it and output data.
163        //         Add to list, but only if it has more than the minimum number
164        //           of pixels.
165        //
166        if(currentMarker != NULLMARKER){
167
168          if(currentMarker == 'S'){
169            psS.push_back(status[PRIOR]);      // PUSH PS onto PSSTACK
170            if(status[CURRENT] == NONOBJECT){
171              psS.push_back(COMPLETE);         // PUSH COMPLETE ONTO PSSTACK;
172              oS.resize(oS.size()+1);          // PUSH OBSTACK;
173              oS.back().info = store[posX];
174            }
175            else oS.back().info = oS.back().info + store[posX];
176         
177            status[PRIOR] = OBJECT;
178          }
179
180          /*---------*/
181          if(currentMarker == 's'){
182
183            if( (status[CURRENT] == OBJECT) && (status[PRIOR] == COMPLETE) ){
184              status[PRIOR] = psS.back();
185              psS.pop_back();                   //POP PSSTACK ONTO PS
186
187              //            oS.at(oS.size()-2).info.addAnObject( oS.back().info );
188              //            if(oS.at(oS.size()-2).start == NULLSTART)
189              //              oS.at(oS.size()-2).start = oS.back().start;
190              //            else marker[oS.back().start] = 's';
191
192              oS[oS.size()-2].info = oS[oS.size()-2].info + oS.back().info;
193              if(oS[oS.size()-2].start == NULLSTART)
194                oS[oS.size()-2].start = oS.back().start;
195              else marker[oS.back().start] = 's';
196
197              oS.pop_back();
198            }
199
200            status[PRIOR] = OBJECT;
201          }
202
203          /*---------*/
204          if(currentMarker == 'f') status[PRIOR] = INCOMPLETE;
205
206          /*---------*/
207          if(currentMarker == 'F') {
208
209            status[PRIOR] = psS.back();
210            psS.pop_back();                    //POP PSSTACK ONTO PS
211
212            if( (status[CURRENT] == NONOBJECT) && (status[PRIOR] == COMPLETE) ){
213
214              if(oS.back().start == NULLSTART){
215                // The object is completed. If it is big enough, add to
216                // the end of the output list.       
217                if(oS.back().info.getSize() >= this->minSize){
218                  //oS.back().info.calcParams(); // work out midpoints, fluxes etc
219                  outputlist.push_back(oS.back().info);
220                }
221              }
222              else{
223                marker[ oS.back().end ] = 'F';
224                store[ oS.back().start ] = oS.back().info;
225              }
226
227              oS.pop_back();
228
229              status[PRIOR] = psS.back();
230              psS.pop_back();
231            }
232          }
233
234        } // end of PROCESSMARKER section ( if(currentMarker!=NULLMARKER) )
235
236        if (isObject){
237          oS.back().info.addPixel(posX,posY);
238        }
239        else{
240          //
241          // ----------------------------- END SEGMENT -------------------------
242          // If the current pixel is background and the previous pixel was an
243          // object, then finish the segment.
244          // If the prior status is COMPLETE, it's the end of the final segment
245          // of the object section.
246          // If not, it's end of the segment, but not necessarily the section.
247          //
248          if ( status[CURRENT] == OBJECT) {
249
250            status[CURRENT] = NONOBJECT;
251
252            if(status[PRIOR] != COMPLETE){
253              marker[posX] = 'f';
254              oS.back().end = posX;
255            }
256            else{
257              status[PRIOR] = psS.back();
258              psS.pop_back();                   //POP PSSTACK onto PS;
259              marker[posX] = 'F';
260              store[ oS.back().start ] = oS.back().info;
261              oS.pop_back();
262            }
263          }
264       
265        } //-> end of END SEGMENT else{ clause
266
267      }//-> end of loop over posX
268
269    }//-> end of loop over posY
270
271    // clean up and remove declared arrays
272    delete [] marker;
273    delete [] store;
274    delete [] status;
275
276    return outputlist;
277
278  }
279
280}
Note: See TracBrowser for help on using the repository browser.