#!/usr/local/bin/ruby # Resize jpeg files # $Id$ # Last modified Sun Aug 31 17:27:47 2003 on firestorm # 基本的な使い方 # あるディレクトリにたくさんの大きなJPEGファイル(デジカメで撮ったままの # ファイルとか)があるときに、それらをカレントディレクトリに適当なサイズ/ # 明るさに一括変換して吐き出す。吐き出したjpgファイルのコメントエリアに # はそのファイルのタイムスタンプが埋め込まれる。 # # 【WWW用に幅400くらいにしたい】 # % rjpg -x 400 /dos/d/*jpg # 【やっぱりもとの半分のサイズで】 # % rjpg -s 0.5 -f /dos/d/*jpg (-f付けないと上書きしない) # 【γ値1.5で】 # % rjpg -x 400 -g 1.5 /dos/d/*.jpg (デフォルトは1.2) # 【90度回転して】 # % rjpg -x 400 -r 90 /dos/d/dcp01234.jpg # 【中央部70%だけ残るようトリミング】 # % rjpg -x 600 -c 70% /dos/d/dcp01235.jpg rdjpg = "rdjpgcom" wrjpg = "wrjpgcom" djpg = "djpeg" cjpg = "cjpeg" pnmgamma = "pnmgamma %s" pnmcut = "pnmcut" cat = "cat" outputdir = "." resize = false quality = "75" gamma = "1.2" tformat = "%a %b %d %T %Y %Z" uniqfile = false uniqonly = false scalefmt = "pnmscale -xsize %d -ysize %d" fnformat = "i%Y%m%d-" fnsuffixfigure = 3 xtable = File.basename($0) + ".xtb" $debug = false scale = false width = false force = false rotate = false fntitle = false Yfix = false tsname = false mkxtable = false putexif = false cutx = nil cuty = nil usage = <<_EOU_ #{File.basename($0)} [options] JpegFiles Options are... -x WIDTH Set new jpg file's width to WIDTH -y HEIGHT Set new jpg file's height to HEIGHT -s SCALE Set scale (for pnmscale) to SCALE -o DIR Output directory -q N Jpeg compression quality -f Force Overwrite -g N Gamma correction -r ANGLE Rotate ANGLE degrees in unti-clockwise -c X,Y,W,H Cut W*H rectangle from coordinate (X,Y) -c N% Cut the N% center of photo -p Create progressive JPG -i Use ImageMagick (Aprx. 4times slow) -fn Put file name in jpg comment area, instead of time -ex Put Exif data in jpg comment area (requires jhead) -tf FMT Time format same as strftime(3) -u Uniq File Name according to time stamp -uf (with -u)Filename format in strftime(3) (#{fnformat}) -sf N (with -u)File name counter's figure (default: 3) -xt (with -u)Create filename translation table -uo Uniquify File Name Only, no conversion _EOU_ #' while (/^-.+/ =~ ($_ = ARGV[0]) && ARGV.shift) $_= $_.dup break if ~/^--$/ while ~/^-[A-z]/ if ~/^-x$/ width = ARGV.shift elsif ~/^-y$/ height = ARGV.shift Yfix = true; elsif ~/^-s$/ scale = ARGV.shift elsif ~/^-d$/ $debug = true elsif ~/^-o$/ outputdir = ARGV.shift elsif ~/^-q$/ quality = ARGV.shift elsif ~/^-f$/ force = true elsif ~/^-g/ gamma = ARGV.shift elsif ~/^-r/ rotate = ARGV.shift elsif ~/^-p$/ cjpg << " -progressive" elsif ~/^-fn$/ fntitle = true; sub!(/./, '') elsif ~/^-ex$/ putexif = true; sub!(/./, '') elsif ~/^-i$/ #djpg = "convert - PNM:-" pnmgamma = "convert -gamma %s - -" scalefmt = "convert -geo %dx%d - -" elsif ~/^-c$/ $_ = ARGV.shift if ~/(\d+)%/ $cut = $1 else $cut=$_ cutx, cuty = $cut.split(/,/)[0][2..3] $cut = "#{pnmcut} " + $_.split(/,/).join(" ") end elsif ~/^-u$/ uniqfile = true elsif ~/^-uf$/ fnformat = ARGV.shift; sub!(/u/, '') elsif ~/^-sf$/ fnsuffixfigure = ARGV.shift.to_i; sub!(/s/, '') elsif ~/^-xt$/ mkxtable = true; sub!(/x/, '') elsif ~/^-uo$/ uniqonly = uniqfile = true; sub!(/./, '') else puts "Invalid option #{$_}" print usage exit 0 end $_.sub!(/^-.(.*)/, '-\1') end end def intr () unlink(outfile) if test(?f, outfile) STDERR.puts "User break: #{outfile} deleted" exit 0; end def charAt(handle, at) handle.seek(at, 0) return handle.read(1)[0] end def jpgsize(file) # Return [x, y, comment] if success, else nil filesize=File.size(file) return nil if filesize < 2 # must have jpeg id (2bytes) at least open(file, "r"){|h| buf = h.read(2) comment="" return nil if buf[0] != 0xff || buf[1] != 0xd8 seekpoint = 2 catch (:exit) { while seekpoint < filesize-4 if charAt(h, seekpoint) != 0xff then throw :exit, 0 elsif [192, 198, 194, 195, 197, 198, 199, 201, 202, 203, 205, 206, 207].index(charAt(h, 1+seekpoint)) then # jpeg geometry area found! h.seek(5+seekpoint, 0) buf = h.read(4) x = buf[2..3].unpack("n")[0] y = buf[0..1].unpack("n")[0] throw :exit, [x, y, comment] elsif 0xFE == charAt(h, 1+seekpoint) then # JPEG comment area h.seek(2+seekpoint, 0) len = h.read(2).unpack("n")[0] comment << h.read(len-2)+" " else # Other marker # Do nothing end seekpoint += 2 h.seek(seekpoint, 0) buf = h.read(2) seekpoint += buf.unpack("n")[0] end } } end for f in ARGV if ! test(?f, f) STDERR.puts "No such file: #{f}"; next end timestamp = File.mtime(f) comment = `#{rdjpg} #{f}`.chomp if comment > "" stamp = comment puts "#{f} Comment: #{stamp}" if $debug elsif ! fntitle # stamp = &timeformat($timestamp); stamp = Time.at(timestamp).strftime(tformat) puts "#{f} Stamp: #{stamp}" if $debug else stamp = File.basename(f) end if width && !Yfix #x, y = `#{djpg} #{f}|pnmfile -`.scan(/(\d+) by (\d+)/)[0] x, y = jpgsize(f) if width == x scale = 1 else if cutx && cuty height = width.to_i * cuty.to_i / cutx.to_i else height = y.to_i * width.to_i / x.to_i end ##resize = "pnmscale -xsize #{width} -ysize #{height}" resize = sprintf(scalefmt, width, height) end elsif height ##x, y = `#{djpg} #{f}|pnmfile -`.scan(/(\d+) by (\d+)/)[0] x, y = jpgsize(f) if height == y scale = 1 else if cutx && cuty width = height.to_i * cutx.to_i / cuty.to_i else width = x.to_i * height.to_i / y.to_i end #resize = "pnmscale -xsize #{width} -ysize #{height}" resize = sprintf(scalefmt, width, height) end elsif scale if ~/convert/ resize = sprintf("convert -geo %d - -", (scale.to_i*100).round) else resize = "pnmscale #{scale}" end end # Construct file name outfile = '' fnsuffixfmt = "%0#{fnsuffixfigure}d.jpg" if uniqfile fbase = "#{outputdir}/" + Time.at(timestamp).strftime(fnformat) 0.upto(10**fnsuffixfigure-1) {|i| outfile = fbase + sprintf(fnsuffixfmt, i) break unless test(?f, outfile) } if mkxtable open(xtable, "a"){|x| x.printf "s/%s/%s/\n", File.basename(f), File.basename(outfile) } end if uniqonly File.link(f, outfile) print "ln #{f} #{outfile}\n" unless $quiet next end else outfile = "#{outputdir}/" + File.basename(f) end # sleep(1); if test(?f, outfile) s1, s2 = File.stat(f), File.stat(outfile) if s1.dev==s2.dev && s1.ino==s2.ino puts "Skipping #{f}..." next end if ! force outfile.sub!(/\.jpg$/, "\.jpeg") puts "Set out file to #{outfile}" end end trap(:INT, 'intr') open(f) do |img| img.binmode if resize if /#{pnmcut}/o =~ $cut cmdline = "#{djpg} | #{$cut}" elsif $cut && $cut.to_i > 0 c = $cut.to_i ox, oy = `#{rdjpg} -v #{f}`.scan(/(\d+)w \* (\d+)h/)[0] ox, oy = ox.to_i, oy.to_i x1, y1, w1, h1 = ox/2*(100-c)/100, oy/2*(100-c)/100, ox*c/100, oy*c/100 cmdline = "%s | %s %d %d %d %d" % [djpg, pnmcut, x1, y1, w1, h1] # cmdline = "djpeg | pnmcut 264 175.2 1232 817.6" #cmdline = "djpeg | pnmcut 200 100 800 600 " # cmdline = "djpeg" else cmdline = djpg.dup end cmdline << " | #{resize} | " + sprintf(pnmgamma, gamma) if rotate cmdline << "| pnmrotate #{rotate}" end cmdline << "| #{cjpg} -q #{quality} | #{wrjpg} -c \"#{stamp}\" > #{outfile}" puts "#{cat} #{f} | #{cmdline}" open("| #{cmdline}", "w") do |out| out.binmode out.sync=true #out.print img.readlines begin out.print img.read rescue STDERR.puts "#{f}: Trailing superfluous data ignored." end end trap('PIPE', 'DEFAULT') else # resizing puts("#{wrjpg} -c \"#{stamp}\" #{f} > #{outfile}") unless $quiet open("| #{wrjpg} -c \"#{stamp}\" > #{outfile}", "w") do |out| out.binmode begin out.print img.read rescue STDERR.puts "#{f}: Trailing superfluous data ignored." end end end end trap('INT', "DEFAULT") if putexif puts "jhead -te '#{f}' #{outfile}" unless $quiet system "jhead -te '#{f}' #{outfile}" tf = "#{outputdir}/rjpg-#{$$}.tmp" File.unlink(tf) if test(?f, tf) File.rename(outfile, tf) system "#{wrjpg} -c \"#{stamp}\" #{tf} > #{outfile}" File.unlink(tf) end File.utime(timestamp, timestamp, outfile) end