File Coverage

blib/lib/PDL/ImageND.pm
Criterion Covered Total %
statement 127 169 75.1
branch 28 58 48.2
condition 2 9 22.2
subroutine 14 17 82.3
pod 2 7 28.5
total 173 260 66.5


line stmt bran cond sub pod time code
1              
2             #
3             # GENERATED WITH PDL::PP! Don't modify!
4             #
5             package PDL::ImageND;
6              
7             @EXPORT_OK = qw( kernctr PDL::PP convolve ninterpol PDL::PP rebin circ_mean circ_mean_p PDL::PP convolveND );
8             %EXPORT_TAGS = (Func=>[@EXPORT_OK]);
9              
10 3     3   491 use PDL::Core;
  3         7  
  3         52  
11 3     3   23 use PDL::Exporter;
  3         5  
  3         17  
12 3     3   14 use DynaLoader;
  3         6  
  3         242  
13              
14              
15              
16            
17             @ISA = ( 'PDL::Exporter','DynaLoader' );
18             push @PDL::Core::PP, __PACKAGE__;
19             bootstrap PDL::ImageND ;
20              
21              
22              
23              
24              
25             =head1 NAME
26              
27             PDL::ImageND - useful image processing in N dimensions
28              
29             =head1 DESCRIPTION
30              
31             These routines act on PDLs as N-dimensional objects, not as threaded
32             sets of 0-D or 1-D objects. The file is sort of a catch-all for
33             broadly functional routines, most of which could legitimately
34             be filed elsewhere (and probably will, one day).
35              
36             ImageND is not a part of the PDL core (v2.4) and hence must be explicitly
37             loaded.
38              
39             =head1 SYNOPSIS
40              
41             use PDL::ImageND;
42              
43             $y = $x->convolveND($kernel,{bound=>'periodic'});
44             $y = $x->rebin(50,30,10);
45            
46             =cut
47              
48              
49              
50              
51              
52              
53              
54              
55             =head1 FUNCTIONS
56              
57              
58              
59             =cut
60              
61              
62              
63              
64              
65 3     3   21 use Carp;
  3         7  
  3         830  
