CompleTran


概要

CompleTran は JPEGTRAN の機能を補強するための Perl スクリプトです。


JPEGTRAN とは

JPEGTRAN は、Independent JPEG Group (IJG) が作成した JPEG ファイル最適化ツールです。ftp://ftp.uu.net/graphics/jpeg/ などから最新のソースコードをダウンロードすることができます。

JPEGTRAN を使うと、JPEG ファイルの画質を損なうことなくファイルサイズを小さくできます。詳細に知りたい方は、付録 : JPEGTRAN 付属のドキュメント usage.doc からの抜粋 (英語) を参照してください。

CompleTran は、オリジナルの JPEGTRAN に無い以下の機能を提供します。


使い方

あらかじめ、JPEGTRAN を実行パスの通ったディレクトリに置いておいてください。

completran.pl を実行すると、カレントディレクトリ以下にある JPEG ファイル全てに JPEGTRAN を行います。

-f オプションを指定すると、上書きモードで動作します。変換後のファイルで変換前のファイルを上書きします。

ソースコード中の以下の部分で変換オプションを指定しています。この部分を書き換えることで、好みの変換オプションで JPEGTRAN を駆動することができます。

# JPEGTRAN に渡す変換オプション。
@OptionList = (
   "-copy all -optimize -progressive",
   "-copy all -optimize",
   "-copy all -progressive",
   "-copy all"
);

動作環境

Perl 5 とパスの通ったディレクトリに JPEGTRAN がインストールされていれば動くと思います。以下は、作者の環境です。

Perl : v5.6.1 built for cygwin

JPEGTRAN : Independent JPEG Group's JPEGTRAN, version 6b 27-Mar-1998


ライセンス

GPL2 です。


ダウンロード

completran.tar.gz 2449バイト

ファイルサイズが巨大なものではないので、以下にも添えておきます。


#!/bin/perl

# *********************************************************************
# CompleTran
# Version : 0.7
# Release Date : Apr. 5, 2001
# Feature : カレントディレクトリ以下のJPEG ファイルをを再帰的に検索し、
#           最適なオプションで JPEGTRAN を行います。
# Requirements : パスの通ったところに JPEGTRAN が置かれている必要があります。
# History : Apr. 5, 2001 Version 0.7 初回リリース。
# Author : Sonic 
# URL : http://www.geocities.com/ds888zx/
# Licence : GPL2
# *********************************************************************



# 引数のチェック。-f が指定されたときは、元ファイルを上書きするモードで動作する。
if ((defined($ARGV[0])) && ($ARGV[0] eq "-f")) {
    print "-f option (Overwrite mode) on.\n\n";
} else { $ARGV[0] = ""; }

# -f オプションが指定されていないときに、出力ファイルにつけるサフィックス
$OutputFileSuffix = ".smallest.jpg";

$TempFileSuffix = ".jpg.temp"; # テンポラリファイルに付けるサフィックス
$LogfileName = "tran.log"; # このスクリプトを実行したときのログファイル名

# JPEGTRAN に渡す変換オプション。
@OptionList = (
   "-copy all -optimize -progressive",
   "-copy all -optimize",
   "-copy all -progressive",
   "-copy all"
);





# メインルーチン開始。
open(LOGFILE, "> $LogfileName");{
my $StartTime = time; # 変換に要する時間を計測するため、開始時刻を取得


# ログファイルのヘッダ部を生成する
print(LOGFILE "JPEGTRAN start at " . localtime(time) . "\n\n");
print(LOGFILE "****************** Confirmation of option setting ******************\n");
foreach $Option (@OptionList) {
    print(LOGFILE "$Option\n");
}
print(LOGFILE '*' x 68 . "\n\n");


print "Search target file...\n";
my @TargetFileList = &SearchTarget(".");
my $TotalFileCount = $#TargetList +1;
print "$TotalFileCount JPEG files found.\n";

print"\nStart JPEGTRAN\n";
my $FileCount = 1;

$TotalFileSize = 0; # 処理したファイルのサイズの合計を格納
$TotalDecreaseSize = 0; # 削減できたファイルサイズの合計を格納

foreach $FileName (@TargetFileList) {
    print "JPEGTRAN : $FileCount / $TotalFileCount $FileName\n";
    print(LOGFILE "No. $FileCount / $TotalFileCount ");
    &Transform($FileName);
    $FileCount++;
}

if (0 != $TotalFileSize) {
    $TotalDecreaseRatio = ($TotalDecreaseSize / $TotalFileSize) * 100;
} else {
    $TotalDecreaseRatio = 0;
}

my $FinishTime = time;
my $TotalProcessTime = $FinishTime - $StartTime;

print(LOGFILE "JPEGTRAN complete at " . localtime(time) . " ($TotalProcessTime second)\n\n");
print(LOGFILE "Total $TotalFileCount files\n");
print(LOGFILE "Total filesize $TotalFileSize byte\n");
print(LOGFILE "Total decrease size $TotalDecreaseSize byte ($TotalDecreaseRatio \%)\n");

close(LOGFILE);
}





