在一般狀況下,圖片是否清晰是個感性認識,同一個圖,有可能你以爲還過得去,而別人會以爲不清晰,缺少一個統一的標準。然而有一些算法能夠去量化圖片的清晰度,作到有章可循。python
若是以前瞭解過信號處理,就會知道最直接的方法是計算圖片的快速傅里葉變換,而後查看高低頻分佈。若是圖片有少許的高頻成分,那麼該圖片就能夠被認爲是模糊的。然而,區分高頻量多少的具體閾值倒是十分困難的,不恰當的閾值將會致使極差的結果。git
咱們指望的是一個單一的浮點數就能夠表示圖片的清晰度。 Pech-Pacheco 在 2000 年模式識別國際會議提出將圖片中某一通道(通常用灰度值)經過拉普拉斯掩模作卷積運算,而後計算標準差,出來的值就能夠表明圖片清晰度。程序員
這種方法湊效的緣由就在於拉普拉斯算子定義自己。它被用來測量圖片的二階導數,突出圖片中強度快速變化的區域,和 Sobel 以及 Scharr 算子十分類似。而且,和以上算子同樣,拉普拉斯算子也常常用於邊緣檢測。此外,此算法基於如下假設:若是圖片具備較高方差,那麼它就有較廣的頻響範圍,表明着正常,聚焦準確的圖片。可是若是圖片具備有較小方差,那麼它就有較窄的頻響範圍,意味着圖片中的邊緣數量不多。正如咱們所知道的,圖片越模糊,其邊緣就越少。github
有了表明清晰度的值,剩下的工做就是設定相應的閥值,若是某圖片方差低於預先定義的閾值,那麼該圖片就能夠被認爲是模糊的,高於閾值,就不是模糊的。算法
原理看起來比較複雜,涉及到不少信號啊圖片處理的相關知識,下面咱們來實操一下,直觀感覺下。json
因爲人生苦短,以及我我的是朋友圈第一 Python 吹子,我選擇使用 Python 來實現,核心代碼簡單到使人髮指:微信
import cv2
def getImageVar(imgPath):
image = cv2.imread(imgPath);
img2gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
imageVar = cv2.Laplacian(img2gray, cv2.CV_64F).var()
return imageVar
複製代碼
真是人生苦短啊,核心代碼就三行,簡單解釋下。spa
import cv2
使用了一個著名的圖像處理庫 OpenCV,關於 OpenCV 的安裝這裏很少贅述,須要注意的是它依賴 numpy。3d
image = cv2.imread(imgPath)
使用 OpenCV 提供的方法讀取圖片。 img2gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
轉化爲灰度圖。以下圖:code
原圖是這樣的:
cv2.Laplacian(img2gray, cv2.CV_64F)
對圖片用 3x3 拉普拉斯算子作卷積,這裏的 cv2.CV_64F
就是拉普拉斯算子。
原理部分說過,拉普拉斯算子常常用於邊緣檢測,因此這裏通過拉普拉斯算子以後,留下的都是檢測到的邊緣。上圖通過這步處理以後是這樣的:
能夠看到這裏圖片人物大體仍是比較清晰的。
cv2.Laplacian(img2gray, cv2.CV_64F).var()
計算出方差,並最後返回。
上面那張圖按這個計算出來時 3170 多,這個就是最後咱們用來判斷清晰度的值。
能夠再找一張看看:
原圖:
作灰度和通過拉普拉斯算子以後,能夠看到人物部分已經不是很清晰了。
最後算出來的方差只有 530
剩下的工做就是根據總體圖片質量肯定閥值了。
經過上面的實操,咱們知道這個算法的技巧在於設置合適的閥值,閾值過低會致使正常圖片被誤斷爲模糊圖片,閾值過高會致使模糊圖片被誤判爲正常圖片。閥值依賴於你實際應用的業務場景,須要根據使用場景的不一樣作不一樣的定製。
真正的銀彈並不存在。除了須要定個閥值外,有些圖片可能會故意作個背景模糊或者背景虛化,這種圖片很容易被誤殺。
好比:
計算出來是這樣的,後面一大片都是黑的。
這個圖前景其實看着還行,可是背景有大片的虛化和模糊,這種狀況下比較容易被誤殺。
因此最好仍是在瞭解原理以後,根據實際場景來使用。
最後寫了個簡單的腳本,對傳入的圖片路徑的圖片進行計算,而後返回一個 json 字符串。
用法 python getRank.py --imgs=./1.jpg,./2.jpg
記錄一些所思所想,寫寫科技與人文,寫寫生活狀態,寫寫讀書心得,主要是扯淡和感悟。 歡迎關注,交流。
微信公衆號:程序員的詩和遠方
公衆號ID : MonkeyCoder-Life