File Coverage

blib/lib/PDL/Transform.pm
Criterion Covered Total %
statement 393 881 44.6
branch 141 402 35.0
condition 51 166 30.7
subroutine 30 64 46.8
pod 17 24 70.8
total 632 1537 41.1


line stmt bran cond sub pod time code
1              
2             #
3             # GENERATED WITH PDL::PP! Don't modify!
4             #
5             package PDL::Transform;
6              
7             @EXPORT_OK = qw( apply invert map PDL::PP map unmap t_inverse t_compose t_wrap t_identity t_lookup t_linear t_scale t_offset t_rot t_fits t_code t_cylindrical t_radial t_quadratic t_cubic t_quadratic t_spherical t_projective );
8             %EXPORT_TAGS = (Func=>[@EXPORT_OK]);
9              
10 1     1   927 use PDL::Core;
  1         4  
  1         9  
11 1     1   7 use PDL::Exporter;
  1         2  
  1         5  
12 1     1   14 use DynaLoader;
  1         2  
  1         121  
13              
14              
15              
16            
17             @ISA = ( 'PDL::Exporter','DynaLoader' );
18             push @PDL::Core::PP, __PACKAGE__;
19             bootstrap PDL::Transform ;
20              
21              
22              
23              
24              
25             =head1 NAME
26              
27             PDL::Transform - Coordinate transforms, image warping, and N-D functions
28              
29             =head1 SYNOPSIS
30              
31             use PDL::Transform;
32              
33             my $t = new PDL::Transform::()
34              
35             $out = $t->apply($in) # Apply transform to some N-vectors (Transform method)
36             $out = $in->apply($t) # Apply transform to some N-vectors (PDL method)
37              
38             $im1 = $t->map($im); # Transform image coordinates (Transform method)
39             $im1 = $im->map($t); # Transform image coordinates (PDL method)
40              
41             $t2 = $t->compose($t1); # compose two transforms
42             $t2 = $t x $t1; # compose two transforms (by analogy to matrix mult.)
43              
44             $t3 = $t2->inverse(); # invert a transform
45             $t3 = !$t2; # invert a transform (by analogy to logical "not")
46              
47             =head1 DESCRIPTION
48              
49             PDL::Transform is a convenient way to represent coordinate
50             transformations and resample images. It embodies functions mapping
51             R^N -> R^M, both with and without inverses. Provision exists for
52             parametrizing functions, and for composing them. You can use this
53             part of the Transform object to keep track of arbitrary functions
54             mapping R^N -> R^M with or without inverses.
55              
56             The simplest way to use a Transform object is to transform vector
57             data between coordinate systems. The L method
58             accepts a PDL whose 0th dimension is coordinate index (all other
59             dimensions are threaded over) and transforms the vectors into the new
60             coordinate system.
61              
62             Transform also includes image resampling, via the L method.
63             You define a coordinate transform using a Transform object, then use
64             it to remap an image PDL. The output is a remapped, resampled image.
65              
66             You can define and compose several transformations, then apply them
67             all at once to an image. The image is interpolated only once, when
68             all the composed transformations are applied.
69              
70             In keeping with standard practice, but somewhat counterintuitively,
71             the L engine uses the inverse transform to map coordinates
72             FROM the destination dataspace (or image plane) TO the source dataspace;
73             hence PDL::Transform keeps track of both the forward and inverse transform.
74              
75             For terseness and convenience, most of the constructors are exported
76             into the current package with the name C<< t_ >>, so the following
77             (for example) are synonyms:
78              
79             $t = new PDL::Transform::Radial(); # Long way
80              
81             $t = t_radial(); # Short way
82              
83             Several math operators are overloaded, so that you can compose and
84             invert functions with expression syntax instead of method syntax (see below).
85              
86             =head1 EXAMPLE
87              
88             Coordinate transformations and mappings are a little counterintuitive
89             at first. Here are some examples of transforms in action:
90              
91             use PDL::Transform;
92             $x = rfits('m51.fits'); # Substitute path if necessary!
93             $ts = t_linear(Scale=>3); # Scaling transform
94              
95             $w = pgwin(xs);
96             $w->imag($x);
97              
98             ## Grow m51 by a factor of 3; origin is at lower left.
99             $y = $ts->map($x,{pix=>1}); # pix option uses direct pixel coord system
100             $w->imag($y);
101              
102             ## Shrink m51 by a factor of 3; origin still at lower left.
103             $c = $ts->unmap($x, {pix=>1});
104             $w->imag($c);
105              
106             ## Grow m51 by a factor of 3; origin is at scientific origin.
107             $d = $ts->map($x,$x->hdr); # FITS hdr template prevents autoscaling
108             $w->imag($d);
109              
110             ## Shrink m51 by a factor of 3; origin is still at sci. origin.
111             $e = $ts->unmap($x,$x->hdr);
112             $w->imag($e);
113              
114             ## A no-op: shrink m51 by a factor of 3, then autoscale back to size
115             $f = $ts->map($x); # No template causes autoscaling of output
116              
117             =head1 OPERATOR OVERLOADS
118              
119             =over 3
120              
121             =item '!'
122              
123             The bang is a unary inversion operator. It binds exactly as
124             tightly as the normal bang operator.
125              
126             =item 'x'
127              
128             By analogy to matrix multiplication, 'x' is the compose operator, so these
129             two expressions are equivalent:
130              
131             $f->inverse()->compose($g)->compose($f) # long way
132             !$f x $g x $f # short way
133              
134             Both of those expressions are equivalent to the mathematical expression
135             f^-1 o g o f, or f^-1(g(f(x))).
136              
137             =item '**'
138              
139             By analogy to numeric powers, you can apply an operator a positive
140             integer number of times with the ** operator:
141              
142             $f->compose($f)->compose($f) # long way
143             $f**3 # short way
144              
145             =back
146              
147             =head1 INTERNALS
148              
149             Transforms are perl hashes. Here's a list of the meaning of each key:
150              
151             =over 3
152              
153             =item func
154              
155             Ref to a subroutine that evaluates the transformed coordinates. It's
156             called with the input coordinate, and the "params" hash. This
157             springboarding is done via explicit ref rather than by subclassing,
158             for convenience both in coding new transforms (just add the
159             appropriate sub to the module) and in adding custom transforms at
160             run-time. Note that, if possible, new Cs should support
161             L operation to save memory when the data are flagged
162             inplace. But C should always return its result even when
163             flagged to compute in-place.
164              
165             C should treat the 0th dimension of its input as a dimensional
166             index (running 0..N-1 for R^N operation) and thread over all other input
167             dimensions.
168              
169             =item inv
170              
171             Ref to an inverse method that reverses the transformation. It must
172             accept the same "params" hash that the forward method accepts. This
173             key can be left undefined in cases where there is no inverse.
174              
175             =item idim, odim
176              
177             Number of useful dimensions for indexing on the input and output sides
178             (ie the order of the 0th dimension of the coordinates to be fed in or
179             that come out). If this is set to 0, then as many are allocated as needed.
180              
181             =item name
182              
183             A shorthand name for the transformation (convenient for debugging).
184             You should plan on using UNIVERAL::isa to identify classes of
185             transformation, e.g. all linear transformations should be subclasses
186             of PDL::Transform::Linear. That makes it easier to add smarts to,
187             e.g., the compose() method.
188              
189             =item itype
190              
191             An array containing the name of the quantity that is expected from the
192             input piddle for the transform, for each dimension. This field is advisory,
193             and can be left blank if there's no obvious quantity associated with
194             the transform. This is analogous to the CTYPEn field used in FITS headers.
195              
196             =item oname
197              
198             Same as itype, but reporting what quantity is delivered for each
199             dimension.
200              
201             =item iunit
202              
203             The units expected on input, if a specific unit (e.g. degrees) is expected.
204             This field is advisory, and can be left blank if there's no obvious
205             unit associated with the transform.
206              
207             =item ounit
208              
209             Same as iunit, but reporting what quantity is delivered for each dimension.
210              
211             =item params
212              
213             Hash ref containing relevant parameters or anything else the func needs to
214             work right.
215              
216             =item is_inverse
217              
218             Bit indicating whether the transform has been inverted. That is useful
219             for some stringifications (see the PDL::Transform::Linear
220             stringifier), and may be useful for other things.
221              
222             =back
223              
224             Transforms should be inplace-aware where possible, to prevent excessive
225             memory usage.
226              
227             If you define a new type of transform, consider generating a new stringify
228             method for it. Just define the sub "stringify" in the subclass package.
229             It should call SUPER::stringify to generate the first line (though
230             the PDL::Transform::Composition bends this rule by tweaking the
231             top-level line), then output (indented) additional lines as necessary to
232             fully describe the transformation.
233              
234             =head1 NOTES
235              
236             Transforms have a mechanism for labeling the units and type of each
237             coordinate, but it is just advisory. A routine to identify and, if
238             necessary, modify units by scaling would be a good idea. Currently,
239             it just assumes that the coordinates are correct for (e.g.) FITS
240             scientific-to-pixel transformations.
241              
242             Composition works OK but should probably be done in a more
243             sophisticated way so that, for example, linear transformations are
244             combined at the matrix level instead of just strung together
245             pixel-to-pixel.
246              
247             =head1 MODULE INTERFACE
248              
249             There are both operators and constructors. The constructors are all
250             exported, all begin with "t_", and all return objects that are subclasses
251             of PDL::Transform.
252              
253             The L, L, L,
254             and L methods are also exported to the C package: they
255             are both Transform methods and PDL methods.
256              
257             =cut
258              
259              
260              
261              
262              
263              
264              
265             =head1 FUNCTIONS
266              
267              
268              
269             =cut
270              
271              
272              
273              
274              
275             =head2 apply
276              
277             =for sig
278              
279             Signature: (data(); PDL::Transform t)
280              
281             =for usage
282              
283             $out = $data->apply($t);
284             $out = $t->apply($data);
285              
286             =for ref
287              
288             Apply a transformation to some input coordinates.
289              
290             In the example, C<$t> is a PDL::Transform and C<$data> is a PDL to
291             be interpreted as a collection of N-vectors (with index in the 0th
292             dimension). The output is a similar but transformed PDL.
293              
294             For convenience, this is both a PDL method and a Transform method.
295              
296             =cut
297              
298 1     1   6 use Carp;
  1         3  
  1         10762  