# 渡されたパス以下に存在している拡張子がjpg のファイルを再帰的に検索し、
# パス+ファイル名の配列を返す。
sub SearchTarget{
    local($DirectoryName) = @_;
    opendir(DIR, "$DirectoryName");
    foreach $Entry (readdir(DIR)) {
        $FileName = "$DirectoryName/$Entry";
        if (-d $FileName) {
            if (($Entry eq ".") || ($Entry eq "..")){
                next;
            } else {
                &SearchTarget($FileName);
            }
        } elsif ($FileName =~ /\.[j|J][p|P][g|G]$/) {
           push(@TargetList, $FileName);
        }
    }
    closedir(DIR);
    return(sort(@TargetList));
}





# 渡された jpeg ファイルを JPEGTRAN にかけて変換を行う。
sub Transform{

    local $MasterSize = (stat("$FileName"))[7];

    if ($MasterSize == 0) {
      print(LOGFILE "$FileName is 0 byte, skip this file.\n\n");
      return();
    }
    print(LOGFILE "$FileName $MasterSize byte\n");

    # 最小ファイル名、最小ファイルのサイズ、最小ファイル生成時の変換オプション。
    # デフォルトの値として、元ファイルの値を使用する。
    my $SmallestSize = $MasterSize;
    my $SmallestFileName = $FileName;
    my $SmallestOption = "**** Master File ****";

    my $OutputSize; # Size of output file.

    # Try JPEGTRAN with each options, and compear
    # JPEGTRAN を 各オプションで実行し、生成されたファイルサイズを比較する。
    foreach $Option (@OptionList) {
        system("/bin/jpegtran $Option -outfile $FileName$TempFileSuffix $FileName");

        $OutputSize = (stat("$FileName$TempFileSuffix"))[7];
        my $Ratio = (($OutputSize / $MasterSize) *100);
        my $DecreaseSize = -($MasterSize - $OutputSize);
        printf(LOGFILE "%-38s %8dbyte %7d\ (%6.3f%%)\n",$Option, $OutputSize, $DecreaseSize, $Ratio);


        # 変換前ファイルと変換後ファイルを比較。
        if ($OutputSize < $SmallestSize) {
            if ($SmallestFileName ne $FileName) {
              unlink("$SmallestFileName");
            }
            $SmallestFileName = "$FileName$OutputFileSuffix";
            rename("$FileName$TempFileSuffix", "$SmallestFileName");
            $SmallestSize = $OutputSize;
            $SmallestOption = $Option;
        }
    }

    unlink("$FileName$TempFileSuffix");
    $TotalFileSize += $MasterSize;
    $TotalDecreaseSize += ($MasterSize - $SmallestSize);

    # 加工前のファイルのタイムスタンプを取得
    ($atime, $mtime) = (stat($FileName))[8,9];

    # -f オプションの有無と最小ファイルの状態により、処理が異なる。
        # -f option : on, Minimum File : Converted file の場合、
        # 変換前ファイルを変換後ファイルで上書き。変換後ファイルは削除。
        # -f option : on, Minimum File : Master の場合、
        # なにもしない。
        # -f option : off, Minimum File : Master の場合、
        # 変換前ファイルを変換後ファイル名でコピー。
        # -f option : off, Minimum File : Converted file の場合、
        # 変換後ファイルをそのまま利用。
    use File::Copy;
    if ($ARGV[0] eq "-f") {
        if ($SmallestSize != $MasterSize) {
            copy("$SmallestFileName", "$FileName");
            unlink("$SmallestFileName");
            $SmallestFileName = $FileName;
        }
    } elsif ($SmallestSize == $MasterSize) {
        copy("$FileName", "$FileName$OutputFileSuffix");
        $SmallestFileName = "$FileName$OutputFileSuffix";
    }

    # 変換済みのファイルにタイムスタンプをセット
    utime $atime, $mtime, "$SmallestFileName";

    print(LOGFILE "The best setting is \"$SmallestOption\"\n\n");
}



作者

Sonic

E-Mail : [email protected]

ご意見、ご要望、パッチ、改善提案、批判その他は大歓迎です。上記アドレスに頂いたメールは、このページなどで公開されることがあります。

最近、忙しくあまりWebやメールをチェックしていません。上記メールアドレスにメールを頂いても、チェックできないかもしれません。というわけで、改善提案やパッチはあなたが主催するウェブサイトに掲載していただくと助かります。ついでに、このページへのリンクを張っていただければ最高です。


付録 : JPEGTRAN 付属のドキュメント usage.doc からの抜粋 (英語)


JPEGTRAN

jpegtran performs various useful transformations of JPEG files.
It can translate the coded representation from one variant of JPEG to another,
for example from baseline JPEG to progressive JPEG or vice versa.  It can also
perform some rearrangements of the image data, for example turning an image
from landscape to portrait format by rotation.

jpegtran works by rearranging the compressed data (DCT coefficients), without
ever fully decoding the image.  Therefore, its transformations are lossless:
there is no image degradation at all, which would not be true if you used
djpeg followed by cjpeg to accomplish the same conversion.  But by the same
token, jpegtran cannot perform lossy operations such as changing the image
quality.

jpegtran uses a command line syntax similar to cjpeg or djpeg.
On Unix-like systems, you say:
	jpegtran [switches] [inputfile] >outputfile
On most non-Unix systems, you say:
	jpegtran [switches] inputfile outputfile
where both the input and output files are JPEG files.

To specify the coded JPEG representation used in the output file,
jpegtran accepts a subset of the switches recognized by cjpeg:
	-optimize	Perform optimization of entropy encoding parameters.
	-progressive	Create progressive JPEG file.
	-restart N	Emit a JPEG restart marker every N MCU rows, or every
			N MCU blocks if "B" is attached to the number.
	-scans file	Use the scan script given in the specified text file.
See the previous discussion of cjpeg for more details about these switches.
If you specify none of these switches, you get a plain baseline-JPEG output
file.  The quality setting and so forth are determined by the input file.

The image can be losslessly transformed by giving one of these switches:
	-flip horizontal	Mirror image horizontally (left-right).
	-flip vertical		Mirror image vertically (top-bottom).
	-rotate 90		Rotate image 90 degrees clockwise.
	-rotate 180		Rotate image 180 degrees.
	-rotate 270		Rotate image 270 degrees clockwise (or 90 ccw).
	-transpose		Transpose image (across UL-to-LR axis).
	-transverse		Transverse transpose (across UR-to-LL axis).

The transpose transformation has no restrictions regarding image dimensions.
The other transformations operate rather oddly if the image dimensions are not
a multiple of the iMCU size (usually 8 or 16 pixels), because they can only
transform complete blocks of DCT coefficient data in the desired way.

jpegtran's default behavior when transforming an odd-size image is designed
to preserve exact reversibility and mathematical consistency of the
transformation set.  As stated, transpose is able to flip the entire image
area.  Horizontal mirroring leaves any partial iMCU column at the right edge
untouched, but is able to flip all rows of the image.  Similarly, vertical
mirroring leaves any partial iMCU row at the bottom edge untouched, but is
able to flip all columns.  The other transforms can be built up as sequences
of transpose and flip operations; for consistency, their actions on edge
pixels are defined to be the same as the end result of the corresponding
transpose-and-flip sequence.

For practical use, you may prefer to discard any untransformable edge pixels
rather than having a strange-looking strip along the right and/or bottom edges
of a transformed image.  To do this, add the -trim switch:
	-trim		Drop non-transformable edge blocks.
Obviously, a transformation with -trim is not reversible, so strictly speaking
jpegtran with this switch is not lossless.  Also, the expected mathematical
equivalences between the transformations no longer hold.  For example,
"-rot 270 -trim" trims only the bottom edge, but "-rot 90 -trim" followed by
"-rot 180 -trim" trims both edges.

Another not-strictly-lossless transformation switch is:
	-grayscale	Force grayscale output.
This option discards the chrominance channels if the input image is YCbCr
(ie, a standard color JPEG), resulting in a grayscale JPEG file.  The
luminance channel is preserved exactly, so this is a better method of reducing
to grayscale than decompression, conversion, and recompression.  This switch
is particularly handy for fixing a monochrome picture that was mistakenly
encoded as a color JPEG.  (In such a case, the space savings from getting rid
of the near-empty chroma channels won't be large; but the decoding time for
a grayscale JPEG is substantially less than that for a color JPEG.)

jpegtran also recognizes these switches that control what to do with "extra"
markers, such as comment blocks:
	-copy none	Copy no extra markers from source file.  This setting
			suppresses all comments and other excess baggage
			present in the source file.
	-copy comments	Copy only comment markers.  This setting copies
			comments from the source file, but discards
			any other inessential data.
	-copy all	Copy all extra markers.  This setting preserves
			miscellaneous markers found in the source file, such
			as JFIF thumbnails and Photoshop settings.  In some
			files these extra markers can be sizable.
The default behavior is -copy comments.  (Note: in IJG releases v6 and v6a,
jpegtran always did the equivalent of -copy none.)

Additional switches recognized by jpegtran are:
	-outfile filename
	-maxmemory N
	-verbose
	-debug
These work the same as in cjpeg or djpeg.


以上

Hosted by www.Geocities.ws

1