ピンボケ写真の判定方法

# はじめに
画像を用いたディープラーニングを行おうとした場合、大量の画像データが必要となります。そのため、人にお願いして画像を集めたりしますが、集めた画像データの中にはピンボケ写真があったりします。:disappointed_relieved:
**『人に頼んでて文句言うな！』と言われそうですが・・・:sweat_smile:**
正しい学習データを集めるには事前にピンボケ画像を取り除きたいと思ったので、ピンボケ画像の判定方法についてざっと調べてみると、

1. 高速フーリエ変換(FFT:Fast Fourie Transform)​で判定
2. ディープラーニング​で実装
3. OpenCVで実装

の３つがありましたのでそれぞれの特徴を記載していきます。


## 1.高速フーリエ変換(FFT:Fast Fourie Transform)​で判定

ピンボケ写真は画像のエッジ部分が不鮮明なので高周波成分を含みません。

高速フーリエ変換(FFT:Fast Fourie Transform)を活用することで画像のスペクトル分布を求めることができます。

ピンボケの場合はパワースペクトル※が低いので周波数が０になりますが、ピントが合っている場合はパワースペクトルが高周波数側まで伸びます。

※パワースペクトルとは、時間信号 x（t）のパワーを、FFT分析することによりある周波数バンド幅（すなわち周波数分解能 Δ f ）毎のパワーをもとめ、横軸を周波数としてグラフ化しているものです。　
https://www.onosokki.co.jp/HP-WK/c_support/faq/fft_common/fft_spectrum_6.htm

この差を求め、ピントがズレているか？あっているか？という判定を行うことで、カメラのオートフォーカスに応用されています。

http://www.ekouhou.net/%E3%82%AA%E3%83%BC%E3%83%88%E3%83%95%E3%82%A9%E3%83%BC%E3%82%AB%E3%82%B9%E8%A3%85%E7%BD%AE%E5%8F%8A%E3%81%B3%E3%82%AA%E3%83%BC%E3%83%88%E3%83%95%E3%82%A9%E3%83%BC%E3%82%AB%E3%82%B9%E6%96%B9%E6%B3%95/disp-A,2006-301270.html

こちらの特許はオートフォーカス装置について請求されています。ピンボケ写真の判定では請求されていないので、この特許を応用して、与えられた画像に対して、FFTを行い周波数を違いを求めることでピンボケ写真かそうでないかの判定ができるのではないか？と思います。
**※このあたりの特許に詳しい方問題があればご指摘ください。:grinning:**

なお、コンピュータでフーリエ変換を実装する場合、本来であれば離散フーリエ変換 (DFT:Discrete Fourier Transform) を利用しますが、PCでの計算では時間がかかるため高速に計算するアルゴリズムとして、FFTを用いるのが一般的なようです。

高速フーリエ変換の実装方法
http://lang.sist.chukyo-u.ac.jp/classes/OpenCV/py_tutorials/py_imgproc/py_transforms/py_fourier_transform/py_fourier_transform.html

magnitude_spectrum の計算結果が信号の周波数スペクトルの大きさを返すので、これを使うのか・・・:thinking:

## 2.ディープラーニング​で実装

ピントばっちり画像とピンボケ画像を大量にあつめて学習させて判断します。
ピンボケ画像はバッチリ画像から作ってしまえば良いという発想で・・・

【学習イメージ】
ピントばっちりの元画像→FFTでピンボケに変換→与えられた画像をチェックしてピンボケかそうでないかを人が判断して学習用データを作成→学習
 
【判定イメージ】
未学習のピントばっちりの元画像→FFTでピンボケに変換→与えられた画像→ディープラーニングにかけて判定

参考情報
http://tech.unifa-e.com/entry/2017/03/10/124033

なかなか面白い。でもピンボケにはボケ具合にはいろいろあるように思います。単純にピントが合わなくてボケてたり、手振れでボケてたりするので単一な画像変換の手法だけではダメなような気がします。実際に集めた画像データで学習させるのがいいかも？:thinking:

## 3.OpenCVで実装

OpenCVを利用したピンボケ判定方法としてラプラシアン微分を活用する方法があります。

これは画像をグレースケールに変換し、3 x 3のラプラシアンカーネルで畳み込んで標準偏差の二乗を計算し、その値によってボケているかどうかを判断するというもの。

https://qiita.com/koji-t/items/93fed9acf14dab59de65#%E3%83%9C%E3%82%B1%E6%A4%9C%E5%87%BA

ラプラシアン微分によってエッジ検出する方法として、

~~~
cv2.Laplacian(image, cv2.CV_64F).var()
~~~

を発行して閾値より低い場合はピンボケだと判断することが可能です。

https://www.pyimagesearch.com/2015/09/07/blur-detection-with-opencv/ 

直球ですね。

でも、認識したい場所にフォーカスがあっている前提であればよいかもしれませんが、認識したい場所以外にフォーカスがあっている場合は想定した結果が得られないかもしれません。

まずはディープラーニングでの実装と、このララプラシアン微分のどちらが精度が高いか？実装・運用が楽か？という感じでしょうか？:thinking:
 
### 参考
ラプラシアン微分によってエッジ検出する方法

http://labs.eecs.tottori-u.ac.jp/sd/Member/oyamada/OpenCV/html/py_tutorials/py_imgproc/py_gradients/py_gradients.html

せっかくなのでソースコード追加。(2018.9.4)

~~~py
# -*- coding: utf-8 -*-
import cv2

path ="images/"
sub_dir="report/"

image_file = "blur.jpg"

image_path = path + image_file
out_image_path = path + sub_dir + image_file

image = cv2.imread(image_path) # 画像ファイル読み込み
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # グレースケースに変換

laplacian = cv2.Laplacian(gray, cv2.CV_64F) #ラプラシアン値

print("laplacian.var()の値は？",laplacian.var())

if laplacian.var() < 100: # 閾値は100以下だとピンボケ画像と判定
   text = "Blurry"
else:
   text = "Not Blurry"

cv2.putText(image, "{}: {:.2f}".format(text, laplacian.var()), (10, 30),
                cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 3)

cv2.imwrite(out_image_path, image)
~~~

## その他の参考情報

ピンボケ判定に関する特許

http://www.ekouhou.net/%E3%83%9C%E3%82%B1%E7%94%BB%E5%83%8F%E5%88%A4%E5%AE%9A%E6%96%B9%E6%B3%95%E3%81%8A%E3%82%88%E3%81%B3%E3%83%9C%E3%82%B1%E7%94%BB%E5%83%8F%E5%88%A4%E5%AE%9A%E8%A3%85%E7%BD%AE/disp-A,2010-231695.html