299              
300             *PDL::apply = \&apply;
301             sub apply {
302 21     21 1 551 my($me) = shift;
303 21         39 my($from) = shift;
304              
305 21 100       121 if(UNIVERSAL::isa($me,'PDL')){
306 13         29 my($x) = $from;
307 13         24 $from = $me;
308 13         27 $me = $x;
309             }
310              
311 21 50 33     194 if(UNIVERSAL::isa($me,'PDL::Transform') && UNIVERSAL::isa($from,'PDL')){
312 21 50 33     189 croak "Applying a PDL::Transform with no func! Oops.\n" unless(defined($me->{func}) and ref($me->{func}) eq 'CODE');
313 21         63 my $result = &{$me->{func}}($from,$me->{params});
  21         66  
314 21         85 $result->is_inplace(0); # clear inplace flag, just in case.
315 21         154 return $result;
316             } else {
317 0         0 croak "apply requires both a PDL and a PDL::Transform.\n";
318             }
319              
320             }
321              
322              
323              
324              
325             =head2 invert
326              
327             =for sig
328              
329             Signature: (data(); PDL::Transform t)
330              
331             =for usage
332              
333             $out = $t->invert($data);
334             $out = $data->invert($t);
335              
336             =for ref
337              
338             Apply an inverse transformation to some input coordinates.
339              
340             In the example, C<$t> is a PDL::Transform and C<$data> is a piddle to
341             be interpreted as a collection of N-vectors (with index in the 0th
342             dimension). The output is a similar piddle.
343              
344             For convenience this is both a PDL method and a PDL::Transform method.
345              
346             =cut
347              
348             *PDL::invert = \&invert;
349             sub invert {
350 28     28 1 64 my($me) = shift;
351 28         49 my($data) = shift;
352              
353 28 100       157 if(UNIVERSAL::isa($me,'PDL')){
354 1         3 my($x) = $data;
355 1         2 $data = $me;
356 1         2 $me = $x;
357             }
358              
359 28 50 33     191 if(UNIVERSAL::isa($me,'PDL::Transform') && UNIVERSAL::isa($data,'PDL')){
360 28 50 33     167 croak "Inverting a PDL::Transform with no inverse! Oops.\n" unless(defined($me->{inv}) and ref($me->{inv}) eq 'CODE');
361 28         53 my $result = &{$me->{inv}}($data, $me->{params});
  28         90  
362 28         162 $result->is_inplace(0); # make sure inplace flag is clear.
363 28         80 return $result;
364             } else {
365 0         0 croak("invert requires a PDL and a PDL::Transform (did you want 'inverse' instead?)\n");
366             }
367             }
368              
369              
370              
371              
372              
373             =head2 map
374              
375             =for sig
376              
377             Signature: (k0(); SV *in; SV *out; SV *map; SV *boundary; SV *method;
378             SV *big; SV *blur; SV *sv_min; SV *flux; SV *bv)
379              
380              
381             =head2 match
382              
383             =for usage
384              
385             $y = $x->match($c); # Match $c's header and size
386             $y = $x->match([100,200]); # Rescale to 100x200 pixels
387             $y = $x->match([100,200],{rect=>1}); # Rescale and remove rotation/skew.
388              
389             =for ref
390              
391             Resample a scientific image to the same coordinate system as another.
392              
393             The example above is syntactic sugar for
394              
395             $y = $x->map(t_identity, $c, ...);
396              
397             it resamples the input PDL with the identity transformation in
398             scientific coordinates, and matches the pixel coordinate system to
399             $c's FITS header.
400              
401             There is one difference between match and map: match makes the
402             C option to C default to 0, not 1. This only affects
403             matching where autoscaling is required (i.e. the array ref example
404             above). By default, that example simply scales $x to the new size and
405             maintains any rotation or skew in its scientific-to-pixel coordinate
406             transform.
407              
408             =head2 map
409              
410             =for usage
411              
412             $y = $x->map($xform,[