File Coverage

blib/lib/PDL/IO/Pnm.pm
Criterion Covered Total %
statement 133 158 84.1
branch 85 128 66.4
condition 29 54 53.7
subroutine 13 17 76.4
pod 1 7 14.2
total 261 364 71.7


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