Video-FrameGrab
view release on metacpan or search on metacpan
FrameGrab.pm view on Meta::CPAN
return 0 if $data_points == 0;
return int(1.0*$intensity/$data_points);
}
###########################################
sub cropdetect_mplayer {
###########################################
my($self, $time) = @_;
my($stdout, $stderr, $rc) =
tap $self->{mplayer}, qw(-vf cropdetect -ss), $time,
"-frames", 10,
"-vo", "null",
"-ao", "null",
$self->{video};
if(defined $stdout and
$stdout =~ /-vf crop=(\d+):(\d+):(\d+):(\d+)/) {
DEBUG "Suggested crop: $1, $2, $3, $4";
return ($1, $2, $3, $4);
}
ERROR "$stderr";
return undef;
}
###########################################
sub cropdetect_average {
###########################################
my($self, $nof_probes, $opts) = @_;
$opts = {} unless defined $opts;
$self->result_clear();
my @images = ();
if(exists $opts->{images}) {
for my $img (@{ $opts->{images} }) {
push @images, $img;
}
} else {
for my $probe (
$self->equidistant_snap_times( $nof_probes,
$opts ) ) {
my $data = $self->snap( $probe );
my $img = Imager->new();
my $rc = $img->read( data => $data );
if(! $rc) {
LOGWARN "Reading snapshop at time $probe failed ($!)";
next;
}
push @images, $img;
}
}
# average all snapshots to obtain a single overlay image
my $overlay;
my $i = 1;
for my $img (@images) {
$img->filter(type=>"gaussian", stddev=>10)
or die $overlay->errstr;
if(! defined $overlay) {
$overlay = $img;
next;
}
$overlay->compose( src => $img, combine => 'add' );
$overlay->filter(type=>"postlevels", levels=>3) or
die $overlay->errstr;
if(get_logger()->is_trace()) {
$overlay->write(file => "i-$i.jpg");
}
$i++;
}
my @params = $self->cropdetect( 0, { image => $overlay } );
return @params;
# my @params = $self->cropdetect( $probe, $opts );
# if(! defined $params[0] ) {
# ERROR "cropdetect returned an error";
# next;
# }
# DEBUG "Cropdetect at $probe yielded (@params)";
# $self->result_push( @params );
# }
#
# my @result = $self->result_majority_decision();
# DEBUG "Majority decision: (@result)";
# return @result;
}
###########################################
sub result_clear {
###########################################
my($self) = @_;
$self->{result} = [];
}
###########################################
sub result_push {
###########################################
my($self, @result) = @_;
for(0..$#result) {
$self->{result}->[$_]->{ $result[$_] }++;
}
}
###########################################
sub result_majority_decision {
###########################################
my($self) = @_;
my @result = ();
for my $sample (@{ $self->{result} }) {
my($majority) = sort { $sample->{$b} <=> $sample->{$a} } keys %$sample;
push @result, $majority;
}
return @result;
}
###########################################
sub jpeg_data {
###########################################
my($self) = @_;
return $self->{jpeg};
}
###########################################
sub jpeg_save {
FrameGrab.pm view on Meta::CPAN
Save a grabbed frame as a jpeg image in $file on disk.
=item meta_data()
Runs mplayer's identify() function and returns a reference to a hash
containing something like
demuxer => MOV
video_format => AVC1
video_bitrate => 0
video_width => 320
video_height => 240
video_fps => 29.970
video_aspect => 0.0000
audio_format => MP4A
audio_bitrate => 0
audio_rate => 48000
audio_nch => 2
length => 9515.94
=item equidistant_snap_times( $howmany, [$opts] )
If you want to snap N frames at constant intervals throughout the movie,
use equidistant_snap_times( $n ) to get a list of timestamps you can use
later pass to snap(). For example, on a two hour movie,
equidistant_snap_times( 5 ) will return
00:20:00
00:40:00
01:00:00
01:20:00
01:40:00
as a list of strings. The movie length is determined by a call to meta
data, but some formats don't allow retrieving the movie length that way,
therefore the optional options hash can set the movie_length entry
to the movie length (or the length of the overall interval to perform
the snapshots in) in seconds.
my @times =
$fg->equidistant_snap_times( $howmany, { movie_length => 3600 } );
=item cropdetect( $time, [$opts] )
If this is a 16:9 movie converted to 4:3 format, the black bars at the bottom
and the top of the screen should be cropped out. To help with this task,
C<cropdetect> will return a list of ($width, $height, $x, $y) to be passed
to mplayer/mencoder in the form C<-vf crop=w:h:x:y> to accomplish the
suggested cropping.
The default algorithm is a homegrown detection mechanism
C<{algorithm =E<gt> "schilli"}>, which first blurs the
image with the Gaussian Blur algorithm with a radius of
C<$opts-E<gt>{gaussian_blur_radius}> (which defaults to 3),
and then measures if any of the left, right, upper or lower border
pixel lines of the snapped frame average an intensity of less than
C<$opts-E<gt>{min_intensity_average}>, which defaults to 20.
Note that this is just a guess and might be incorrect at times. In a
dark scene, black pixels might protrude far into the video, making it
impossible to detect the border reliably. However, if you overlay a number
of frames, obtained at several times during the movie (e.g. by using
the equidistant_snap_times method described above), the result
is fairly predicatblye and accurate. C<cropdetect_average>,
described below, does exactly that.
The alternative algorithm, C<"mplayer">,
asks mplayer to come up with a recommendation on how to crop the video.
This technique delivers incorrect results if there are sporadic white
spots within the dark bars.
=item cropdetect_average( $number_of_probes, [$opts] )
Takes C<$number_of_probes> from the movie at equidistant intervals,
overlays the frames and performs a border detection on the resulting
images, which is almost white in the viewing area.
See C<equidistant_snap_times> for setting the movie length in
the optional C<$opts> parameter.
=item aspect_ratio_guess( ["16:9", "4:3"] )
This function will take the width and height of the video and
map it to the best matching aspect ratio given in a reference
to an array.
=item dimensions()
Snaps a frame in the middle of the movie, determines its width and
height and returns them in a list:
my($width, $height) = $grabber->dimensions();
Dimensions are usually also available via the meta_data() call.
dimensions() works even in absence of meta data.
=head1 CAVEATS
Note that the mplayer-based frame grabbing mechanism used in
this module allows you to snap a picture about every 10 seconds into the
movie, on shorter intervals, you'll get the same frame back.
=back
=head1 LEGALESE
Copyright 2009 by Mike Schilli, all rights reserved.
This program is free software, you can redistribute it and/or
modify it under the same terms as Perl itself.
=head1 AUTHOR
2009, Mike Schilli <cpan@perlmeister.com>
( run in 0.948 second using v1.01-cache-2.11-cpan-39bf76dae61 )