File Coverage

blib/lib/PDLA/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 PDLA::PP! Don't modify!
4             #
5             package PDLA::IO::Pnm;
6              
7             @EXPORT_OK = qw( rpnm wpnm PDLA::PP pnminraw PDLA::PP pnminascii PDLA::PP pnmout );
8             %EXPORT_TAGS = (Func=>[@EXPORT_OK]);
9              
10 13     13   535 use PDLA::Core;
  13         30  
  13         77  
11 13     13   101 use PDLA::Exporter;
  13         28  
  13         69  
12 13     13   67 use DynaLoader;
  13         36  
  13         905  
13              
14              
15              
16            
17             @ISA = ( 'PDLA::Exporter','DynaLoader' );
18             push @PDLA::Core::PP, __PACKAGE__;
19             bootstrap PDLA::IO::Pnm ;
20              
21              
22              
23              
24             =head1 NAME
25              
26             PDLA::IO::Pnm -- pnm format I/O for PDLA
27              
28             =head1 SYNOPSIS
29              
30             use PDLA::IO::Pnm;
31             $im = wpnm $pdl, $file, $format[, $raw];
32             rpnm $stack->slice(':,:,:,(0)'),"PDLA.ppm";
33              
34             =head1 DESCRIPTION
35              
36             pnm I/O for PDLA.
37              
38             =cut
39              
40 13     13   77 use PDLA::Core qw/howbig convert/;
  13         38  
  13         76  
41 13     13   99 use PDLA::Types;
  13         32  
  13         1474  
42 13     13   86 use PDLA::Basic; # for max/min
  13         30  
  13         101  
43 13     13   1346 use PDLA::IO::Misc;
  13         39  
  13         87  
44 13     13   87 use Carp;
  13         26  
  13         737  
45 13     13   8331 use File::Temp qw( tempfile );
  13         128856  
  13         24956  
