pythonで画像の陰の除去

## はじめに 
テスト投稿も兼ねて最近調べて試したことのメモを載せます。
問題などあれば指摘していただけるとありがたいです。

## 目的：画像中の陰（shade）を除去
画像処理ド素人がスマホで撮影したノート等の写真をきれいにするアプリ的な動きをするものを自分でも作ってみたいと思い立ちました。
まずは画像中にどうしても入ってしまう陰が邪魔なので、これを取り除きます。
影と書くとsilhouetteの意味合いが強くなるそうなので、shadeの意味を込めて陰と表記します。

## 処理
https://www.jstage.jst.go.jp/article/iieej/36/3/36_3_204/_article/-char/ja/
こちらの論文を参考に、「凹凸係数」を利用して影を除去します。
凹凸係数は画像中のそれぞれの画素を周囲の画素の平均値で除算することで求められ、フィルタサイズより大きい勾配を平坦にすることができるもののようです。
なので今回「陰の除去」と書きましたが、カラー画像だと陰だけでなく色も飛んでしまうことが考えられます。

## 環境
OS：Windows8.1
言語：Python3.6

## 実装
```python:outotsu.py
import cv2
import numpy as np

img = cv2.imread("image.jpg")
ksize = 51
blur = cv2.blur(img, (ksize, ksize))
rij = img/blur
index_1 = np.where(rij >= 0.98)
index_0 = np.where(rij < 0.98)
rij[index_0] = 0
rij[index_1] = 1
cv2.imwrite("rij_image.png", rij*255) # rijの値は0～1になるはずなので255倍
```
論文に書かれている通りに実装します。
※ksize=51は特に根拠なく適当な数字なので、あとで変えて比較します。
※rijの値は0～1になるはずとしていますが、単純に除算しているだけなので実際は1以上の値になることがあります。

## 試行
この2枚の画像についてそれぞれ上記の処理にかけてみます。
風景写真の方は今回の目的には必要ありませんが、色も陰扱いで消えるだろうということで試してみます。
<img src="https://qiita-image-store.s3.amazonaws.com/0/343311/a50642b2-a733-7268-8205-8a2734678f10.jpeg" width=48%> <img src="https://qiita-image-store.s3.amazonaws.com/0/343311/63b1f299-cbb9-fadc-941a-e6c38e242949.jpeg" width=48%>
どちらも手持ちのiPhone7で撮影したもので、画像サイズは4032*3024（3024*4032）です。
上記をそのまま実行すると以下のような状態になります。
<img src="https://qiita-image-store.s3.amazonaws.com/0/343311/0cd3b462-d42d-756f-0144-d1a1213c48dd.png" width=48%> <img src="https://qiita-image-store.s3.amazonaws.com/0/343311/5b204b4b-50d9-6913-ed11-b74ba1810cbe.png" width=48%>

二値化なしだとこうなります。
<img src="https://qiita-image-store.s3.amazonaws.com/0/343311/4ddfaea8-7ebd-85f8-9f02-52d8df9175ab.png" width=48%> <img src="https://qiita-image-store.s3.amazonaws.com/0/343311/40529dba-3b2a-7344-d670-2f661c8550c4.png" width=48%>

パッと見綺麗なので、二値化をちゃんとやればもっと良い感じになるんじゃないでしょうか。
風景写真の方はやはり空の色がかなり消えています。海の部分は波や砂浜の陰影的な部分が残るようです。

ksizeの値をいろいろ変えて試した結果が以下です。
文字写真の方だけ載せます。順に、ksize=3, 21, 51, 151, 351です。値は適当です。
<img src="https://qiita-image-store.s3.amazonaws.com/0/343311/253154ca-f2f2-8b4b-607e-0438cf2c1942.png" width=18%> <img src="https://qiita-image-store.s3.amazonaws.com/0/343311/0e203239-9e19-12d1-93a1-a2ef8a786a45.png" width=18%> <img src="https://qiita-image-store.s3.amazonaws.com/0/343311/4ddfaea8-7ebd-85f8-9f02-52d8df9175ab.png" width=18%> <img src="https://qiita-image-store.s3.amazonaws.com/0/343311/4269ba03-c49c-a271-40a5-343d6c3cacad.png" width=18%> <img src="https://qiita-image-store.s3.amazonaws.com/0/343311/f3999349-6756-5ed9-3351-23a571f0bc96.png" width=18%>
だんだん色が濃くなってる感じもありますが、21以降はあんまり変わり映えしていません。ここでksize=3の結果の一部を拡大してみると、
![image.png](https://qiita-image-store.s3.amazonaws.com/0/343311/f527cc0c-a85f-7419-dac4-7b08f92cc2ff.png)
このように白抜けになっており、エッジを残して色が消えていることが分かります。ksizeよりも大きい範囲が平坦化され、ksize以下の勾配が残っている、ということで元論文にあった通りです。
よって、ノートを撮影する目的であればksizeはそこそこ大きい数にしておけば安全と考えられます。ksize=21以降の代わり映えのしなさを見るに、画像に合わせてksizeを微調整する必要は特になさそうです。

## 二値化
単純な閾値での二値化は残念なことになってしまいましたが、他の方法での二値化なら？ということで結果画像を大津の方法で二値化してみました。
ksize=51で、手順は以下です。白黒をくっきりさせたいため、HSV画像のVを大津の方法で二値化しています。

```python:outotsu2.py
rij = img/blur
index_1 = np.where(rij >= 1.00) # 1以上の値があると邪魔なため
rij[index_1] = 1
rij_int = np.array(rij*255, np.uint8) # 除算結果が実数値になるため整数に変換
rij_HSV = cv2.cvtColor(rij_int, cv2.COLOR_BGR2HSV)
ret, thresh = cv2.threshold(rij_HSV[:,:,2], 0, 255, cv2.THRESH_OTSU)
rij_HSV[:,:,2] = thresh
rij_ret = cv2.cvtColor(rij_HSV, cv2.COLOR_HSV2BGR)
cv2.imwrite("result.png", rij_ret)
```
結果は以下左。右は比較用に初めの結果を載せています。
<img src="https://qiita-image-store.s3.amazonaws.com/0/343311/8002bafa-4c9d-f6b6-b2d4-639c20ed9597.png" width=48%> <img src="https://qiita-image-store.s3.amazonaws.com/0/343311/0cd3b462-d42d-756f-0144-d1a1213c48dd.png" width=48%>
だいぶ見やすくなったのではないでしょうか。少なくともページの変わり目の無駄な黒線や、何もない場所の色ムラなどは消えています。

## 感想
陰を除去するだけでも結構調べることが多くて大変でした。
そして手元のメモではなく記事に結果をまとめるのも大変でした。
もし実際アプリケーションを作るならあとは少なくとも色の鮮明化処理、必要な部分の切り出し処理も必要ということで（あとはUI等も…）、こんな大変なアプリが無料で使える世の中ってすごいなあと感じました。