PythonとPILで、サブピクセルレンダリング縮小をする
ちょっと今更な話ですが、ライフネット生命副社長である岩瀬大輔さん著、文春新書『生命保険のカラクリ』が、全編PDFで公開されています。
これを印刷して通勤時間を利用して読もうと思ったのですが、綺麗に印刷することができないよう制限がかけられていました。なので、携帯電話で読んでやろうと思い、私の携帯電話に適したサイズに変換することにしました。
なお、私の携帯電話のスペックは、480x800の3インチ液晶です。
縮小の手順
という手順を取ることにしました。
1のPDFビットマップ変換はGhostScriptを使用し、2と3のビットマップ画像を縮小とトリミングにはPythonとPILを使いました
PDFをビットマップに変換
色々試した結果、PDFを150dpiでラスタライズ(ビットマップ変換)した画像から余白をトリミングしたら、丁度480x800になることがわかりました。ということで、PDFからPNGへの変換は以下のようにして変換しました。
% gs -dTextAlphaBits=4 -sDEVICE=pnggray -dBATCH -q -dNOPAUSE -sOutputFile=seiho/%03d.png -r451x150 seiho.pdf
後ほどRGBのサブピクセルを計算する余地として、横方向の解像度を3倍としておきます。
変換結果:
ビットマップをサブピクセルレンダリング縮小する
単純な実装
元の画像はグレースケールにアンチエイリアシングしてあり、横方向に3倍の解像度を持っています。これを、以下のようにRGBの各サブピクセルに割り当ててみました。
aを1/3に縮めてbのように表示するデータがc、という感じかな……。
さきほどのページをこの方法で縮小した画像がはこちら。
曲線部分は綺麗に出ていますが、縦画の左右に偽色が出ているのがわかると思います。単純に考えると、
のように見えるはずなのですが……。
左右のピクセルとの平均を取るようにした実装
エッジが目立ちすぎているので、アンチエイリアシングの基本に立ち返り、左右のピクセルとの平均を取るようにしてみました。
言葉で説明すると難しいですが、左からグレースケールの値(a)が以下のようなデータの場合、
00,55,ff,ff,ff,ff,00,00,00,00,ff,ff,ff,ff,...
左から順に、00,55,ffの平均の71、55,ff,ffの平均のc6、ff,ff,ffでff、ff,ff,ffでff、ff,ff,00でaa、としていきます.。
ということで、綺麗になりました。
携帯電話でもとても綺麗に表示されます。3インチでも問題なく読むことができます。
今回の例は、元データがモノクロのグレースケール画像だったのが、処理を非常に簡単にしています。
また、今回の実装は実装の簡単さと速度を考慮して、3ピクセルの算術平均にしましたが、アンチエイリアスの平均の取得方法には色々なアルゴリズムがあるようです。
最後に、おまけとして、Python + PILのコードを掲載しておきます。元画像や変換後のサイズに汎用性がない、私専用のものではありますが……。それにしても、PILは簡単でいいですね。
あと、言葉で説明するのが難しくて、支離滅裂でわかりにくかったらすいません。
Python + PIL のコード
#! /usr/bin/env python # -*- coding: utf-8 -*- from PIL import Image, ImageChops # 縮小後のサイズ destSize = (480, 800) # 元サイズでのトリミングの起点(左上原点) geom = (240, 115) def main(sourceFile): sourceImage = Image.open(sourceFile) shrinked = shrink(sourceImage) shrinked.save("out/%s" % sourceFile, "PNG") def shrink(sourceImage): targetImage = Image.new('RGB', destSize) targetPix = targetImage.load() sourcePix = sourceImage.load() (l, t) = geom for y in range(destSize[1]): l = geom[0] gg = sourcePix[l-2, t] + sourcePix[l-1, t] + sourcePix[l, t] for x in range(destSize[0]): r = gg = gg - sourcePix[l-2, t] + sourcePix[l+1, t] g = gg = gg - sourcePix[l-1, t] + sourcePix[l+2, t] b = gg = gg - sourcePix[l , t] + sourcePix[l+3, t] targetPix[x, y] = (r/3, g/3, b/3) l += 3 t += 1 return targetImage if __name__ == "__main__": import sys for sourceFile in sys.argv[1:]: print sourceFile main(sourceFile)
EclipseでのJava開発 with Maven2 and Bazaar
対象: Eclipse-3.5, Maven-2.2.x, Bazaar-2.0.x
JavaでSwingアプリの開発をやっています。
具体的には、Maven2で構成管理をし、Bazaarでソースコード管理をし、NetBeansでGUIを作成し、Eclipseでプログラムを書いています。開発の上で試行錯誤の末辿りついたBazaarとMaven2とEclipseの構成を紹介します。(EclipseとNetBeansの連携の話は後日また)
なお、私のBazaar使用歴は短かいです(2.0からのユーザです)。以下で紹介する構成には、誤解や誤りに基く内容が含まれている可能性がありますのでご注意ください。識者のご指導ご鞭撻を期待しつつ、エントリを上げている次第です。
vboxapiでVirtualBoxをPythonで操作その4
対象: VirtualBox-3.1, Python-2.6
ということで、調査中に見つけた情報源を以下に記述しつつ、VirtualBoxシリーズは一旦ここまにしたいと思います。
http://www.virtualbox.org/
VirtualBoxの公式サイトです。
公式サイトのダウンロードページには以下のようなものが含まれます。
- 各種VirtualBoxのバイナリ
- VirtualBox OpenSourceEditionのソースファイル
- ユーザーマニュアル
- SDK
vboxapiでVirtualBoxをPythonで操作その3
対象: VirtualBox-3.1, Python-2.6
前回、前々回の続きです。
VMを起動したり電源をブチっと切ってみたり、一時停止したりしてみます。これらの操作はIMachineのメソッドでははありません。m.pause() とかできたら簡単そうなのですが。
まず、VMがどのような状態を持つのかを確認しておきましょう。
VMが持つ状態
ドキュメントのenum MachineStateに状態遷移図がありますので、以下に引用します。
(略) +-> PoweredOff --+-->[powerUp()]--> Starting --+ | +-----[resume()]-----+ | | | | V | | Aborted -----+ +--> Running --[pause()]--> Paused | | ^ | ^ | (略)
非常にワクワクする図ですね。
VMの起動
ドキュメント通りにpowerUp()で起動するとなぜか失敗してしまいますので、前回ちらりと書いたように、VMの起動はopenRemoteSessionでRemoteSessionを開く方法を使います。
ドキュメントと違いますが、vboxshell.pyもopenRemoteSessionでやっていますので気にしないようにします。
vboxapiでVirtualBoxをPythonで操作その2
対象: VirtualBox-3.1, Python-2.6
前回の続きです。
昨日は、VM一覧の取得と簡単な内容表示まで書きました。
machinesというのは、リファレンスでいうところの、IMachineインターフェースを実装したオブジェクトのリストになっています。
import vboxapi vbm = vboxapi.VirtualBoxManager(None, None) vbox = vbm.vbox machines = vbm.getArray(vbox, 'machines') for m in machines: print "uuid =", m.id
IMachineが保持している情報のうち、特に重要なものがid属性です。idには各VMのwikipedia:UUIDを保持しています。これを使ってVirtualBoxに「このVM」を指定する仕様になっています。
続きを読むvboxapiを使ってVirtualBoxをPythonから操作その1
対象: VirtualBox-3.1, Python-2.6
日常的にデスクトップ用途としてUbuntu 9.10を使っています。たまに他のOSを使う用事があって、そういうときにはVirtualBoxから使っています。
デスクトップOSとして使っているだけなのでVMの起動も終了もGUIからやってしまうのですが、プログラムからVirtualBoxの機能にアクセスできるAPIが用意されているので、何ができるのか調べてみました。
APIにはPythonとC++用が用意されているのですが、Python用を使うことにします。