source: trunk/src/Detection/lutz_detect.cc

Last change on this file was 1290, checked in by MatthewWhiting, 11 years ago

Removing the Pixel class, as we never use it. Will use Voxels if we ever need to, so adding a constructor for Voxels that just takes an x & a y value. Preparation for ticket #200

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