46              
47             # return the upper limit of data values an integer PDLA data type
48             # can hold
49             sub dmax {
50 36     36 0 61 my $type = shift;
51 36         110 my $sz = 8*howbig($type);
52 36 50 33     162 $sz-- if ($type == $PDLA_S || $type == $PDLA_L); # signed types
53 36         132 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 = \&PDLA::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 = \&PDLA::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 = \&PDLA::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 PDLA_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)'),"PDLA.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 492 sub rpnm {PDLA->rpnm(@_)}
249             sub PDLA::rpnm {
250 26 50 33 26 0 137 barf 'Usage: $im = rpnm($file) or $im = $pdl->rpnm($file)'
251             if $#_<0 || $#_>2;
252 26         84 my ($pdl,$file,$maybe) = @_;
253              
254              
255 26 100       68 if (ref($file)) { # $file is really a pdl in this case
256 10         20 $pdl = $file;
257 10         19 $file = $maybe;
258             } else {
259 16         106 $pdl = $pdl->initialize;
260             }
261              
262 26         81 my ($errfh, $efile) = tempfile();
263             # catch STDERR
264 26         8209 open(SAVEERR, ">&STDERR");
265 26 50       1081 open(STDERR, ">$efile") || barf "Can't redirect stderr";
266 26         1272 my $succeed = open(PNM, $file);
267             # redirection now in effect for child
268             # close(STDERR);
269 26         584 open(STDERR, ">&PDLA::IO::Pnm::SAVEERR");
270 26 50       102 rbarf $efile,"Can't open pnm file '$file'" unless $succeed;
271 26         59 binmode PNM;
272              
273 26         289 read(PNM,(my $magic),2);
274 26 50       175 rbarf $efile, "Oops, this is not a PNM file" unless $magic =~ /P[1-6]/;
275 26 50       73 print "reading pnm file with magic $magic\n" if $PDLA::debug>1;
276              
277 26         68 my ($isrgb,$israw,$params) = (0,0,3);
278 26 100       103 $israw = 1 if $magic =~ /P[4-6]/;
279 26 100       78 $isrgb = 1 if $magic =~ /P[3,6]/;
280 26 100       76 if ($magic =~ /P[1,4]/) { # PBM data
281 7         14 $params = 2;
282 7         15 $dims[2] = 1; }
283              
284             # get the header information
285 26         71 my ($line, $pgot, @dims) = ("",0,0,0,0);
286 26   66     169 while (($pgot<$params) && ($line=)) {
287 71         134 $line =~ s/#.*$//;
288 71 100       293 next if $line =~ /^\s*$/; # just white space
289 45   66     183 while ($line !~ /^\s*$/ && $pgot < $params) {
290 71 50       255 if ($line =~ /\s*(\S+)(.*)$/) {
291 71         216 $dims[$pgot++] = $1; $line = $2; }
  71         352  
292             else {
293 0         0 rbarf $efile, "no valid header info in pnm";}
294             }
295             }
296              
297 26         53 my $type = $PDLA_B;
298 26         35 do {
299 26         37 TYPES: { my $pdlt;
  26         37  
300 26         51 foreach $pdlt ($PDLA_B,$PDLA_US,$PDLA_L){
301 36 100       97 if ($dims[2] <= dmax($pdlt))
302 26         52 { $type = $pdlt;
303 26         65 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       59 rbarf $efile, "no valid header info in pnm" if $pgot < $params;
312 26 50 33     127 rbarf $efile,
313             "Dimensions must be > 0" if ($dims[0] <= 0) || ($dims[1] <= 0);
314              
315 26         88 my @Dims = @dims[0,1];
316 26 100       51 $Dims[0] *= 3 if $isrgb;
317 26 100 33     254 if ($pdl->getndims==1 && $pdl->getdim(0)==0 && $isrgb) { #input pdl is null
      66        
318 2         4 local $PDLA::debug = 0; # shut up
319 2         13 $pdl = $pdl->zeroes(PDLA::Type->new($type),3,@dims[0,1]);
320             }
321 26 100       83 my $npdl = $isrgb ? $pdl->clump(2) : $pdl;
322 26 100       57 if ($israw) {
323 18         58 pnminraw (convert(pdl(0),$type), $npdl, $Dims[0], $Dims[1],
324             $magic eq "P4", 'PDLA::IO::Pnm::PNM');
325             } else {
326 8 50       36 my $form = $1 if $magic =~ /P([1-3])/;
327 8         24 pnminascii (convert(pdl(0),$type), $npdl, $Dims[0], $Dims[1],
328             $form, 'PDLA::IO::Pnm::PNM');
329             }
330 26 50       329 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 $PDLA::debug;
333 26         752 unlink($efile);
334              
335             # need to byte swap for little endian platforms
336 26 50       128 unless ( isbigendian() ) {
337 26 100       68 if ($israw ) {
338 18 100 66     897 $pdl->bswap2 if $type==$PDLA_US or $pdl->type == ushort;
339 18 50       70 $pdl->bswap4 if $type==$PDLA_L; # not likely, but supported anyway
340             }
341             }
342 26         715 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 = \&PDLA::wpnm;
372             sub PDLA::wpnm {
373 24 50   24 0 1237 barf ('Usage: wpnm($pdl,$filename,$format[,$raw]) ' .
374             'or $pdl->wpnm($filename,$format[,$raw])') if $#_ < 2;
375 24         76 my ($pdl,$file,$type,$raw) = @_;
376 24         62 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         103 my $swap_inplace = $pdl->is_inplace;
382              
383 24 50       136 barf "wpnm: unknown format '$type'" if $type !~ /P[P,G,B]M/;
384              
385             # check the data
386 24         89 my @Dims = $pdl->dims;
387 24 50 33     87 barf "wpnm: expecting 3D (3,w,h) input"
      66        
388             if ($type =~ /PPM/) && (($#Dims != 2) || ($Dims[0] != 3));
389 24 50 66     153 barf "wpnm: expecting 2D (w,h) input"
390             if ($type =~ /P[G,B]M/) && ($#Dims != 1);
391 24 50 33     182 barf "wpnm: user should convert float and double data to appropriate type"
392             if ($pdl->get_datatype == $PDLA_F) || ($pdl->get_datatype == $PDLA_D);
393 24 50 66     181 barf "wpnm: expecting prescaled data"
      33        
394             if (($pdl->get_datatype != $PDLA_B) || ($pdl->get_datatype != $PDLA_US)) &&
395             ($pdl->min < 0);
396              
397             # check for raw format
398 24 50 66     184 $israw = 1 if (($pdl->get_datatype == $PDLA_B) || ($pdl->get_datatype == $PDLA_US) || ($type =~ /PBM/));
      33        
399 24 100 100     101 $israw = 0 if (defined($raw) && !$raw);
400              
401              
402 24 100       96 $magic = $israw ? "P4" : "P1" if $type =~ /PBM/;
    100          
403 24 100       110 $magic = $israw ? "P5" : "P2" if $type =~ /PGM/;
    100          
404 24 50       68 $magic = $israw ? "P6" : "P3" if $type =~ /PPM/;
    100          
405 24 100       93 $isrgb = 1 if $magic =~ /P[3,6]/;
406              
407             # catch STDERR and sigpipe
408 24         90 my ($errfh, $efile) = tempfile();
409 0     0   0 local $SIG{"PIPE"} = sub { show_err($efile);
410 24         8247 die "Bad write to pipe $? $!"; };
  0         0  
411              
412 24 100       173 my $pref = ($file !~ /^\s*[|>]/) ? ">" : ""; # test for plain file name
413 24         489 open(SAVEERR, ">&STDERR");
414 24 50       1166 open(STDERR, ">$efile") || barf "Can't redirect stderr";
415 24         1348 my $succeed = open(PNM, $pref . $file);
416             # close(STDERR);
417 24         589 open(STDERR, ">&PDLA::IO::Pnm::SAVEERR");
418 24 50       93 rbarf $efile, "Can't open pnm file" unless $succeed;
419 24         59 binmode PNM;
420              
421 24         122 $max =$pdl->max;
422 24 50       191 print "writing ". ($israw ? "raw" : "ascii") .
    100          
423             "format with magic $magic\n" if $PDLA::debug;
424             # write header
425 24         238 print PNM "$magic\n";
426 24         99 print PNM "$Dims[-2] $Dims[-1]\n";
427 24 100       93 if ($type !~ /PBM/) { # fix maxval for raw output formats
428 17         34 my $outmax = 0;
429              
430 17 100       65 if ($max < 256) {
    50          
431 9         24 $outmax = "255";
432             } elsif ($max < 65536) {
433 8         21 $outmax = "65535";
434             } else {
435 0         0 $outmax = $max;
436             };
437              
438 17 50       65 print PNM "$outmax\n" unless $type =~ /PBM/;
439             };
440              
441             # if rgb clump first two dims together
442 24 100       141 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       107 unless ( isbigendian() ) {
447 24 100       60 if ($israw ) {
448             # make copy if needed
449 16 50       69 $out = $out->copy unless $swap_inplace;
450 16 100 66     112 if ( (255 < $max) and ($max < 65536)) {
    50          
451 6         805 $out->bswap2;
452             } elsif ($max >= 65536) {
453 0         0 $out->bswap4;
454             }
455             }
456             }
457 24         785 pnmout($out,$israw,$type eq "PBM",'PDLA::IO::Pnm::PNM');
458              
459             # check if our child returned an error (in case of a pipe)
460 24 50       858 if (!(close PNM)) {
461 0         0 my $err = show_err($efile,0);
462 0         0 barf "wpnm: pbmconverter error: $err";
463             }
464 24         1925 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 PDLA
491             distribution. If this file is separated from the PDLA 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