View difference between Paste ID: Fvaw7AxY and pe33VFgj
SHOW: | | - or go back to the newest paste.
1
#!/usr/bin/perl -ws
2
# jpegrescan by Loren Merritt
3
# Last updated: 2008-11-29 / 2013-03-19
4
# This code is public domain.
5
6
use File::Slurp;
7
use File::Temp qw/ tempfile /;
8
use IPC::Run qw(start pump finish);
9
10
@ARGV==2 or die "usage: jpegrescan in.jpg out.jpg
11
tries various progressive scan orders
12
switches:
13
  -s strip from all extra markers (`jpegtran -copy none` otherwise `jpegtran -copy all`)
14
  -v verbose output
15
  -q supress all output
16
  -a use arithmetic coding (unsupported by most software)
17
";
18
$fin = $ARGV[0];
19
$fout = $ARGV[1];
20
(undef, $ftmp) = tempfile(SUFFIX => ".scan");
21
$jtmp = $fout;
22
$verbose = $v;
23
$quiet = $q;
24
@strip = $s ? ("-copy","none") : ("-copy","all");
25
@arith = $a ? ("-arithmetic") : ();
26
undef $_ for $v,$q,$s,$a;
27
undef $/;
28
$|=1;
29
30
# convert the input to baseline, just to make all the other conversions faster
31
# FIXME there's still a bunch of redundant computation in separate calls to jpegtran
32
open $OLDERR, ">&", STDERR;
33
open STDERR, ">", $ftmp;
34-
my $handle=start \("jpegtran", "-v", @strip, "-optimize", $fin),\$in,\$out,\$err;
34+
my $handle=start \("jpegtran", "-v", @strip, "-optimize", $fin),\$in;
35-
$in=$jtmp;
35+
open FILE,$jtmp;
36
$in=join "",<FILE>;
37-
finish $h or die "something is really wrong";
37+
close FILE;
38
pump $handle while length($in);
39
finish $handle or die "something is really wrong";
40
41
open STDERR, ">&", $OLDERR;
42
43
$type = read_file($ftmp);
44
$type =~ /components=(\d+)/ or die;
45
$rgb = $1==3 ? 1 : $1==1 ? 0 : die "not RGB nor gray\n";
46
47
# FIXME optimize order for either progressive transfer or decoding speed
48
sub canonize {
49
    my $txt = $prefix.$suffix.shift;
50
    $txt =~ s/\s*;\s*/;\n/g;
51
    $txt =~ s/^\s*//;
52
    $txt =~ s/ +/ /g;
53
    $txt =~ s/: (\d+) (\d+)/sprintf ": %2d %2d", $1, $2/ge;
54
    # treat u and v identically. I shouldn't need to do this, but with jpegtran overhead it saves 9% speed. cost: .008% bitrate.
55
    $txt =~ s/^2:.*\n//gm;
56
    $txt =~ s/^1:(.+)\n/1:$1\n2:$1\n/gm;
57
    # dc before ac, coarse before fine
58
    my @txt = sort {"$a\n$b" =~ /: *(\d+) .* (\d);\n.*: *(\d+) .* (\d);/ or die; !$3 <=> !$1 or $4 <=> $2 or $a cmp $b;} split /\n/, $txt;
59
    return join "\n", @txt;
60
}
61
62
sub try {
63
    my $txt = canonize(shift);
64
    return $memo{$txt} if $memo{$txt};
65
    write_file($ftmp, $txt);
66
    open TRAN, "-|", "jpegtran", @arith, @strip, "-scans", $ftmp, $jtmp or die;
67
    $data = <TRAN>;
68
    close TRAN;
69
    my $s = length $data;
70
    $s or die;
71
    $memo{$txt} = $s;
72
    !$quiet && print $verbose ? "$txt\n$s\n\n" : ".";
73
    return $s;
74
}
75
76
sub triesn {
77
    my($bmode, $bsize);
78
    my ($limit, @modes) = @_;
79
    my $overshoot = 0;
80
    for(@modes) {
81
        my $s = try($_);
82
        if(!$bsize || $s < $bsize) {
83
            $bsize = $s;
84
            $bmode = $_;
85
            $overshoot = 0;
86
        } elsif(++$overshoot >= $limit) {
87
            last;
88
        }
89
    }
90
    return $bmode;
91
}
92
93
sub tries { triesn(99, @_); }
94
95
$prefix = "";
96
$suffix = "";
97
98
if($rgb) {
99
    # 012 helps very little
100
    # 0/12 and 0/1/2 are pretty evenly matched in frequency, but 0/12 wins in total size if every image had to use the same mode
101
    # dc refinement passes never help
102
    $dc = tries(
103
    #           "0: 0 0 0 0; 1 2: 0 0 0 0;", # two scans expose a bug in Opera <= 11.61
104
                "0: 0 0 0 0; 1: 0 0 0 0; 2: 0 0 0 0;");
105
    # jpegtran won't let me omit dc entirely, but I can at least quantize it away to make the rest of the tests faster.
106
    $prefix = "0 1 2: 0 0 0 9;";
107
} else {
108
    $dc = "0: 0 0 0 0;";
109
    $prefix = "0: 0 0 0 9;";
110
}
111
112
# luma can make use of up to 3 refinement passes.
113
# chroma can make use of up to 2 refinement passes.
114
# refinement passes have some chance of being split (luma: 4%,4%,4%. chroma: 20%,8%) but the total bit gain is negligible.
115
# msb pass should almost always be split (luma: 87%, chroma: 81%).
116
# I have no theoretical reason for this list of split positions, they're just the most common in practice.
117
# splitting into 3 ections is often slightly better, but the total number of bits saved is negligible.
118
# FIXME: penalize lots of refinement passes because it's slower to decode. if so, then also force overwrite if bigger than the input.
119
sub try_splits {
120
    my $str = shift;
121
    my %n = map {$_ => sprintf "$c: 1 %d $str; $c: %d 63 $str;", $_, $_+1} 2,5,8,12,18;
122
    my $mode = triesn(3, "$c: 1 63 $str;", @n{2,8,5});
123
    return $mode if $mode ne $n{8};
124
    return triesn(1, $mode, @n{12,18});
125
}
126
127
foreach $c (0..$rgb) {
128
    my @modes;
129
    my $ml = "";
130
    for(0..($c?2:3)) {
131
        push @modes, "$c: 1 8 0 $_; $c: 9 63 0 $_;".$ml;
132
        $ml .= sprintf("$c: 1 63 %d %d;", $_+1, $_);
133
    }
134
    my $refine = triesn(1, @modes);
135
    $refine =~ s/.* (0 \d);//;
136
    $ac .= $refine . try_splits($1);
137
}
138
139
$prefix = "";
140
undef %memo;
141
$mode = canonize($dc.$ac);
142
try($mode);
143
$size = $memo{$mode};
144
!$quiet && print "\n$mode\n$size\n";
145
$old_size = -s $fin;
146
!$quiet && printf "%+.2f%%\n", ($size/$old_size-1)*100;
147
if($size <= $old_size) {
148
    write_file($fout, $data);
149
}
150
unlink $ftmp;