プログラム

OpenCVで自炊本をモノクロ2値に変換してみた

自炊代行業者さん(Bookscan)に依頼した本のPDFがたくさんあるのだが,読みやすく美白化して白黒2値に変換したい。それを実現するためのプログラム。まだ試作段階だが,とりあえず動作している。以前,GIMP用のスクリプトやImageMagick用のPerlスクリプトで行っていたことを,OpenCVでやってみた。以前のバージョンに比べて,明らかにスピードが速い。OpenCVはもちろんC++でプログラム書いたことないので,無事にコンパイル出来たときは,ちょっと嬉しかったな。

参考にしたのは,次のページ。参考というよりは,ほとんどそのまま頂いている。有難きは先達なり。

橋本商会 scansnapで自炊した本をkindleで読めるように補正する(2)

このページに書かれているプログラムをひな形として,自分の目的に合うように処理内容をあちこち変えた。実は,boost::filesystemのバージョンが上記ページが書かれた頃から変わっていたため,そのままではコンパイル出来なかった。また,OpenCVのバージョンも上がっていて,ライブラリーの名前などが以前とは変わっているようなので,それに応じてMakefileも少し修正を必要とした。ちなみに,OpenCVもboostもHomebrewでインストールした。バージョンは brew info によれば,それぞれ opencv: stable 2.4.5, boost: stable 1.54.0 (bottled) となっている。

やっていることは,次の通り。

  • 赤チャンネルだけを抽出することでグレースケールに変換
  • 上下左右の余白を白で塗りつぶす
  • Lanczos法により2倍に拡大
  • ルックアップテーブルを作成することにより,レベル補正,ガンマ補正
  • 2値化
  • PNGで保存

レベル補正,ガンマ補正の式が多分正しくない。gamma=1のときは直線補間だから正しいと思うが,それ以外のときの式がわからない。ImageMagickの level high low gamma と同じにしたいのだが。これは今後の課題。

PNGで保存しているが,可能であれば,CCITT G4圧縮のTIFFで保存したい。これも今後の課題。

#include "cv.h"
#include "highgui.h"
#include <boost /program_options.hpp>
#include </boost><boost /filesystem/operations.hpp>
#include </boost><boost /filesystem/path.hpp>
#include </boost><boost /filesystem/fstream.hpp>
#include <iostream>
using namespace boost;
using namespace std;
namespace fs = boost::filesystem;

//---------------------------------------------------------------
// my_Level
// レベル補正・ガンマ補正 
// (ImageMagickの convert -level white_point black_point gamma と同じにしたいのだが)
// src    = 入力画像
// dst    = 出力画像
// high   = ホワイトポイント (0..255)
// low    = ブラックポイント (0..255)
// gamma  = ガンマ補正値
// 注意:暫定版。ガンマ補正の式はこれで正しいのか不明。
// 0<x &lt;1 と正規化したとき,f(x)={(x-low)/(high-low)}^{1/gamma} としてみた。
//---------------------------------------------------------------

// グローバル変数
uchar g_LUT&#91;256&#93;; // Lookup table用の配列
CvMat g_lut_mat;  // それをopencvの行列とみなしたもの

