読者です 読者をやめる 読者になる 読者になる

EPS -> ラスタライズされたEPS

大量のデータを基にプロットをして、EPSファイルを作成した場合、高品質ではあるけど数十MBというファイルサイズになってしまうことがあります。このままだと、PDFを作成したり閲覧、印刷するのにとても時間が掛かってしまいます。このような場合には、ベクトル形式のEPS画像をビットマップ形式にラスタライズすることで、品質とサイズのトレードオフを調整するのが有効です。


僕の場合、最初は EPS -> PNG -> EPS と、PNG形式を経由してラスタライズすることを考えました。
EPS -> PNG 変換は、奥村先生のTeX Wikiの1ページ(http://oku.edu.mie-u.ac.jp/~okumura/texwiki/?Ghostscript#g1adcf94)より

$ gs -q -dSAFER -dNOPAUSE -dBATCH -sDEVICE=pngalpha -dEPSCrop -r300 \
-dTextAlphaBits=4 -dGraphicsAlphaBits=4 -sOutputFile=tmp.png input.eps

とすればできます。


EPSCrop オプションはEPSファイルのヘッダ中にある bounding box の指定を読み取り、その範囲のみを出力させるためのオプションです。これが無いと、用紙の左下隅に図が置かれたような出力になってしまいます。ここで注意すべきは、bounding box指定の読み取りは「gsがそのファイルがEPSであると認識した場合に限られる」ということです。今回の経験から、gsはファイル先頭のコメント行を頼りに判定を行っているらしいことが分かりました。
もし、ファイルの先頭行が

%!PS-Adobe-3.0

であれば、例え拡張子が.epsであってもbounding boxの処理は行われません。

%!PS-Adobe-3.0 EPSF-3.0

であれば、EPSと判定されるようです。


PNG -> EPS変換については以下を参考にしました。EPS -> PNGについても書かれています。
EPS figures and PDF conversion
http://electron.mit.edu/~gsteele/pdf/
ImageMagickのconvertコマンドを用いて

$ convert tmp.png eps3:output.eps

で完成です。


さて、最後に用いたconvertコマンドは多種多様な形式の画像ファイルを相互変換可能な、非常に高機能なプログラムです。もしかして、convertに掛かればEPS -> EPSのラスタライズなんて一発で出来るのでは?
やってみましょう。

% convert -verbose -density 300x300 input.eps eps3:output.eps
"gs" -q -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 "-sDEVICE=pngalpha" -dTextAlphaBits=4 -dGraphicsAlphaBits=4 "-r300x300" -g1200x1200  "-sOutputFile=/tmp/magick-XXYKHpB5" "-f/tmp/magick-XXFyuy9L" "-f/tmp/magick-XXXCYLHs"
/tmp/magick-XXYKHpB5 PNG 1200x1200 1200x1200+0+0 8-bit DirectClass 736KBB 0.050u 0:00.050
input.eps PS 1200x1200 1200x1200+0+0 16-bit DirectClass 736KBB 0.000u 0:00.009
input.eps=>output.eps PS 1200x1200 1200x1200+0+0 16-bit DirectClass 5.443MBB 0.540u 0:00.429

ご覧の通り、convertは内部でgsコマンドを呼び出し、我々が上でやったこととほぼ同じことをやってくれるようです。EPSCropあたりの動作が異なるようですが、まあ、上手くやってくれてるようです。


という訳で、convertコマンドは出来る子なので、困ったら彼に相談してみましょう。

「固定幅部分レンジ」の列に変換するレンジアダプタを書いてみた

動機

系列データの処理にでは、隣接する複数要素間の関係性について調べたいということが良くあると思う。自然言語処理なんかだと、単語の列(文)に対して「2つの単語X, Yが連続して現われたら、次はどんな単語が来るだろう?」というような事を考えたりする(N-gramモデルとか)。

このような場合に、系列中の「隣接するN要素を列挙」できると嬉しいのだけど、現行のBoost.RangeやP-Stade.Ovenには、そういう列挙を直接的に表現するアダプタが無いので自作してみた、という話。
以下、「隣接するN要素」を「長さNの部分レンジ」と言うことにする。

使用例

使用例は以下の通り。
自作したアダプタは yuttie::windowed(3) の部分で、これが入力レンジを「長さ3の部分レンジの集まりを表わすレンジ」に変換する。

#include <iostream>
#include <string>
#include <iterator>
#include <boost/range/algorithm/copy.hpp>
#include <boost/xpressive/xpressive.hpp>
#include <pstade/oven/foreach.hpp>
#include <pstade/oven/stream_writer.hpp>
#include <pstade/oven/xpressive_tokenized.hpp>

#include "windowed.hpp"


int main(int argc, char *argv[]) {
    using namespace std;
    namespace xp = boost::xpressive;
    namespace oven = pstade::oven;

    string const str = "The quick brown fox jumps over the lazy dog.";

    PSTADE_OVEN_FOREACH(r, str | oven::xpressive_tokenized(+xp::alnum)
                               | yuttie::windowed(3))
    {
        boost::copy(r, oven::stream_writer(cout, ", "));
        cout << endl;
    }

    return 0;
}

実行結果:

The, quick, brown
quick, brown, fox
brown, fox, jumps
fox, jumps, over
jumps, over, the
over, the, lazy
the, lazy, dog

ソースコード

#ifndef WINDOWED_HPP
#define WINDOWED_HPP


#include <boost/iterator/iterator_facade.hpp>
#include <boost/range/sub_range.hpp>
#include <boost/range/iterator_range.hpp>
#include <boost/utility.hpp>


namespace yuttie {


    template <class Range>
    struct windowed_iterator
        : public boost::iterator_facade<
            windowed_iterator<Range>,
            boost::sub_range<Range> const,
            boost::forward_traversal_tag
        >
    {
        windowed_iterator()
            : cur_rng_(), end_it_()
        {}

        windowed_iterator(Range const& r, std::size_t len)
            : cur_rng_(boost::begin(r), boost::next(boost::begin(r), len)),
              end_it_(boost::end(r))
        {}

    private:
        friend class boost::iterator_core_access;

        typedef typename boost::range_iterator<Range>::type rng_iterator;
        typedef boost::sub_range<Range> sub_range;

        sub_range const& dereference() const {
            return cur_rng_;
        }

        bool equal(windowed_iterator const& other) const {
            return this->cur_rng_ == other.cur_rng_;
        }

        void increment() {
            if (cur_rng_.end() == end_it_) {
                cur_rng_ = sub_range();
                end_it_ = rng_iterator();
            }
            else {
                cur_rng_.advance_begin(1);
                cur_rng_.advance_end(1);
            }
        }

    private:
        sub_range cur_rng_;
        rng_iterator end_it_;
    };


    template <class Range>
    struct windowed_range : boost::iterator_range<windowed_iterator<Range> > {
        typedef windowed_iterator<Range> iterator;

    private:
        typedef boost::iterator_range<windowed_iterator<Range> > base;

    public:
        windowed_range(Range& r, std::size_t length)
            : base(iterator(r, length), iterator())
        {}
    };

    struct windowed {
        windowed(std::size_t length)
            : length_(length)
        {}
        std::size_t length_;
    };

    template <class ForwardRng>
    inline windowed_range<ForwardRng>
    operator|(ForwardRng& r, windowed const& f) {
        return windowed_range<ForwardRng>(r, f.length_);
    }

    template <class ForwardRng>
    inline windowed_range<const ForwardRng>
    operator|(ForwardRng const& r, windowed const& f) {
        return windowed_range<const ForwardRng>(r, f.length_);
    }


}  // namespace yuttie


#endif  /* WINDOWED_HPP */

inertial-scroll.el をホイールでも

id:kiwanami さんの inertial-scroll.el というスムーススクロールを実現する el を利用していて、是非マウスのホイールでも利用したいと思ったので設定してみた。

(global-set-key (vector mouse-wheel-down-event) 'inertias-down)
(global-set-key (vector mouse-wheel-up-event)   'inertias-up)

ホイール関連のキーバインディングは始めてなので、他により適切な方法があるかもしれない。もしかしたら inertias-define-keymap で定義できるかも。

AUCTeXでpxdviを使う

EmacsLaTeX環境の1つAUCTeXで、dviのデフォルトビューアをxdviからpxdviに変更する設定に苦闘していたが、ようやく解決できたヽ(*´∀`)ノ


どのように設定すれば良いか、なんてのはググればすぐ見つかって、TeX-view-style や TeX-output-view-style といった変数を変更することでdviのデフォルトビューアを変更できるらしい、というところまで辿りつける。例えば、AUCTeX - TeX Wikiには

DVI Viewerを変更する場合は、

  (setq TeX-output-view-style '(("^dvi$" "." "/path/viewer %d")))

のようにします。

と書いてある。しかし残念なことに、僕の環境 (Gentoo Linux, AUCTeX-11.86) では、これらの変数を変更しても全く効果がなかった。


うまくいかない原因が全く検討つかなくてしばらくの間放置していたんだけど、AUCTeXのソース tex.el を眺めてやっとその答えが分かった。

;;; Viewing
(defgroup TeX-view nil ... )
(defcustom TeX-view-style ... )
(defcustom TeX-output-view-style ... )

;;; Viewing (new implementation)
(defvar TeX-view-predicate-list-builtin ... )
(defcustom TeX-view-predicate-list ... )
(defvar TeX-view-program-list-builtin ... )
(defcustom TeX-view-program-list ... )
(defcustom TeX-view-program-selection ... )
(defun TeX-match-style ... )
(defun TeX-view-match-predicate ... )
(defun TeX-view-command-raw ... )

つまり、*-view-style 系の変数は古いバージョンで使われてたもので、現在は使われてないってことなんじゃないかと思う。だから、いくら変更を試しても効果が無かった。代わりに、これからは新しい実装が用意してくれてる3つのカスタマイズ変数を利用すれば良いはず。

3つの変数の内 TeX-view-program-list はプログラム名とコマンドラインの対応の定義に用いられるもので、今回の目的だとこいつを変更すれば十分。結局、

(eval-after-load "tex" '(progn
  (setq TeX-view-program-list
        (list (assoc "xdvi" TeX-view-program-list-builtin)))
  (setcar (cadr (assoc "xdvi"  TeX-view-program-list))
          "%(o?)pxdvi")))

という風にして、pxdviをdviのデフォルトビューアに変更することに成功。