File Coverage

blib/lib/PDL/IO/Pic.pm
Criterion Covered Total %
statement 161 263 61.2
branch 81 214 37.8
condition 32 96 33.3
subroutine 25 35 71.4
pod 5 20 25.0
total 304 628 48.4


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             PDL::IO::Pic -- image I/O for PDL
4              
5             =head1 DESCRIPTION
6              
7             This package implements I/O for a number of popular image formats
8             by exploiting the xxxtopnm and pnmtoxxx converters from the netpbm package
9             (which is based on the original pbmplus by Jef Poskanzer).
10              
11             Netpbm is available at
12             ftp://wuarchive.wustl.edu/graphics/graphics/packages/NetPBM/
13             Pbmplus (on which netpbm is based) might work as well, I haven't tried it.
14             If you want to read/write JPEG images you additionally need the two
15             converters cjpeg/djpeg which come with the libjpeg distribution (the
16             "official" archive site for this software is L).
17              
18             Image I/O for all formats is established by reading and writing only
19             the PNM format directly while the netpbm standalone apps take care of
20             the necessary conversions. In accordance with netpbm parlance PNM stands
21             here for 'portable any map' meaning any of the PBM/PGM/PPM formats.
22              
23             As it appeared to be a reasonable place this package also contains the
24             routine wmpeg to write mpeg movies from PDLs representing image
25             stacks (the image stack is first written as a sequence of PPM images into some
26             temporary directory). For this to work you need the program ffmpeg also.
27              
28             =cut
29              
30             package PDL::IO::Pic;
31              
32 14     14   2435 use strict;
  14         37  
  14         578  
33 14     14   75 use warnings;
  14         31  
  14         2758  
34              
35             our @EXPORT_OK = ('imageformat', map +("r$_", "w$_"), qw(mpeg im pic piccan));
36             our %EXPORT_TAGS = (Func => \@EXPORT_OK);
37             our ($Dflags, %converter);
38             our @ISA = qw( PDL::Exporter );
39              
40 14     14   129 use PDL::Core;
  14         67  
  14         193  
41 14     14   113 use PDL::Exporter;
  14         46  
  14         95  
42 14     14   84 use PDL::Types;
  14         48  
  14         3057  
43 14     14   8290 use PDL::ImageRGB;
  14         54  
  14         159  
44 14     14   10255 use PDL::IO::Pnm;
  14         66  
  14         186  
45 14     14   149 use PDL::Options;
  14         428  
  14         1068  
46 14     14   138 use File::Basename;
  14         31  
  14         1375  
47 14     14   93 use File::Spec;
  14         31  
  14         497  
48 14     14   7726 use Text::ParseWords qw(shellwords);
  14         27731  
  14         1122  
49 14     14   7532 use File::Which ();
  14         21950  
  14         82189  
