キャリブレーションパターンの管理と生成

# はじめに
 - この記事は[OpenCV Advent Calendar 2017](https://qiita.com/advent-calendar/2017/opencv) 8日目の記事です。関連記事は[目次](https://qiita.com/advent-calendar/2017/opencv)にまとめられています。

# キャリブレーションパターンの作り方
 - キャリブレーションパターンを作る[サイト](https://mementoo.info/archives/598)、[スクリプト](https://qiita.com/yu4u/items/b44108d9b587b5a52211)、[手順](http://dominoc925.blogspot.jp/2012/06/create-camera-calibration-chess-board.html)など、多数公開されており、その意味ではN番煎じも甚だしい記事です。
 - ただし、通常と違うのは、今回は「**バージョン管理も念頭に入れ**」て、「**ベクタ形式で生成する**」ことです

# 後日談というか、今回のオチ
 - ビルドするとチェッカーパターンのパワーポイント(pptx)を生成する[プログラムを公開しました](https://github.com/tomoaki0705/calibrationPatternGenerator)
 - ここでパターンの数とサイズを指定して
![cmake.png](https://qiita-image-store.s3.amazonaws.com/0/12162/19b66cae-dc0f-143f-addf-1ba034888eb5.png)

 - こうなって
![vs.png](https://qiita-image-store.s3.amazonaws.com/0/12162/80ab05bf-e503-3c80-637e-f6325b66135f.png)

 - こう
![ppt.png](https://qiita-image-store.s3.amazonaws.com/0/12162/5710304d-db97-4d68-fa73-58abf9f2d815.png)

 - という具合に**ビルドするだけでパワーポイント用のバイナリファイル**が作られます
 - あとはこれを、「**倍率指定100%**」で印刷する（**用紙サイズ指定ではありません**）と、誤差1mm以下の精度で印刷してくれます[^1]
 - プリンタで印刷するとサイズが変わってしまう報告がありますが、私が試した限りでは、「用紙指定」ではなく「倍率指定」なのがキモ、という認識です。
[^1]: 最近のプリンタはすごいですね。

# 中身の詳細
 - 大まかに説明すれば
   - xmlファイルを複数含むディレクトリをプログラムで生成
   - ディレクトリを単一zipファイルに圧縮
   - zipファイルをpptxにリネーム
 - をしているだけです。
 - Office は拡張子の末尾に**x**がつくようになってから、ファイルの実態は複数のxmlファイルを単一のファイルにZIP形式で圧縮したもの、となりました
 - 今回はCMakeを使ってpptxファイルを生成しています

# 円形キャリブレーションパターンの方が精度良いんじゃないの？
 - はい、[随所で円形パターンの方が精度が良いことが報告](https://qiita.com/gou_koutaki/items/a0b232227d655248f22d)されています
 - なので、cmakeを使ってる理由はそこで、cmakeのコンフィグ時に、パターンの個数や長さの他に種類も変更できるようにしてあります
![explanationGenerator.png](https://qiita-image-store.s3.amazonaws.com/0/12162/aa4c9962-04ac-ea79-e73a-e333b53309fc.png)

 - 図中にも示しましたが、```MARKER_TYPES```に```marker_Chessboard```、```marker_CircleSymmetrical```と```markerCircleAsymmetrical```の3種類があり、それぞれ
   - チェッカーボード
   - 円形パターン(格子)
   - 円形パターン(互い違いに配置)
 - となっております
 - この通り
![circleSymmetricalCapture.png](https://qiita-image-store.s3.amazonaws.com/0/12162/ed9e6ba6-6fa7-7481-1986-e9e1ed2d45c0.png)

 - 色んなパターンも生成できます
![circleAsymmetricalCapture.png](https://qiita-image-store.s3.amazonaws.com/0/12162/9ce9d848-d041-f15d-9012-828cfc27197c.png)

# 印刷がきれいになるだけなの？
 - 最大の特徴が、キャリブレーションパターンの情報がヘッダファイルに生成される点です
 - このプロジェクトをcmakeでコンフィグすると、buildディレクトリ直下にconfig.hというファイルが生成されます

```cpp:config.h
#ifndef CONFIGURE_H_INCLUDED
#define CONFIGURE_H_INCLUDED

/* number of markers in column */
const int cNumMarkerCols = 4;

/* number of markers in row */
const int cNumMarkerRows = 9;

/* gap between markers in cm (= size of each box of chessboard) */
const float cMarkerGap = 1.0f; // in cm

/* diameter of the circle in cm (not used for chessboard) */
const float cSizeCircle = 0.5f; // in cm

/* offset from the edge in cm */
const float cOffset = 1.0f; // in cm

/* name of powerpoint slide file name */
#define DEFAULT_XML_FILENAME "slide1.xml"

/* name of thumbnail file */
#define THUMBNAIL_FILENAME "thumbnail.jpeg"

enum markerType
{
    marker_Chessboard,
    marker_CircleSymmetrical,
    marker_CircleAsymmetrical,
};

const enum markerType cMarkerType = marker_Chessboard;

#endif // CONFIGURE_H_INCLUDED
```
 - で、この中に先程のパターン数、パターンの種類、長さ、全ての情報が埋め込まれているわけです。
   - cmakeのコマンド、[```configure_file```](https://cmake.org/cmake/help/v3.0/command/configure_file.html)があり、これよってファイルの中に埋め込んだ変数がcmakeで得られた実際の数値/文字列に置き換えられます
   - OpenCVの```HAVE_FFMPEG```とか```WITH_OPENCL```を持つ、```cvconfig.h```と同じ方法です 
 - 何が良いかというと、これのおかげでソースコードとキャリブレーションパターンが一意にバージョン管理することができるのが最大のウリです。
 - キャリブレーションパターンとソースコード、別々に管理してたりしませんか？
   - そもそもgit、ひいてはGithubはあまりバイナリの管理には向いていません
   - 圧縮して全履歴を保存することが理由ですが、バイナリには圧縮がほとんどきかないため、Gitのリポジトリに入れると、容易にサイズが肥大化します
   - そのような理由から[Githubでも1コミットのサイズに制約がついています](https://help.github.com/articles/working-with-large-files/)

> You will not be allowed to push files larger than 100 MB.


- もちろんそれでもパワーポイントのpptxファイル、特にキャリブレーションパターン程度ならばkbオーダなので、無視してもいいぐらいのサイズです。
- しかしながら、あとから実験の記録を見返したときに、「あれ？このキャリブレーションパターン**いつ使ったんだっけ？**」とか、「この写真中のボード、**間隔何cmだったかな？**」とか、悩んだりしないでいいわけです！
- その時点の実験を再現するならば、ビルドするだけで、全く同じキャリブレーションパターンを生成できるからです！**すごい！**

# このプログラムキャリブレーションパターン生成するだけ？
 - キャリブレーションパターンを生成してそれでおしまいということはまず無いので、このソースコードを使いたい人は、何らかキャリブレーション用ソースコードとともに、このCMakeLists.txtを管理したいと考えていると思います
 - ここでは、その応用した使い方を紹介したいと思います
   - 直接埋め込む方法
   - execute_process でcmake時点でビルドする方法

## 直接埋め込む方法
 - 詳細は割愛します。公開したプロジェクトをforkなりダウンロードなりして改造してくれれば良いと思います

## execute_process を使う方法
 - CMakeには[execute_processという強力な機能](https://cmake.org/cmake/help/v3.0/command/execute_process.html)があります。
 - cmakeするときに、裏側でソースのダウンロード、cmakeによるconfiguration、ビルド、実行までcmakeの裏側で行えます
 - パターンを再生成するためには、cmakeからやり直す必要があります
    - が現実問題パターンの生成をやり直すことはそうそうないのではないでしょうか。
    - 以下のコードをご自身のCMakeLists.txtに追加するだけで、裏側で勝手にpptxが生成さる、手軽さがウリです。
    - ```pptx```は```build\third-party\build\generated.pptx```に生成されます。
    - 下記の例では```${TARGET_NAME}```ターゲットを```main.cpp```から生成して、その際に```calibrationPatternGenerator```が生成する```config.h```をインクルードしています

```cmake:CMakeLists.txt
set(PATTERN_GENERATOR_WORK  "${CMAKE_BINARY_DIR}/third-party")
set(PATTERN_GENERATOR_BUILD "${PATTERN_GENERATOR_WORK}/build")
set(DOWNLOADED_ZIP          "${CMAKE_BINARY_DIR}/patternGenerator.zip")
file(MAKE_DIRECTORY ${PATTERN_GENERATOR_WORK} ${PATTERN_GENERATOR_BUILD})
file(DOWNLOAD "https://github.com/tomoaki0705/calibrationPatternGenerator/archive/v1.0.zip" ${DOWNLOADED_ZIP})
execute_process(COMMAND ${CMAKE_COMMAND} -E tar xz ${DOWNLOADED_ZIP} WORKING_DIRECTORY ${PATTERN_GENERATOR_WORK})
execute_process(COMMAND ${CMAKE_COMMAND} -G${CMAKE_GENERATOR} "../calibrationPatternGenerator-1.0/" WORKING_DIRECTORY ${PATTERN_GENERATOR_BUILD})
execute_process(COMMAND ${CMAKE_COMMAND} --build . --target ALL_BUILD --config Release WORKING_DIRECTORY ${PATTERN_GENERATOR_BUILD})

# executable
add_executable(${TARGET_NAME} main.cpp ${PATTERN_GENERATOR_BUILD}/config.h)

```

# 実際にやってみた
- チェスボード
    - ![captureChessboard.png](https://qiita-image-store.s3.amazonaws.com/0/12162/f9fdb95e-d52a-74bb-9f1b-31e86cf40d70.png)
    - ![resultChessboard.png](https://qiita-image-store.s3.amazonaws.com/0/12162/a22a7794-2068-9dcf-a9ef-702d5740aaeb.png)
- 円形パターン(格子)
    - ![captureSymmetricalCircle.png](https://qiita-image-store.s3.amazonaws.com/0/12162/29c5bb4f-746e-4bf9-2222-47ff22df5c66.png)
    - ![resultSymmetricalCircle.png](https://qiita-image-store.s3.amazonaws.com/0/12162/887cd686-3a80-fc38-4647-d8eab49ea109.png)
- 円形パターン(互い違いに配置)
    - ![captureAsymmetricalCircle.png](https://qiita-image-store.s3.amazonaws.com/0/12162/3f7a22e6-444b-9f62-0e25-c0420949351b.png)
    - ![resultAsymmetricalCircle.png](https://qiita-image-store.s3.amazonaws.com/0/12162/5c53026e-0d88-9b9f-edb4-231887ed26b5.png)

  
# 苦労した点
## 日本語に苦労(xml作成編)
 - もちろん最初は手動でpptxファイルを作り、それと同じコンテンツをプログラムから出力しようとしました
 - が、一向にエラーで開けない[^2]
 - 最終的にはバイナリエディタで比較したところ、「楕円1」という文字列がもとのpptxにあり、それをダイレクトに出力すると文字コードの関係でファイルのエンコードが破壊されていました
 - なので、日本語を諦めて「ellipse 1」もしくは「box 1」と修正

[^2]: 誤ったフォーマットのpptxファイルをパワーポイントで開こうとすると、「ファイルが壊れています」というダイアログが表示され、無情にも真っ白なファイルが。。。

## 圧縮プログラムに苦労
 - 実はcmakeには、[標準で複数ファイルを圧縮する機能がついています](https://cmake.org/cmake/help/v3.3/manual/cmake.1.html#command-line-tool-mode)
 - ```cmake -E tar cf hoge.zip  file1.xml file2.xml``` とコマンドを叩くと、cmakeが圧縮ファイルを作ってくれます
 - が、これも何故かpptxとしては開けない。バイナリエディタで比較すると、どうにもファイルフォーマットがzipでない模様。
 - で、コマンドが```tar```となってるので、ファイル名はzipでも、tar圧縮されたファイルが生成されていた、というオチ
 - 仕方ないので7zipの実行バイナリをcmake時にダウンロードして来て、裏で実行するスクリプトに改造したのですが、そのあとで、[cmakeの3.3からzipフォーマットでの出力がサポートされた](https://stackoverflow.com/questions/7050997/zip-files-using-cmake)ことを発見[^3]
 - ```cmake -E tar cf hoge.zip --format=zip file1.xml file2.xml``` と、formatオプションを渡すと、ちゃんとZIPファイルとして作ってくれる。

[^3]: [リンク先](https://stackoverflow.com/questions/7050997/zip-files-using-cmake)には「3.2から」と書かれているが、Windows版のバイナリで検証した結果、[多分3.2では非対応](https://cmake.org/cmake/help/v3.2/manual/cmake.1.html#command-line-tool-mode)で、[3.3から対応が盛り込まれた](https://cmake.org/cmake/help/v3.3/manual/cmake.1.html#command-line-tool-mode)
  
# まとめ
 - 筆者は以下の環境で検証しました

|項目|環境1|環境2|環境3|
|------------------:|:-----------------|:--|:--|
| OS| Windows 7 Professional 64bit|Windows 10 Pro 64bit|Ubuntu 14.04 32bit|
| Office| 2016|2016|2016[^5]|
| OpenCV| 3.3.0|3.2.0|3.1.0|
| コンパイラ| Visual Studio 2013 Update 5|Visual Studio 2013 Update 5|gcc 4.8.4|
| CMake| 3.9.1|3.10.0|3.5.1|
| CUDA| 8.0|8.0|-|

 - 明日は[TaroYamada](https://qiita.com/TaroYamada)さんの記事で、執筆時点でのタイトルは「仮) OpenCV と Intel Movidius Neural Compute Stick のSDKを連携させた。」です。楽しみ！


[^5]: Linuxで生成した後にWindowsに```pptx```ファイルだけ転送。この表には記載しなかったが、OpenOfficeでも開けることだけは確認した。印刷は試してないので不明。


# 補足
## pptxだけしかダメなの？docxは？
 - 検証していませんが、docxも中身はzipファイルです。本プログラムを改造して、docxを出力するようにすれば申請書のフォーマットに則ってdocxファイルを出力したり、ネ申エクセルのフォーマットに則ったxlsxファイルを出力することも原理的には可能のはずです。うまくいけば、**texファイルからdocxを生成できる**夢もあるかも知れません(が、恐ろしく高い障壁が立っている気もします)

## OpenCV関係なくね？CMakeだけじゃね？
 - 非常に鋭いツッコミでありますが、OpenCVも必須であります。
 - ZIPに圧縮するファイル群の中に、一つだけ、サムネイル画像として**thumbnail.jpegが必須です**。この[thumbnail.jpeg](https://github.com/tomoaki0705/calibrationPatternGenerator/blob/master/CMakeLists.txt#L21)も、xmlファイルを吐き出すときに[合わせて生成しています](https://github.com/tomoaki0705/calibrationPatternGenerator/blob/master/main.cpp#L39)
 - 裏を返すと、この[jpegを吐き出すためだけ](https://github.com/tomoaki0705/calibrationPatternGenerator/blob/master/main.cpp#L85)にOpenCVを使っています[^4]

[^4]: 検証していませんが、多分2.4系列でも動くのではないかと思います

## XMLを直書きしてるけれど、OpenCVのFileStorageクラスは使わなかったの？
 - OpenCVにはxmlで行列やパラメータを書き出せる```cv::FileStorage```クラスが存在します
 - 実はこれで書き出すことも試したのですが、実は書き出す以前に読み込むことに失敗しました。
 - ですので、どうやらOffice用のxmlとOpenCVがパースできるxmlのフォーマットのどこかに差異があるようです
 - 今回はそこまで検証しておりません、あしからず