66              
67              
68              
69              
70              
71             =head2 convolve
72              
73             =for sig
74              
75             Signature: (a(m); b(n); indx adims(p); indx bdims(q); [o]c(m))
76              
77             =for ref
78              
79             N-dimensional convolution (Deprecated; use convolveND)
80              
81             =for usage
82              
83             $new = convolve $x, $kernel
84              
85             Convolve an array with a kernel, both of which are N-dimensional. This
86             routine does direct convolution (by copying) but uses quasi-periodic
87             boundary conditions: each dim "wraps around" to the next higher row in
88             the next dim.
89              
90             This routine is kept for backwards compatibility with earlier scripts;
91             for most purposes you want L instead:
92             it runs faster and handles a variety of boundary conditions.
93              
94              
95              
96             =for bad
97              
98             convolve does not process bad values.
99             It will set the bad-value flag of all output piddles if the flag is set for any of the input piddles.
100              
101              
102             =cut
103              
104              
105              
106              
107              
108              
109             # Custom Perl wrapper
110              
111             sub PDL::convolve{
112 1     1 0 8 my($x,$y,$c) = @_;
113 1 50 33     9 barf("Usage: convolve(a(*), b(*), [o]c(*)") if $#_<1 || $#_>2;
114 1 50       7 $c = PDL->null if $#_<2;
115 1 50       4 &PDL::_convolve_int( $x->clump(-1), $y->clump(-1),
116             long([$x->dims]), long([$y->dims]),
117             ($c->getndims>1? $c->clump(-1) : $c)
118             );
119 1         24 $c->setdims([$x->dims]);
120              
121 1 50       7 if($x->is_inplace) {
122 0         0 $x .= $c;
123 0         0 $x->set_inplace(0);
124 0         0 return $x;
125             }
126 1         4 return $c;
127             }
128              
129              
130              
131             *convolve = \&PDL::convolve;
132              
133              
134              
135              
136             =head2 ninterpol()
137              
138             =for ref
139              
140             N-dimensional interpolation routine
141              
142             =for sig
143              
144             Signature: ninterpol(point(),data(n),[o]value())
145              
146             =for usage
147              
148             $value = ninterpol($point, $data);
149              
150             C uses C to find a linearly interpolated value in
151             N dimensions, assuming the data is spread on a uniform grid. To use
152             an arbitrary grid distribution, need to find the grid-space point from
153             the indexing scheme, then call C -- this is far from
154             trivial (and ill-defined in general).
155              
156             See also L, which includes boundary
157             conditions and allows you to switch the method of interpolation, but
158             which runs somewhat slower.
159              
160             =cut
161              
162              
163             *ninterpol = \&PDL::ninterpol;
164              
165             sub PDL::ninterpol {
166 3     3   24 use PDL::Math 'floor';
  3         6  
  3         21  
167 3     3   20 use PDL::Primitive 'interpol';
  3         6  
  3         21  
168 0 0   0 0 0 print 'Usage: $x = ninterpolate($point(s), $data);' if $#_ != 1;
169 0         0 my ($p, $y) = @_;
170 0         0 my ($ip) = floor($p);
171             # isolate relevant N-cube
172 0         0 $y = $y->slice(join (',',map($_.':'.($_+1),list $ip)));
173 0         0 for (list ($p-$ip)) { $y = interpol($_,$y->xvals,$y); }
  0         0  
174 0         0 $y;
175             }
176              
177              
178              
179              
180              
181             =head2 rebin
182              
183             =for sig
184              
185             Signature: (a(m); [o]b(n); int ns => n)
186              
187             =for ref
188              
189             N-dimensional rebinning algorithm
190              
191             =for usage
192              
193             $new = rebin $x, $dim1, $dim2,..;.
194             $new = rebin $x, $template;
195             $new = rebin $x, $template, {Norm => 1};
196              
197             Rebin an N-dimensional array to newly specified dimensions.
198             Specifying `Norm' keeps the sum constant, otherwise the intensities
199             are kept constant. If more template dimensions are given than for the
200             input pdl, these dimensions are created; if less, the final dimensions
201             are maintained as they were.
202              
203             So if C<$x> is a 10 x 10 pdl, then C is a 15 x 10 pdl,
204             while C is a 15 x 16 x 17 pdl (where the values
205             along the final dimension are all identical).
206              
207             Expansion is performed by sampling; reduction is performed by averaging.
208             If you want different behavior, use L
209             instead. PDL::Transform::map runs slower but is more flexible.
210              
211              
212              
213             =for bad
214              
215             rebin does not process bad values.
216             It will set the bad-value flag of all output piddles if the flag is set for any of the input piddles.
217              
218              
219             =cut
220              
221              
222              
223              
224              
225              
226             # Custom Perl wrapper
227              
228             sub PDL::rebin {
229 1     1 0 9 my($x) = shift;
230 1 50       4 my($opts) = ref $_[-1] eq "HASH" ? pop : {};
231 1         4 my(@idims) = $x->dims;
232 1 50       5 my(@odims) = ref $_[0] ? $_[0]->dims : @_;
233 1         3 my($i,$y);
234 1         3 foreach $i (0..$#odims) {
235 2 50       9 if ($i > $#idims) { # Just dummy extra dimensions
    50          
236 0         0 $x = $x->dummy($i,$odims[$i]);
237 0         0 next;
238             # rebin_int can cope with all cases, but code
239             # 1->n and n->1 separately for speed
240             } elsif ($odims[$i] != $idims[$i]) { # If something changes
241 2 50       8 if (!($odims[$i] % $idims[$i])) { # Cells map 1 -> n
    50          
242 0         0 my ($r) = $odims[$i]/$idims[$i];
243 0         0 $y = $x->mv($i,0)->dummy(0,$r)->clump(2);
244             } elsif (!($idims[$i] % $odims[$i])) { # Cells map n -> 1
245 2         7 my ($r) = $idims[$i]/$odims[$i];
246 2         13 $x = $x->mv($i,0);
247             # -> copy so won't corrupt input PDL
248 2         24 $y = $x->slice("0:-1:$r")->copy;
249 2         15 foreach (1..$r-1) {
250 2         9 $y += $x->slice("$_:-1:$r");
251             }
252 2         8 $y /= $r;
253             } else { # Cells map n -> m
254 0         0 &PDL::_rebin_int($x->mv($i,0), $y = null, $odims[$i]);
255             }
256 2         20 $x = $y->mv(0,$i);
257             }
258             }
259 1 50 33     10 if (exists $opts->{Norm} and $opts->{Norm}) {
260 1         4 my ($norm) = 1;
261 1         4 for $i (0..$#odims) {
262 2 50       6 if ($i > $#idims) {
263 0         0 $norm /= $odims[$i];
264             } else {
265 2         6 $norm *= $idims[$i]/$odims[$i];
266             }
267             }
268 1         128 return $x * $norm;
269             } else {
270             # Explicit copy so i) can't corrupt input PDL through this link
271             # ii) don't waste space on invisible elements
272 0         0 return $x -> copy;
273             }
274             }
275              
276              
277             *rebin = \&PDL::rebin;
278              
279              
280              
281              
282             =head2 circ_mean_p
283              
284             =for ref
285              
286             Calculates the circular mean of an n-dim image and returns
287             the projection. Optionally takes the center to be used.
288              
289             =for usage
290              
291             $cmean=circ_mean_p($im);
292             $cmean=circ_mean_p($im,{Center => [10,10]});
293              
294             =cut
295              
296              
297             sub circ_mean_p {
298 0     0 1 0 my ($x,$opt) = @_;
299 0         0 my ($rad,$sum,$norm);
300              
301 0 0       0 if (defined $opt) {
302 0         0 $rad = long PDL::rvals($x,$opt);
303             }
304             else {
305 0         0 $rad = long rvals $x;
306             }
307 0         0 $sum = zeroes($rad->max+1);
308 0         0 PDL::indadd $x->clump(-1), $rad->clump(-1), $sum; # this does the real work
309 0         0 $norm = zeroes($rad->max+1);
310 0         0 PDL::indadd pdl(1), $rad->clump(-1), $norm; # equivalent to get norm
311 0         0 $sum /= $norm;
312 0         0 return $sum;
313             }
314              
315             =head2 circ_mean
316              
317             =for ref
318              
319             Smooths an image by applying circular mean.
320             Optionally takes the center to be used.
321              
322             =for usage
323              
324             circ_mean($im);
325             circ_mean($im,{Center => [10,10]});
326              
327             =cut
328              
329              
330             sub circ_mean {
331 0     0 1 0 my ($x,$opt) = @_;
332 0         0 my ($rad,$sum,$norm,$a1);
333              
334 0 0       0 if (defined $opt) {
335 0         0 $rad = long PDL::rvals($x,$opt);
336             }
337             else {
338 0         0 $rad = long rvals $x;
339             }
340 0         0 $sum = zeroes($rad->max+1);
341 0         0 PDL::indadd $x->clump(-1), $rad->clump(-1), $sum; # this does the real work
342 0         0 $norm = zeroes($rad->max+1);
343 0         0 PDL::indadd pdl(1), $rad->clump(-1), $norm; # equivalent to get norm
344 0         0 $sum /= $norm;
345 0         0 $a1 = $x->clump(-1);
346 0         0 $a1 .= $sum->index($rad->clump(-1));
347              
348 0         0 return $x;
349             }
350              
351              
352              
353              
354             =head2 kernctr
355              
356             =for ref
357              
358             `centre' a kernel (auxiliary routine to fftconvolve)
359              
360             =for usage
361              
362             $kernel = kernctr($image,$smallk);
363             fftconvolve($image,$kernel);
364              
365             kernctr centres a small kernel to emulate the behaviour of the direct
366             convolution routines.
367              
368             =cut
369              
370              
371             *kernctr = \&PDL::kernctr;
372              
373             sub PDL::kernctr {
374             # `centre' the kernel, to match kernel & image sizes and
375             # emulate convolve/conv2d. FIX: implement with phase shifts
376             # in fftconvolve, with option tag
377 2 50   2 0 39 barf "Must have image & kernel for kernctr" if $#_ != 1;
378 2         9 my ($imag, $kern) = @_;
379 2         18 my (@ni) = $imag->dims;
380 2         10 my (@nk) = $kern->dims;
381 2 50       9 barf "Kernel and image must have same number of dims" if $#ni != $#nk;
382 2         13 my ($newk) = zeroes(double,@ni);
383 2         8 my ($k,$n,$y,$d,$i,@stri,@strk,@b);
384 2         13 for ($i=0; $i <= $#ni; $i++) {
385 4         8 $k = $nk[$i];
386 4         7 $n = $ni[$i];
387 4 50       11 barf "Kernel must be smaller than image in all dims" if ($n < $k);
388 4         15 $d = int(($k-1)/2);
389 4         19 $stri[$i][0] = "0:$d,";
390 4         14 $strk[$i][0] = (-$d-1).":-1,";
391 4 50       15 $stri[$i][1] = $d == 0 ? '' : ($d-$k+1).':-1,';
392 4 50       21 $strk[$i][1] = $d == 0 ? '' : '0:'.($k-$d-2).',';
393             }
394             # kernel is split between the 2^n corners of the cube
395 2         7 my ($nchunk) = 2 << $#ni;
396             CHUNK:
397 2         9 for ($i=0; $i < $nchunk; $i++) {
398 8         17 my ($stri,$strk);
399 8         22 for ($n=0, $y=$i; $n <= $#ni; $n++, $y >>= 1) {
400 16 50       35 next CHUNK if $stri[$n][$y & 1] eq '';
401 16         25 $stri .= $stri[$n][$y & 1];
402 16         39 $strk .= $strk[$n][$y & 1];
403             }
404 8         15 chop ($stri); chop ($strk);
  8         12  
405 8         28 ($t = $newk->slice($stri)) .= $kern->slice($strk);
406             }
407 2         21 $newk;
408             }
409              
410              
411              
412              
413              
414             =head2 convolveND
415              
416             =for sig
417              
418             Signature: (k0(); SV *k; SV *aa; SV *a)
419              
420              
421             =for ref
422              
423             Speed-optimized convolution with selectable boundary conditions
424              
425             =for usage
426              
427             $new = convolveND($x, $kernel, [ {options} ]);
428              
429             Conolve an array with a kernel, both of which are N-dimensional.
430              
431             If the kernel has fewer dimensions than the array, then the extra array
432             dimensions are threaded over. There are options that control the boundary
433             conditions and method used.
434              
435             The kernel's origin is taken to be at the kernel's center. If your
436             kernel has a dimension of even order then the origin's coordinates get
437             rounded up to the next higher pixel (e.g. (1,2) for a 3x4 kernel).
438             This mimics the behavior of the earlier L and
439             L routines, so convolveND is a drop-in
440             replacement for them.
441              
442              
443             The kernel may be any size compared to the image, in any dimension.
444              
445             The kernel and the array are not quite interchangeable (as in mathematical
446             convolution): the code is inplace-aware only for the array itself, and
447             the only allowed boundary condition on the kernel is truncation.
448              
449             convolveND is inplace-aware: say C to modify
450             a variable in-place. You don't reduce the working memory that way -- only
451             the final memory.
452              
453             OPTIONS
454              
455             Options are parsed by PDL::Options, so unique abbreviations are accepted.
456              
457             =over 3
458              
459             =item boundary (default: 'truncate')
460              
461             The boundary condition on the array, which affects any pixel closer
462             to the edge than the half-width of the kernel.
463              
464             The boundary conditions are the same as those accepted by
465             L, because this option is passed directly
466             into L. Useful options are 'truncate' (the
467             default), 'extend', and 'periodic'. You can select different boundary
468             conditions for different axes -- see L for more
469             detail.
470              
471             The (default) truncate option marks all the near-boundary pixels as BAD if
472             you have bad values compiled into your PDL and the array's badflag is set.
473              
474             =item method (default: 'auto')
475              
476             The method to use for the convolution. Acceptable alternatives are
477             'direct', 'fft', or 'auto'. The direct method is an explicit
478             copy-and-multiply operation; the fft method takes the Fourier
479             transform of the input and output kernels. The two methods give the
480             same answer to within double-precision numerical roundoff. The fft
481             method is much faster for large kernels; the direct method is faster
482             for tiny kernels. The tradeoff occurs when the array has about 400x
483             more pixels than the kernel.
484              
485             The default method is 'auto', which chooses direct or fft convolution
486             based on the size of the input arrays.
487              
488             =back
489              
490             NOTES
491              
492             At the moment there's no way to thread over kernels. That could/should
493             be fixed.
494              
495             The threading over input is cheesy and should probably be fixed:
496             currently the kernel just gets dummy dimensions added to it to match
497             the input dims. That does the right thing tersely but probably runs slower
498             than a dedicated threadloop.
499              
500             The direct copying code uses PP primarily for the generic typing: it includes
501             its own threadloops.
502              
503              
504              
505             =for bad
506              
507             convolveND does not process bad values.
508             It will set the bad-value flag of all output piddles if the flag is set for any of the input piddles.
509              
510              
511             =cut
512              
513              
514              
515              
516              
517 3     3   25 use PDL::Options;
  3         219  
  3         1952  
