画像の幅と高さを取得するスクリプト

webページを作るのにあると便利なので、画像ファイルの幅(width)と高さ(height)を取得するプログラムをSchemeで書いてみた。対応している画像フォーマットは、JPEG、GIF、PNGの3種。使用した処理系はGaucheで、モジュールとして再利用できるようにしている。以下そのソース。

;;;
;;; image-size.scm - For getting image size
;;;
;;;   Copyright (c) 2009  Tetsurou, All Rights Reserved.
;;;
;;;   version: 1.0 (21-09-17)
;;;

(define-module tetsurou.image-size
  (use binary.pack)
  (export get-image-width&height
	  get-image-width
	  get-image-height)
  )
(select-module tetsurou.image-size)


;; getting size of image
(define (get-image-width&height file-name)
  (if (file-exists? file-name)
      (let ((type ((#/\.(.+?)$/ (sys-basename file-name)) 1)))
	(call-with-input-file file-name
	  (lambda (p)
	    (if p
		(case (string->symbol type)
		  ((gif) (get-gif-image-size p))
		  ((png) (get-png-image-size p))
		  ((jpeg jpg) (get-jpg-image-size p))
		  (else (error "Image file is not supported: " (string->symbol type))))
		(error "Image file read error!")))
	  :if-does-not-exist #f))
      (error "Image file is not exist.")))

;; getting width of image
(define (get-image-width file-name)
  (receive (w h) (get-image-width&height file-name) w))

;; getting height of image
(define (get-image-height file-name)
  (receive (w h) (get-image-width&height file-name) h))

;; getting size of gif image
(define (get-gif-image-size port)
  (if (string=? (string-incomplete->complete (read-block 3 port) :omit) "GIF")
      (apply values (unpack "x3v2" :input port))
      (error "This file is not formated by GIF!")))

;; getting size of png image
(define (get-png-image-size port)
  (if (equal? (read-block 8 port) #*"\x89PNG\r\n\x1a\n")
      (apply values (unpack "x8N2" :input port))
      (error "This file is not formated by PNG!")))

;; getting size of jpeg image
(define (get-jpg-image-size port)
  (if (eq? (read-byte port) 255)
      (case (read-byte port)
	((192 194) (apply values (reverse (unpack "x3n2" :input port))))
	((216 217) (get-jpg-image-size port))
	((0)       (error "Analysis failed."))
	(else      (port-seek port (- (car (unpack "n" :input port)) 2) SEEK_CUR)
		   (get-jpg-image-size port)))
      (error "This file is not formated by JPEG!")))

(provide "tetsurou/image-size")

画像ファイルからのサイズ取得方法は「画像ファイルの幅と高さを取得 - メモ@wantora」を参考にした。Rubyで書かれているのを書き直す際、Schemeっぽく再帰を使ってみる。結構すっきりしたんじゃないだろうか。binaryなファイルをいじるのははじめてだけど、unpuckモジュールを使えば比較的楽にできた。ただ、不完全文字列には若干手こずったけど…。

サイズ取得はフォーマット毎に関数をわけ、それぞれget-XXX-image-sizeとしている。これらの関数は、入力ポートを唯一の引数に取り、取得した画像の幅と高さを多値で返す。エラー処理は適当。

GIFとPNGは決まった位置にサイズの情報があるので比較的簡単。正しいフォーマットかチェックして、サイズ情報が書き込まれている位置を読めばOK。一方JPEGはそうゆう訳にはいかず、マーカコード(marker code)と呼ばれる識別子で情報が区切られているため、まずこれを見つけてやる必要がある。画像のサイズに関係のないマーカは不要なので、マーカの長さを取得しport-seekで読み飛ばしている。またJPEGの画像サイズは、高さ、幅の順で書き込まれているため、reverseをかけて他のフォーマットに合わせた。

フォーマットの振り分けは簡単に拡張子で判断している。実用上は問題ないだろう。モジュール外から利用できる関数は、get-image-width&height、get-image-width、get-image-heightの3つを用意した。

あとは以下のようなスクリプトを適当に書いてやって、シェルから呼び出せば画像の大きさが取得できる。

#!/usr/local/bin/gosh

(use tetsurou.image-size)

(define (usage)
  (format (current-error-port)
	  "Usage: ~a file-name\n" *program-name*)
  (exit 2))

(define (main args)
  (if (= (length args) 2)
      (receive (width height)
	(get-image-width&height (cadr args))
	(print "Width: " width ", Height: " height))
      (usage))
  0)
戻る