50              
51             =head2 Configuration
52              
53             The executables from the netpbm package are assumed to be in your path.
54             Problems in finding the executables may show up as PNM format
55             errors when calling wpic/rpic. If you run into this kind of problem run
56             your program with perl C<-w> so that perl prints a message if it can't find
57             the filter when trying to open the pipe. [']
58              
59             =cut
60              
61              
62             # list of converters by type
63             # might get more fields in the future to provide a generic representation
64             # of common flags like COMPRESSION, LUT, etc which would hold the correct
65             # flags for the particular converter or NA if not supported
66             # conventions:
67             # NONE we need no converter (directly supported format)
68             # NA feature not available
69             # 'whatevertopnm' name of the executable
70             # The 'FLAGS' key must be used if the converter needs other flags than
71             # the default flags ($Dflags)
72             #
73             #
74             # The "referral" field, if present, contains a within-perl referral
75             # to other methods for reading/writing the PDL as that type of file. The
76             # methods must have the same syntax as wpic/rpic (e.g. wfits/rfits).
77             #
78              
79             $PDL::IO::Pic::debug = $PDL::IO::Pic::debug || 0;
80             init_converter_table();
81              
82             # setup functions
83              
84             sub init_converter_table {
85             # default flag to be used with any converter unless overridden with FLAGS
86 14     14 0 39 $Dflags = '';
87 14         36 %converter = ();
88              
89 14 0       313 if (eval {require PDL::IO::GD; PDL::IO::GD->can ('to_rpic') && PDL::IO::GD->can ('write_Jpeg')}) {
  14 50       2207  
  0         0  
90             $converter{JPEG} = {referral => {
91             put => sub {
92 0     0   0 my $pdl = $_[0];
93 0 0 0     0 $pdl = $pdl->mv(0,-1) if $pdl->ndims > 2 && $pdl->dim(0) == 3;
94 0         0 PDL::IO::GD->new(pdl=>$pdl->slice(',-1:0'))->write_Jpeg($_[1], -1);
95             },
96             get => sub {
97 0     0   0 my $pdl = PDL::IO::GD->new($_[1])->to_rpic;
98 0 0       0 $pdl->diff2->zcheck ? $pdl->slice('(0)')->sever : $pdl; # greyscale
99             },
100 0         0 }};
101             }
102              
103             # Pbmplus systems have cjpeg/djpeg; netpbm systems have pnmtojpeg and
104             # jpegtopnm.
105             $converter{$_} = {put => "pnmto\L$_", get => "\L${_}topnm"}
106 14 50 33     192 for qw/TIFF SGI RAST PCX/, !$converter{PNG} ? 'PNG' : (),
    50          
107             !$converter{JPEG} && File::Which::which('pnmtojpeg') ? "JPEG" : ();
108              
109 14 50 33     4245 $converter{$_->[0]} = {put => $_->[1], get => $_->[2]} for
110             ['PNM','NONE','NONE'],
111             ['PS','pnmtops -dpi=100',
112             'pstopnm -stdout -xborder=0 -yborder=0 -quiet -dpi=100'],
113             ['GIF','ppmtogif','giftopnm'],
114             ['XBM','pbmtoxbm','xbmtopbm'],
115             ['IFF','ppmtoilbm','ilbmtoppm'],
116             !$converter{JPEG} && File::Which::which('cjpeg') ? ['JPEG', 'cjpeg' ,'djpeg'] : ();
117              
118 14         3222 $converter{FITS}= {referral => {put => \&PDL::wfits, get => \&PDL::rfits}};
119              
120             # these converters do not understand pbmplus flags:
121 14         220 $converter{JPEG}{FLAGS} = '';
122 14         46 $converter{GIF}{Prefilt} = 'ppmquant 256 |';
123              
124 14         105 for my $key (keys %converter) {
125             $converter{$key}{Rok} = File::Which::which($converter{$key}{get})
126 168 100       660 if defined($converter{$key}{get});
127             $converter{$key}{Wok} = File::Which::which($converter{$key}{put})
128 168 100       27544 if defined($converter{$key}{put});
129 168 100       26610 if (defined $converter{$key}{Prefilt}) {
130 14 50       170 my $filt = $1 if $converter{$key}{Prefilt} =~ /^\s*(\S+)\s+/;
131 14 50       73 $converter{$key}{Wok} = File::Which::which($filt) if $converter{$key}{Wok};
132             }
133             }
134              
135 14         399 $converter{$_}{ushortok} = 1 for grep !m/GIF|TIFF/, keys %converter;
136             }
137              
138             =head1 FUNCTIONS
139              
140             =head2 rpiccan, wpiccan
141              
142             =for ref
143              
144             Test which image formats can be read/written
145              
146             =for example
147              
148             $im = PDL->rpic('PDL.jpg') if PDL->rpiccan('JPEG');
149             @wformats = PDL->wpiccan();
150              
151             finds out if PDL::IO::Pic can read/write certain image formats.
152             When called without arguments returns a list of supported
153             formats. When called with an argument returns true if format
154             is supported on your computer (requires appropriate filters in
155             your path), false otherwise.
156              
157             =cut
158              
159 0     0 1 0 sub rpiccan {return PDL->rpiccan(@_)}
160 0     0 1 0 sub wpiccan {return PDL->wpiccan(@_)}
161 6     6 0 403341 sub PDL::rpiccan {splice @_,1,0,'R';
162 6         21 return PDL::IO::Pic::piccan(@_)}
163 2     2 0 190968 sub PDL::wpiccan {splice @_,1,0,'W';
164 2         5 return PDL::IO::Pic::piccan(@_)}
165              
166              
167             =head2 rpic
168              
169             =for ref
170              
171             Read images in many formats with automatic format detection.
172              
173             =for example
174              
175             $im = rpic $file;
176             $im = PDL->rpic 'PDL.jpg' if PDL->rpiccan('JPEG');
177              
178             I
179              
180             =for opt
181              
182             FORMAT => 'JPEG' # explicitly read this format
183             XTRAFLAGS => '-nolut' # additional flags for converter
184              
185             Reads image files in most of the formats supported by netpbm. You can
186             explicitly specify a supported format by additionally passing a hash
187             containing the FORMAT key as in
188              
189             $im = rpic ($file, {FORMAT => 'GIF'});
190              
191             This is especially useful if the particular format isn't identified by
192             a magic number and doesn't have the 'typical' extension or you want to
193             avoid the check of the magic number if your data comes in from a pipe.
194             The function returns a pdl of the appropriate type upon completion.
195             Option parsing uses the L module and
196             therefore supports minimal options matching.
197              
198             You can also read directly into an existing pdl that has to have the
199             right size(!). This can come in handy when you want to read a sequence
200             of images into a datacube, e.g.
201              
202             $stack = zeroes(byte,3,500,300,4);
203             rpic $stack->slice(':,:,:,(0)'),"PDL.jpg";
204              
205             reads an rgb image (that had better be of size (500,300)) into the
206             first plane of a 3D RGB datacube (=4D pdl datacube). You can also do
207             transpose/inversion upon read that way.
208              
209             =cut
210              
211             my $rpicopts = {
212             FORMAT => undef,
213             XTRAFLAGS => undef,
214             };
215              
216 8     8 1 36 sub rpic {PDL->rpic(@_)}
217              
218             sub PDL::rpic {
219 11 50   11 0 52 barf 'Usage: $im = rpic($file[,hints]) or $im = PDL->rpic($file[,hints])'
220             if !@_;
221 11         31 my ($class,$file,$hints,$maybe) = @_;
222 11         17 my ($type, $pdl);
223              
224 11 50       27 if (ref($file)) { # $file is really a pdl in this case
225 0         0 $pdl = $file;
226 0         0 $file = $hints;
227 0         0 $hints = $maybe;
228             } else {
229 11         63 $pdl = $class->initialize;
230             }
231              
232 11 100       54 $hints = { iparse $rpicopts, $hints } if ref $hints;
233 11 100       45 if (defined($$hints{FORMAT})) {
234 9         18 $type = $$hints{FORMAT};
235             barf "unsupported (input) image format"
236             unless exists($converter{$type}) && (
237             ($converter{$type}{referral} && $converter{$type}{referral}{get}) ||
238 9 50 33     108 $converter{$type}{get} !~ /NA/);
      33        
239             } else {
240 2         5 $type = chkform($file);
241 2 50       6 barf "can't figure out file type, specify explicitly"
242             if $type =~ /UNKNOWN/;
243             }
244              
245 11         23 my($converter) = $PDL::IO::Pic::converter;
246 11 50       31 if (defined($converter{$type}{referral})) {
247 0 0       0 if(ref ($converter{$type}{referral}{get}) eq 'CODE') {
248 0         0 return &{$converter{$type}{referral}{get}}(@_);
  0         0  
249             } else {
250 0         0 barf "rpic: internal error with referral (format is $type)\n";
251             }
252             }
253              
254 11         20 my $fh;
255 11 50 33     54 if ($converter{$type}->{'get'} and $converter{$type}->{'get'} =~ /^NONE/) {
256 11         638 open $fh, $file;
257             } else {
258 0   0     0 my @cmd = $converter{$type}{get} // barf "No converter for '$type'";
259 0   0     0 push @cmd, shellwords $converter{$type}{FLAGS} // $Dflags;
260 0 0       0 push @cmd, shellwords $$hints{XTRAFLAGS} if defined($$hints{XTRAFLAGS});
261 0 0       0 open $fh, '-|', @cmd, $file
262             or barf "spawning '@cmd' failed: $? ($!)";
263 0 0       0 print "conversion by '@cmd'\n" if $PDL::IO::Pic::debug > 10;
264             }
265 11         43 binmode $fh;
266 11         22 my @frames;
267 11         273 while (!eof $fh) {
268 11         67 push @frames, rpnm $fh;
269             }
270 11 50       336 @frames == 1 ? $frames[0] : cat(@frames);
271             }
272              
273             =head2 wpic
274              
275             =for ref
276              
277             Write images in many formats with automatic format selection.
278              
279             =for usage
280              
281             Usage: wpic($pdl,$filename[,{ options... }])
282              
283             =for example
284              
285             wpic $pdl, $file;
286             $im->wpic('web.gif',{LUT => $lut});
287             for (@images) {
288             $_->wpic($name[0],{CONVERTER => 'ppmtogif'})
289             }
290              
291              
292             Write out an image file. Function will try to guess correct image
293             format from the filename extension, e.g.
294              
295             $pdl->wpic("image.gif")
296              
297             will write a gif file. The data written out will be scaled to byte if
298             input is of type float/double. Input data that is of a signed integer
299             type and contains negative numbers will be rejected (assuming the user
300             should have the desired conversion to an unsigned type already). A number
301             of options can be specified (as a hash reference) to get more direct control of
302             the image format that is being written. Valid options are (key
303             => example_value):
304              
305             =for options
306              
307             CONVERTER => 'ppmtogif', # explicitly specify pbm converter
308             FLAGS => '-interlaced -transparent 0', # flags for converter
309             IFORM => 'PGM', # explicitly specify intermediate format
310             XTRAFLAGS => '-imagename iris', # additional flags to defaultflags
311             FORMAT => 'PCX', # explicitly specify output image format
312             COLOR => 'bw', # specify color conversion
313             LUT => $lut, # use color table information
314              
315             Option parsing uses the L module and
316             therefore supports minimal options matching. A detailed explanation of
317             supported options follows.
318              
319             =over 7
320              
321             =item CONVERTER
322              
323             directly specify the converter,
324             you had better know what you are doing, e.g.
325              
326             CONVERTER => 'ppmtogif',
327              
328             =item FLAGS
329              
330             flags to use with the converter;
331             ignored if !defined($$hints{CONVERTER}), e.g. with the gif format
332              
333             FLAGS => '-interlaced -transparent 0',
334              
335             =item IFORM
336              
337             intermediate PNM/PPM/PGM/PBM format to use;
338             you can append the strings 'RAW' or 'ASCII'
339             to enforce those modes, eg IFORMAT=>'PGMRAW' or
340              
341             IFORM => 'PGM',
342              
343             =item XTRAFLAGS
344              
345             additional flags to use with an automatically chosen
346             converter, this example works when you write SGI
347             files (but will give an error otherwise)
348              
349             XTRAFLAGS => '-imagename iris',
350              
351             =item FORMAT
352              
353             explicitly select the format you want to use. Required if wpic cannot
354             figure out the desired format from the file name extension. Supported
355             types are currently TIFF,GIF,SGI,PNM,JPEG,PS,RAST(Sun Raster),IFF,PCX,
356             e.g.
357              
358             FORMAT => 'PCX',
359              
360             =item COLOR
361              
362             you want black and white (value B), other possible value is
363             B which will write a dithered black&white
364             image from the input data, data conversion will be done appropriately,
365             e.g.
366              
367             COLOR => 'bw',
368              
369             =item LUT
370              
371             This is a palette image and the value of this key should be a
372             pdl containing an RGB lookup table (3,x), e.g.
373              
374             LUT => $lut,
375              
376             =back
377              
378             Using the CONVERTER hint you can also build a pipe and perform
379             several netpbm operations to get the special result you like. Using it
380             this way the first converter/filecommand in the pipe should be
381             specified with the CONVERTER hint and subsequent converters + flags in
382             the FLAGS hint. This is because wpic tries to figure out the required
383             format to be written by wpnm based on the first converter. Be careful when
384             using the PBMBIN var as it will only be prepended to the converter. If more
385             converters are in the FLAGS part specify the full path unless they are in
386             your PATH anyway.
387              
388             Example:
389              
390             $im->wpic('test.ps',{CONVERTER => 'pgmtopbm',
391             FLAGS => "-dither8 | pnmtops" })
392              
393             Some of the options may appear silly at the moment and probably
394             are. The situation will hopefully improve as people use the code and
395             the need for different/modified options becomes clear. The general
396             idea is to make the function perl compliant: easy things should be
397             easy, complicated tasks possible.
398              
399             =cut
400              
401             my %wpicopts = map {($_ => undef)}
402             qw/IFORM CONVERTER FLAGS FORMAT
403             XTRAFLAGS COLOR LUT/;
404             my $wpicopts = \%wpicopts;
405              
406             *wpic = \&PDL::wpic;
407              
408             sub PDL::wpic {
409 5 50   5 0 77 barf 'Usage: wpic($pdl,$filename[,$hints]) ' .
410             'or $pdl->wpic($filename,[,$hints])' if @_ < 2;
411              
412 5         13 my ($pdl,$file,$hints) = @_;
413 5         10 my ($type, $cmd, $form,$iform,$iraw);
414              
415 5 100       25 $hints = {iparse($wpicopts, $hints)} if ref $hints;
416             # figure out the right converter
417 5         22 my ($conv, $flags, $format, $referral) = getconv($pdl,$file,$hints);
418              
419 5 50       16 if(defined($referral)) {
420 0 0       0 if(ref ($referral->{'put'}) eq 'CODE') {
421 0         0 return &{$referral->{'put'}}(@_);
  0         0  
422             } else {
423 0         0 barf "wpic: internal error with referral (format is $format)\n";
424             }
425             }
426              
427 5 100       64 print "Using the command $conv with the flags $flags\n"
428             if $PDL::IO::Pic::debug>10;
429              
430 5 50       17 if (defined($$hints{IFORM})) {
431 0         0 $iform = $$hints{IFORM}; }
432             else { # check if converter requires a particular intermediate format
433 5 50       33 $iform = 'PPM' if $conv =~ /^\s*(ppm)|(cjpeg)/;
434 5 50       19 $iform = 'PGM' if $conv =~ /^\s*pgm/;
435 5 50       27 $iform = 'PBM' if $conv =~ /^\s*pbm/;
436 5 50       26 $iform = 'PNM' if $conv =~ /^\s*(pnm)|(NONE)/; }
437             # get final values for $iform and $pdl (check conversions, consistency,etc)
438 5         18 ($pdl,$iform) = chkpdl($pdl,$iform,$hints,$format);
439 5 100       28 print "using intermediate format $iform\n" if $PDL::IO::Pic::debug>10;
440              
441 5         21 $cmd = "|" . qq{$conv $flags >"$file"};
442 5 50       19 $cmd = ">" . $file if $conv =~ /^NONE/;
443 5 100       16 print "built the command $cmd to write image\n" if $PDL::IO::Pic::debug>10;
444              
445 5 50 33     16 $iraw = 1 if (defined($$hints{IFORM}) && $$hints{IFORM} =~ /RAW/);
446 5 50 33     31 $iraw = 0 if (defined($$hints{IFORM}) && $$hints{IFORM} =~ /ASCII/);
447              
448 5     0   94 local $SIG{PIPE}= sub {}; # Prevent crashing if converter dies
449              
450 5         37 wpnm($pdl, $cmd, $iform , $iraw);
451             }
452              
453             =head2 rim
454              
455             =for usage
456              
457             Usage: $x = rim($file);
458             or rim($x,$file);
459              
460             =for ref
461              
462             Read images in most formats, with improved RGB handling.
463              
464             You specify a filename and get back a PDL with the image data in it.
465             Any PNM handled format or FITS will work. In the second form, $x is an
466             existing PDL that gets loaded with the image data.
467              
468             If the image is in one of the standard RGB formats, then you get back
469             data in (,,) format -- that is to say, the third dim
470             contains the color information. That allows you to do simple indexing
471             into the image without knowing whether it is color or not -- if present,
472             the RGB information is silently broadcasted over. (Contrast L, which
473             munges the information by putting the RGB index in the 0th dim, screwing
474             up subsequent broadcasting operations).
475              
476             If the image is in FITS format, then you get the data back in exactly
477             the same order as in the file itself.
478              
479             Images with a ".Z" or ".gz" extension are assumed to be compressed with
480             UNIX L<"compress"|compress> or L<"gzip"|gzip>, respectively, and are
481             automatically uncompressed before reading.
482              
483             OPTIONS
484              
485             The same as L, which is used as an engine:
486              
487             =over 3
488              
489             =item FORMAT
490              
491             If you don't specify this then formats are autodetected. If you do specify
492             it then only the specified interpreter is tried. For example,
493              
494             $x = rim("foo.gif",{FORMAT=>"JPEG"})
495              
496             forces JPEG interpretation.
497              
498             =item XTRAFLAGS
499              
500             Contains extra command line flags for the pnm interpreter. For example,
501              
502             $x = rim("foo.jpg",{XTRAFLAGS=>"-nolut"})
503              
504             prevents use of a lookup table in JPEG images.
505              
506             =back
507              
508             =cut
509              
510             sub rim {
511             ## Handle dest-PDL-first case
512 6 100 66 6 1 179 my $dest = @_ >= 2 && UNIVERSAL::isa($_[0],'PDL') ? shift : undef;
513 6         19 my $out = rpic(@_);
514 6   66     145 my $isrgb = $out->ndims == 3 && $out->dim(0) == 3;
515             $out = $out->reorder(1,2,0) if $isrgb &&
516 6 100 33     55 !(defined($out->gethdr) && $out->gethdr->{SIMPLE});
      66        
517 6 100       31 $dest .= $out if defined $dest;
518 6         56 $out;
519             }
520              
521             =head2 wim
522              
523             =for ref
524              
525             Write a pdl to an image file with selected type (or using filename extensions)
526              
527             =for usage
528              
529             wim $pdl,$file;
530             $pdl->wim("foo.gif",{LUT=>$lut});
531              
532             Write out an image file. You can specify the format explicitly as an
533             option, or the function will try to guess the correct image
534             format from the filename extension, e.g.
535              
536             $pdl->wim("image.gif");
537             $pdl->wim("image.fits");
538              
539             will write a gif and a FITS file. The data written out will be scaled
540             to byte if the input if of type float/double. Input data that is of a
541             signed integer type and contains negative numbers will be rejected.
542              
543             If you append C<.gz> or C<.Z> to the end of the file name, the final
544             file will be automatically compressed with L<"gzip"|gzip> |
545             L<"compress"|compress>, respectively.
546              
547             OPTIONS
548              
549             You can pass in a hash ref whose keys are options. The code uses the
550             PDL::Options module so unique abbreviations are accepted. Accepted
551             keys are the same as for L, which is used as an engine:
552              
553             =over 3
554              
555             =item CONVERTER
556              
557             Names the converter program to be used by pbmplus (e.g. "ppmtogif" to
558             output a gif file)
559              
560             =item FLAGS
561              
562             Flags that should be passed to the converter (replacing any default flag list)
563             e.g. "-interlaced" to make an interlaced GIF
564              
565             =item IFORM
566              
567             Explicitly specifies the intermediate format (e.g. PGM, PPM, or PNM).
568              
569             =item XTRAFLAGS
570              
571             Flags that should be passed to the converter (in addition to any default
572             flag list).
573              
574             =item FORMAT
575              
576             Explicitly specifies the output image format (allowing pbmplus to pick an
577             output converter)
578              
579             =item COLOR
580              
581             Specifies color conversion (e.g. 'bw' converts to black-and-white; see
582             pbmplus for details).
583              
584             =item LUT
585              
586             Use color-table information
587              
588             =back
589              
590             =cut
591              
592             *wim = \&PDL::wim;
593              
594             sub PDL::wim {
595 0     0 0 0 my(@args) = @_;
596 0         0 my($im) = $args[0];
597 0   0     0 my $isrgb = $im->ndims == 3 && $im->dim(2) == 3;
598             $args[0] = $im->reorder(2,0,1)
599             if $isrgb and !(
600             ( $args[1] =~ m/\.fits$/i )
601             or
602 0 0 0     0 ( ref $args[2] eq 'HASH' and $args[2]->{FORMAT} =~ m/fits/i )
      0        
603             );
604 0         0 wpic(@args);
605             }
606              
607             =head2 rmpeg
608              
609             =for ref
610              
611             Read an image sequence (a (3,x,y,n) byte pdl) from an animation.
612              
613             =for usage
614              
615             $ndarray = rmpeg('movie.mpg'); # $ndarray is (3,x,y,nframes) byte
616              
617             Reads a stack of RGB images from a movie. While the
618             format generated is nominally MPEG, the file extension
619             is used to determine the video encoder type.
620             It uses the program C, and throws an exception if not found.
621              
622             =cut
623              
624             *rmpeg = \&PDL::rmpeg;
625             sub PDL::rmpeg {
626 0 0   0 0 0 barf 'Usage: rmpeg($filename)' if @_ != 1;
627 0         0 my ($file) = @_;
628 0 0       0 die "rmpeg: ffmpeg not found in PATH" if !File::Which::which('ffmpeg');
629 0 0       0 open my $fh, '-|', qw(ffmpeg -loglevel quiet -i), $file, qw(-f image2pipe -codec:v ppm -)
630             or barf "spawning ffmpeg failed: $?";
631 0         0 binmode $fh;
632 0         0 my @frames;
633 0         0 while (!eof $fh) {
634 0         0 push @frames, rpnm $fh;
635             }
636 0         0 cat(@frames);
637             }
638              
639             =head2 wmpeg
640              
641             =for ref
642              
643             Write an image sequence (a (3,x,y,n) byte pdl) as an animation.
644              
645             =for usage
646              
647             $ndarray->wmpeg('movie.mpg'); # $ndarray is (3,x,y,nframes) byte
648              
649             Writes a stack of RGB images as a movie. While the
650             format generated is nominally MPEG, the file extension
651             is used to determine the video encoder type.
652             E.g. F<.mpg> for MPEG-1 encoding, F<.mp4> for MPEG-4 encoding, F<.gif>
653             for GIF animation
654              
655             C requires a 4-D pdl of type B as
656             input. The first dim B to be of size 3 since
657             it will be interpreted as RGB pixel data.
658             C returns 1 on success and undef on failure.
659              
660             =for example
661              
662             use strict; use warnings;
663             use PDL;
664             use PDL::IO::Pic;
665             my ($width, $height, $framecount, $xvel, $maxheight, $ballsize) = (320, 80, 100, 15, 60, 8);
666             my $frames = zeros byte, $width, $height, $framecount;
667             my $coords = yvals(3, $framecount); # coords for drawing ball, all val=frameno
668             my ($xcoords, $ycoords) = map $coords->slice($_), 0, 1;
669             $xcoords *= $xvel; # moves $xvel pixels/frame
670             $xcoords .= $width - abs(($xcoords % (2*$width)) - $width); # back and forth
671             my $sqrtmaxht = sqrt $maxheight;
672             $ycoords .= indx($maxheight - ((($ycoords % (2*$sqrtmaxht)) - $sqrtmaxht)**2));
673             my $val = pdl(byte,250); # start with white
674             $frames->range($coords, [$ballsize,$ballsize,1], 't') .= $val;
675             $frames = $frames->dummy(0, 3)->copy; # now make the movie
676             $frames->wmpeg('bounce.gif'); # or bounce.mp4, ffmpeg deals OK
677              
678             # iterate running this with:
679             rm bounce.gif; perl scriptname.pl && animate bounce.gif
680              
681             Some of the input data restrictions will have to
682             be relaxed in the future but routine serves as
683             a proof of principle at the moment. It uses the
684             program ffmpeg to encode the frames into video.
685             Currently, wmpeg
686             doesn't allow modification of the parameters
687             written through its calling interface. This will
688             change in the future as needed.
689              
690             In the future it might be much nicer to implement
691             a movie perl object that supplies methods for
692             manipulating the image stack (insert, cut, append
693             commands) and a final movie->make() call would
694             invoke ffmpeg on the picture stack (which will
695             only be held on disk). This should get around the
696             problem of having to hold a huge amount of data
697             in memory to be passed into wmpeg (when you are,
698             e.g. writing a large animation from PDL3D rendered
699             fly-throughs).
700              
701             =cut
702              
703             *wmpeg = \&PDL::wmpeg;
704             sub PDL::wmpeg {
705 0 0   0 0 0 barf 'Usage: wmpeg($pdl,$filename) or $pdl->wmpeg($filename)' if @_ != 2;
706 0         0 my ($pdl,$file) = @_;
707             # return undef if no ffmpeg in path
708 0 0       0 if (! File::Which::which('ffmpeg')) {
709 0         0 warn("wmpeg: ffmpeg not found in PATH");
710 0         0 return;
711             }
712 0         0 my @Dims = $pdl->dims;
713             # too strict in general but alright for the moment
714             # especially restriction to byte will have to be relaxed
715 0 0 0     0 barf "input must be byte (3,x,y,z)" if (@Dims != 4) || ($Dims[0] != 3)
      0        
716             || ($pdl->get_datatype != $PDL_B);
717 0         0 my $nims = $Dims[3];
718             # $frame is 16N x 16N frame (per mpeg standard), insert each image in as $inset
719 0         0 my (@MDims) = (3,map(16*int(($_+15)/16),@Dims[1..2]));
720 0         0 my $frame = zeroes(byte,@MDims);
721 0         0 my $inset = $frame->slice(join ',',
722             map int(($MDims[$_]-$Dims[$_])/2).':'.
723             int(($MDims[$_]+$Dims[$_])/2-1),0..2);
724 0         0 local $SIG{PIPE} = 'IGNORE';
725 0         0 my $loglevel = 'quiet';
726 0 0       0 $loglevel = 'verbose' if $PDL::verbose;
727 0 0       0 $loglevel = 'debug' if $PDL::debug;
728 0 0       0 open my $fh, '|-', qw(ffmpeg -y -loglevel), $loglevel, qw(-f image2pipe -codec:v ppm -i -), $file
729             or barf "spawning ffmpeg failed: $?";
730 0         0 binmode $fh;
731 0         0 for ($pdl->dog) {
732 0         0 $inset .= $_;
733 0         0 wpnm($frame, $fh, 'PPM', 1);
734             }
735 0         0 return 1;
736             }
737              
738             =head2 imageformat
739              
740             =for ref
741              
742             Figure out the format of an image file from its magic numbers, or else, from its extension.
743              
744             Currently recognized image formats are: PNM, GIF, TIFF, JPEG, SGI,
745             RAST, IFF, PCX, PS, FITS, PNG, XBM. If the format can not be determined,
746             the string 'UNKNOWN' is returned.
747              
748             =for example
749              
750             $format=imageformat($path); # find out image format of certain file
751             print "Unknown image format" if $format eq 'UNKNOWN';
752             $canread=rpiccan($format); # check if this format is readable in this system
753             if($canread){
754             $pdl=rpic($path) ; # attempt to read image ONLY if we can
755             } else {
756             print "Image can't be read\n"; # skip unreadable file
757             }
758              
759             =cut
760              
761 0     0 1 0 sub imageformat {PDL->imageformat(@_)}
762              
763             sub PDL::imageformat {
764 0     0 0 0 my($class, $file)=@_;
765 0         0 return chkform($file);
766             }
767              
768             sub piccan {
769 8     8 0 35 my $class = shift;
770 8 100       39 my $rw = (shift =~ /r/i) ? 'Rok' : 'Wok';
771 8 100       23 my $refer_rw = $rw eq 'Rok' ? 'get' : 'put';
772 8 50 0     24 return sort grep $converter{$_}{$rw} || ($converter{$_}{referral} && $converter{$_}{referral}{$refer_rw}), keys %converter if !@_;
773 8         15 my $format = shift;
774 8 50       25 barf 'unknown format' unless defined($converter{$format});
775 8   33     104 return $converter{$format}{$rw} || ($converter{$format}{referral} && $converter{$format}{referral}{$refer_rw});
776             }
777              
778             sub getext {
779             # changed to a more os independent way
780 2     2 0 3 my $file = shift;
781 2         119 my ($base,$dir,$ext) = fileparse($file,'\.[^.]*');
782 2 50       11 $ext = $1 if $ext =~ /^.([^;]*)/; # chop off VMS version numbers
783 2         8 return $ext;
784             }
785              
786             # try to figure out the format of a supposed image file from the extension
787             # a couple of extensions are only checked when the optional parameter
788             # $wmode is set (because those should have been identified by magic numbers
789             # when reading)
790             # todo: check completeness
791             sub chkext {
792 2     2 0 6 my ($ext,$wmode) = @_;
793 2 50       11 $wmode = 0 unless defined $wmode;
794              
795             # there are not yet file formats which wouldn't have been identified
796             # by magic no's if in reading mode
797              
798 2 50       5 if ($wmode) {
799 2 50       12 return 'PNM' if $ext =~ /^(pbm)|(pgm)|(ppm)|(pnm)$/;
800 0 0       0 return 'JPEG' if $ext =~ /^(jpg)|(jpeg)$/;
801 0 0       0 return 'TIFF' if $ext =~ /^(tiff)|(tif)$/;
802 0 0       0 return 'PCX' if $ext =~ /^pcx$/;
803 0 0       0 return 'SGI' if $ext =~ /^rgb$/;
804 0 0       0 return 'GIF' if $ext =~ /^gif$/;
805 0 0       0 return 'RAST' if $ext =~ /^(r)|(rast)$/;
806 0 0       0 return 'IFF' if $ext =~ /^(iff)|(ilbm)$/;
807 0 0       0 return 'PS' if $ext =~ /^ps/;
808 0 0       0 return 'FITS' if $ext =~ /^f(i?ts|it)$/;
809 0 0       0 return 'PNG' if $ext =~ /^png$/i;
810 0 0       0 return 'XBM' if $ext =~ /^xbm$/i;
811             }
812              
813              
814 0         0 return 'UNKNOWN';
815             }
816              
817              
818              
819             # try to figure out the format of a supposed image file
820             # from the magic numbers (numbers taken from magic in netpbm and
821             # the file format routines in xv)
822             # if no magics match try extension for non-magic file types
823             # todo: make more complete
824              
825             sub chkform {
826 2     2 0 2 my $file = shift;
827 2         5 my ($format, $magic, $len, $ext) = ("","",0,"");
828 2 50       65 open my $fh, $file or barf "Can't open image file";
829 2         5 binmode $fh;
830             # should first check if file is long enough
831 2         41 $len = read($fh, $magic,12);
832 2 50 33     10 if (!defined($len) ||$len != 12) {
833 0         0 barf "end of file when checking magic number";
834 0         0 close $fh;
835 0         0 return 'UNKNOWN';
836             }
837 2         18 close $fh;
838 2 50       16 return 'PNM' if $magic =~ /^P[1-6]/;
839 0 0       0 return 'GIF' if $magic =~ /(^GIF87a)|(^GIF89a)/;
840 0 0       0 return 'TIFF' if $magic =~ /(^MM)|(^II)/;
841 0 0       0 return 'JPEG' if $magic =~ /^(\377\330\377)/;
842 0 0       0 return 'SGI' if $magic =~ /^(\001\332)|(\332\001)/;
843 0 0       0 return 'RAST' if $magic =~ /^\131\246\152\225/;
844 0 0       0 return 'IFF' if $magic =~ /ILBM$/;
845 0 0       0 return 'PCX' if $magic =~ /^\012[\000-\005]/;
846 0 0       0 return 'PS' if $magic =~ /%!\s*PS/;
847 0 0       0 return 'FITS' if $magic =~ /^SIMPLE \=/;
848 0 0       0 return 'PNG' if $magic =~ /^.PNG\r/;
849 0 0       0 return 'XBM' if $magic =~ /^#define\s+/;
850 0         0 return chkext(getext($file)); # then try extensions
851             }
852              
853             # helper proc for wpic
854             # process hints for direct converter control and try to guess from extension
855             # otherwise
856             sub getconv {
857 5     5 0 14 my ($pdl,$file,$hints) = @_;
858              
859             return ($$hints{CONVERTER},$$hints{FLAGS})
860 5 50       15 if defined($$hints{CONVERTER}); # somebody knows what they're doing
861              
862 5         10 my $type = "";
863 5 100       13 if (defined($$hints{'FORMAT'})) {
864 3         8 $type = $$hints{'FORMAT'};
865             barf "unsupported (output) image format"
866             unless exists($converter{$type}) && (
867             ($converter{$type}{referral} && $converter{$type}{referral}{put}) ||
868 3 50 33     52 $converter{$type}{put} !~ /NA/);
      33        
869             } else {
870 2         5 $type = chkext(getext($file),1);
871 2 50       6 if ($type =~ /UNKNOWN/) {
872 0         0 barf "can't figure out desired file type, using PNM" ;
873 0         0 $type = 'PNM';
874             }
875             }
876              
877 5         15 my $conv = $converter{$type}->{'put'};
878              
879             # the datatype check is only a dirty fix for the ppmquant problem with
880             # types > byte
881             # a ppmquant is anyway only warranted when $isrgb!!!
882             $conv = $converter{$type}->{Prefilt}.$conv
883 5 50       24 if defined($converter{$type}->{Prefilt});
884              
885 5         12 my $flags = $converter{$type}->{FLAGS};
886 5 50       13 $flags = "$Dflags" unless defined($flags);
887 5 50       15 $flags .= " $$hints{XTRAFLAGS}" if defined($$hints{XTRAFLAGS});
888 5 50 33     24 if (defined($$hints{'COLOR'}) && $$hints{'COLOR'} =~ /bwdither/) {
889 0         0 $flags = " | $conv $flags";
890 0         0 $conv = "pgmtopbm -floyd"; }
891              
892 5         12 my($referral) = $converter{$type}->{referral};
893              
894 5         25 return ($conv, $flags, $type, $referral);
895             }
896              
897             # helper proc for wpic
898             # if a certain type of pnm is required check data and make compliant if possible
899             # else if intermediate format is pnm or ppm figure out the appropriate format
900             # from the pdl
901             sub chkpdl {
902 5     5 0 16 my ($pdl, $iform, $hints, $format) = @_;
903              
904 5 50 66     58 if ($pdl->get_datatype >= $PDL_L ||
      33        
      66        
905             $pdl->get_datatype == $PDL_S ||
906             (!$converter{$format}->{ushortok} && $pdl->get_datatype == $PDL_US)) {
907 1 50       11 print "scaling data to type byte...\n" if $PDL::IO::Pic::debug;
908 1         8 $pdl = bytescl($pdl,-255);
909             }
910              
911 5         29 my @Dims = $pdl->dims;
912 5   66     22 my $isrgb = @Dims >= 3 && $Dims[0] == 3;
913 5 50 66     42 barf "expecting 2D or 3D-RGB-interlaced data as input"
914             unless $isrgb || @Dims == 2;
915              
916 5 50       18 $$hints{'COLOR'} = "" unless defined($$hints{'COLOR'});
917 5         9 my $form = "";
918 5 50       68 if ($iform =~ /P[NP]M/) { # figure out the format from the data
919 5 100       13 $form = 'PPM' if $isrgb;
920 5 100 66     29 $form = 'PGM' if (@Dims == 2) || ($$hints{'COLOR'} =~ /bwdither/i);
921 5 50       14 $form = 'PBM' if ($$hints{'COLOR'} =~ /bw/i);
922 5         11 $iform = $form; }
923             # this is the place for data conversions
924 5 50 66     20 if ($isrgb && ($iform =~ 'P[B,G]M')) {
925 0         0 print "wpic: converting to grayscale...\n";
926 0         0 $pdl = rgbtogr($pdl); # colour to grayscale
927             }
928 5 50       13 if (defined $$hints{LUT}) { # make LUT images into RGB
929 0 0       0 barf "luts only with non RGB data" if $isrgb;
930 0 0       0 print "starting palette->RGB conversion...\n" if $PDL::IO::Pic::debug;
931 0         0 $pdl = interlrgb($pdl,$$hints{LUT});
932 0         0 $iform = 'PPM'; # and tell everyone we are now RGB
933 0 0       0 print "finished conversion\n" if $PDL::IO::Pic::debug;
934             }
935 5         17 return ($pdl, $iform);
936             }
937              
938             =head1 BUGS
939              
940             Currently only a random selection of converters/formats provided by
941             pbmplus/netpbm is supported. It is hoped that the more important formats
942             are covered. Other formats can be added as needed. Please send patches to
943             the author.
944              
945             =head1 AUTHOR
946              
947             Copyright (C) 1996,1997 Christian Soeller
948             All rights reserved. There is no warranty. You are allowed
949             to redistribute this software / documentation under certain
950             conditions. For details, see the file COPYING in the PDL
951             distribution. If this file is separated from the PDL distribution,
952             the copyright notice should be included in the file.
953              
954             =cut
955              
956             1; # Return OK status