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

Last change on this file since 1441 was 393, checked in by MatthewWhiting, 17 years ago

Fixed up headers for trunk as well.

File size: 9.0 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/** 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/** Simple enumeration to enable obvious reference to current or prior
43    row. */
44enum ROW { PRIOR=0, CURRENT};
45
46/** A couple of null values: the default starting value for markers,
47    and one used for debugging. */
48enum NULLS { NULLSTART=-1, ///< Default start/end value, obviously
49                           ///   outside valid range.
50             NULLMARKER=45 ///< ASCII 45 = '-', which eases printing
51                           ///   for debugging purposes
52};
53
54//---------------------------
55/**
56 * A simple class local to lutz_detect.cc to help manage detected
57 * objects.
58 *
59 * Keeps a track of a detection, as well as the start and finish
60 * locations of the detection on the current row.
61 */
62class FoundObject{
63public:
64  /** Basic constructor, setting the start & end to NULL values. */
65  FoundObject(){start=NULLSTART; end=NULLSTART;};
66  int start; ///< Pixel on the current row where the detection starts.
67  int end;   ///< Pixel on the current row where the detection finishes.
68  Object2D info; ///< Collection of detected pixels.
69};
70//---------------------------
71
72namespace duchamp
73{
74
75  std::vector<Object2D> Image::lutz_detect()
76  {
77    /**
78     *  A detection algorithm for 2-dimensional images based on that of
79     *  Lutz (1980).
80     * 
81     *  The image is raster-scanned, and searched row-by-row. Objects
82     *  detected in each row are compared to objects in subsequent rows,
83     *  and combined if they are connected (in an 8-fold sense).
84     *
85     *  Note that "detected" here means according to the
86     *  Image::isDetection(long,long) function.
87     *
88     *  Upon return, the detected objects are stored in the
89     *  Image::objectList vector.
90     *
91     */
92
93    // Allocate necessary arrays.
94    std::vector<Object2D> outputlist;
95    STATUS *status  = new STATUS[2];
96    Object2D *store = new Object2D[this->axisDim[0]+1];
97    char *marker    = new char[this->axisDim[0]+1];
98    for(int i=0; i<(this->axisDim[0]+1); i++) marker[i] = NULLMARKER;
99    std::vector<FoundObject> oS;
100    std::vector<STATUS>      psS;
101
102    Pixel pix;
103
104    for(int posY=0;posY<(this->axisDim[1]+1);posY++){
105      // Loop over each row -- consider rows one at a time
106   
107      status[PRIOR] = COMPLETE;
108      status[CURRENT] = NONOBJECT;
109
110      for(int posX=0;posX<(this->axisDim[0]+1);posX++){
111        // Now the loop for a given row, looking at each column individually.
112
113        char currentMarker = marker[posX];
114        marker[posX] = NULLMARKER;
115
116        bool isObject;
117        if((posX<this->axisDim[0])&&(posY<this->axisDim[1])){
118          // if we are in the original image
119          isObject = this->isDetection(posX,posY);
120        }
121        else isObject = false;
122        // else we're in the padding row/col and isObject=FALSE;
123
124        //
125        // ------------------------------ START SEGMENT ------------------------
126        // If the current pixel is object and the previous pixel is not, then
127        // start a new segment.
128        // If the pixel touches an object on the prior row, the marker is either
129        // an S or an s, depending on whether the object has started yet.
130        // If it doesn't touch a prior object, this is the start of a completly
131        // new object on this row.
132        //
133        if ( (isObject) && (status[CURRENT] != OBJECT) ){
134
135          status[CURRENT] = OBJECT;
136          if(status[PRIOR] == OBJECT){
137         
138            if(oS.back().start==NULLSTART){
139              marker[posX] = 'S';
140              oS.back().start = posX;
141            }
142            else  marker[posX] = 's';
143          }
144          else{
145            psS.push_back(status[PRIOR]);  //PUSH PS onto PSSTACK;
146            marker[posX] = 'S';
147            oS.resize(oS.size()+1);        //PUSH OBSTACK;
148            oS.back().start = posX;
149
150            status[PRIOR] = COMPLETE;
151          }
152        }
153
154        //
155        // ------------------------------ PROCESS MARKER -----------------------
156        // If the current marker is not blank, then we need to deal with it.
157        // Four cases:
158        //   S --> start of object on prior row. Push priorStatus onto PSSTACK
159        //         and set priorStatus to OBJECT
160        //   s --> start of a secondary segment of object on prior row.
161        //         If current object joined, pop PSSTACK and join the objects.
162        //       Set priorStatus to OBJECT.
163        //   f --> end of a secondary segment of object on prior row.
164        //         Set priorStatus to INCOMPLETE.
165        //   F --> end of object on prior row. If no more of the object is to
166        //          come (priorStatus=COMPLETE), then finish it and output data.
167        //         Add to list, but only if it has more than the minimum number
168        //           of pixels.
169        //
170        if(currentMarker != NULLMARKER){
171
172          if(currentMarker == 'S'){
173            psS.push_back(status[PRIOR]);      // PUSH PS onto PSSTACK
174            if(status[CURRENT] == NONOBJECT){
175              psS.push_back(COMPLETE);         // PUSH COMPLETE ONTO PSSTACK;
176              oS.resize(oS.size()+1);          // PUSH OBSTACK;
177              oS.back().info = store[posX];
178            }
179            else oS.back().info = oS.back().info + store[posX];
180         
181            status[PRIOR] = OBJECT;
182          }
183
184          /*---------*/
185          if(currentMarker == 's'){
186
187            if( (status[CURRENT] == OBJECT) && (status[PRIOR] == COMPLETE) ){
188              status[PRIOR] = psS.back();
189              psS.pop_back();                   //POP PSSTACK ONTO PS
190
191              //            oS.at(oS.size()-2).info.addAnObject( oS.back().info );
192              //            if(oS.at(oS.size()-2).start == NULLSTART)
193              //              oS.at(oS.size()-2).start = oS.back().start;
194              //            else marker[oS.back().start] = 's';
195
196              oS[oS.size()-2].info = oS[oS.size()-2].info + oS.back().info;
197              if(oS[oS.size()-2].start == NULLSTART)
198                oS[oS.size()-2].start = oS.back().start;
199              else marker[oS.back().start] = 's';
200
201              oS.pop_back();
202            }
203
204            status[PRIOR] = OBJECT;
205          }
206
207          /*---------*/
208          if(currentMarker == 'f') status[PRIOR] = INCOMPLETE;
209
210          /*---------*/
211          if(currentMarker == 'F') {
212
213            status[PRIOR] = psS.back();
214            psS.pop_back();                    //POP PSSTACK ONTO PS
215
216            if( (status[CURRENT] == NONOBJECT) && (status[PRIOR] == COMPLETE) ){
217
218              if(oS.back().start == NULLSTART){
219                // The object is completed. If it is big enough, add to
220                // the end of the output list.       
221                if(oS.back().info.getSize() >= this->minSize){
222                  //oS.back().info.calcParams(); // work out midpoints, fluxes etc
223                  outputlist.push_back(oS.back().info);
224                }
225              }
226              else{
227                marker[ oS.back().end ] = 'F';
228                store[ oS.back().start ] = oS.back().info;
229              }
230
231              oS.pop_back();
232
233              status[PRIOR] = psS.back();
234              psS.pop_back();
235            }
236          }
237
238        } // end of PROCESSMARKER section ( if(currentMarker!=NULLMARKER) )
239
240        if (isObject){
241          oS.back().info.addPixel(posX,posY);
242        }
243        else{
244          //
245          // ----------------------------- END SEGMENT -------------------------
246          // If the current pixel is background and the previous pixel was an
247          // object, then finish the segment.
248          // If the prior status is COMPLETE, it's the end of the final segment
249          // of the object section.
250          // If not, it's end of the segment, but not necessarily the section.
251          //
252          if ( status[CURRENT] == OBJECT) {
253
254            status[CURRENT] = NONOBJECT;
255
256            if(status[PRIOR] != COMPLETE){
257              marker[posX] = 'f';
258              oS.back().end = posX;
259            }
260            else{
261              status[PRIOR] = psS.back();
262              psS.pop_back();                   //POP PSSTACK onto PS;
263              marker[posX] = 'F';
264              store[ oS.back().start ] = oS.back().info;
265              oS.pop_back();
266            }
267          }
268       
269        } //-> end of END SEGMENT else{ clause
270
271      }//-> end of loop over posX
272
273    }//-> end of loop over posY
274
275    // clean up and remove declared arrays
276    delete [] marker;
277    delete [] store;
278    delete [] status;
279
280    return outputlist;
281
282  }
283
284}
Note: See TracBrowser for help on using the repository browser.