File Coverage

blib/lib/PDL/Image2D.pm
Criterion Covered Total %
statement 39 42 92.8
branch 15 26 57.6
condition 7 18 38.8
subroutine 7 8 87.5
pod 0 4 0.0
total 68 98 69.3


line stmt bran cond sub pod time code
1             #
2             # GENERATED WITH PDL::PP from lib/PDL/Image2D.pd! Don't modify!
3             #
4             package PDL::Image2D;
5              
6             our @EXPORT_OK = qw( conv2d med2d med2df box2d patch2d patchbad2d max2d_ind centroid2d crop cc8compt cc4compt ccNcompt polyfill pnpoly polyfillv rotnewsz rot2d bilin2d rescale2d fitwarp2d applywarp2d warp2d warp2d_kernel );
7             our %EXPORT_TAGS = (Func=>\@EXPORT_OK);
8              
9 1     1   794 use PDL::Core;
  1         5  
  1         7  
10 1     1   8 use PDL::Exporter;
  1         2  
  1         7  
11 1     1   15 use DynaLoader;
  1         3  
  1         774  
12              
13              
14            
15             our @ISA = ( 'PDL::Exporter','DynaLoader' );
16             push @PDL::Core::PP, __PACKAGE__;
17             bootstrap PDL::Image2D ;
18              
19              
20              
21              
22              
23              
24              
25              
26             #line 11 "lib/PDL/Image2D.pd"
27              
28             use strict;
29             use warnings;
30              
31             =head1 NAME
32              
33             PDL::Image2D - Miscellaneous 2D image processing functions
34              
35             =head1 DESCRIPTION
36              
37             Miscellaneous 2D image processing functions - for want
38             of anywhere else to put them.
39              
40             =head1 SYNOPSIS
41              
42             use PDL::Image2D;
43              
44             =cut
45              
46             use PDL; # ensure qsort routine available
47             use PDL::Math;
48             use Carp;
49              
50             my %boundary2value = (Reflect=>1, Truncate=>2, Replicate=>3);
51             #line 52 "lib/PDL/Image2D.pm"
52              
53              
54             =head1 FUNCTIONS
55              
56             =cut
57              
58              
59              
60              
61              
62              
63              
64              
65              
66              
67              
68              
69              
70              
71              
72              
73              
74              
75             =head2 conv2d
76              
77             =for sig
78              
79             Signature: (a(m,n); kern(p,q); [o]b(m,n); indx [t]mapi(isize=CALC($SIZE(p) + $SIZE(m))); indx [t]mapj(jsize=CALC($SIZE(q) + $SIZE(n))); int opt)
80             Types: (sbyte byte short ushort long ulong indx ulonglong longlong
81             float double ldouble cfloat cdouble cldouble)
82              
83             =for ref
84              
85             2D convolution of an array with a kernel (smoothing)
86              
87             For large kernels, using a FFT routine,
88             such as L,
89             will be quicker.
90              
91             =for usage
92              
93             $new = conv2d $old, $kernel, {OPTIONS}
94              
95             =for example
96              
97             $smoothed = conv2d $image, ones(3,3), {Boundary => Reflect}
98              
99             =for options
100              
101             Boundary - controls what values are assumed for the image when kernel
102             crosses its edge:
103             => Default - periodic boundary conditions
104             (i.e. wrap around axis)
105             => Reflect - reflect at boundary
106             => Truncate - truncate at boundary
107             => Replicate - repeat boundary pixel values
108              
109             =pod
110              
111             Broadcasts over its inputs.
112              
113             =for bad
114              
115             Unlike the FFT routines, conv2d is able to process bad values.
116              
117             =cut
118              
119              
120              
121              
122              
123             sub PDL::conv2d {
124 12 100   12 0 58 my $opt; $opt = pop @_ if ref($_[$#_]) eq 'HASH';
  12         54  
125 12 50 33     68 die 'Usage: conv2d( a(m,n), kern(p,q), [o]b(m,n), {Options} )'
126             if $#_<1 || $#_>2;
127 12         28 my($x,$kern) = @_;
128 12 50       59 my $c = $#_ == 2 ? $_[2] : $x->nullcreate;
129             PDL::_conv2d_int($x,$kern,$c,
130             (!($opt && exists $$opt{Boundary}))?0:$boundary2value{$$opt{Boundary}}
131 12 100 66     188903 );
132 12         146 return $c;
133             }
134              
135              
136              
137             *conv2d = \&PDL::conv2d;
138              
139              
140              
141              
142              
143              
144             =head2 med2d
145              
146             =for sig
147              
148             Signature: (a(m,n); kern(p,q); [o]b(m,n); double+ [t]tmp(pq=CALC($SIZE(p)*$SIZE(q))); indx [t]mapi(isize=CALC($SIZE(p) + $SIZE(m))); indx [t]mapj(jsize=CALC($SIZE(q) + $SIZE(n))); int opt)
149             Types: (sbyte byte short ushort long ulong indx ulonglong longlong
150             float double ldouble)
151              
152             =for ref
153              
154             2D median-convolution of an array with a kernel (smoothing)
155              
156             Note: only points in the kernel E0 are included in the median, other
157             points are weighted by the kernel value (medianing lots of zeroes
158             is rather pointless)
159              
160             =for usage
161              
162             $new = med2d $old, $kernel, {OPTIONS}
163              
164             =for example
165              
166             $smoothed = med2d $image, ones(3,3), {Boundary => Reflect}
167              
168             =for options
169              
170             Boundary - controls what values are assumed for the image when kernel
171             crosses its edge:
172             => Default - periodic boundary conditions (i.e. wrap around axis)
173             => Reflect - reflect at boundary
174             => Truncate - truncate at boundary
175             => Replicate - repeat boundary pixel values
176              
177             =pod
178              
179             Broadcasts over its inputs.
180              
181             =for bad
182              
183             Bad values are ignored in the calculation. If all elements within the
184             kernel are bad, the output is set bad.
185              
186             =cut
187              
188              
189              
190              
191              
192             #line 311 "lib/PDL/Image2D.pd"
193             sub PDL::med2d {
194             my $opt; $opt = pop @_ if ref($_[$#_]) eq 'HASH';
195             die 'Usage: med2d( a(m,n), kern(p,q), [o]b(m,n), {Options} )'
196             if $#_<1 || $#_>2;
197             my($x,$kern) = @_;
198             croak "med2d: kernel must contain some positive elements.\n"
199             if all( $kern <= 0 );
200             my $c = $#_ == 2 ? $_[2] : $x->nullcreate;
201             PDL::_med2d_int($x,$kern,$c,
202             (!($opt && exists $$opt{Boundary}))?0:$boundary2value{$$opt{Boundary}}
203             );
204             return $c;
205             }
206             #line 207 "lib/PDL/Image2D.pm"
207              
208             *med2d = \&PDL::med2d;
209              
210              
211              
212              
213              
214              
215             =head2 med2df
216              
217             =for sig
218              
219             Signature: (a(m,n); [o]b(m,n); indx [t]mapi(isize=CALC($SIZE(p) + $SIZE(m))); indx [t]mapj(jsize=CALC($SIZE(q) + $SIZE(n))); IV p_size=>p; IV q_size=>q; int opt)
220             Types: (sbyte byte short ushort long ulong indx ulonglong longlong
221             float double ldouble)
222              
223             =for ref
224              
225             2D median-convolution of an array in a pxq window (smoothing)
226              
227             Note: this routine does the median over all points in a rectangular
228             window and is not quite as flexible as C in this regard
229             but slightly faster instead
230              
231             =for usage
232              
233             $new = med2df $old, $xwidth, $ywidth, {OPTIONS}
234              
235             =for example
236              
237             $smoothed = med2df $image, 3, 3, {Boundary => Reflect}
238              
239             =for options
240              
241             Boundary - controls what values are assumed for the image when kernel
242             crosses its edge:
243             => Default - periodic boundary conditions (i.e. wrap around axis)
244             => Reflect - reflect at boundary
245             => Truncate - truncate at boundary
246             => Replicate - repeat boundary pixel values
247              
248             =pod
249              
250             Broadcasts over its inputs.
251              
252             =for bad
253              
254             C does not process bad values.
255             It will set the bad-value flag of all output ndarrays if the flag is set for any of the input ndarrays.
256              
257             =cut
258              
259              
260              
261              
262             sub PDL::med2df {
263 1 50   1 0 10 my $opt; $opt = pop @_ if ref($_[$#_]) eq 'HASH';
  1         7  
264 1 50 33     9 die 'Usage: med2df( a(m,n), [o]b(m,n), p, q, {Options} )'
265             if $#_<2 || $#_>3;
266 1         3 my($x,$p,$q) = @_;
267 1 50 33     6 croak "med2df: kernel must contain some positive elements.\n"
268             if $p == 0 && $q == 0;
269 1 50       7 my $c = $#_ == 3 ? $_[3] : $x->nullcreate;
270             &PDL::_med2df_int($x,$c,$p,$q,
271             (!($opt && exists $$opt{Boundary}))?0:$boundary2value{$$opt{Boundary}}
272 1 50 33     160 );
273 1         11 return $c;
274             }
275              
276              
277              
278             *med2df = \&PDL::med2df;
279              
280              
281              
282              
283              
284              
285             =head2 box2d
286              
287             =for sig
288              
289             Signature: (a(n,m); [o] b(n,m); int wx; int wy; int edgezero)
290             Types: (sbyte byte short ushort long ulong indx ulonglong longlong
291             float double ldouble cfloat cdouble cldouble)
292              
293             =for usage
294              
295             $b = box2d($a, $wx, $wy, $edgezero);
296             box2d($a, $b, $wx, $wy, $edgezero); # all arguments given
297             $b = $a->box2d($wx, $wy, $edgezero); # method call
298             $a->box2d($b, $wx, $wy, $edgezero);
299              
300             =for ref
301              
302             fast 2D boxcar average
303              
304             =for example
305              
306             $smoothim = $im->box2d($wx,$wy,$edgezero=1);
307              
308             The edgezero argument controls if edge is set to zero (edgezero=1)
309             or just keeps the original (unfiltered) values.
310              
311             C should be updated to support similar edge options
312             as C and C etc.
313              
314             Boxcar averaging is a pretty crude way of filtering. For serious stuff
315             better filters are around (e.g., use L with the appropriate
316             kernel). On the other hand it is fast and computational cost grows only
317             approximately linearly with window size.
318              
319             =pod
320              
321             Broadcasts over its inputs.
322              
323             =for bad
324              
325             C does not process bad values.
326             It will set the bad-value flag of all output ndarrays if the flag is set for any of the input ndarrays.
327              
328             =cut
329              
330              
331              
332              
333             *box2d = \&PDL::box2d;
334              
335              
336              
337              
338              
339              
340             =head2 patch2d
341              
342             =for sig
343              
344             Signature: (a(m,n); int bad(m,n); [o]b(m,n))
345             Types: (sbyte byte short ushort long ulong indx ulonglong longlong
346             float double ldouble cfloat cdouble cldouble)
347              
348             =for usage
349              
350             $b = patch2d($a, $bad);
351             patch2d($a, $bad, $b); # all arguments given
352             $b = $a->patch2d($bad); # method call
353             $a->patch2d($bad, $b);
354              
355             =for ref
356              
357             patch bad pixels out of 2D images using a mask
358              
359             C<$bad> is a 2D mask array where 1=bad pixel 0=good pixel.
360             Pixels are replaced by the average of their non-bad neighbours;
361             if all neighbours are bad, the original data value is
362             copied across.
363              
364             =pod
365              
366             Broadcasts over its inputs.
367              
368             =for bad
369              
370             This routine does not handle bad values - use L instead
371              
372             =cut
373              
374              
375              
376              
377             *patch2d = \&PDL::patch2d;
378              
379              
380              
381              
382              
383              
384             =head2 patchbad2d
385              
386             =for sig
387              
388             Signature: (a(m,n); [o]b(m,n))
389             Types: (sbyte byte short ushort long ulong indx ulonglong longlong
390             float double ldouble cfloat cdouble cldouble)
391              
392             =for usage
393              
394             $b = patchbad2d($a);
395             patchbad2d($a, $b); # all arguments given
396             $b = $a->patchbad2d; # method call
397             $a->patchbad2d($b);
398              
399             =for ref
400              
401             patch bad pixels out of 2D images containing bad values
402              
403             Pixels are replaced by the average of their non-bad neighbours;
404             if all neighbours are bad, the output is set bad.
405             If the input ndarray contains I bad values, then a straight copy
406             is performed (see L).
407              
408             =pod
409              
410             Broadcasts over its inputs.
411              
412             =for bad
413              
414             patchbad2d handles bad values. The output ndarray I contain
415             bad values, depending on the pattern of bad values in the input ndarray.
416              
417             =cut
418              
419              
420              
421              
422             *patchbad2d = \&PDL::patchbad2d;
423              
424              
425              
426              
427              
428              
429             =head2 max2d_ind
430              
431             =for sig
432              
433             Signature: (a(m,n); [o]val(); int [o]x(); int[o]y())
434             Types: (sbyte byte short ushort long ulong indx ulonglong longlong
435             float double ldouble)
436              
437             =for usage
438              
439             ($val, $x, $y) = max2d_ind($a);
440             max2d_ind($a, $val, $x, $y); # all arguments given
441             ($val, $x, $y) = $a->max2d_ind; # method call
442             $a->max2d_ind($val, $x, $y);
443              
444             =for ref
445              
446             Return value/position of maximum value in 2D image
447              
448             Contributed by Tim Jenness
449              
450             =pod
451              
452             Broadcasts over its inputs.
453              
454             =for bad
455              
456             Bad values are excluded from the search. If all pixels
457             are bad then the output is set bad.
458              
459             =cut
460              
461              
462              
463              
464             *max2d_ind = \&PDL::max2d_ind;
465              
466              
467              
468              
469              
470              
471             =head2 centroid2d
472              
473             =for sig
474              
475             Signature: (im(m,n); x(); y(); box(); [o]xcen(); [o]ycen())
476             Types: (sbyte byte short ushort long ulong indx ulonglong longlong
477             float double ldouble)
478              
479             =for usage
480              
481             ($xcen, $ycen) = centroid2d($im, $x, $y, $box);
482             centroid2d($im, $x, $y, $box, $xcen, $ycen); # all arguments given
483             ($xcen, $ycen) = $im->centroid2d($x, $y, $box); # method call
484             $im->centroid2d($x, $y, $box, $xcen, $ycen);
485              
486             =for ref
487              
488             Refine a list of object positions in 2D image by centroiding in a box
489              
490             C<$box> is the full-width of the box, i.e. the window
491             is C<+/- $box/2>.
492              
493             =pod
494              
495             Broadcasts over its inputs.
496              
497             =for bad
498              
499             Bad pixels are excluded from the centroid calculation. If all elements are
500             bad (or the pixel sum is 0 - but why would you be centroiding
501             something with negatives in...) then the output values are set bad.
502              
503             =cut
504              
505              
506              
507              
508             *centroid2d = \&PDL::centroid2d;
509              
510              
511              
512              
513              
514             #line 708 "lib/PDL/Image2D.pd"
515              
516             =head2 crop
517              
518             =for ref
519              
520             Return bounding box of given mask in an C ndarray, so it can broadcast.
521             Use other operations (such as L, or
522             L with a colour vector) to create a mask suitable
523             for your application.
524              
525             =for example
526              
527             $x1x2y1y2 = crop($image);
528              
529             =cut
530              
531             *crop = \&PDL::crop;
532             sub PDL::crop {
533             my ($mask) = @_;
534             $mask->xchg(0,1)->orover->_which_int(my $out = null, null);
535             $out->badflag(1); $out->badvalue(-1);
536             my ($x1, $x2) = $out->minmaximum;
537             $mask->orover->_which_int($out = null, null);
538             $out->badflag(1); $out->badvalue(-1);
539             my ($y1, $y2) = $out->minmaximum;
540             $x1->cat($x2, $y1, $y2)->mv(-1,0);
541             }
542              
543             #line 738 "lib/PDL/Image2D.pd"
544              
545             =head2 cc8compt
546              
547             =for ref
548              
549             Connected 8-component labeling of a binary image.
550              
551             Connected 8-component labeling of 0,1 image - i.e. find separate
552             segmented objects and fill object pixels with object number.
553             8-component labeling includes all neighboring pixels.
554             This is just a front-end to ccNcompt. See also L.
555              
556             =for example
557              
558             $segmented = cc8compt( $image > $threshold );
559              
560             =head2 cc4compt
561              
562             =for ref
563              
564             Connected 4-component labeling of a binary image.
565              
566             Connected 4-component labeling of 0,1 image - i.e. find separate
567             segmented objects and fill object pixels with object number.
568             4-component labling does not include the diagonal neighbors.
569             This is just a front-end to ccNcompt. See also L.
570              
571             =for example
572              
573             $segmented = cc4compt( $image > $threshold );
574              
575             =cut
576              
577             sub PDL::cc8compt{
578             return ccNcompt(shift,8);
579             }
580             *cc8compt = \&PDL::cc8compt;
581              
582             sub PDL::cc4compt{
583             return ccNcompt(shift,4);
584             }
585             *cc4compt = \&PDL::cc4compt;
586             #line 587 "lib/PDL/Image2D.pm"
587              
588              
589             =head2 ccNcompt
590              
591             =for sig
592              
593             Signature: (a(m,n); int+ [o]b(m,n); int con)
594             Types: (sbyte byte short ushort long ulong indx ulonglong longlong
595             float double ldouble)
596              
597             =for usage
598              
599             $b = ccNcompt($a, $con);
600             ccNcompt($a, $b, $con); # all arguments given
601             $b = $a->ccNcompt($con); # method call
602             $a->ccNcompt($b, $con);
603              
604             =for ref
605              
606             Connected component labeling of a binary image.
607              
608             Connected component labeling of 0,1 image - i.e. find separate
609             segmented objects and fill object pixels with object number.
610             See also L and L.
611              
612             The connectivity parameter must be 4 or 8.
613              
614             =for example
615              
616             $segmented = ccNcompt( $image > $threshold, 4);
617              
618             $segmented2 = ccNcompt( $image > $threshold, 8);
619              
620             where the second parameter specifies the connectivity (4 or 8) of the labeling.
621              
622             =pod
623              
624             Broadcasts over its inputs.
625              
626             =for bad
627              
628             C ignores the bad-value flag of the input ndarrays.
629             It will set the bad-value flag of all output ndarrays if the flag is set for any of the input ndarrays.
630              
631             =cut
632              
633              
634              
635              
636             *ccNcompt = \&PDL::ccNcompt;
637              
638              
639              
640              
641              
642             #line 905 "lib/PDL/Image2D.pd"
643              
644             =head2 polyfill
645              
646             =for ref
647              
648             fill the area of the given polygon with the given colour.
649              
650             This function works inplace, i.e. modifies C.
651              
652             =for usage
653              
654             polyfill($im,$ps,$colour,[\%options]);
655              
656             The default method of determining which points lie inside of the polygon used
657             is not as strict as the method used in L. Often, it includes vertices
658             and edge points. Set the C option to change this behaviour.
659              
660             =for option
661              
662             Method - Set the method used to determine which points lie in the polygon.
663             => Default - internal PDL algorithm
664             => pnpoly - use the L algorithm
665              
666             =for example
667              
668             # Make a convex 3x3 square of 1s in an image using the pnpoly algorithm
669             $ps = pdl([3,3],[3,6],[6,6],[6,3]);
670             polyfill($im,$ps,1,{'Method' =>'pnpoly'});
671              
672             =cut
673             sub PDL::polyfill {
674             my $opt;
675             $opt = pop @_ if ref($_[-1]) eq 'HASH';
676             barf('Usage: polyfill($im,$ps,$colour,[\%options])') unless @_==3;
677             my ($im,$ps,$colour) = @_;
678              
679             if($opt) {
680             use PDL::Options qw();
681             my $parsed = PDL::Options->new({'Method' => undef});
682             $parsed->options($opt);
683             if( $parsed->current->{'Method'} eq 'pnpoly' ) {
684             PDL::pnpolyfill_pp($im,$ps,$colour);
685             }
686             }
687             else
688             {
689             PDL::polyfill_pp($im,$ps,$colour);
690             }
691             return $im;
692              
693             }
694              
695             *polyfill = \&PDL::polyfill;
696              
697             #line 962 "lib/PDL/Image2D.pd"
698              
699             =head2 pnpoly
700              
701             =for ref
702              
703             'points in a polygon' selection from a 2-D ndarray
704              
705             =for usage
706              
707             $mask = $img->pnpoly($ps);
708              
709             # Old style, do not use
710             $mask = pnpoly($x, $y, $px, $py);
711              
712             For a closed polygon determined by the sequence of points in {$px,$py}
713             the output of pnpoly is a mask corresponding to whether or not each
714             coordinate (x,y) in the set of test points, {$x,$y}, is in the interior
715             of the polygon. This is the 'points in a polygon' algorithm from
716             L
717             and vectorized for PDL by Karl Glazebrook.
718              
719             =for example
720              
721             # define a 3-sided polygon (a triangle)
722             $ps = pdl([3, 3], [20, 20], [34, 3]);
723              
724             # $tri is 0 everywhere except for points in polygon interior
725             $tri = $img->pnpoly($ps);
726              
727             With the second form, the x and y coordinates must also be specified.
728             B< I >.
729              
730             $px = pdl( 3, 20, 34 );
731             $py = pdl( 3, 20, 3 );
732             $x = $img->xvals; # get x pixel coords
733             $y = $img->yvals; # get y pixel coords
734              
735             # $tri is 0 everywhere except for points in polygon interior
736             $tri = pnpoly($x,$y,$px,$py);
737              
738             =cut
739              
740             # From: http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
741             #
742             # Fixes needed to pnpoly code:
743             #
744             # Use topdl() to ensure ndarray args
745             #
746             # Add POD docs for usage
747             #
748             # Calculate first term in & expression to use to fix divide-by-zero when
749             # the test point is in line with a vertical edge of the polygon.
750             # By adding the value of $mask we prevent a divide-by-zero since the &
751             # operator does not "short circuit".
752              
753             sub PDL::pnpoly {
754             barf('Usage: $mask = pnpoly($img, $ps);') unless(@_==2 || @_==4);
755             my ($tx, $ty, $vertx, $verty) = @_;
756              
757             # if only two inputs, use the pure PP version
758             unless( defined $vertx ) {
759             barf("ps must contain pairwise points.\n") unless $ty->getdim(0) == 2;
760              
761             # Input mapping: $img => $tx, $ps => $ty
762             return PDL::pnpoly_pp($tx,$ty);
763             }
764              
765             my $testx = PDL::Core::topdl($tx)->dummy(0);
766             my $testy = PDL::Core::topdl($ty)->dummy(0);
767             my $vertxj = PDL::Core::topdl($vertx)->rotate(1);
768             my $vertyj = PDL::Core::topdl($verty)->rotate(1);
769             my $mask = ( ($verty>$testy) == ($vertyj>$testy) );
770             my $c = sumover( ! $mask & ( $testx < ($vertxj-$vertx) * ($testy-$verty)
771             / ($vertyj-$verty+$mask) + $vertx) ) % 2;
772             return $c;
773             }
774              
775             *pnpoly = \&PDL::pnpoly;
776              
777             #line 1045 "lib/PDL/Image2D.pd"
778              
779             =head2 polyfillv
780              
781             =for ref
782              
783             return the (dataflowed) area of an image described by a polygon
784              
785             =for usage
786              
787             polyfillv($im,$ps,[\%options]);
788              
789             The default method of determining which points lie inside of the polygon used
790             is not as strict as the method used in L. Often, it includes vertices
791             and edge points. Set the C option to change this behaviour.
792              
793             =for option
794              
795             Method - Set the method used to determine which points lie in the polygon.
796             => Default - internal PDL algorithm
797             => pnpoly - use the L algorithm
798              
799             =for example
800              
801             # increment intensity in area bounded by $poly using the pnpoly algorithm
802             $im->polyfillv($poly,{'Method'=>'pnpoly'})++; # legal in perl >= 5.6
803              
804             # compute average intensity within area bounded by $poly using the default algorithm
805             $av = $im->polyfillv($poly)->avg;
806              
807             =cut
808              
809             sub PDL::polyfillv :lvalue {
810             my $opt;
811             $opt = pop @_ if ref($_[-1]) eq 'HASH';
812             barf('Usage: polyfillv($im,$ps,[\%options])') unless @_==2;
813              
814             my ($im,$ps) = @_;
815             barf("ps must contain pairwise points.\n") unless $ps->getdim(0) == 2;
816              
817             if($opt) {
818             use PDL::Options qw();
819             my $parsed = PDL::Options->new({'Method' => undef});
820             $parsed->options($opt);
821             return $im->where(PDL::pnpoly_pp($im, $ps)) if $parsed->current->{'Method'} eq 'pnpoly';
822             }
823              
824             PDL::polyfill_pp(my $msk = zeroes(long,$im->dims), $ps, 1);
825             return $im->where($msk);
826             }
827             *polyfillv = \&PDL::polyfillv;
828             #line 829 "lib/PDL/Image2D.pm"
829              
830              
831             =head2 rot2d
832              
833             =for sig
834              
835             Signature: (im(m,n); float angle(); bg(); int aa(); [o] om(p,q))
836             Types: (byte)
837              
838             =for usage
839              
840             $om = rot2d($im, $angle, $bg, $aa);
841             rot2d($im, $angle, $bg, $aa, $om); # all arguments given
842             $om = $im->rot2d($angle, $bg, $aa); # method call
843             $im->rot2d($angle, $bg, $aa, $om);
844              
845             =for ref
846              
847             rotate an image by given C
848              
849             =for example
850              
851             # rotate by 10.5 degrees with antialiasing, set missing values to 7
852             $rot = $im->rot2d(10.5,7,1);
853              
854             This function rotates an image through an C between -90 and + 90
855             degrees. Uses/doesn't use antialiasing depending on the C flag.
856             Pixels outside the rotated image are set to C.
857              
858             Code modified from pnmrotate (Copyright Jef Poskanzer) with an algorithm based
859             on "A Fast Algorithm for General Raster Rotation" by Alan Paeth,
860             Graphics Interface '86, pp. 77-81.
861              
862             Use the C function to find out about the dimension of the
863             newly created image
864              
865             ($newcols,$newrows) = rotnewsz $oldn, $oldm, $angle;
866              
867             L offers a more general interface to
868             distortions, including rotation, with various types of sampling; but
869             rot2d is faster.
870              
871             =pod
872              
873             Broadcasts over its inputs.
874              
875             =for bad
876              
877             C ignores the bad-value flag of the input ndarrays.
878             It will set the bad-value flag of all output ndarrays if the flag is set for any of the input ndarrays.
879              
880             =cut
881              
882              
883              
884              
885             *rot2d = \&PDL::rot2d;
886              
887              
888              
889              
890              
891              
892             =head2 bilin2d
893              
894             =for sig
895              
896             Signature: (Int(n,m); [io] O(q,p))
897             Types: (sbyte byte short ushort long ulong indx ulonglong longlong
898             float double ldouble cfloat cdouble cldouble)
899              
900             =for usage
901              
902             bilin2d($Int, $O); # all arguments given
903             $Int->bilin2d($O); # method call
904              
905             =for ref
906              
907             Bilinearly maps the first ndarray in the second. The
908             interpolated values are actually added to the second
909             ndarray which is supposed to be larger than the first one.
910              
911             =pod
912              
913             Broadcasts over its inputs.
914              
915             =for bad
916              
917             C ignores the bad-value flag of the input ndarrays.
918             It will set the bad-value flag of all output ndarrays if the flag is set for any of the input ndarrays.
919              
920             =cut
921              
922              
923              
924              
925             *bilin2d = \&PDL::bilin2d;
926              
927              
928              
929              
930              
931              
932             =head2 rescale2d
933              
934             =for sig
935              
936             Signature: (Int(m,n); [io] O(p,q))
937             Types: (sbyte byte short ushort long ulong indx ulonglong longlong
938             float double ldouble cfloat cdouble cldouble)
939              
940             =for usage
941              
942             rescale2d($Int, $O); # all arguments given
943             $Int->rescale2d($O); # method call
944              
945             =for ref
946              
947             The first ndarray is rescaled to the dimensions of the second
948             (expanding or meaning values as needed) and then added to it in place.
949             Nothing useful is returned.
950              
951             If you want photometric accuracy or automatic FITS header metadata
952             tracking, consider using L
953             instead: it does these things, at some speed penalty compared to
954             rescale2d.
955              
956             =pod
957              
958             Broadcasts over its inputs.
959              
960             =for bad
961              
962             C ignores the bad-value flag of the input ndarrays.
963             It will set the bad-value flag of all output ndarrays if the flag is set for any of the input ndarrays.
964              
965             =cut
966              
967              
968              
969              
970             *rescale2d = \&PDL::rescale2d;
971              
972              
973              
974              
975              
976             #line 1331 "lib/PDL/Image2D.pd"
977              
978             =head2 fitwarp2d
979              
980             =for ref
981              
982             Find the best-fit 2D polynomial to describe
983             a coordinate transformation.
984              
985             =for usage
986              
987             ( $px, $py ) = fitwarp2d( $x, $y, $u, $v, $nf, { options } )
988              
989             Given a set of points in the output plane (C<$u,$v>), find
990             the best-fit (using singular-value decomposition) 2D polynomial
991             to describe the mapping back to the image plane (C<$x,$y>).
992             The order of the fit is controlled by the C<$nf> parameter
993             (the maximum power of the polynomial is C<$nf - 1>), and you
994             can restrict the terms to fit using the C option.
995              
996             C<$px> and C<$py> are C by C element ndarrays which describe
997             a polynomial mapping (of order C)
998             from the I C<(u,v)> image to the I C<(x,y)> image:
999              
1000             x = sum(j=0,np-1) sum(i=0,np-1) px(i,j) * u^i * v^j
1001             y = sum(j=0,np-1) sum(i=0,np-1) py(i,j) * u^i * v^j
1002              
1003             The transformation is returned for the reverse direction (ie
1004             output to input image) since that is what is required by the
1005             L routine. The L
1006             routine can be used to convert a set of C<$u,$v> points given
1007             C<$px> and C<$py>.
1008              
1009             Options:
1010              
1011             =for options
1012              
1013             FIT - which terms to fit? default ones(byte,$nf,$nf)
1014              
1015             =begin comment
1016              
1017             old option, caused trouble
1018             THRESH - in svd, remove terms smaller than THRESH * max value
1019             default is 1.0e-5
1020              
1021             =end comment
1022              
1023             =over 4
1024              
1025             =item FIT
1026              
1027             C allows you to restrict which terms of the polynomial to fit:
1028             only those terms for which the FIT ndarray evaluates to true will be
1029             evaluated. If a 2D ndarray is sent in, then it is
1030             used for the x and y polynomials; otherwise
1031             C<< $fit->slice(":,:,(0)") >> will be used for C<$px> and
1032             C<< $fit->slice(":,:,(1)") >> will be used for C<$py>.
1033              
1034             =begin comment
1035              
1036             =item THRESH
1037              
1038             Remove all singular values whose value is less than C
1039             times the largest singular value.
1040              
1041             =end comment
1042              
1043             =back
1044              
1045             The number of points must be at least equal to the number of
1046             terms to fit (C<$nf*$nf> points for the default value of C).
1047              
1048             =for example
1049              
1050             # points in original image
1051             $x = pdl( 0, 0, 100, 100 );
1052             $y = pdl( 0, 100, 100, 0 );
1053             # get warped to these positions
1054             $u = pdl( 10, 10, 90, 90 );
1055             $v = pdl( 10, 90, 90, 10 );
1056             #
1057             # shift of origin + scale x/y axis only
1058             $fit = byte( [ [1,1], [0,0] ], [ [1,0], [1,0] ] );
1059             ( $px, $py ) = fitwarp2d( $x, $y, $u, $v, 2, { FIT => $fit } );
1060             print "px = ${px}py = $py";
1061             px =
1062             [
1063             [-12.5 1.25]
1064             [ 0 0]
1065             ]
1066             py =
1067             [
1068             [-12.5 0]
1069             [ 1.25 0]
1070             ]
1071             #
1072             # Compared to allowing all 4 terms
1073             ( $px, $py ) = fitwarp2d( $x, $y, $u, $v, 2 );
1074             print "px = ${px}py = $py";
1075             px =
1076             [
1077             [ -12.5 1.25]
1078             [ 1.110223e-16 -1.1275703e-17]
1079             ]
1080             py =
1081             [
1082             [ -12.5 1.6653345e-16]
1083             [ 1.25 -5.8546917e-18]
1084             ]
1085              
1086             # A higher-degree polynomial should not affect the answer much, but
1087             # will require more control points
1088              
1089             $x = $x->glue(0,pdl(50,12.5, 37.5, 12.5, 37.5));
1090             $y = $y->glue(0,pdl(50,12.5, 37.5, 37.5, 12.5));
1091             $u = $u->glue(0,pdl(73,20,40,20,40));
1092             $v = $v->glue(0,pdl(29,20,40,40,20));
1093             ( $px3, $py3 ) = fitwarp2d( $x, $y, $u, $v, 3 );
1094             print "px3 =${px3}py3 =$py3";
1095             px3 =
1096             [
1097             [-6.4981162e+08 71034917 -726498.95]
1098             [ 49902244 -5415096.7 55945.388]
1099             [ -807778.46 88457.191 -902.51612]
1100             ]
1101             py3 =
1102             [
1103             [-6.2732159e+08 68576392 -701354.77]
1104             [ 48175125 -5227679.8 54009.114]
1105             [ -779821.18 85395.681 -871.27997]
1106             ]
1107              
1108             #This illustrates an important point about singular value
1109             #decompositions that are used in fitwarp2d: like all SVDs, the
1110             #rotation matrices are not unique, and so the $px and $py returned
1111             #by fitwarp2d are not guaranteed to be the "simplest" solution.
1112             #They do still work, though:
1113              
1114             ($x3,$y3) = applywarp2d($px3,$py3,$u,$v);
1115             print approx $x3,$x,1e-4;
1116             [1 1 1 1 1 1 1 1 1]
1117             print approx $y3,$y;
1118             [1 1 1 1 1 1 1 1 1]
1119              
1120             =head2 applywarp2d
1121              
1122             =for ref
1123              
1124             Transform a set of points using a 2-D polynomial mapping
1125              
1126             =for usage
1127              
1128             ( $x, $y ) = applywarp2d( $px, $py, $u, $v )
1129              
1130             Convert a set of points (stored in 1D ndarrays C<$u,$v>)
1131             to C<$x,$y> using the 2-D polynomial with coefficients stored in C<$px>
1132             and C<$py>. See L
1133             for more information on the format of C<$px> and C<$py>.
1134              
1135             =cut
1136              
1137             # use SVD to fit data. Assuming no errors.
1138              
1139             =pod
1140              
1141             =begin comment
1142              
1143             Some explanation of the following three subroutines (_svd, _mkbasis,
1144             and fitwarp2d): See Wolberg 1990 (full ref elsewhere in this
1145             documentation), Chapter 3.6 "Polynomial Transformations". The idea is
1146             that, given a set of control points in the input and output images
1147             denoted by coordinates (x,y) and (u,v), we want to create a polynomial
1148             transformation that relates u to linear combinations of powers of x
1149             and y, and another that relates v to powers of x and y.
1150              
1151             The transformations used here and by Wolberg differ slightly, but the
1152             basic idea is something like this. For each u and each v, define a
1153             transform:
1154              
1155             u = (sum over j) (sum over i) a_{ij} x**i * y**j , (eqn 1)
1156             v = (sum over j) (sum over i) b_{ij} x**i * y**j . (eqn 2)
1157              
1158             We can write this in matrix notation. Given that there are M control
1159             points, U is a column vector with M rows. A and B are vectors containing
1160             the N coefficients (related to the degree of the polynomial fit). W
1161             is an MxN matrix of the basis row-vectors (the x**i y**j).
1162              
1163             The matrix equations we are trying to solve are
1164             U = W A , (eqn 3)
1165             V = W B . (eqn 4)
1166              
1167             We need to find the A and B column vectors, those are the coefficients
1168             of the polynomial terms in W. W is not square, so it has no inverse.
1169             But is has a pseudo-inverse W^+ that is NxM. We are going to use that
1170             pseudo-inverse to isolate A and B, like so:
1171              
1172             W^+ U = W^+ W A = A (eqn 5)
1173             W^+ V = W^+ W B = B (eqn 6)
1174              
1175             We are going to get W^+ by performing a singular value decomposition
1176             of W (to use some of the variable names below):
1177              
1178             W = $svd_u x SIGMA x $svd_v->transpose (eqn 7)
1179             W^+ = $svd_v x SIGMA^+ x $svd_u->transpose . (eqn 8)
1180              
1181             Here SIGMA is a square diagonal matrix that contains the singular
1182             values of W that are in the variable $svd_w. SIGMA^+ is the
1183             pseudo-inverse of SIGMA, which is calculated by replacing the non-zero
1184             singular values with their reciprocals, and then taking the transpose
1185             of the matrix (which is a no-op since the matrix is square and
1186             diagonal).
1187              
1188             So the code below does this:
1189              
1190             _mkbasis computes the matrix W, given control coordinates u and v and
1191             the maximum degree of the polynomial (and the terms to use).
1192              
1193             _svd takes the svd of W, computes the pseudo-inverse of W, and then
1194             multiplies that with the U vector (there called $y). The output of
1195             _svd is the A or B vector in eqns 5 & 6 above. Rarely is the matrix
1196             multiplication explicit, unfortunately, so I have added EXPLANATIONs
1197             below.
1198              
1199             =end comment
1200              
1201             =cut
1202              
1203             sub _svd ($$) {
1204             my $basis = shift;
1205             my $y = shift;
1206             # my $thresh = shift;
1207              
1208             # if we had errors for these points, would normalise the
1209             # basis functions, and the output array, by these errors here
1210              
1211             # perform the SVD
1212             my ( $svd_u, $svd_w, $svd_v ) = svd( $basis );
1213              
1214             # DAL, 09/2017: the reason for removing ANY singular values, much less
1215             #the smallest ones, is not clear. I commented the line below since this
1216             #actually removes the LARGEST values in SIGMA^+.
1217             # $svd_w *= ( $svd_w >= ($svd_w->max * $thresh ) );
1218             # The line below would instead remove the SMALLEST values in SIGMA^+, but I can see no reason to include it either.
1219             # $svd_w *= ( $svd_w <= ($svd_w->min / $thresh ) );
1220              
1221             # perform the back substitution
1222             # EXPLANATION: same thing as $svd_u->transpose x $y->transpose.
1223             my $tmp = $y x $svd_u;
1224              
1225             #EXPLANATION: the division by (the non-zero elements of) $svd_w (the singular values) is
1226             #equivalent to $sigma_plus x $tmp, so $tmp is now SIGMA^+ x $svd_u x $y
1227             $tmp /= $svd_w->setvaltobad(0.0);
1228             $tmp->inplace->setbadtoval(0.0);
1229              
1230             #EXPLANATION: $svd_v x SIGMA^+ x $svd_u x $y
1231             return sumover( $svd_v * $tmp );
1232              
1233             } # sub: _svd()
1234              
1235             #_mkbasis returns an ndarray in which the k(=j*n+i)_th column is v**j * u**i
1236             #k=0 j=0 i=0
1237             #k=1 j=0 i=1
1238             #k=2 j=0 i=2
1239             #k=3 j=1 i=0
1240             # ...
1241              
1242             #each row corresponds to a control point
1243             #and the rows for each of the control points look like this, e.g.:
1244             # (1) (u) (u**2) (v) (vu) (v(u**2)) (v**2) ((v**2)u) ((v**2)(u**2))
1245             # and so on for the next control point.
1246              
1247             sub _mkbasis ($$$$) {
1248             my $fit = shift; # dims n n
1249             my $npts = shift; # scalar
1250             my $u = shift; # dims npts
1251             my $v = shift; # dims npts
1252             my $ncoeff = sum( $fit );
1253             my $fit_coords = $fit->whichND; # dims uv ncoeff
1254             cat($u,$v) # npts uv
1255             ->transpose # uv npts
1256             ->dummy(1,$ncoeff) # uv ncoeff npts
1257             ->ipow($fit_coords) # same
1258             ->prodover # ncoeff npts
1259             ;
1260             } # sub: _mkbasis()
1261              
1262             sub PDL::fitwarp2d {
1263             croak "Usage: (\$px,\$py) = fitwarp2d(x(m);y(m);u(m);v(m);\$nf; { options })"
1264             if $#_ < 4 or ( $#_ >= 5 and ref($_[5]) ne "HASH" );
1265              
1266             my $x = shift;
1267             my $y = shift;
1268             my $u = shift;
1269             my $v = shift;
1270             my $nf = shift;
1271              
1272             my $opts = PDL::Options->new( { FIT => ones(byte,$nf,$nf) } ); #, THRESH => 1.0e-5 } );
1273             $opts->options( $_[0] ) if $#_ > -1;
1274             my $oref = $opts->current();
1275              
1276             # safety checks
1277             my $npts = $x->nelem;
1278             croak "fitwarp2d: x, y, u, and v must be the same size (and 1D)"
1279             unless $npts == $y->nelem and $npts == $u->nelem and $npts == $v->nelem
1280             and $x->getndims == 1 and $y->getndims == 1 and $u->getndims == 1 and $v->getndims == 1;
1281              
1282             # my $svd_thresh = $$oref{THRESH};
1283             # croak "fitwarp2d: THRESH option must be >= 0."
1284             # if $svd_thresh < 0;
1285              
1286             my $fit = $$oref{FIT};
1287             my $fit_ndim = $fit->getndims();
1288             croak "fitwarp2d: FIT option must be sent a (\$nf,\$nf[,2]) element ndarray"
1289             unless UNIVERSAL::isa($fit,"PDL") and
1290             ($fit_ndim == 2 or ($fit_ndim == 3 and $fit->getdim(2) == 2)) and
1291             $fit->getdim(0) == $nf and $fit->getdim(1) == $nf;
1292              
1293             # how many coeffs to fit (first we ensure $fit is either 0 or 1)
1294             $fit = convert( $fit != 0, byte );
1295              
1296             my ( $fitx, $fity, $ncoeffx, $ncoeffy, $ncoeff );
1297             if ( $fit_ndim == 2 ) {
1298             $fitx = $fit;
1299             $fity = $fit;
1300             $ncoeff = $ncoeffx = $ncoeffy = sum( $fit );
1301             } else {
1302             $fitx = $fit->slice(",,(0)");
1303             $fity = $fit->slice(",,(1)");
1304             $ncoeffx = sum($fitx);
1305             $ncoeffy = sum($fity);
1306             $ncoeff = $ncoeffx > $ncoeffy ? $ncoeffx : $ncoeffy;
1307             }
1308              
1309             croak "fitwarp2d: number of points ($npts) must be >= \$ncoeff ($ncoeff)"
1310             unless $npts >= $ncoeff;
1311              
1312             # create the basis functions for the SVD fitting
1313             my ( $basisx, $basisy );
1314              
1315             $basisx = _mkbasis( $fitx, $npts, $u, $v );
1316              
1317             if ( $fit_ndim == 2 ) {
1318             $basisy = $basisx;
1319             } else {
1320             $basisy = _mkbasis( $fity, $npts, $u, $v );
1321             }
1322              
1323             my $px = _svd( $basisx, $x ); # $svd_thresh);
1324             my $py = _svd( $basisy, $y ); # $svd_thresh);
1325              
1326             # convert into $nf x $nf element ndarrays, if necessary
1327             my $nf2 = $nf * $nf;
1328              
1329             return ( $px->reshape($nf,$nf), $py->reshape($nf,$nf) )
1330             if $ncoeff == $nf2 and $ncoeffx == $ncoeffy;
1331              
1332             # re-create the matrix
1333             my $xtmp = zeroes( $nf, $nf );
1334             my $ytmp = zeroes( $nf, $nf );
1335              
1336             my $kx = 0;
1337             my $ky = 0;
1338             foreach my $i ( 0 .. ($nf - 1) ) {
1339             foreach my $j ( 0 .. ($nf - 1) ) {
1340             if ( $fitx->at($i,$j) ) {
1341             $xtmp->set($i,$j, $px->at($kx) );
1342             $kx++;
1343             }
1344             if ( $fity->at($i,$j) ) {
1345             $ytmp->set($i,$j, $py->at($ky) );
1346             $ky++;
1347             }
1348             }
1349             }
1350              
1351             return ( $xtmp, $ytmp )
1352              
1353             } # sub: fitwarp2d
1354              
1355             *fitwarp2d = \&PDL::fitwarp2d;
1356              
1357             sub PDL::applywarp2d {
1358             # checks
1359             croak "Usage: (\$x,\$y) = applywarp2d(px(nf,nf);py(nf,nf);u(m);v(m);)"
1360             if $#_ != 3;
1361              
1362             my $px = shift;
1363             my $py = shift;
1364             my $u = shift;
1365             my $v = shift;
1366             my $npts = $u->nelem;
1367              
1368             # safety check
1369             croak "applywarp2d: u and v must be the same size (and 1D)"
1370             unless $npts == $u->nelem and $npts == $v->nelem
1371             and $u->getndims == 1 and $v->getndims == 1;
1372              
1373             my $nf = $px->getdim(0);
1374             my $nf2 = $nf * $nf;
1375              
1376             # could remove terms with 0 coeff here
1377             # (would also have to remove them from px/py for
1378             # the matrix multiplication below)
1379             #
1380             my $mat = _mkbasis( ones(byte,$nf,$nf), $npts, $u, $v );
1381              
1382             my $x = reshape( $mat x $px->flat->transpose(), $npts );
1383             my $y = reshape( $mat x $py->flat->transpose(), $npts );
1384             return ( $x, $y );
1385              
1386             } # sub: applywarp2d
1387              
1388             *applywarp2d = \&PDL::applywarp2d;
1389             #line 1390 "lib/PDL/Image2D.pm"
1390              
1391              
1392             =head2 warp2d
1393              
1394             =for sig
1395              
1396             Signature: (img(m,n); ldouble px(np,np); ldouble py(np,np); [o] warp(m,n); ldouble [t] poly(np); ldouble [t] kernel(ns); char *kernel_type; double noval; IV nsamples => ns)
1397             Types: (float double ldouble)
1398              
1399             =for ref
1400              
1401             Warp a 2D image given a polynomial describing the I mapping.
1402              
1403             =for usage
1404              
1405             $out = warp2d( $img, $px, $py, { options } );
1406              
1407             Apply the polynomial transformation encoded in the C<$px> and
1408             C<$py> ndarrays to warp the input image C<$img> into the output
1409             image C<$out>.
1410              
1411             The format for the polynomial transformation is described in
1412             the documentation for the L routine.
1413              
1414             At each point C, the closest 16 pixel values are combined
1415             with an interpolation kernel to calculate the value at C.
1416             The interpolation is therefore done in the image, rather than
1417             Fourier, domain.
1418             By default, a C kernel is used, but this can be changed
1419             using the C option discussed below
1420             (the choice of kernel depends on the frequency content of the input image).
1421              
1422             The routine is based on the C command from
1423             the Eclipse data-reduction package - see http://www.eso.org/eclipse/ - and
1424             for further details on image resampling see
1425             Wolberg, G., "Digital Image Warping", 1990, IEEE Computer
1426             Society Press ISBN 0-8186-8944-7).
1427              
1428             Currently the output image is the same size as the input one,
1429             which means data will be lost if the transformation reduces
1430             the pixel scale. This will (hopefully) be changed soon.
1431              
1432             =for example
1433              
1434             $img = rvals(byte,501,501);
1435             imag $img, { JUSTIFY => 1 };
1436             #
1437             # use a not-particularly-obvious transformation:
1438             # x = -10 + 0.5 * $u - 0.1 * $v
1439             # y = -20 + $v - 0.002 * $u * $v
1440             #
1441             $px = pdl( [ -10, 0.5 ], [ -0.1, 0 ] );
1442             $py = pdl( [ -20, 0 ], [ 1, 0.002 ] );
1443             $wrp = warp2d( $img, $px, $py );
1444             #
1445             # see the warped image
1446             imag $warp, { JUSTIFY => 1 };
1447              
1448             The options are:
1449              
1450             =for options
1451              
1452             KERNEL - default value is tanh
1453             NOVAL - default value is 0
1454              
1455             C is used to specify which interpolation kernel to use
1456             (to see what these kernels look like, use the
1457             L routine).
1458             The options are:
1459              
1460             =over 4
1461              
1462             =item tanh
1463              
1464             Hyperbolic tangent: the approximation of an ideal box filter by the
1465             product of symmetric tanh functions.
1466              
1467             =item sinc
1468              
1469             For a correctly sampled signal, the ideal filter in the fourier domain is a rectangle,
1470             which produces a C interpolation kernel in the spatial domain:
1471              
1472             sinc(x) = sin(pi * x) / (pi * x)
1473              
1474             However, it is not ideal for the C<4x4> pixel region used here.
1475              
1476             =item sinc2
1477              
1478             This is the square of the sinc function.
1479              
1480             =item lanczos
1481              
1482             Although defined differently to the C kernel, the result is very
1483             similar in the spatial domain. The Lanczos function is defined as
1484              
1485             L(x) = sinc(x) * sinc(x/2) if abs(x) < 2
1486             = 0 otherwise
1487              
1488             =item hann
1489              
1490             This kernel is derived from the following function:
1491              
1492             H(x) = a + (1-a) * cos(2*pi*x/(N-1)) if abs(x) < 0.5*(N-1)
1493             = 0 otherwise
1494              
1495             with C and N currently equal to 2001.
1496              
1497             =item hamming
1498              
1499             This kernel uses the same C as the Hann filter, but with
1500             C.
1501              
1502             =back
1503              
1504             C gives the value used to indicate that a pixel in the
1505             output image does not map onto one in the input image.
1506              
1507             =pod
1508              
1509             Broadcasts over its inputs.
1510              
1511             =for bad
1512              
1513             C ignores the bad-value flag of the input ndarrays.
1514             It will set the bad-value flag of all output ndarrays if the flag is set for any of the input ndarrays.
1515              
1516             =cut
1517              
1518              
1519              
1520              
1521             # support routine
1522             {
1523             my %warp2d = map { ($_,1) } qw( tanh sinc sinc2 lanczos hamming hann );
1524             # note: convert to lower case
1525             sub _check_kernel ($$) {
1526 6     6   20 my $kernel = lc shift;
1527 6         13 my $code = shift;
1528             barf "Unknown kernel $kernel sent to $code\n" .
1529             "\tmust be one of [" . join(',',keys %warp2d) . "]\n"
1530 6 50       24 unless exists $warp2d{$kernel};
1531 6         22 return $kernel;
1532             }
1533             }
1534              
1535             sub PDL::warp2d {
1536 6     6 0 110 my $opts = PDL::Options->new( { KERNEL => "tanh", NOVAL => 0 } );
1537 6 50       33 $opts->options( pop(@_) ) if ref($_[$#_]) eq "HASH";
1538 6 50 33     54 die "Usage: warp2d( in(m,n), px(np,np); py(np,np); [o] out(m,n), {Options} )"
1539             if $#_<2 || $#_>3;
1540 6         13 my $img = shift;
1541 6         13 my $px = shift;
1542 6         11 my $py = shift;
1543 6 50       39 my $out = $#_ == -1 ? PDL->null() : shift;
1544             # safety checks
1545 6         25 my $copt = $opts->current();
1546 6         26 my $kernel = _check_kernel( $$copt{KERNEL}, "warp2d" );
1547 6         149489 &PDL::_warp2d_int( $img, $px, $py, $out, $kernel, $$copt{NOVAL}, _get_kernel_size() );
1548 6         183 return $out;
1549             }
1550              
1551              
1552              
1553             *warp2d = \&PDL::warp2d;
1554              
1555              
1556              
1557              
1558              
1559              
1560             =head2 warp2d_kernel
1561              
1562             =for sig
1563              
1564             Signature: ([o] x(n); [o] k(n); ldouble [t] kernel(n); char *name; PDL_Indx nsize => n)
1565             Types: (float double ldouble)
1566              
1567             =for ref
1568              
1569             Return the specified kernel, as used by L
1570              
1571             =for usage
1572              
1573             ( $x, $k ) = warp2d_kernel( $name )
1574              
1575             The valid values for C<$name> are the same as the C option
1576             of L.
1577              
1578             =for example
1579              
1580             line warp2d_kernel( "hamming" );
1581              
1582             =pod
1583              
1584             Broadcasts over its inputs.
1585              
1586             =for bad
1587              
1588             C ignores the bad-value flag of the input ndarrays.
1589             It will set the bad-value flag of all output ndarrays if the flag is set for any of the input ndarrays.
1590              
1591             =cut
1592              
1593              
1594              
1595              
1596              
1597             sub PDL::warp2d_kernel ($) {
1598 0     0 0   my $kernel = _check_kernel( shift, "warp2d_kernel" );
1599 0           &PDL::_warp2d_kernel_int( my $x=PDL->null, my $k=PDL->null, $kernel, _get_kernel_size() );
1600 0           return ( $x, $k );
1601             }
1602              
1603              
1604              
1605             *warp2d_kernel = \&PDL::warp2d_kernel;
1606              
1607              
1608              
1609              
1610              
1611              
1612              
1613             #line 37 "lib/PDL/Image2D.pd"
1614              
1615             =head1 AUTHORS
1616              
1617             Copyright (C) Karl Glazebrook 1997 with additions by Robin Williams
1618             (rjrw@ast.leeds.ac.uk), Tim Jenness (timj@jach.hawaii.edu),
1619             and Doug Burke (burke@ifa.hawaii.edu).
1620              
1621             All rights reserved. There is no warranty. You are allowed
1622             to redistribute this software / documentation under certain
1623             conditions. For details, see the file COPYING in the PDL
1624             distribution. If this file is separated from the PDL distribution,
1625             the copyright notice should be included in the file.
1626              
1627             =cut
1628             #line 1629 "lib/PDL/Image2D.pm"
1629              
1630             # Exit with OK status
1631              
1632             1;