はじめに
久しぶりに記事を書いています、NamedPythonです。
卒業が近づき、卒業研究も大詰めになってまいりました。テーマは「陰影を含む文書画像からの陰影除去」と題して画像処理をちまちまやっていました。
ぶっちゃけほぼ既存だし、目新しいこともしてないので論文書くついでに研究?のまとめとして記事を書きたいと思います。
なんならGitHubで公開しています、ハハハ。もはやこうやって記事のネタにすること、公開することを想定してなんとDocker
にのっけてあります。すぐ試せるね。
プロジェクトのコードネームはdeshade-doc(でしぇーど どっく)
。 sha
をde
で挟むことによる謎のそれっぽさを演出しています。スタバで10分考えました。
愚かにも入力画像をignore
していないですが、見逃してください。
2020/07/11 追記
嬉しいことにStarが一個ついたので、README書いたり、OpenCVのバージョンを固定、不要な画像を削除するなどしました。
jjanzic/docker-python3-opencv というDockerイメージをFROM
に指定しているんですが、今でもメンテされているみたいで、結構感動しました。
OpenCVのバージョンを固定、と書きましたが、
FROM jjanzic/docker-python3-opencv
...
FROM jjanzic/docker-python3-opencv:contrib-opencv-3.4.2
...
こうしただけです。
追記おわり!
きっかけ
授業ノートとか、書類とかを紙でとっておくのは面倒なので、Scanbotというアプリ でスキャンして管理していました。
そこそこ気に入っていて、ある程度のノイズは取り除いてくれていました。しかし、影にはどうも弱いらしく黒つぶれが多発していました。
表面化していた問題はそこではなく、OCRが効かないというところだったので、当初はOCRの精度改善をテーマにしようと考えていましたが、なにせ郵便局あたりが頑張ってしまって研究の余地がない。ないというか、僕の技術では追いつけない。
じゃあOCRかける前段階、二値化とか画像への前処理を研究すればいいんじゃないの、っていうことで陰影の除去をテーマにしました。
一応重複とか類似の研究は調べましたが、ここまでピンポイントなのはなかった気がします。あったらこっそり教えてください。といっても時すでに遅しですが。
環境
めちゃんこわかりやすい。
- MacBook Pro (13-inch, 2016, Four Thunderbolt 3 Ports)
- macOS Mojave 10.14.2
- Docker CE 2.0.0.0-mac81 (29211)
- VSCode, Terminal, ...
あとはこの上でドッカーン。コンテナの中身は
- Debian GNU/Linux 9 (stretch)
- Python 3.7.0
- Pillow 5.3.0
- OpenCV 3.4.2
です。もともと FROM
ですでにあるイメージを引っ張ってきてPillow
を足してるだけなのでシンプル。他の授業でも使うので流用ができている、良きかな。
理論みたいな
そんな大層なものではないですが、考えなしにやっているわけでもないのでそれっぽく。
二値化の手法としてそこそこ有名?な判別分析法
ではうまく二値化できないことを利用して、「陰影を含んでいる文書は判別分析法でうまく二値化できない。問題がある。」といちゃもんをつけて問題を解決する形をとっています。
しかし、判別分析法のいいところ(と、勝手に思っているの)は、曖昧な影の輪郭(領域)を、ヒストグラムをもとにはっきり示してくれるところです。しかしこの判断ができるのは、文書画像のみであることに注意されたい。風景写真ではこんなガバガバな判断基準で陰影の領域は断定できないです。
では、「判別分析法によってはっきりした領域を切り出して、個別に処理してあげればいいではないか」と考えて、手っ取り早くガンマ補正をしました。
一回ではそうはうまくいかないので、ちまちま二値化、切り出し、ガンマ補正、元の位置に貼り付け、二値化、...という風に繰り返す処理にしました。
切り出しの条件はというと、面積です。そこそこ大きい面積の暗い領域を影と決めつけて、切り出しています。
そして、この影が見つからなくなったら繰り返し脱出、有限回で終了、アルゴリズムとしても破綻していない(はず)です。
以下はこれらに結果をつけながらつらつらと。
各ステップと結果
1.
文書を携帯端末で撮影、クロップします。なので入力画像はクロップされた3チャンネル画像です。画像は実際に僕がとったノート。
2.
判別分析法を用いて二値化。 すると、影がある場合はヒストグラム(および割り出される閾値)が偏って影の部分は黒く潰れます。以下は大津の二値化をかけたもの。すごく黒く潰れる。
3.
影の部分を切り出してガンマ補正。γ=1.2
くらい(これらは1.18
)でゆっくり明るくしていく。以下は切り出した影の部分(左)とガンマ補正をかけたあと(右)。
4.
元の位置に貼り付けます。実はMatで貼り付けるのがめんどくさくてImage.paste()
を使うためにここだけPIL
を使っていたり。貼り付けると明るくしたのがわかりやすい。
5.
2の手順に戻って判別分析法で二値化します。以下は2との比較。
あれ、なんか領域広くなってますね。いいんです。これはまだ途中経過。こうやって階層的に明るくする範囲を拡大していくんです。
6.
以降は、切り出し、ガンマ補正、元の位置に貼り付けまで一気にやったものを、前後比較しながら見ていきましょう。
少し段ができましたね。次も見ていきましょう。
7.
プログラム内のループで言えば3ループ目になります。
8.
そうこうして15ループします。このループ数は影の分散具合とγ
をどれくらい細かく(>1.00)するかによります。これが最終結果。元画像と比べてみましょう。
...できてはいるけど汚い。っていうのは心にしまっておいてください。階層的に明るくしていった感じがわかると思います。思いの外、この階層が影の濃度ごとに分かれていて面白い。
9.
最後のものを判別分析法によって二値化します。プログラム的には、ここである一定の面積の黒い領域がなければbreak
となっています。以下は最初の二値化と最後の二値化。
こう見ると、だいぶ綺麗になっています(言い聞かせるように)。 かすれている部分は、僕の筆圧が足りていなかったり過度に照明が当たっていたりする部分です。
まとめと所感
「やってみた感」と思ったあなた、正解です。研究としての体裁を最低限保ったやってみたです。でも研究の動機はそんくらいがいいだろうと思っているので、僕は僕を許します。
ちなみに実行時間はまあまあ現実的。入力画像はリサイズしていたりします。
上の結果は15ステップですが、実行時間は約20[sec]
。time
コマンドをつけて計測したreal
タイム。リアルタイム処理は想定していないのでこんくらいが妥当だと愚考。
もちろん、伸び代はあります。γ
は可変にできますし、現在の処理に加えて明るすぎる領域は明度を下げて白飛び黒つぶれ両方に対応したらもっと綺麗になると思います。
ですが、今回はここまで。あとは論文を書くだけです。
さいごに
読んでいただきありがとうございました。まとめかた、およびアウトプットが下手なので、とりあえず数を打って改善を図ります。
GitHubに上がってはいますが、なんというかバックアップに近いので、README
は需要があれば書きます。
ではまた。