518              
519             # Perl wrapper conditions the data to make life easier for the PP sub.
520              
521             sub PDL::convolveND {
522 6     6 0 35 my($a0,$k,$opt0) = @_;
523 6         22 my $inplace = $a0->is_inplace;
524 6         20 my $x = $a0->new_or_inplace;
525              
526            
527 6 50       32 barf("convolveND: kernel (".join("x",$k->dims).") has more dims than source (".join("x",$x->dims).")\n")
528             if($x->ndims < $k->ndims);
529            
530              
531             # Coerce stuff all into the same type. Try to make sense.
532             # The trivial conversion leaves dataflow intact (nontrivial conversions
533             # don't), so the inplace code is OK. Non-inplace code: let the existing
534             # PDL code choose what type is best.
535 6         11 my $type;
536 6 50       11 if($inplace) {
537 0         0 $type = $a0->get_datatype;
538             } else {
539 6         18 my $z = $x->flat->index(0) + $k->flat->index(0);
540 6         135 $type = $z->get_datatype;
541             }
542 6         21 $x = $x->convert($type);
543 6         18 $k = $k->convert($type);
544            
545              
546             ## Handle options -- $def is a static variable so it only gets set up once.
547 6         10 our $def;
548 6 100       13 unless(defined($def)) {
549 1         8 $def = new PDL::Options( {
550             Method=>'a',
551             Boundary=>'t'
552             }
553             );
554 1         4 $def->minmatch(1);
555 1         3 $def->casesens(0);
556             }
557              
558 6         18 my $opt = $def->options(PDL::Options::ifhref($opt0));
559              
560             ###
561             # If the kernel has too few dimensions, we thread over the other
562             # dims -- this is the same as supplying the kernel with dummy dims of
563             # order 1, so, er, we do that.
564 6 50       68 $k = $k->dummy($x->dims - 1, 1)
565             if($x->ndims > $k->ndims);
566 6         22 my $kdims = pdl($k->dims);
567              
568             ###
569             # Decide whether to FFT or directly convolve: if we're in auto mode,
570             # choose based on the relative size of the image and kernel arrays.
571             my $fft = ( ($opt->{Method} =~ m/^a/i) ?
572             ( $x->nelem > 2500 and ($x->nelem) <= ($k->nelem * 500) ) :
573 6 50 0     32 ( $opt->{Method} !~ m/^[ds]/i )
574             );
575              
576             ###
577             # Pad the array to include boundary conditions
578 6         17 my $adims = pdl($x->dims);
579 6         345 my $koff = ($kdims/2)->ceil - 1;
580              
581             my $aa = $x->range( -$koff, $adims + $kdims, $opt->{Boundary} )
582 6         184 ->sever;
583              
584 6 100       84 if($fft) {
585             # The eval here keeps conflicts from happening at compile time
586 3     1   234 eval "use PDL::FFT" ;
  1     1   583  
  1     1   5  
  1         6  
  1         7  
  1         3  
  1         4  
  1         8  
  1         2  
  1         5  
587              
588 3 50       15 print "convolveND: using FFT method\n" if($PDL::debug);
589              
590             # FFT works best on doubles; do our work there then cast back
591             # at the end.
592 3         11 $aa = double($aa);
593 3         12 my $aai = $aa->zeroes;
594              
595 3         10 my $kk = $aa->zeroes;
596 3         10 my $kki = $aa->zeroes;
597 3         7 my $tmp; # work around new perl -d "feature"
598 3         147 ($tmp = $kk->range( - ($kdims/2)->floor, $kdims, 'p')) .= $k;
599 3         41 PDL::fftnd($kk, $kki);
600 3         9 PDL::fftnd($aa, $aai);
601              
602             {
603 3         6 my($ii) = $kk * $aai + $aa * $kki;
  3         152  
604 3         146 $aa = $aa * $kk - $kki * $aai;
605 3         22 $aai .= $ii;
606             }
607              
608 3         13 PDL::ifftnd($aa,$aai);
609 3         10 $x .= $aa->range( $koff, $adims);
610              
611             } else {
612 3 50       9 print "convolveND: using direct method\n" if($PDL::debug);
613              
614             ### The first argument is a dummy to set $GENERIC.
615 3         10 &PDL::_convolveND_int( $k->flat->index(0), $k, $aa, $x );
616              
617             }
618              
619              
620 6         84 $x;
621             }
622              
623              
624              
625             *convolveND = \&PDL::convolveND;
626              
627              
628              
629             ;
630              
631              
632             =head1 AUTHORS
633              
634             Copyright (C) Karl Glazebrook and Craig DeForest, 1997, 2003
635             All rights reserved. There is no warranty. You are allowed
636             to redistribute this software / documentation under certain
637             conditions. For details, see the file COPYING in the PDL
638             distribution. If this file is separated from the PDL distribution,
639             the copyright notice should be included in the file.
640              
641             =cut
642              
643              
644              
645              
646              
647              
648             # Exit with OK status
649              
650             1;
651              
652