ImageMagickを使わずperlだけで画像サイズを取得

2015/9/18 [23:52:37] (金) 天気

ハイブリッド型EPUB3を生成するスクリプトのフィックスページ部分で画像サイズを取得する必要があった。

「かんたんEPUB3作成ハイブリッド型EPUB3対応版」http://t2aki.doncha.net/easy_epub-hybrid


定番のImageMagickを使えば簡単なんだけど、ImageMagickが簡単じゃなかった。


WINDOWSではImageMagickのインストールが必要で、さらにActivePerlをインストールしたら一緒にインストールされるppmを使ってImageMagickを使うためのperlのモジュールのインストールが必要。ユーザーさんに別途インストールしてもらうにはちょっとハードルが高い。


macはさらに致命的で、macport でインストールしても、パッケージがあったのでそれをインストールしても、ソースからコンパイルしても、肝腎のJPEG、PNGに対応したものを作れなかった(JPEGはどうにかこうにか、まずJPEGのライブラリをインストールしてmakeしなおしたらjpeg対応版ができたけど、PNGがお手上げ)

JPEGにもPNGにも対応したImageMagickを使ってるひとがいるようなので、わたしのスキル、知識不足。だけど、自分でうまく行ってないものをユーザーさんにインストールしてね、というのは無理すぎる。致命的。



「かんたん」を謳う以上、面倒くさいことはひとつでも増えちゃいけない。


てことで、扱うのはJPEGとPNGだけだし、perlだけで画像サイズを取得する方法を探してgoogleさま。


ありがたいことに、そのまま利用できるサブルーチンなどがいくつかアップされていた。

ただ、そのまま=ブラックボックスのまま使わせてもらうと、あとあとメンテの時などに困るのは火を見るより明らか。猿程度の理解は必要だろうと、画像について、その扱いについて調べたので、備忘録。



・PNGは簡単らしい。


16byte4byte4byte
widthheight

16byte目からの4byteに幅、続く4byteに高さのピクセル数が入っているので決め打ち。


seek(IMG, 16,0);
read(IMG, $buf, 8);
my($width, $height) = unpack("NN", $buf);

幅高さをネットワークバイトオーダーのビッグエンディアン(32bit)で取得するだけでOK。


・JPEGがちょっと面倒。


JPEGというのは複数のセグメントの塊

マーカー2Bytes
セグメントサイズ2Bytes
セグメント本体可変

画像情報を格納しているのはSOFという種類のセグメント。


マーカーというのは16進数の「FF」がセグメントの開始を表していて、これはどういうセグメントですよという内容を意味するのが「FF」に続く1Byteのバイナリ。

SOFセグメントのマーカーで、JPEGのベースラインは「FF」「C0」プログレッシブは「FF」「C2」ということらしい。


ファイルの先頭から見て「FF」が見つかったらそこからセグメントの開始となる(ファイル先頭と最後にはマーカーだけのセグメントがあるので、先頭の2Bytesは読み飛ばす)


必要なのはJPEGの情報が入ったSOFセグメント。


1)

4Bytes読み込む。

2)

unpackでマーカーとセグメントサイズを取得する。


3)

→マーカーの1byteがFFじゃなかったら終了。

→マーカーの1byteがFFでセグメント内容がC0かC2なら続くセグメント本体を読み込む。

→マーカーの1byteがFFでセグメント内容がC0でもC2でもなかったら、セグメントの本体サイズ分読み飛ばして次のマーカーからの4byteを読む


4)

画像サイズはセグメント本体先頭に入っている。

サンプルの精度画像縦サイズ画像横サイズ
1Byte2Byte2Byte

unpackで取得できる。


てことで、easy_epub-hybridに組みこんだサブルーチンが以下。


sub get_image_size{
    my $self = shift;
    my $args = shift;

    open(IMG, $args->{image}) || return;
    my $buf;
    my $w; my $h;
    binmode IMG;
    if($args->{image} =~ m!¥.png!){
        seek(IMG, 16,  0);
        read(IMG, $buf, 8);
        ($w, $h) = unpack("NN", $buf);
    }
    else{
        seek(IMG, 2, 0);
        while(1){
            read(IMG, $buf, 4);
            my($mark, $c, $len) = unpack("H2 H2 n", $buf);
            last if($mark ne ’ff’);

            if($c eq ’c0’ || $c eq ’c2’){
                read(IMG, $buf, 5);
                ($h, $w) = unpack("x nn", $buf);
                last;
            }
            else{
                seek(IMG, $len-2, 1);
            }
        }
    }
    close(IMG);
    return {width=>$w, height=>$h};
}

スクリプトの中からImageMagickを使うシチュエーションのほとんどが画像サイズの取得なので、これは重宝しそうだ。



jpeg画像について、おそらく間違ってるところがあると思うので、きちんとした知識は以下のサイトへどうぞ。


参考にさせていただいたサイト

http://tohoho-web.com/lng/200003/00030402.htm

http://www2c.biglobe.ne.jp/~osakana/vc/pc/jpegsize.html

http://hp.vector.co.jp/authors/VA032610/JPEGFormat/marker/SOF.htm

http://www.setsuki.com/hsp/ext/segment/sof0.htm

情報、丁寧な解説ありがとうございます。



そういえば。Ingress Lv.15になった。なんかAP稼ぎだけで今イチ面白みがなくなってきたなぁ。

image
<<2026/1>>
    123
45678910
11121314151617
18192021222324
25262728293031
検索:

【最近の20件】