Advertisement
overloop

resync_audio.pl

Aug 9th, 2014
313
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Perl 9.41 KB | None | 0 0
  1. #!/usr/bin/perl
  2.  
  3. use List::Util qw(min max sum);
  4. use strict;
  5. use GD;
  6. use POSIX;
  7. use Data::Dumper;
  8. use File::Temp;
  9.  
  10. sub parse_report {
  11.   my ($report) = @_;
  12.   my @result = ();
  13.   open INPUT,"<",$report;
  14.   for my $line (<INPUT>) {
  15.     if ($line =~ m/t:([0-9.]{3,}).*select_out:0/) {
  16.       push(@result,$1);
  17.     }
  18.   }
  19.   return @result;
  20. }
  21.  
  22. sub image_names {
  23.     my ($dir,$code) = @_;
  24.     opendir(INPUT,$dir);
  25.     my @images = map { $dir . "/" . $_ } sort { $a <=> $b } grep { /\.png$/ } readdir(INPUT);
  26.     closedir(INPUT);
  27.     return @images;
  28. }
  29.  
  30. sub load_imagedata {
  31.     my ($filename) = @_;
  32.     my $image = GD::Image->new($filename);
  33.     my ($width,$height) = $image->getBounds();
  34.     my @imagedata = ();
  35.     for (my $x=0;$x<$width;$x++) {
  36.         for (my $y=0;$y<$height;$y++) {
  37.             my ($r,$g,$b) = $image->rgb($image->getPixel($x,$y));
  38.             push(@imagedata,$r);
  39.         }
  40.     }
  41.     my $minv = min @imagedata;
  42.     my $maxv = max @imagedata;
  43.     my $maxv_minv = $maxv - $minv;
  44.     if ($maxv != $minv) {
  45.         @imagedata = map { ($_ - $minv)/$maxv_minv } @imagedata;
  46.     } else {
  47.         @imagedata = map { 0 } @imagedata;
  48.     }
  49.     return @imagedata;
  50. }
  51.  
  52. sub imagedata_diff {
  53.     my ($imagedata1_ref,$imagedata2_ref) = @_;
  54.     #my @imagedata1 = @$imagedata1_ref;
  55.     #my @imagedata2 = @$imagedata2_ref;
  56.    
  57.     my $len = min(scalar(@$imagedata1_ref),scalar(@$imagedata2_ref));
  58.     my @diff = map { abs($imagedata1_ref->[$_] - $imagedata2_ref->[$_]) } (0..$len-1);
  59.     return sum(@diff) / scalar(@diff);
  60. }
  61.  
  62. sub display_imagedata {
  63.     my ($q,$imagedata1_ref) = @_;
  64.     my $qpow = pow(10,$q);
  65.     my @rounded = map { floor($_*$qpow)/$qpow } @$imagedata1_ref;
  66.     print join(' ',@rounded) . "\n";
  67. }
  68.  
  69. sub find_best_match {
  70.     my ($index1,$index2min,$index2max,$imagedata1_ref,$imagedata2_ref) = @_;
  71.     my $match_diff = 0.1;
  72.     my $match_image;
  73.     for my $index2 ($index2min..$index2max){
  74.         #print $index2;
  75.         if (defined $imagedata2_ref->[$index2]) {
  76.             my $diff = imagedata_diff($imagedata1_ref->[$index1],$imagedata2_ref->[$index2]);
  77.             if ($diff < $match_diff ) {
  78.                 $match_diff = $diff;
  79.                 $match_image = $index2;
  80.             }
  81.             #print "diff: " . $diff;
  82.         } else {
  83.             #print $index2 . " undefined\n";
  84.         }
  85.     }
  86.     if (defined $match_image) {
  87.         return ($match_image,$match_diff);
  88.     }
  89.     return (-1,-1);
  90. }
  91.  
  92. sub timehms {
  93.     my ($s) = @_;
  94.     my $h = floor($s/3600);
  95.     $s-=$h*3600;
  96.     my $m=floor($s/60); $s-=$m*60;
  97.     return sprintf("%02d:%02d:%05.2f",$h,$m,$s);
  98. };
  99.  
  100. sub output_comparsiontable {
  101.     my ($filename, $comparsiontable_ref,$d1,$f1,$d2,$f2,$v1,$v2) = @_;
  102.     open OUTPUT,">",$filename;
  103.     print OUTPUT "<html><head><style>table {border-collapse: collapse;} table td {border: 1px solid black; text-align: right;}</style></head><body>";
  104.     if (length($v1)>0) {
  105.       print OUTPUT '<p>' . join('</p><p>',$v1,$v2) . '</p>';
  106.     }
  107.     print OUTPUT '<p>' . join('</p><p>',$f1,$d1,$f2,$d2) . "</p>\n";
  108.     print OUTPUT "<table>\n";
  109.     for my $ref (@$comparsiontable_ref) {
  110.         my @outp = ();
  111.         if ($ref->[1] > -1) {
  112.             @outp = ($ref->[0],$ref->[1],timehms($ref->[2]),timehms($ref->[3]),sprintf("%.2f",$ref->[2]-$ref->[3]),$ref->[4],$ref->[5],$ref->[6],$ref->[7],sprintf("%.3f",$ref->[8]));
  113.         } else {
  114.             @outp = ($ref->[0],'?',timehms($ref->[2]),'?','?',$ref->[4],'?',$ref->[6],$ref->[7],'?');
  115.         }
  116.         print OUTPUT '<tr><td>' . join('</td><td>',@outp) . "</td></tr>\n"; # if (not($filtration && $ref->[1] < 0));
  117.     }
  118.     print OUTPUT "</table>\n";
  119.     print OUTPUT "</body></html>";
  120.     close OUTPUT;
  121. }
  122.  
  123. sub median
  124. {
  125.     my @vals = sort {$a <=> $b} @_;
  126.     my $len = @vals;
  127.     if($len%2)
  128.     {
  129.         return $vals[int($len/2)];
  130.     }
  131.     else
  132.     {
  133.         return ($vals[int($len/2)-1] + $vals[int($len/2)])/2;
  134.     }
  135. }
  136.  
  137. sub extract_frames {
  138.   my ($filename,$dir,$report,$image_size) = @_;
  139.   my $divider = 10;
  140.   `export FFREPORT=file=$report && ffmpeg -report -v 0 -i "$filename" -vsync 0 -f image2 -vf select='not(mod(n\\,$divider))' -s $image_size $dir/%d.png`;  
  141. }
  142.  
  143. sub parse_report {
  144.   my ($report) = @_;
  145.   my @result = ();
  146.   open INPUT,"<",$report;
  147.   for my $line (<INPUT>) {
  148.     if ($line =~ m/t:([0-9.]{3,}).*select_out:0/) {
  149.       push(@result,$1);
  150.     }
  151.   }
  152.   return @result;
  153. }
  154.  
  155. sub main_extractframes {
  156.  
  157.   my ($filename1,$filename2) = @ARGV;
  158.   if (! -f $filename1) {
  159.     print "$filename1 does not exist\n";
  160.     #exit(-1);
  161.   }
  162.   if (! -f $filename2) {
  163.     print "$filename2 does not exist\n";
  164.     #exit(-1);
  165.   }
  166.  
  167.   my $dir1 = File::Temp->newdir(CLEANUP => 0);
  168.   my $dir2 = File::Temp->newdir(CLEANUP => 0);
  169.   my $report1 = File::Temp->new(SUFFIX => '.log', UNLINK => 0);
  170.   my $report2 = File::Temp->new(SUFFIX => '.log', UNLINK => 0);
  171.  
  172.   #print $dir1 . "\n" . $report . "\n";
  173.  
  174.   my $image_divider = 40;
  175.   my $image_size = int(1920 / $image_divider) . "x" . int(1080 / $image_divider);
  176.   print STDERR "Extracting frames from " . $filename1 . "\n";
  177.   extract_frames($filename1,$dir1,$report1,$image_size);
  178.   print STDERR "Extracting frames from " . $filename2 . "\n";
  179.   extract_frames($filename2,$dir2,$report2,$image_size);
  180.   print STDERR join("\n",$dir1,$report1,$dir2,$report2) . "\n";
  181.   main_compareframes($dir1,$report1,$dir2,$report2,$filename1,$filename2);
  182. }
  183.  
  184. sub gen_filename {
  185.   my ($i,$filename) = @_;
  186.   if (index($filename,"%")<0) {
  187.     return (-1,"");
  188.   }
  189.   my $f = $filename;
  190.   $f =~ s/%/$i/;
  191.   while ( -f $f ) {
  192.     $i++;
  193.     $f = $filename;
  194.     $f =~ s/%/$i/;
  195.   }
  196.   return ($i,$f);
  197. }
  198.  
  199. sub main_compareframes {
  200.  
  201.   my ($dir1,$report1,$dir2,$report2) = @_;
  202.  
  203.   my $vid1 = "";
  204.   my $vid2 = "";
  205.   if (scalar(@_)>=6) {
  206.     $vid1 = $_[4];
  207.     $vid2 = $_[5];
  208.   }
  209.  
  210.   #$dir1 = "tmp/nRYaFcFI9O"; #3420
  211.   #$report1 = "tmp/Ueqc1FGMQS"; #3452
  212.   #$dir2 = "tmp/QaZDpphSqx.log"; #3420
  213.   #$report2 = "tmp/rcNTAS5d0U.log"; #3452
  214.  
  215.   #my @images1 = map { $dir1 . "/" . $_ . ".png" } (1..500);
  216.   my @images1 = image_names($dir1);
  217.   my @images2 = image_names($dir2);
  218.  
  219.   #@images1 = splice(@images1,0,100);
  220.   #@images2 = splice(@images2,0,100);
  221.  
  222.   my @reportdata1 = parse_report($report1);
  223.   my @reportdata2 = parse_report($report2);
  224.  
  225.   #print join("\n",@images1); exit(0);
  226.   #my @images2 = map { $dir2 . "/" . $_ . ".png" } (1..500);
  227.  
  228.   #my @images = (@images1,@images2);
  229.  
  230.   print STDERR "Loading frames\n";
  231.   my @imagedata1 = map { my @d = load_imagedata($_); \@d } @images1;
  232.   my @imagedata2 = map { my @d = load_imagedata($_); \@d } @images2;
  233.   print STDERR "Comparing frames\n";
  234.  
  235.   #display_imagedata(1,$imagedata1[5]);
  236.   #display_imagedata(1,$imagedata2[5]);
  237.  
  238.   my @comparsiondata = (); #index1, index2, time1, time2, img1, img2, index2min, index2max, diff
  239.   my $k = scalar(@images2)/scalar(@images1);
  240.   my $image_range = 30;
  241.   for my $index1 (0..scalar(@images1)-1) {
  242.  
  243.     print STDERR "." if (not($index1 % 50));
  244.  
  245.       #print $index1 . " " . $index1*$k . "\n" if ($index1 % 100 == 52);
  246.       my $index2min = int($index1*$k-$image_range);
  247.       my $index2max = int($index1*$k+$image_range);
  248.       my ($index2,$diff) = find_best_match($index1,$index2min,$index2max,\@imagedata1,\@imagedata2);
  249.       my $time1 = $reportdata1[$index1];
  250.       my $img1 = '<img src="' . $images1[$index1] . '"/>';
  251.      
  252.       my $time2 = 0;
  253.       my $img2 = '';
  254.       if ($index2 > -1) {
  255.           $time2 = $reportdata2[$index2];
  256.           $img2 = '<img src="' . $images2[$index2] . '"/>';
  257.       }
  258.       my @comparsiondata_item = ($index1,$index2,$time1,$time2,$img1,$img2,$index2min,$index2max,$diff);
  259.       push(@comparsiondata,\@comparsiondata_item);
  260.   }
  261.   print STDERR "\n";
  262.  
  263.   # эксплицитно копируем данные сравнения (одновременно отфильтровывая беспарные кадры)
  264.   # чтобы применить к ним медианную фильтрацию и не испортить исходные данные
  265.   my @comparsiondata_ = ();
  266.   for my $ref (@comparsiondata) {
  267.       if ($ref->[1] > -1) {
  268.           my @row = @$ref;
  269.           push(@comparsiondata_,\@row);
  270.       }
  271.   }
  272.  
  273.   # рассчет медианы в окне нужно вынести в функцию (размер окна, массив) -> (массив)
  274.   my $median_halfsize = 10;
  275.   my @median_window = ();
  276.   push(@median_window,$comparsiondata_[$_]->[1] - $comparsiondata_[$_]->[0]) for (0..$median_halfsize-1);
  277.   for (0..scalar(@comparsiondata_)-1) {
  278.       my $i = $_+$median_halfsize;
  279.       push(@median_window,$comparsiondata_[$i]->[1] - $comparsiondata_[$i]->[0]) if ($i<scalar(@comparsiondata_));
  280.       shift(@median_window) if ($_>$median_halfsize);
  281.      
  282.       #print join(" ",@median_window) . "\n";
  283.      
  284.       my $index1 = $comparsiondata_[$_]->[0];
  285.       my $index2 = int($index1 + median(@median_window));
  286.       my $time2 = $reportdata2[$index2];
  287.       my $img2 = '<img src="' . $images2[$index2] . '"/>';
  288.      
  289.       $comparsiondata_[$_]->[1] = $index2;
  290.       $comparsiondata_[$_]->[3] = $time2;
  291.       $comparsiondata_[$_]->[5] = $img2;
  292.       $comparsiondata_[$_]->[8] = 0; #diff
  293.   }
  294.  
  295.   my ($i1,$f1) = gen_filename(1,"resync_audio_report_raw_%.html");
  296.   my ($i2,$f2) = gen_filename(1,"resync_audio_report_filtered_%.html");
  297.  
  298.   output_comparsiontable($f1,\@comparsiondata,$dir1,$report1,$dir2,$report2,$vid1,$vid2);
  299.   output_comparsiontable($f2,\@comparsiondata_,$dir1,$report1,$dir2,$report2,$vid1,$vid2);
  300.  
  301.   `firefox "$f1"`;
  302.   `firefox "$f2"`;
  303. }
  304.  
  305. if (scalar(@ARGV) == 0) {
  306.   print "to extract and compare frames: " . $0 . " video1.mkv video2.mkv\n";
  307.   print "to compare frames " . $0 . " dir1 report1 dir2 report2\n";
  308. } elsif (scalar(@ARGV) == 2) {
  309.   main_extractframes(@ARGV);
  310. } elsif (scalar(@ARGV) == 4) {
  311.   main_compareframes(@ARGV);
  312. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement