File Coverage

blib/lib/PDL/IO/Pnm.pm
Criterion Covered Total %
statement 9 9 100.0
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 12 12 100.0


line stmt bran cond sub pod time code
1             #
2             # GENERATED WITH PDL::PP from lib/PDL/IO/Pnm.pd! Don't modify!
3             #
4             package PDL::IO::Pnm;
5              
6             our @EXPORT_OK = qw(rpnm wpnm pnminraw pnminascii pnmout );
7             our %EXPORT_TAGS = (Func=>\@EXPORT_OK);
8              
9 15     15   696 use PDL::Core;
  15         32  
  15         112  
10 15     15   120 use PDL::Exporter;
  15         34  
  15         135  
11 15     15   81 use DynaLoader;
  15         30  
  15         6424  
12              
13              
14            
15             our @ISA = ( 'PDL::Exporter','DynaLoader' );
16             push @PDL::Core::PP, __PACKAGE__;
17             bootstrap PDL::IO::Pnm ;
18              
19              
20              
21              
22              
23              
24              
25              
26             #line 11 "lib/PDL/IO/Pnm.pd"
27              
28             use strict;
29             use warnings;
30              
31             =head1 NAME
32              
33             PDL::IO::Pnm -- pnm format I/O for PDL
34              
35             =head1 SYNOPSIS
36              
37             use PDL::IO::Pnm;
38             $im = wpnm $pdl, $file, $format[, $raw];
39             rpnm $stack->slice(':,:,:,(0)'),"PDL.ppm";
40              
41             =head1 DESCRIPTION
42              
43             pnm I/O for PDL.
44              
45             =cut
46              
47             use PDL::Core qw/howbig convert/;
48             use PDL::Types;
49             use PDL::Basic; # for max/min
50             use PDL::IO::Misc;
51             use Carp;
52             use File::Temp qw( tempfile );
53              
54             # return the upper limit of data values an integer PDL data type
55             # can hold
56             sub dmax {
57             my $type = shift;
58             my $sz = 8*howbig($type);
59             $sz-- if !PDL::Type->new($type)->unsigned;
60             return ((1 << $sz)-1);
61             }
62             #line 63 "lib/PDL/IO/Pnm.pm"
63              
64              
65             =head1 FUNCTIONS
66              
67             =cut
68              
69              
70              
71              
72              
73              
74             =head2 pnminraw
75              
76             =for sig
77              
78             Signature: (type(); byte+ [o] im(m,n); byte [t] buf(llen); IV ms => m; IV ns => n;
79             int isbin; PerlIO *fp)
80             Types: (byte ushort long)
81              
82             =for usage
83              
84             $im = pnminraw($type, $ms, $ns, $isbin, $fp);
85             pnminraw($type, $im, $ms, $ns, $isbin, $fp); # all arguments given
86             $im = $type->pnminraw($ms, $ns, $isbin, $fp); # method call
87             $type->pnminraw($im, $ms, $ns, $isbin, $fp);
88              
89             =for ref
90              
91             Read in a raw pnm file.
92              
93             read a raw pnm file. The C argument is only there to
94             determine the type of the operation when creating C or trigger
95             the appropriate type conversion (maybe we want a byte+ here so that
96             C follows I the type of C).
97              
98             =pod
99              
100             Broadcasts over its inputs.
101              
102             =for bad
103              
104             C does not process bad values.
105             It will set the bad-value flag of all output ndarrays if the flag is set for any of the input ndarrays.
106              
107             =cut
108              
109              
110              
111              
112             *pnminraw = \&PDL::pnminraw;
113              
114              
115              
116              
117              
118              
119             =head2 pnminascii
120              
121             =for sig
122              
123             Signature: (type(); byte+ [o] im(m,n); IV ms => m; IV ns => n;
124             int format; PerlIO *fp)
125             Types: (byte ushort short long)
126              
127             =for usage
128              
129             $im = pnminascii($type, $ms, $ns, $format, $fp);
130             pnminascii($type, $im, $ms, $ns, $format, $fp); # all arguments given
131             $im = $type->pnminascii($ms, $ns, $format, $fp); # method call
132             $type->pnminascii($im, $ms, $ns, $format, $fp);
133              
134             =for ref
135              
136             Read in an ascii pnm file.
137              
138             =pod
139              
140             Broadcasts over its inputs.
141              
142             =for bad
143              
144             C does not process bad values.
145             It will set the bad-value flag of all output ndarrays if the flag is set for any of the input ndarrays.
146              
147             =cut
148              
149              
150              
151              
152             *pnminascii = \&PDL::pnminascii;
153              
154              
155              
156              
157              
158              
159             =head2 pnmout
160              
161             =for sig
162              
163             Signature: (a(m); int israw; int isbin; PerlIO *fp)
164             Types: (byte ushort short long)
165              
166             =for usage
167              
168             pnmout($a, $israw, $isbin, $fp); # all arguments given
169             $a->pnmout($israw, $isbin, $fp); # method call
170              
171             =for ref
172              
173             Write a line of pnm data.
174              
175             This function is implemented this way so that broadcasting works
176             naturally.
177              
178             =pod
179              
180             Broadcasts over its inputs.
181             Can't use POSIX threads.
182              
183             =for bad
184              
185             C does not process bad values.
186             It will set the bad-value flag of all output ndarrays if the flag is set for any of the input ndarrays.
187              
188             =cut
189              
190              
191              
192              
193             *pnmout = \&PDL::pnmout;
194              
195              
196              
197              
198              
199              
200              
201             #line 49 "lib/PDL/IO/Pnm.pd"
202              
203             =head2 rpnm
204              
205             =for ref
206              
207             Read a pnm (portable bitmap/pixmap, pbm/ppm) file into an ndarray.
208              
209             =for usage
210              
211             Usage: $im = rpnm $file;
212              
213             Reads a file (or open file-handle) in pnm format (ascii or raw) into a pdl (magic numbers P1-P6).
214             Based on the input format it returns pdls with arrays of size (width,height)
215             if binary or grey value data (pbm and pgm) or (3,width,height) if rgb
216             data (ppm). This also means for a palette image that the distinction between
217             an image and its lookup table is lost which can be a problem in cases (but can
218             hardly be avoided when using netpbm/pbmplus). Datatype is dependent
219             on the maximum grey/color-component value (for raw and binary formats
220             always PDL_B). rpnm tries to read chopped files by zero padding the
221             missing data (well it currently doesn't, it barfs; I'll probably fix it
222             when it becomes a problem for me ;). You can also read directly into an
223             existing pdl that has to have the right size(!). This can come in handy
224             when you want to read a sequence of images into a datacube.
225              
226             For details about the formats see appropriate manpages that come with the
227             netpbm/pbmplus packages.
228              
229             =for example
230              
231             $stack = zeroes(byte,3,500,300,4);
232             rpnm $stack->slice(':,:,:,(0)'),"PDL.ppm";
233              
234             reads an rgb image (that had better be of size (500,300)) into the
235             first plane of a 3D RGB datacube (=4D pdl datacube). You can also do
236             inplace transpose/inversion that way.
237              
238             =cut
239              
240             sub rpnm {PDL->rpnm(@_)}
241             sub PDL::rpnm {
242             barf 'Usage: $im = rpnm($file) or $im = $pdl->rpnm($file)'
243             if !@_ || @_>3;
244             my $pdl = ref($_[1]) && UNIVERSAL::isa($_[1], 'PDL')
245             ? (splice @_, 0, 2)[1] : shift->initialize;
246             my $file = shift;
247              
248             my $fh;
249             if (ref $file) {
250             $fh = $file;
251             } else {
252             open $fh, $file or barf "Can't open pnm file '$file': $!";
253             }
254             binmode $fh;
255              
256             read($fh,(my $magic),2);
257             barf "Oops, this is not a PNM file" unless $magic =~ /P([1-6])/;
258             my $magicno = $1;
259             print "reading pnm file with magic $magic\n" if $PDL::debug>1;
260              
261             my $israw = $magicno > 3 ? 1 : 0;
262             my $isrgb = ($magicno % 3) == 0;
263             my $ispbm = ($magicno % 3) == 1;
264             my ($params, @dims) = ($ispbm ? 2 : 3, 0, 0, $ispbm ? 1 : 0);
265             # get the header information
266             my $pgot = 0;
267             while (($pgot<$params) && defined(my $line=<$fh>)) {
268             $line =~ s/#.*$//;
269             next if $line =~ /^\s*$/; # just white space
270             while ($line !~ /^\s*$/ && $pgot < $params) {
271             if ($line =~ /\s*(\S+)(.*)$/) {
272             $dims[$pgot++] = $1; $line = $2; }
273             else {
274             barf "no valid header info in pnm";}
275             }
276             }
277             # the file ended prematurely
278             barf "no valid header info in pnm" if $pgot < $params;
279             barf "Dimensions must be > 0" if ($dims[0] <= 0) || ($dims[1] <= 0);
280              
281             my ($type) = grep $dims[2] <= dmax($_), $PDL_B,$PDL_US,$PDL_L;
282             barf "rraw: data from ascii pnm file out of range" if !defined $type;
283              
284             my @Dims = @dims[0,1];
285             $Dims[0] *= 3 if $isrgb;
286             $pdl = $pdl->zeroes(PDL::Type->new($type),3,@dims[0,1])
287             if $pdl->isnull and $isrgb;
288             my $npdl = $isrgb ? $pdl->clump(2) : $pdl;
289             if ($israw) {
290             pnminraw (convert(pdl(0),$type), $npdl, $Dims[0], $Dims[1],
291             $ispbm, $fh);
292             } else {
293             pnminascii (convert(pdl(0),$type), $npdl, $Dims[0], $Dims[1],
294             $magicno, $fh);
295             }
296             print("loaded pnm file, $dims[0]x$dims[1], gmax: $dims[2]",
297             $isrgb ? ", RGB data":"", $israw ? ", raw" : " ASCII"," data\n")
298             if $PDL::debug;
299              
300             # need to byte swap for little endian platforms
301             $pdl->type->bswap->($pdl) if !isbigendian() and $israw;
302             return $pdl;
303             }
304              
305             =head2 wpnm
306              
307             =for ref
308              
309             Write a pnm (portable bitmap/pixmap, pbm/ppm) file into a file or open file-handle.
310              
311             =for usage
312              
313             Usage: $im = wpnm $pdl, $file, $format[, $raw];
314              
315             Writes data in a pdl into pnm format (ascii or raw) (magic numbers P1-P6).
316             The $format is required (normally produced by B) and routine just
317             checks if data is compatible with that format. All conversions should
318             already have been done. If possible, usage of B is preferred. Currently
319             RAW format is chosen if compliant with range of input data. Explicit control
320             of ASCII/RAW is possible through the optional $raw argument. If RAW is
321             set to zero it will enforce ASCII mode. Enforcing RAW is
322             somewhat meaningless as the routine will always try to write RAW
323             format if the data range allows (but maybe it should reduce to a RAW
324             supported type when RAW == 'RAW'?). For details about the formats
325             consult appropriate manpages that come with the netpbm/pbmplus
326             packages.
327              
328             =cut
329              
330             my %type2base = (PBM => 1, PGM => 2, PPM => 3);
331             *wpnm = \&PDL::wpnm;
332             sub PDL::wpnm {
333             barf ('Usage: wpnm($pdl,$filename,$format[,$raw]) ' .
334             'or $pdl->wpnm($filename,$format[,$raw])') if $#_ < 2;
335             my ($pdl,$file,$type,$raw) = @_;
336             barf "wpnm: unknown format '$type'" if !exists $type2base{$type};
337              
338             # need to copy input arg since bswap[24] work inplace
339             # might be better if the bswap calls detected if run in
340             # void context
341             my $swap_inplace = $pdl->is_inplace;
342              
343             # check the data
344             my @Dims = $pdl->dims;
345             barf "wpnm: expecting 3D (3,w,h) input"
346             if ($type =~ /PPM/) && (($#Dims != 2) || ($Dims[0] != 3));
347             barf "wpnm: expecting 2D (w,h) input"
348             if ($type =~ /P[GB]M/) && ($#Dims != 1);
349             barf "wpnm: user should convert float etc data to appropriate type"
350             if !$pdl->type->integer;
351             my $max = $pdl->max;
352             barf "wpnm: expecting prescaled data (0-65535)"
353             if $pdl->min < 0 or $max > 65535;
354              
355             # check for raw format
356             my $israw =
357             (defined($raw) && !$raw) ? 0 :
358             (($pdl->get_datatype == $PDL_B) || ($pdl->get_datatype == $PDL_US) || ($type eq 'PBM')) ? 3 :
359             0;
360              
361             my $magic = 'P' . ($type2base{$type} + $israw);
362             my $isrgb = $type eq 'PPM';
363              
364             my $pref = ($file !~ /^\s*[|>]/) ? ">" : ""; # test for plain file name
365             my ($already_open, $fh) = 0;
366             if (ref $file) {
367             $fh = $file, $already_open = 1;
368             } else {
369             open $fh, $pref . $file or barf "Can't open pnm file: $!";
370             }
371             binmode $fh;
372              
373             print "writing ". ($israw ? "raw" : "ascii") .
374             "format with magic $magic, max=$max\n" if $PDL::debug;
375             # write header
376             print $fh "$magic\n";
377             print $fh "$Dims[-2] $Dims[-1]\n";
378             if ($type ne 'PBM') { # fix maxval for raw output formats
379             my $outmax = 0;
380             if ($max < 256) {
381             $outmax = "255";
382             } elsif ($max < 65536) {
383             $outmax = "65535";
384             } else {
385             $outmax = $max;
386             };
387             print $fh "$outmax\n";
388             };
389              
390             # if rgb clump first two dims together
391             my $out = ($isrgb ? $pdl->slice(':,:,-1:0')->clump(2)
392             : $pdl->slice(':,-1:0'));
393             # handle byte swap issues for little endian platforms
394             if (!isbigendian() and $israw) {
395             $out = $out->copy unless $swap_inplace;
396             $out->type->bswap->($out);
397             }
398             pnmout($out,$israw,$type eq "PBM",$fh);
399             # check if our child returned an error (in case of a pipe)
400             barf "wpnm: pbmconverter error: $!" if !$already_open and !close $fh;
401             }
402              
403             ;# Exit with OK status
404              
405             1;
406              
407             =head1 BUGS
408              
409             C currently relies on the fact that the header is separated
410             from the image data by a newline. This is not required by the p[bgp]m
411             formats (in fact any whitespace is allowed) but most of the pnm
412             writers seem to comply with that. Truncated files are currently
413             treated ungracefully (C just barfs).
414              
415             =head1 AUTHOR
416              
417             Copyright (C) 1996,1997 Christian Soeller
418             All rights reserved. There is no warranty. You are allowed
419             to redistribute this software / documentation under certain
420             conditions. For details, see the file COPYING in the PDL
421             distribution. If this file is separated from the PDL distribution,
422             the copyright notice should be included in the file.
423              
424             =cut
425              
426             ############################## END PM CODE ################################
427             #line 428 "lib/PDL/IO/Pnm.pm"
428              
429             # Exit with OK status
430              
431             1;