void make_lut(int high, int low, double gamma){
    int i;
    double x;
    const double p = 1.0 / gamma ;
    const double a = (double) low;
    const double b = (double) high;
  
    //補正用のルックアップテーブルの作成
    for (i=0; i<low; i++) { g_LUT&#91;i&#93; = 0; }
    for (i=high; i&lt;256; i++) { g_LUT&#91;i&#93; = 255; }
    for (i=low; i<high; i++) 
    {
      x = (double) i;
      // 16階調にする場合 16x16=256
      //g_LUT&#91;i&#93; = cvCeil( ((x-a)/(b-a)) * 15 ) * 16;
      g_LUT&#91;i&#93; = cvCeil( 255.0 * pow((x-a)/(b-a), p) );
    }
 
    //CvMatへ変換
    g_lut_mat = cvMat(1, 256, CV_8UC1, g_LUT);
}

void my_Level(IplImage* src, IplImage* dst) {
  //ルックアップテーブル変換
  cvLUT(src, dst, &g_lut_mat);
}

IplImage *adjust_image(IplImage *img, program_options::variables_map argmap){
  int cleft = argmap&#91;"cleft"&#93;.as<int>();
  int cright = argmap["cright"].as<int>();
  int ctop = argmap["ctop"].as</int><int>();
  int cbottom = argmap["cbottom"].as</int><int>();
  int threshold = argmap["threshold"].as</int><int>();

  const int w = img->width;
  const int h = img->height;
  const int w1 = 2*w;
  const int h1 = 2*h;
  const int x1 = cleft;
  const int x2 = w-cright;
  const int y1 = ctop;
  const int y2 = h-cbottom;

  IplImage *img_gray;

  //赤チャンネルを抽出してグレースケールの画像を得る
  //チャンネル毎に分割して,赤チャンネル以外は捨てる
  // IplImageはB,G,Rの順に格納されている
  // アルファチャンネルは用いないので,5番目の引数はNULLにしておく
  // cvSplit(img,channelB,channelG,channelR,NULL);
  img_gray = cvCreateImage(cvSize(w,h),IPL_DEPTH_8U,1);
  //cvCvtColor(img, img_gray, CV_BGR2GRAY);
  cvSplit(img,NULL,NULL,img_gray,NULL);

  //周囲を白で塗る
  cvRectangle(img_gray, cvPoint (0,0), cvPoint (x1,h), cvScalar (255), CV_FILLED, 8, 0);
  cvRectangle(img_gray, cvPoint (x2,0), cvPoint (w,h), cvScalar (255), CV_FILLED, 8, 0);
  cvRectangle(img_gray, cvPoint (0,0), cvPoint (w,y1), cvScalar (255), CV_FILLED, 8, 0);
  cvRectangle(img_gray, cvPoint (0,y2), cvPoint (w,h), cvScalar (255), CV_FILLED, 8, 0);

  //2倍に拡大
  IplImage *img_resize = cvCreateImage(cvSize(w1,h1), IPL_DEPTH_8U, 1);
  cvResize(img_gray, img_resize, CV_INTER_LANCZOS4);

  //レベル補正
  IplImage *img_level = cvCreateImage(cvSize(w1,h1), IPL_DEPTH_8U, 1);
  my_Level(img_resize, img_level);

  if (threshold > 0) {
    // 2値化する場合
    IplImage *img_bin = cvCreateImage(cvSize(w1,h1), IPL_DEPTH_8U, 1);
    // ガウシアンフィルタで平滑化を行う
    // cvSmooth (img_level, img_level, CV_GAUSSIAN, 5);
    // 2値化
    cvThreshold(img_level, img_bin, threshold, 255, CV_THRESH_BINARY);
    //cvThreshold(img_level, img_bin, 0, 255, CV_THRESH_BINARY | CV_THRESH_OTSU); // 大津の手法
    // メモリー開放
    cvReleaseImage(&img_gray);
    cvReleaseImage(&img_resize);
    cvReleaseImage(&img_level);
    // 2値化した画像へのポインターを返す
    return img_bin;
  } else {
    // グレースケールで出力する場合
    // メモリー開放
    cvReleaseImage(&img_gray);
    cvReleaseImage(&img_resize);
    // レベル補正したグレースケール画像へのポインターを返す
    return img_level;
  }
}

int main(int argc, char* argv[]) {
  program_options::options_description opts("options");
  opts.add_options()
    ("help", "ヘルプを表示")
    ("high", program_options::value</int><int>()->default_value(255), "level high")
    ("low", program_options::value</int><int>()->default_value(0), "level low")
    ("gamma", program_options::value<double>()->default_value(1.0), "level gamma")
    ("threshold,t", program_options::value<int>()->default_value(-1), "binarize threshold")
    ("input,i", program_options::value<string>(), "input directory name")
    ("output,o", program_options::value</string><string>(), "output directory name")
    ("cleft", program_options::value<int>()->default_value(0), "crop left (pixel)")
    ("cright", program_options::value</int><int>()->default_value(0), "crop right (pixel)")
    ("ctop", program_options::value</int><int>()->default_value(0), "crop top (pixel)")
    ("cbottom", program_options::value</int><int>()->default_value(0), "crop bottom (pixel)");
  program_options::variables_map argmap;
  program_options::store(parse_command_line(argc, argv, opts), argmap);
  program_options::notify(argmap);
  if (argmap.count("help") || !argmap.count("input") || !argmap.count("output") ||
      !argmap.count("high") || !argmap.count("low")) {
    cerr < < "&#91;input, output, high, low&#93; required" << endl;
    cerr << opts << endl;
    return 1;
  }

  int high = argmap&#91;"high"&#93;.as<int>();
  int low  = argmap["low"].as</int><int>();
  double gamma  = argmap["gamma"].as<double>();

  // ルックアップテーブルを作成する。ルックアップテーブルはグローバル変数 g_lut_mat である。
  make_lut(high,low,gamma);

  string in_dir = argmap["input"].as<string>();
  fs::path path = complete(fs::path(in_dir));
  fs::directory_iterator end;
  for (fs::directory_iterator i(path); i!=end; i++){
    string img_fullname = in_dir + i->path().filename().string();
    cout < < img_fullname << endl;
    IplImage *img, *img_result;
    img = cvLoadImage(img_fullname.c_str());
    if(!img){
      cerr << "image file load error" << endl;
    }
    else{
      img_result = adjust_image(img, argmap);
      // string out_filename = argmap&#91;"output"&#93;.as<string>() + "/" + i->path().filename().string();
      string out_filename = argmap["output"].as</string><string>() + "/" + i->path().stem().string() + ".png";
      cvSaveImage(out_filename.c_str(), img_result);
      cvReleaseImage(&img);
      cvReleaseImage(&img_result);
    }    
  }
}

カスタムタクソノミーのリンク表示

[備忘録]
WordPressでカスタム投稿タイプとそれに付随するカスタムタクソノミー(custom taxonomy カスタム分類)を導入したのだが,それらを各記事のところに表示する方法。

get_the_term_list という関数を用いると,リンク付きの文字列が得られる。使用中のテーマは,WordPress3.5付属のTwenty Twelve というもの。functions.php を修正するのだが,直接変更せずに,Child Theme (子テーマ) を作って,そちらに functions.php を作って,修正する。具体的には,twentytwelve_entry_meta() の中で,次のような記述をすればよい。

echo '分野:' . get_the_term_list($post->ID,'math_cat','',', ');

もっとも,これは単純すぎるので,実際にはカスタム分類があるときに限って表示するようなif文にするのだが。

これで,デフォルトの「投稿」と同じ形式で記事の末尾などに,カスタム分類の項目と,そのアーカイブへのリンクを出力することが出来た。

WordPressでカテゴリー一括登録を手動で行う実験

カテゴリーを一括登録するプラグインもいくつか使ってみたが,その場で登録するものばかりで,事前に用意されたテキストファイルなどを読み込んで,一括して登録するものは見当たらなかった。そこで,見よう見まねで自作することにした。

利用するのは,wp_create_category() という WordPress の内部関数。これを使うためには,これが定義されているファイルをインクルードしなくてはならないらしい。

改行で区切った文字列をヒアドキュメントで定義して,これを改行で分割して配列にする。そして,配列の各々の文字列に対して,カテゴリーとして登録するというもの。

// カテゴリーを手動で登録する実験
function create_my_categories() {
$cat_str =
<<<CATDATA
初等数論
可換環論
ガロア理論
代数曲線論
複素多様体
代数的数論
解析的数論
CATDATA;

	include_once(ABSPATH . 'wp-load.php');
	include_once(ABSPATH . 'wp-admin/includes/taxonomy.php');

	$cat_array = explode("\n", $cat_str);
	foreach($cat_array as $value) {
		wp_create_category($value);
	}
}

とりあえず,実験は成功。自家用としては,これを functions.php に追加して,

add_action('init', 'create_my_categories');

とかすれば良い。汎用性を考えるならファイルから読み込むようにしてプラグインとして動作させるのが良いのだろう。プラグインの作り方,すっかり忘れてしまったが・・・。

Advanced Custom Fields を使ってみる

相変わらず MAMP でローカルにWordPressでのデータベース構築実験,続行中。

標準のカテゴリーだけで処理することも可能かもだが,将来的なことも考えて,Advanced Custom Fields (ACF) というプラグインを試してみることにした。かなり意欲的なプラグインであり,独自APIまでいろいろと準備されている。

オフィシャルサイトのDocumentationとかサンプルを少し読んで,とりあえず自分で作成したフィールドを記事に表示させることが出来たので,忘れないようにメモ。使うのは,get_field, the_field という関数(API)たち。

直にHTMLというかPHPを書き込んでも良いのだが,これも将来的なことを考えて,関数にしてみた。オフィシャルの the_category() という関数のソースを参考にして,適当に自分の関数を定義してみる。例えば名前を show_my_fields() としてみよう。これを,使っているテーマ付属の functions.php の末尾にでも追加する。PHPはまるっきりの初心者で,文字列結合の演算子とかも知らないものだから,思いっきり冗長なベタ書きコードになっているが,まあ,とりあえず動作したということで。

<?php

function show_my_fields( $post_id=false ) {
	if ( get_field('shutten')) {
		echo '<span class="metacat">';
		echo "出典: ";
		echo get_field('shutten');
		if ( get_field('opt')) {
			echo get_field('opt');
		}
		echo "</span>";
	}
}

?>

そして,これをフィールドを表示させたいところに,

 show_my_fields() 

と書けばよい。これで,独自フィールドの表示は成功。

次は,標準のカテゴリー表示のように,特定のフィールドに合致するエントリーをアーカイブとして表示させたいのだが,まだどうしてよいか分からない状態。フィールドに一致するエントリー全体の配列かなにかを返してくれる関数(API)があるはずだとは思うのだが。

あと,PHPの文法も少し勉強しなくては。というか,PHPって,コードとHTMLが同時に混ざっているって,どういう仕様なんだ〜 😯 とか思うのだが。不思議な言語ではある 😉 。

CSV Importerを使ってみた

MAMP上でのWordPressで,簡易データベース構築の実験中。既存の文書から一括して登録するために,ツールをあれこれ探してしるが,CSV Importer というプラグインがあったので,使ってみた。インポートしたいデータをCSV (Comma Separated Value) の形にしておけば,一括して読み込めるというもの。

試した結果,何とか使えそうだが,いくつか問題点も。自分の用途だと,改行のタブとかパラグラフのpタグなどが自動で追加されるのは,ちょっと迷惑。ということで,ソースを読んで改造。

'post_content' => wpautop(convert_chars($data['csv_post_post'])),

という箇所で,記事の本体が取り込まれているのだが,wpautop という関数によって,タグ付きに整形されている。wpautop をはずすことで,タグが付かない元の文章のままで読み込むことが出来た。これで1つ解決。

もう1つは少しやっかい。数学の文章なので,TeXで書いてあるのだが,バックスラッシュが消滅してしまうのだ。記事本体は,ダブルクオートで囲まれていて,そこの部分を1つの文字変数に代入していると思うのだが,その段階で,バックスラッシュがメタ文字と解釈されているのだと思う。試しに,変換前の文章でバックスラッシュ2個にしてみたら,変換後はバックスラッシュ1個になっていたので,多分間違いないと思う。これをどうしたものか。

ImageMagickとXnConvertを併用してみる

[備忘録] 自炊PDFの後処理関係のメモ。

XnConvertのバージョンが1.51に上がり,従来のBrightness, Contrast以外にも,白黒のレベル指定が出来るようになった。これで,ImageMagickでの -level high, low, gamma と同じ(多分ね)パラメーターの指定が可能となった。速度的にはLanczosでの拡大処理を含めてXnConvertの方が速いので,こちらをメインにしようかと思う。

ただ,XnConvertでは,モノクロ2値への変換で threshold値を選ぶことが出来ない。また,ImageMagickの shave (上下左右を切り取る),border (逆に上下左右を塗る) などの処理が出来ないみたいだ。shaveに関しては,cropとrotateを組み合わせれば良いようにも思うが,よく分からない。一番の問題点は,thresholdの値(閾値)を選べないということなので,この部分はImageMagickを使う方が良いだろう。

ということで,試しに一冊処理してみた。hoge.pdfをpdfimagesを使って,hoge_000.jpgからhoge_124.jpgにバラす処理は済んでいるものとする。そこの階層に行って,次のようにする。

nconvert -extract 0 -ratio -rtype lanczos -resize 200% 200%  -levels 60 255 -gamma 0.7  -posterize 64 -posterize 32 -posterize 16 -posterize 8 -posterize 4 -out png  -o nc_% -overwrite  *.jpg

コマンドの意味は次の通り。-extract 0 で赤チャンネル抽出。-ratio 以下は,lanczosアルゴリズムによる200%拡大。-levels 60 255 と -gamma 0.7 は ImageMagickでの level 60,255,0.7 と同じ。今回の本は,スキャンの具合が良くなくて,全体的に文字が薄くてボケた感じなので,白飛びさせると文字が消えてしまいそうだったので,high=255とマックスのままにした。

-posterize は減色(ポスタリゼーション)処理。64色から順に半分ずつにして,4色まで減色している。-out png は出力ファイル形式をPNGにするということ。-o nc_% は出力ファイルの名前の設定。これだと,元ファイルがhoge002.jpgの場合,nc_hoge002.png となる。-overwrite は出力ファイルと同じ名前があった場合に上書きするということ。nconvert による処理は以上で終了。

次は,ImageMagickを使う。mogrifyコマンドを使って,上下左右を削除して白で元にもどす。それから,閾値を80%にして,モノクロ2値化する。出力ファイルはtiffにして,FAX Group4で圧縮する。

mogrify -shave 40x40 -border 40x40 -bordercolor "#FFFFFF" -threshold 80% -format tiff -compress Group4 nc_*.png

続いて,tiffをpdfに変換。

mogrify -format pdf nc_*.tiff

最後に,pdfファイルをまとめて,hoge-mono.pdf にする。

pdftk nc_*.pdf cat output hoge-mono.pdf

こんな手順で,モノクロ2値化が完了。一冊毎にパラメーター変えるのなら,この程度の入力を毎回行なっても,それほど面倒ではない。パラメーター固定で複数の書籍を変換するのなら,やはりバッチ処理用のスクリプトを書いた方が良いだろうなあ,やはり。

ImageMagickのmogrifyコマンドを使う

[備忘録] 以前 The Internet Archive からダウンロードしておいた E. Cahen の Theorie des nombre, tome 2 (整数論,第2巻)。サイズも小さくで良いのだが,何故かMacのプレビュー.appで見ると,ものすごく動作が遅くてスクロールもままならないという状況だった。それで,放っておいたのだった。そのときは対処法など分からなかったのだが,業者に依頼した自炊(?)PDFが時々同じようにスクロールが重たいという経験を経て,どうすれば良いかが自分なりに少し分かってきたので,それを試してみることにした。

処理対象は,thoriedesnombr02caheuoft_bw.pdf というファイル。サイズは27.2MBほど。このままでも良いが,これを cahen2.pdf として保存。Acrobatで開き,jpegにバラして保存。cahen2_Page_001.jpg から cahen2_Page_762.jpg までの762個のjpegファイルが出来る。これらはモノクロの画像なのだが,サイズは500KBないし900KBもあり,かなり大きい。762個で520MBくらいになっている。グレースケールなのかもだが,拡大した様子では,文字部分は2値のようにも感じる。ともかく,これが27MBに圧縮されていたのだから,すごい圧縮率ではある。

さて,これを ImageMagick で白黒2値のtiffに変換して,CCITT Group4の圧縮をかけることにする。ついでに,広すぎる余白を少し削ってみた。iPhoneで見るときは余白ないほうが見やすいから。

ImageMagickのconvertでは多数のファイルを処理させると,ウソみたいに大量のメモリーを消費してしまうので,今回は mogrify というコマンドを使ってみた。convertとほとんど同じ処理が出来る。

mogrify -shave 100x200 -threshold 80% -compress Group4 -format tiff cahen*.jpg
mogrify -format pdf cahen*.tiff
pdftk cahen*.pdf cat output cahen2mono.pdf

作業内容は次の通り。mogrify コマンドで,まず, -shave 100×200 として,左右100ピクセル,上下200ピクセルずつを削除。次に,-threshold 80% で2値化。さらに, -compress Group4 でFAX G4での圧縮,最後に -format tiff でファイル・フォーマットをTIFFにする。これらを cahen*.jpg すべてに適用する。次に,生成されたtiffファイルをpdfに変換し,pdftkによって,これらのpdfを結合して,cahen2mono.pdf にする。最終的なファイルサイズは,25MBくらいになり,Macのプレビューでも,iPhone4Sの i文庫S でも,スムーズにスクロールして,すこぶる快適になった。

最初のページは次のような感じ。

Cahen "Theorie des Nombres" tome 2

iPhone 4Sでのスクリーンショットは大体こんな感じ。

Cahen "Theorie des nombres" on iPhone4S

iPhoneではさすがに文字が小さく,老眼にはつらいのだが,メガネ外すと,それでも何とか読める。何よりも,750ページもあるのに25MBに収まり,動作も軽くなったので,大満足である。

スキャンした書籍の後処理など

自炊した(業者さんに依頼しているから厳密には違うかもだが)書籍のPDFの後処理についてのメモ。

数学関係の本は,ほとんどが白黒なので,最終的にはモノクロ2値にすることにした。PDFをページ毎の画像にばらして,処理したあと,最終的にFAX Group4圧縮でモノクロ2値のTiffにする。こうすると,1頁あたり50KBないしは100KB程度に収まる。

PDFをベージ毎の画像にばらすには,Acrobatあるいは,Xpdf付属のpdfimagesを使う。ただし,pdfimagesはjpeg指定なのにppmを吐き出すことがあって,信頼性にやや欠ける。でも,シェルから呼び出せるので,バッチ処理には便利。

画像の変換には,最初GIMPを使っていた。GIMPをシェルから使うために,Script-Fuというスキーム(Lispの方言)によるスクリプトをいくつか書いた。それはそれで良かったのだが,スピードがいまいち遅いので,ImageMagickに鞍替えした。ImageMagickはびっくりするくらい多機能(たとえば,傾き補正を自動でやってくれる)で,自炊本の後処理くらいなら,これで十分かなと思う。

具体的にはImageMagickのconvertというコマンドを用いて,赤チャンネル抽出(-channel Red -separate), 2倍拡大(-filter Lanczos -resize 200%), レベル補正(たとえば -level 20%,90%,0.6 とか), 傾き補正(-deskew 40%), モノクロ2値(たとえば -threshold 70% とか), 圧縮(-compress Group 4), TIFFで保存(出力先を hoge.tif などと), といった処理を行う。

これをすべてのページに施すのだが,Perlを使って一括処理することを試みた。実のところ,Perlの勉強も兼ねているという泥縄。まあしかし,入門書は本棚にあるし,ネットで適当に調べながら,Getopt::Long という便利なモジュールを利用して,なんとか動作するものが書けた。

しばらく実験していると,プログラムの設計に柔軟性がないことに気付く。もう少し汎用的なツールになっていると良いのにと思う。あるいは,Cコンパイラーのmakeみたいな構造にしても良いなあ,とも思う。このあたりは,思案中。

ともあれ,現時点では次のような感じで,一括変換を行なっている。

% pdf2mono hoge.pdf -level 40 240 0.7 -threshold 180

MBAにHomebrewをインストール

MacBook Airの設定の続き。Asymptoteをビルドする準備として GNU readline をインストールしようとしたが、エラーで挫折。Snow Leopard では、すんなり make 通ったんだけどな。仕方ないので、MacPorts で入れようかとも思ったが、せっかくの機会だから、最近評判の Homebrew を導入することにした。導入は簡単。サイトに書いてあるように、ターミナルに次のコマンドをコピペするだけ。

/usr/bin/ruby -e "$(curl -fsSL https://raw.github.com/gist/323731)"

Homebrew でのパッケージのインストールは、MacPorts に似ている。

brew install readline

とすれば、GNU readline がインストールされる。リンクするかが悩ましい。

brew link readline

とすると、既存のライブラリーを置き換えるのかな? ちょっと、このあたり、これから勉強しないと。それから、ライブラリーの場所も覚えておかないと。以下、インストールに際して表示された内容。

This formula is keg-only, so it was not symlinked into /usr/local.

OS X provides the BSD libedit library, which shadows libreadline.
In order to prevent conflicts when programs look for libreadline we are
defaulting this GNU Readline installation to keg-only.

Generally there are no consequences of this for you.
If you build your own software and it requires this formula, you'll need
to add its lib & include paths to your build variables:

  LDFLAGS: -L/usr/local/Cellar/readline/6.2.1/lib
  CPPFLAGS: -I/usr/local/Cellar/readline/6.2.1/include
==> Summary
/usr/local/Cellar/readline/6.2.1: 32 files, 1.9M, built in 18 seconds

MathJaxを試してみる

MathJaxが良くなっているらしいということで、ちょっこし試してみた。mimetexと同じく、LaTeXで表記すればよい。
\$\sqrt{2}\$ と書けば、$\sqrt{2}$となるし、
\$p=x^2+y^2 \Longleftrightarrow p\equiv 1 \pmod{4}\$ と書けば、
$p=x^2+y^2 \Longleftrightarrow p\equiv 1 \pmod{4}$

ディスプレイ数式も、この通り。
\[ \Gamma(s)=\int_{0}^{\infty} e^{-x}x^{s-1}\,dx \]

数式の画像をリアルタイムで作っているようなので、表示されるまで、ややもたつきが見られるが、mimetexと比べても、格段に美しい!LaTeXRenderに匹敵すると思う。これを javascript で実現しているとは驚きであるなあ。

ということで、これからはMathJaxで数式を書こうと思う。以下、簡単に導入メモ。

MathJaxのサイトから一式をDLして解凍したのち、自分のサイトにアップ。ファイルの個数が多いのでUploadには時間が掛かる。サーバー上で解凍できるならその方がベターかなあ。次に、MathJaxをWordPressで使う為のプラグイン、MathJax-LaTeXをダウンロードする。これをWordPressのpluginフォルダーにアップしてプラグインを有効化する。

プラグインの設定では、MathJaxをアップしたディレクトリーを指定する。あとはそのままでも良いのだが、ワタシの場合、\$…\$ でインライン数式を書きたいので、少し変更。default.js というファイルを開き、tex2jax の部分で、コメントアウトされているところを外す。何故か、[‘\$’,’\$’] がコメントアウトされているので、そのままではダラーで囲んだところが組版されないのだ。個人的な不満はここだけだった。ここを修正して default.js を選べば、設定は完了。