TeXstudioからTeXLive2016を使う(Mac版)

TeXstudioはTeX用の統合エディター。MacではTeXShopが定番ではあるが、code foldingができないのが不満。TeXMakerとそれから派生したTeXstudioでは、latexの\begin...\end環境とセクションなどのcode foldingが出来るらしい。TeXMakerもインストールしたのだが、TeXstudioの方が、さらに高機能らしいので、そちらも導入した。

設定は簡単。とりあえず、latexとdvipfmxへのパスを通せばよい。日本語LaTeXを使うために、コマンドの部分を修正する。LaTeXのところは /usr/texbin/platex -src -interaction=nonstopmode %.texとし、DviPdfのところを /usr/texbin/dvipdfmx %.dvi とする。latexのオプションは修正の余地があるが、とりあえず、元のままにして、プログラムだけをlatexからplatexへと変更した。あと、エディター部分でフォントを適当に。Source Code Pro にしたが、ここはお好みで。

ここで問題が。PDFをプレビューするのだが、内部ビューアーだと日本語フォントが表示されないのだ。フォントを埋め込んでいてもダメ。検索したところ、poppler-dataというのをインストールする必要があるらしい。

実は、Homebrewでpopplerは入れてあった。後は、TeXstudioがそれを認識できるようにすればよい。TeXstudioは /usr/local/share/poppler を読みに行くらしいので、

ln -s /usr/local/Cellar/poppler/0.16.7/share/poppler /usr/local/share/poppler

とシンボリック・リンクを張ってみた。これで、日本語も表示されるようになった。

実は、内部ビューアーを使わずに Skim を外部ビューアーで使う手もあって、こちらも快適。Sublime Textなどは純粋なエディターで内部ビューアーを持っていないから、Skimを使うことになる。

Mavericksにしたら

Mavericksにしたから,というより,Xcode, Homebrew などの開発環境が変わってしまったから,という方が正確なのだが,ともかく,自作のプログラムが動作しなくなった。ちなみに,Homebrewで入れたOpenCVなどを利用してC++で書いた自家用の画像処理プログラム。自炊した書籍を600dpiのカラーJPEGから1200dpiのモノクロTIFFに変換するのに用いている。

まず,そのままターミナルから起動させようとすると,
dyld: Library not loaded: /usr/local/lib/libopencv_core.2.4.dylib というエラー。
よくよく考えると,単にライブラーを探せないという話なので,慌てずにシンボリックリンクを貼り直せが良かったのかもしれない。しかし,開発環境一式をアップデートすることしか頭になかった。結局,何時かはアップデートすることになるので,悪いわけではなかったのだが。

homebrewでbrew doctorとすると,あれこれ古くなっているらしい。そこで,開発環境をアップデートすることにした。MacApp StoreからXcodeの最新版をDLしてインストール。Homebrewもアップデート。そして,OpenCVをhomebrewで入れ直すことに。すると,numpyがないのでインストール出来ないと。そこで,numpyを入れて,brew reinstall opencv として再インストール。

さて,自家用プログラムをビルドしてみる。pkg-configがないよ,とエラー。入れる。Imagemagickがないよ。ん?そんなバカな。あと,boost関係も何か変。

ということで,pkg-config, imagimagick, boost を再インストール。
これで,とりあえずビルドは成功なのだが,実行させるとランタイムエラーが発生。
dyld: Library not loaded: /usr/local/lib/libjpeg.8.dylib

思いつく解決策は2つ。その1。コンパイル時にライブラリーの場所を指定しておく。多分デフォルトと違うのだろう。その2。シンボリックリンクを貼る。どっちにするか,悩ましい。
/usr/local/Cellar/ を探すとあったので,とりあえずリンクを張る。

ln -s /usr/local/Cellar/jpeg/8d/lib/libjpeg.8.dylib /usr/local/lib/

さあ,これで大丈夫かなと思ったら,別のランタイムエラー。libc++abi.dylib: Magick: no encode delegate for this image format `TIFF'

brew reinstall libtiff として,libtiffを再インストールし,brew reinstall imagemagick –with-lib-tiff として,imagemagickもオプション付きで再インストール。これで TIFF もサポートされるはず。

これで何とか動作するようになった。ああ疲れた。

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);
    }    
  }
}

HomebrewのImageMagickをupgradeしたのだが

久しぶりに ImageMagick をターミナルから使おうとしたら,

dyld: Library not loaded: /usr/local/lib/libtiff.3.dylib

というメッセージが出て,起動しなかった。いろいろ入れているうちに,シンボリック・リンクが壊れているのかも。というか,そもそも ImageMagick をどれで入れたのか記憶がない。Homebrewだった気もするが,自信なし。ということで,brew upgrate imagimagick として最新のにした。そのときのコンソール出力が以下。

rdguru:~ aqua$ brew upgrade imagemagick
==> Upgrading 1 outdated package, with result:
imagemagick 6.8.6-3
==> Upgrading imagemagick
==> Installing imagemagick dependency: libtool
==> Downloading http://ftpmirror.gnu.org/libtool/libtool-2.4.2.tar.gz
######################################################################## 100.0%
==> ./configure --prefix=/usr/local/Cellar/libtool/2.4.2 --program-prefix=g --enable-ltdl-install
==> make install
==> Caveats
In order to prevent conflicts with Apple's own libtool we have prepended a "g"
so, you have instead: glibtool and glibtoolize.

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

Xcode 4.2 and below provide glibtool.

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 to your
build variables:

    LDFLAGS:  -L/usr/local/opt/libtool/lib
    CPPFLAGS: -I/usr/local/opt/libtool/include

==> Summary
 /usr/local/Cellar/libtool/2.4.2: 66 files, 2.2M, built in 22 seconds
==> Installing imagemagick
==> Downloading http://downloads.sf.net/project/machomebrew/mirror/ImageMagick-6.8.6-3.tar.bz2
######################################################################## 100.0%
==> ./configure --disable-osx-universal-binary --without-perl --prefix=/usr/local/Cellar/imagemagick/6.8.6-3 --enable-sh
==> make install
  /usr/local/Cellar/imagemagick/6.8.6-3: 1435 files, 21M, built in 2.2 minutes

よく分からないのだが,Apple標準のlibtoolと衝突してるから,インストールはしたけど,/usr/local にシンボリック・リンクを作ってないよ,ということだろうか。