にせねこメモ

はてなダイアリーがUTF-8じゃないので移ってきました。

マーカーを利用した傾き補正

印刷した紙に書き込み、それをスキャンすると、傾き等の歪みが発生する。この傾き等を補正するのを自動化したい。
今回は、そのために傾き補正用のマーカーを紙の四隅に事前に印刷しておき、スキャン画像に含まれるマーカーを元に傾き補正を行う。

次のような手順による。

  1. 与えられた画像内でマーカーを検出する
  2. マーカーの重心の座標を調べる
  3. 4点の座標と印刷位置から射影変換の変換行列を求め、変換する

プログラムは Python 3.5 から OpenCV 3.1.0 を利用した。コードは最後に。

0. 準備

マーカの用意と紙への印刷

マーカ

f:id:nixeneko:20160120203431p:plain
今回は図の様な同心円状のマーカを利用することにした。これは回転によっても形が変わらず、回転に弱い様なパターンマッチアルゴリズムを用いても上手く検出されるようにとの意図である。マーカは上図右の様に縞の幅が全て同じになるようにした。

マーカの印刷

次の図の様な寸法でA4用紙の4角にマーカを印刷する。余白として上下左右6mmずつ距離をとっている。
f:id:nixeneko:20160121061808j:plain:w300
清書する気力が無いので手書きで…。かなりデフォルメしてあるので判りづらい。

1. パターンマッチ

今回はテンプレートマッチングを利用した。

テンプレートマッチングの弱点は回転や拡大縮小に弱いことである。回転についてはマーカーの形は変わらないので問題ない。拡大縮小については、印刷時のサイズを決め打ちしてあるため、入力ファイルの縦横のサイズからおおよそのマーカーサイズを計算し、それによってテンプレートマッチングを行った。

マッチした部分は今回は4点検出すればよいので、4つの最大値(の位置)をとることによってマーカーを検出している。マッチしたところを複数検出するのには numpy.where() を使うという方法もある。

スキャンしたデータが紙よりクロップされていたり、余白が付け足されていたりする場合にどれ位正確になるのかは調べてない。もしかしたら SIFT や ORB (ORB使ってみた - Puku's Laboratory) などの特徴点マッチングや顔認識等で使われるカスケード分類器(OpenCVで物体検出器を作成② Haar-Like特徴【開発会社プロフェッサ】)を作成して検出した方がいいのかもしれない(どうやって使うのかよくわかってない)。これらは拡大縮小及び回転に強い。

2. マーカーの重心の座標を調べる

1. で求めた座標の周囲にROI(Region of Interest)を設定する。ROI内部のマーカーの黒い部分の重心を求める。
cv2.connectedComponentsWithStats を使うとラベリングのついでにラベルの面積と重心の座標がとれるので、得られた各ラベルの重心を面積で重み付けして平均をとって、それをマーカーの座標して用いる。

3. 射影変換

台形補正もできるようにと射影変換ということにしているが、スキャナによって取り込まれたデータを前提としているのでアフィン変換で十分かもしれない。

4点を並べ替え

4点が順番に並んでるか分からないので並べ替えることにした。縦長長方形の左上の点が最初にきて、それから時計回りに長方形の点が格納される順である。

まず4点を時計回りに並べ替える。そして、長方形の短辺2つ(これは4点中の任意の2点の組合せのうちで距離が最小か2番目に小さくなるものとした)のうち、中点のy座標が小さい方を縦長長方形の上辺とみなし、その上辺が最初に来るように並べ替えた

変換行列の取得・実行

変換行列は
cv2.getPerspectiveTransform(src, dst)
で取得できる。
これを使って、
cv2.warpPerspective()
で変換する。

実行結果

マーカーを変な形に配置してみた。

入力画像 出力
f:id:nixeneko:20160121151301j:plain:w200 f:id:nixeneko:20160121151312j:plain:w200

よさそう!

所感

入力画像が適切に調整されていれば適切な結果が得られそうである。
マーカーの位置取得について、周囲の範囲の黒い成分の重心を取っているので、マーカーの周りが汚れているとマーカー位置が動いてしまう可能性がある。

コード

GitHubに上げた。
(2016-02-04 追記 手書きフォント自動生成ソフトの一部になりました)
github.com