source: trunk/src/GenericEdgeDetector.cpp @ 2614

Last change on this file since 2614 was 2614, checked in by Takeshi Nakazato, 12 years ago

New Development: No

JIRA Issue: Yes CAS-2825

Ready for Test: Yes

Interface Changes: No

What Interface Changed: Please list interface changes

Test Programs: not available yet

Put in Release Notes: No

Module(s): Module Names change impacts.

Description: Describe your changes here...

Bug fix on 1D trimming stage.


File size: 11.5 KB
Line 
1//
2// C++ Implimentation: GenericEdgeDetector
3//
4// Description:
5//
6//
7// Author: Takeshi Nakazato <takeshi.nakazato@nao.ac.jp>, (C) 2012
8//
9// Copyright: See COPYING file that comes with this distribution
10//
11//
12#include <math.h>
13
14#include <casa/Arrays/Vector.h>
15#include <casa/Arrays/Matrix.h>
16#include <casa/Arrays/ArrayMath.h>
17#include <casa/Arrays/ArrayIO.h>
18#include <casa/Utilities/GenSort.h>
19
20#include "GenericEdgeDetector.h"
21
22using namespace std ;
23using namespace casa ;
24
25namespace asap {
26
27GenericEdgeDetector::GenericEdgeDetector()
28  : EdgeDetector()
29{}
30
31GenericEdgeDetector::~GenericEdgeDetector()
32{}
33
34Vector<uInt> GenericEdgeDetector::detect()
35{
36  initDetect() ;
37
38  topixel() ;
39  countup() ;
40  thresholding() ;
41  labeling() ;
42  trimming() ;
43  selection() ;
44  tuning() ;
45
46  return off_ ;
47}
48
49void GenericEdgeDetector::parseOption( const Record &option )
50{
51  os_.origin(LogOrigin( "GenericEdgeDetector", "parseOption", WHERE )) ;
52
53  String name = "fraction" ;
54  if ( option.isDefined( name ) ) {
55    if ( option.dataType( name ) == TpString ) {
56      // should be "xx%" format
57      String fstr = option.asString( name ) ;
58      fstr.rtrim( '%' ) ;
59      fraction_ = String::toFloat( fstr ) * 0.01 ;
60    }
61    else {
62      fraction_ = option.asFloat( name ) ;
63    }
64  }
65  else {
66    fraction_ = 0.1 ; // default is 10%
67  }
68 
69  name = "width" ;
70  if ( option.isDefined( name ) ) {
71    width_ = option.asFloat( name ) ;
72  }
73  else {
74    width_ = 0.5 ; // default is half of median separation
75  }
76
77  name = "elongated" ;
78  if ( option.isDefined( name ) ) {
79    elongated_ = option.asBool( name ) ;
80  }
81  else {
82    elongated_ = False ; // default is two-dimensional processing
83  }
84
85  os_ << "OPTION SUMMARY: " << endl
86      << "   fraction=" << fraction_ << endl
87      << "   width=" << width_ << endl
88      << "   elongated=" << (elongated_ ? "True" : "False") << LogIO::POST ;
89}
90
91void GenericEdgeDetector::topixel()
92{
93  os_.origin(LogOrigin( "GenericEdgeDetector", "setup", WHERE )) ;
94
95  setup() ;
96  Double *pdir_p = new Double[dir_.nelements()] ;
97  pdir_.takeStorage( dir_.shape(), pdir_p, TAKE_OVER ) ;
98  uInt len = time_.nelements() ;
99  Bool b ;
100  Double *px_p = pdir_p ;
101  Double *py_p = pdir_p + 1 ;
102  const Double *dir_p = dir_.getStorage( b ) ;
103  const Double *x_p = dir_p ;
104  const Double *y_p = dir_p + 1 ;
105  for ( uInt i = 0 ; i < len ; i++ ) {
106    *px_p = pcenx_ + ( *x_p - cenx_ ) / dx_ ;
107    *py_p = pceny_ + ( *y_p - ceny_ ) / dy_ ;
108    px_p += 2 ;
109    py_p += 2 ;
110    x_p += 2 ;
111    y_p += 2 ;
112  }
113  dir_.freeStorage( dir_p, b ) ;
114
115  os_ << "end topixel" << LogIO::POST ;
116}
117
118void GenericEdgeDetector::setup()
119{
120  os_.origin(LogOrigin( "GenericEdgeDetector", "setup", WHERE )) ;
121
122  uInt len = time_.nelements() ;
123  Matrix<Double> dd = dir_.copy() ;
124  for ( uInt i = len-1 ; i > 0 ; i-- ) {
125    dd(0,i) = dd(0,i) - dd(0,i-1) ;
126    dd(1,i) = dd(1,i) - dd(1,i-1) ;
127  }
128  Vector<Double> dr( len-1 ) ;
129  Bool b ;
130  const Double *dir_p = dd.getStorage( b ) ;
131  const Double *x_p = dir_p + 2 ;
132  const Double *y_p = dir_p + 3 ;
133  for ( uInt i = 0 ; i < len-1 ; i++ ) {
134    dr[i] = sqrt( (*x_p) * (*x_p) + (*y_p) * (*y_p) ) ;
135    x_p += 2 ;
136    y_p += 2 ;
137  }
138  dir_.freeStorage( dir_p, b ) ;
139  Double med = median( dr, False, True, True ) ;
140  dx_ = med * width_ ;
141  dy_ = dx_ ;
142
143  os_ << "dx=" << dx_ << ", dy=" << dy_ << LogIO::POST ;
144
145  Double xmax, xmin, ymax, ymin ;
146  minMax( xmin, xmax, dir_.row( 0 ) ) ;
147  minMax( ymin, ymax, dir_.row( 1 ) ) ;
148  Double wx = ( xmax - xmin ) * 1.1 ;
149  Double wy = ( ymax - ymin ) * 1.1 ;
150
151  cenx_ = 0.5 * ( xmin + xmax ) ;
152  ceny_ = 0.5 * ( ymin + ymax ) ;
153
154  nx_ = uInt( ceil( wx / dx_ ) ) ;
155  ny_ = uInt( ceil( wy / dy_ ) ) ;
156
157  pcenx_ = 0.5 * Double( nx_ - 1 ) ;
158  pceny_ = 0.5 * Double( ny_ - 1 ) ;
159
160  os_ << "rangex=(" << xmin << "," << xmax << ")" << LogIO::POST ;
161  os_ << "rangey=(" << ymin << "," << ymax << ")" << LogIO::POST ;
162  os_ << "median separation between pointings: " << med << LogIO::POST ;
163  os_ << "dx=" << dx_ << ", dy=" << dy_ << LogIO::POST ;
164  os_ << "wx=" << wx << ", wy=" << wy << LogIO::POST ;
165  os_ << "nx=" << nx_ << ", ny=" << ny_ << LogIO::POST ;
166}
167
168void GenericEdgeDetector::countup()
169{
170  os_.origin(LogOrigin( "GenericEdgeDetector", "countup", WHERE )) ;
171
172  uInt *a_p = new uInt[nx_*ny_] ;
173  apix_.takeStorage( IPosition(2,nx_,ny_), a_p, TAKE_OVER ) ;
174  apix_ = 0 ;
175 
176  uInt len = time_.nelements() ;
177  uInt ix ;
178  uInt iy ;
179  // pdir_ is always contiguous
180  const Double *pdir_p = pdir_.data() ;
181  const Double *px_p = pdir_p ;
182  const Double *py_p = pdir_p + 1 ;
183  long offset ;
184  for ( uInt i = 0 ; i < len ; i++ ) {
185    ix = uInt(round( *px_p )) ;
186    iy = uInt(round( *py_p )) ;
187    offset = ix + iy * nx_ ;
188    *(a_p+offset) += 1 ;
189    px_p += 2 ;
190    py_p += 2 ;
191  }
192
193  os_ << "a.max()=" << max(apix_) << ",a.min()=" << min(apix_) << LogIO::POST ;
194}
195
196void GenericEdgeDetector::thresholding()
197{
198  uInt len = apix_.nelements() ;
199  uInt *a_p = apix_.data() ;
200  for ( uInt i = 0 ; i < len ; i++ ) {
201    *a_p = ((*a_p > 0) ? 1 : 0) ;
202    a_p++ ;
203  }
204}
205
206void GenericEdgeDetector::labeling()
207{
208  os_.origin(LogOrigin( "GenericEdgeDetector", "labeling", WHERE )) ;
209
210  uInt n = 1 ;
211  uInt niter = 0 ;
212  const uInt maxiter = 10 ;
213  while ( n > 0 && niter < maxiter ) {
214    n = _labeling( apix_ ) ;
215    os_ << "labeled " << n << " pixels in this cycle" << LogIO::POST ;
216    niter++ ;
217  }
218  if ( niter == maxiter ) {
219    // WARN
220    os_ << LogIO::WARN << "labeling not converged before maxiter=" << maxiter << LogIO::POST ;
221  }
222}
223
224uInt GenericEdgeDetector::_labeling( Matrix<uInt> &a )
225{
226  uInt n = 0 ;
227  for ( uInt ix = 0 ; ix < nx_ ; ix++ ) {
228    Vector<uInt> v( a.row( ix ) ) ;
229    uInt nx = __labeling( v ) ;
230    n += nx ;
231  }
232  for ( uInt iy = 0 ; iy < ny_ ; iy++ ) {
233    Vector<uInt> v( a.column( iy ) ) ;
234    uInt ny = __labeling( v ) ;
235    n += ny ;
236  }
237  return n ;
238}
239
240uInt GenericEdgeDetector::__labeling( Vector<uInt> &a )
241{
242  uInt n = 0 ;
243  if ( allEQ( a, n ) ) {
244    return n ;
245  }
246
247  uInt start ;
248  uInt end ;
249  _search( start, end, a ) ;
250  for ( uInt i = start+1 ; i < end ; i++ ) {
251    if ( a[i] == 0 ) {
252      a[i] = 1 ;
253      n++ ;
254    }
255  }
256  return n ;
257}
258
259void GenericEdgeDetector::_search( uInt &start,
260                                   uInt &end,
261                                   const Vector<uInt> &a )
262{
263  uInt n = a.nelements() ;
264  start = 0 ;
265  while( a[start] == 0 ) {
266    start++ ;
267  }
268  end = n - 1 ;
269  while( a[end] == 0 ) {
270    end-- ;
271  }
272}
273
274void GenericEdgeDetector::trimming()
275{
276  os_.origin(LogOrigin( "GenericEdgeDetector", "trimming", WHERE )) ;
277
278  const uInt n1 = sum( apix_ ) ;
279  const uInt nTrim = uInt(ceil( n1 * fraction_ )) ;
280  os_ << "number of nonzero pixel: " << n1 << LogIO::POST ;
281  os_ << "fraction: " << fraction_ << LogIO::POST ;
282  os_ << "number of pixels to be trimmed: " << nTrim << LogIO::POST ;
283  uInt n = 0 ;
284  uInt niter = 0 ;
285  const uInt maxiter = 100 ;
286  if ( !elongated_ ) {
287    while ( n < nTrim && niter < maxiter ) {
288      uInt m = _trimming( apix_ ) ;
289      os_ << "trimmed " << m << " pixels" << LogIO::POST ;
290      n += m ;
291      niter++ ;
292    }
293  }
294  else if ( nx_ > ny_ ) {
295    os_ << "1D triming along x-axis" << LogIO::POST ;
296    while ( n < nTrim && niter < maxiter ) {
297      uInt m = _trimming1DX( apix_ ) ;
298      n += m ;
299      niter++ ;
300    }
301  }
302  else { // nx_ < ny_
303    os_ << "1D triming along y-axis" << LogIO::POST ;
304    while ( n < nTrim && niter < maxiter ) {
305      uInt m = _trimming1DY( apix_ ) ;
306      n += m ;
307      niter++ ;
308    }
309  }
310  os_ << "number of pixels actually trimmed: " << n << LogIO::POST ;
311
312  if ( niter == maxiter ) {
313    // WARN
314    os_ << LogIO::WARN << "trimming not converged before maxiter=" << maxiter << LogIO::POST ;
315  }
316}
317
318uInt GenericEdgeDetector::_trimming( Matrix<uInt> &a )
319{
320  uInt n = 0 ;
321  const uInt nx = a.nrow() ;
322  const uInt ny = a.ncolumn() ;
323  Block<uInt> flatIdxList( a.nelements() ) ;
324  uInt start ;
325  uInt end ;
326  uInt flatIdx ;
327  for ( uInt ix = 0 ; ix < nx ; ix++ ) {
328    Vector<uInt> v( a.row( ix ) ) ;
329    if ( allEQ( v, (uInt)0 ) ) {
330      continue ;
331    }
332    _search( start, end, v ) ;
333    uInt offset = start * nx ;
334    flatIdx = offset + ix ;
335    flatIdxList[n++] = flatIdx ;
336    if ( start != end ) {
337      offset = end * nx ;
338      flatIdx = offset + ix ;
339      flatIdxList[n++] = flatIdx ;
340    }
341  }
342  for ( uInt iy = 0 ; iy < ny ; iy++ ) {
343    Vector<uInt> v( a.column( iy ) ) ;
344    if ( allEQ( v, (uInt)0 ) ) {
345      continue ;
346    }
347    uInt offset = iy * nx ;
348    _search( start, end, v ) ;
349    flatIdx = offset + start ;
350    flatIdxList[n++] = flatIdx ;
351    if ( start != end ) {
352      flatIdx = offset + end ;
353      flatIdxList[n++] = flatIdx ;
354    }
355  }
356  n = genSort( flatIdxList.storage(),
357               n,
358               Sort::Ascending,
359               Sort::QuickSort | Sort::NoDuplicates ) ;
360  Vector<uInt> v( IPosition(1,nx*ny), a.data(), SHARE ) ;
361  const uInt *idx_p = flatIdxList.storage() ;
362  for ( uInt i = 0 ; i < n ; i++ ) {
363    v[*idx_p] = 0 ;
364    idx_p++ ;
365  }
366
367  return n ;
368}
369
370uInt GenericEdgeDetector::_trimming1DX( Matrix<uInt> &a )
371{
372  uInt n = 0 ;
373  const uInt nx = a.nrow() ;
374  Vector<uInt> v1, v2 ;
375  uInt ix, jx ;
376  for ( ix = 0 ; ix < nx ; ix++ ) {
377    v1.reference( a.row( ix ) ) ;
378    if ( anyNE( v1, n ) ) break ;
379  }
380  for ( jx = nx-1 ; jx > ix ; jx-- ) {
381    v2.reference( a.row( jx ) ) ;
382    if ( anyNE( v2, n ) ) break ;
383  }
384  n += _trimming1D( v1 ) ;
385  if ( ix != jx )
386    n+= _trimming1D( v2 ) ;
387 
388  return n ;
389}
390
391uInt GenericEdgeDetector::_trimming1DY( Matrix<uInt> &a )
392{
393  uInt n = 0 ;
394  const uInt ny = a.ncolumn() ;
395  Vector<uInt> v1, v2 ;
396  uInt iy, jy ;
397  for ( iy = 0 ; iy < ny ; iy++ ) {
398    v1.reference( a.column( iy ) ) ;
399    if ( anyNE( v1, n ) ) break ;
400  }
401  for ( jy = ny-1 ; jy > iy ; jy-- ) {
402    v2.reference( a.column( jy ) ) ;
403    if ( anyNE( v2, n ) ) break ;
404  }
405  n += _trimming1D( v1 ) ;
406  if ( iy != jy )
407    n+= _trimming1D( v2 ) ;
408 
409  return n ;
410}
411
412uInt GenericEdgeDetector::_trimming1D( Vector<uInt> &a )
413{
414  uInt len = a.nelements() ;
415  uInt n = 0 ;
416  for ( uInt i = 0 ; i < len ; i++ ) {
417    if ( a[i] == 1 ) {
418      a[i] = 0 ;
419      n++ ;
420    }
421  }
422 
423  return n ;
424}
425
426void GenericEdgeDetector::selection()
427{
428  os_.origin(LogOrigin( "GenericEdgeDetector", "selection", WHERE )) ;
429
430  uInt nrow = pdir_.shape()[1] ;
431  os_ << "nrow=" << nrow << LogIO::POST ;
432  const Double *px_p = pdir_.data() ;
433  const Double *py_p = px_p + 1 ;
434  Vector<uInt> v( IPosition(1,apix_.nelements()), apix_.data(), SHARE ) ;
435  uInt n = 0 ;
436  for ( uInt irow = 0 ; irow < nrow ; irow++ ) {
437    uInt idx = int(round(*px_p)) + int(round(*py_p)) * nx_ ;
438    if ( v[idx] == 0 ) {
439      tempuInt_[n++] = irow ;
440    }
441    px_p += 2 ;
442    py_p += 2 ;
443  }
444  os_ << "off_.nelements()=" << off_.nelements() << LogIO::POST ;
445  off_ = vectorFromTempStorage( n ) ;
446  os_ << "off_.nelements()=" << off_.nelements() << LogIO::POST ;
447}
448
449void GenericEdgeDetector::tuning()
450{
451  os_.origin(LogOrigin( "GenericEdgeDetector", "tuning", WHERE )) ;
452
453  os_ << "start" << LogIO::POST ;
454  const uInt len = off_.nelements() ;
455  os_ << "len=" << len << LogIO::POST ;
456  if ( len == 0 )
457    return ;
458
459  Vector<uInt> diff = off_.copy() ;
460  for ( uInt i = len-1 ; i > 0 ; i-- ) {
461    diff[i] -= diff[i-1] ;
462  }
463  const uInt threshold = 3 ;
464  uInt n = 0 ;
465  for ( uInt i = 0 ; i < len ; i++ ) {
466    tempuInt_[n++] = off_[i] ;
467  }
468  for ( uInt i = 1 ; i < len ; i++ ) {
469    if ( diff[i] != 1 && diff[i] < threshold ) {
470      uInt t = off_[i-1]+1 ;
471      uInt u = off_[i] ;
472      for ( uInt j = t ; j < u ; j++ ) {
473        os_ << "move " << j << " from ON to OFF" << LogIO::POST ;
474        tempuInt_[n++] = j ;
475      }
476    }
477  }
478  if ( n > len ) {
479    off_.resize() ;
480    off_ = vectorFromTempStorage( n ) ;
481  }
482}
483
484} // namespace asap
Note: See TracBrowser for help on using the repository browser.