利用Python實現簡單的相似圖片搜尋的教程_python

來源:互聯網
上載者:User

大概五年前吧,我那時還在為一家約會網站做開發工作。他們是早期創業公司,但他們也開始擁有了一些穩定使用者量。不像其他約會網站,這家公司向來以潔身自好為主要市場形象。它不是一個供你鬼混的網站——是讓你能找到忠實伴侶的地方。

由於投入了數以百萬計的風險資本(在US大蕭條之前),他們關於真愛並找尋靈魂伴侶的線上廣告勢如破竹。Forbes(福布斯,美國著名財經雜誌)採訪了他們。全國性電視節目也對他們進行了專訪。早期的成功促成了事業起步時讓人垂涎的指數級增長現象——他們的使用者數量以每月加倍的速度增長。對他們而言,一切都似乎順風順水。

但他們有一個嚴重的問題——色情問題。

該約會網站的使用者中會有一些人上傳色情圖片,然後設定為其個人頭像。這種行為破壞了很多其他使用者的體驗——導致很多使用者取消了會員。

可能對於現在的一些約會網站隨處可見幾張色情圖片也許並不能稱之為是問題。或者可以說是習以為常甚至有些期待,只是一個被接受然後被無視的線上約會的副產品。

然而,這樣的行為既不應該被接受也應該被忽視。

別忘了,這次創業可是將自己定位在優秀的約會天堂,免於使用者受到困擾其他約會網站的汙穢和垃圾的煩擾。簡而言之,他們擁有很實在的以風險資本作為背後支撐的名聲,而這也正是他們需要保持的風格。

該約會網站為了能迅速阻止色情圖片的爆發可以說是不顧一切了。他們僱傭了圖片論壇版主團隊,真是不做其他事只是每天盯著監管頁面8個小時以上,然後移除任何被上傳到社交網路的色情圖片。

毫不誇張的說,他們投入了數萬美元(更不用說數不清的人工小時)來解決這個問題,然而也僅僅只是緩解,控制情況不變嚴重而不是在源頭上阻止。

色情圖片的爆發在2009年的七月達到了臨界水平。8個月來第一次使用者量沒能翻倍(甚至已經開始減少了)。更糟糕的是,投資者聲稱若該公司不能解決這個問題將會撤資。事實上,汙穢的潮汐早已開始衝擊這座象牙塔了,將它推翻流入大海也不過是時間問題。

正在這個約會網站巨頭快要撐不住時,我提出了一個更魯棒的長期解決方案:如果我們使用圖片指紋來與色情圖片的爆發鬥爭呢?

你看,每張圖片都有一個指紋。正如人的指紋可以識別人,圖片的指紋能識別圖片。

這促使了一個三階段演算法的實現:

1. 為不雅圖片建立指紋,然後將圖片指紋儲存在一個資料庫中。

2. 當一個使用者上傳一份新的頭像時,我們會將它與資料庫中的圖片指紋對比。如果上傳圖片的指紋與資料庫任意一個不雅圖片指紋相符,我們就阻止使用者將該圖片設定為個人頭像。

3. 當圖片監管人標記新的色情圖片時,這些圖片也被賦予指紋並存入我們的資料庫,建立一個能用於阻止非法上傳且不斷進化的資料庫。

我們的方法,儘管不十分完美,但是也卓有成效。慢慢地,色情圖片爆發的情況有所減慢。它永遠不會消失——但這個演算法讓我們成功將非法上傳的數量減少了80%以上。

這也挽回了投資者的心。他們繼續為我們提供資金支援——直到蕭條到來,我們都失業了。

回顧過去時,我不禁笑了。我的工作並沒持續太久。這個公司也沒有堅持太久。甚至還有幾個投資者捲鋪蓋走人了。

但有一樣確實存活了下來。提取圖片指紋的演算法。幾年之後,我把這個演算法的基本內容分享出來,期望你們可以將它應用到你們自己的項目中。

但最大的問題是,我們怎麼才能建立圖片指紋呢?

繼續讀下去一探究竟吧。
即將要做的事情

我們打算用圖片指紋進行相似圖片的檢測。這種技術通常被稱為“感知映像hash”或是簡單的“圖片hash”。
什麼是圖片指紋/圖片雜湊

 

圖片hash是檢測一張圖片的內容然後根據檢測的內容為圖片建立一個唯一值的過程。

比如,看看本文最上面的那張圖片。給定一張圖片作為輸入,應用一個hash函數,然後基於圖片的視覺計算出一個圖片hash。相似的圖片也應當有相似的hash值。圖片hash演算法的應用使得相似圖片的檢測變得相當簡單了。

特別地,我們將會使用“差別Hash”或簡單的DHash演算法計算圖片指紋。簡單來說,DHash演算法著眼於兩個相鄰像素之間的差值。然後,基於這樣的差值,就建立起一個hash值了。
為什麼不使用md5,sha-1等演算法?

不幸的是,我們不能在實現中使用加密hash演算法。由於加密hash演算法的本質使然,輸入檔案中非常微小的差別也能造成差異極大的hash值。而在圖片指紋的案例中,我們實際上希望相似的輸入可以有相似的hash輸出值。
圖片指紋可以用在哪裡?

正如我上面舉的例子,你可以使用圖片指紋來維護一個儲存不雅圖片的資料庫——當使用者嘗試上傳類似圖片時可以發出警告。

你可以建立一個圖片的逆向搜尋引擎,比如TinEye,它可以記錄圖片以及它們出現的相關網頁。

你還可以使用圖片指紋協助管理你個人的照片收集。假設你有一個硬碟,上面有你照片庫的一些局部備份,但需要一個方法刪除局部備份,一張圖片僅保留一份唯一的備份——圖片指紋可以幫你做到。

簡單來說,你幾乎可以將圖片指紋/雜湊用於任何需要你檢測圖片的相似副本的情境中。
需要的庫有哪些?

為了建立圖片指紋方案,我們打算使用三個主要的Python包:

  1. PIL/Pillow用於讀取和載入圖片
  2. ImageHash,包括DHash的實現
  3. 以及NumPy/SciPy,ImageHash的依賴包

你可以使用下列命令一鍵安裝所需要的必備庫:
 

$ pip install pillow imagehash

第一步:為一個圖片集建立指紋

第一步就是為我們的圖片集建立指紋。

也許你會問,但我們不會,我們不會使用那些我為那家約會網站工作時的色情圖片。相反,我建立了一個可供使用的人工資料集。

對電腦視覺的研究人員而言,資料集CALTECH-101 是一個傳奇般的存在。它包含來自101個不同分類中的至少7500張圖片,內容分別有人物,>機車和飛機。

從這7500多張圖片中,我隨機的挑選了17張。

然後,從這17張隨機挑選的圖片中,以幾個百分點的比例隨機放大/縮小並建立N張新圖片。這裡我們的目標是找到這些近似副本的圖片——有點大海撈針的感覺。

你也想建立一個類似的資料集用於工作嗎?那就下載CALTECH-101 資料集,抽取大概17張圖片即可,然後運行repo下的指令檔gather.py。

迴歸正題,這些圖片除了寬度和高度,其他各方面都是一樣的。而且因為他們沒有相同的形狀,我們不能依賴簡單的md5校正和。最重要的是,有相似內容的圖片可能有完全不相同的md5雜湊。然而,採取圖片雜湊,相似內容的圖片也有相似的雜湊指紋。

所以趕緊開始寫代碼為資料集建立指紋吧。建立一個新檔案,命名為index.py,然後開始工作:

 

# import the necessary packagesfrom PIL import Imageimport imagehashimport argparseimport shelveimport glob # construct the argument parse and parse the argumentsap = argparse.ArgumentParser()ap.add_argument("-d", "--dataset", required = True,help = "path to input dataset of images")ap.add_argument("-s", "--shelve", required = True,help = "output shelve database")args = vars(ap.parse_args()) # open the shelve databasedb = shelve.open(args["shelve"], writeback = True)

要做的第一件事就是引入我們需要的包。我們將使用PIL或Pillow中的Image類載入硬碟上的圖片。這個imagehash庫可以被用於構建雜湊演算法。

Argparse庫用於解析命令列參數,shelve庫用作一個儲存在硬碟上的簡單索引值對資料庫(Python字典)。glob庫能很容易的擷取圖片路徑。

然後傳遞命令列參數。第一個,—dataset是輸入圖片庫的路徑。第二個,—shelve是shelve資料庫的輸出路徑。

下一步,開啟shelve資料庫以寫資料。這個db資料庫儲存圖片雜湊。更多的如下所示:
 

# loop over the image datasetfor imagePath in glob.glob(args["dataset"] + "/*.jpg"):  # load the image and compute the difference hash  image = Image.open(imagePath)  h = str(imagehash.dhash(image))   # extract the filename from the path and update the database  # using the hash as the key and the filename append to the  # list of values  filename = imagePath[imagePath.rfind("/") + 1:]  db[h] = db.get(h, []) + [filename] # close the shelf databasedb.close()

以上就是大部分工作的內容了。開始迴圈從硬碟讀取圖片,建立圖片指紋並存入資料庫。

現在,來看看整個範例中最重要的兩行代碼:
 

filename = imagePath[imagePath.rfind("/") + 1:]db[h] = db.get(h, []) + [filename]

正如本文早些時候提到的,有相同指紋的圖片被認為是一樣的。

因此,如果我們的目標是找到近似圖片,那就需要維護一個有相同指紋值的圖片列表。

而這也正是這幾行代碼做的事情。

前一個程式碼片段提取了圖片的檔案名稱。而後一個程式碼片段維護了一個有相同指紋值的圖片列表。

為了從我們的資料庫中提取圖片指紋並建立雜湊資料庫,運行下列命令:
 

$ python index.py —dataset images —shelve db.shelve

這個指令碼會運行幾秒鐘,完成後,就會出現一個名為db.shelve的檔案,包含了圖片指紋和檔案名稱的索引值對。

這個基本演算法正是幾年前我為這家約會創業公司工作時使用的演算法。我們獲得了一個不雅圖片集,為其中的每張圖片構建一個圖片指紋並將其存入資料庫。當來一張新圖片時,我只需簡單地計算它的雜湊值,檢測資料庫查看是否上傳圖片已被標識為非法內容。

下一步中,我將展示實際如何執行查詢,判定資料庫中是否存在與所給圖片具有相同雜湊值的圖片。
第二步:查詢資料集

 

既然已經建立了一個圖片指紋的資料庫,那麼現在就該搜尋我們的資料集了。

開啟一個新檔案,命名為search.py,然後開始寫代碼:
 

# import the necessary packagesfrom PIL import Imageimport imagehashimport argparseimport shelve # construct the argument parse and parse the argumentsap = argparse.ArgumentParser()ap.add_argument("-d", "--dataset", required = True,  help = "path to dataset of images")ap.add_argument("-s", "--shelve", required = True,  help = "output shelve database")ap.add_argument("-q", "--query", required = True,  help = "path to the query image")args = vars(ap.parse_args())

我們需要再一次匯入相關的包。然後轉換命令列參數。需要三個選項,—dataset初始圖片集的路徑,—shelve,儲存索引值對的資料庫的路徑,—query,查詢/上傳圖片的路徑。我們的目標是對於每個查詢圖片,判定資料庫中是否已經存在。

現在,寫代碼執行實際的查詢:
 

# open the shelve databasedb = shelve.open(args["shelve"]) # load the query image, compute the difference image hash, and# and grab the images from the database that have the same hash# valuequery = Image.open(args["query"])h = str(imagehash.dhash(query))filenames = db[h]print "Found %d images" % (len(filenames)) # loop over the imagesfor filename in filenames:  image = Image.open(args["dataset"] + "/" + filename)  image.show() # close the shelve databasedb.close()

首先開啟資料庫,然後載入硬碟上的圖片,計算圖片的指紋,找到具有相同指紋的所有圖片。

如果有圖片具有相同的雜湊值,會遍曆這些圖片並展示在螢幕上。

這段代碼使我們僅僅使用指紋值就能判定圖片是否已在資料庫中存在。
結果

正如本文早些時候提到的,我從CALTECH-101資料集的7500多張圖片中隨機選取17張,然後通過任意縮放一部分點產生N張新的圖片。

這些圖片在尺寸上僅僅是少數像素不同—但也是因為這一點我們不能依賴於檔案的md5雜湊(這一點已在“最佳化演算法”部分進行了詳盡的描述)。然而,我們可以使用圖片雜湊找到近似圖片。

開啟你的終端並執行下述命令:
 

$ python search.py —dataset images —shelve db.shelve —query images/84eba74d-38ae-4bf6-b8bd-79ffa1dad23a.jpg

如果一切順利你就可以看到下述結果:

左邊是輸入圖片。載入這張圖片,計算它的圖片指紋,在資料庫中搜尋指紋查看是否存在有相同指紋的圖片。

當然——正如右邊所示,我們的資料集中有其他兩張指紋相同的圖片。儘管從截圖中還不能十分明顯的看出,這些圖片,雖然有完全相同的視覺內容,也不是完全相同!這三張圖片的高度寬度各不相同。

嘗試一下另外一個輸入圖片:
 

$ python search.py —dataset images —shelve db.shelve —query images/9d355a22-3d59-465e-ad14-138a4e3880bc.jpg

下面是結果:

左邊仍然是我們的輸入圖片。正如右邊展示的,我們的圖片指紋演算法能夠找出具有相同指紋的三張完全相同的圖片。

最後一個例子:
 

$ python search.py —dataset images —shelve db.shelve —query images/5134e0c2-34d3-40

這一次左邊的輸入圖片是一個>機車。拿到這張>機車圖片,計算它的圖片指紋,然後在指紋資料庫中尋找該指紋。正如我們在右邊看到的,我們也能判斷出資料庫中有三張圖片具有相同指紋。
最佳化演算法

有很多可以最佳化本演算法的方法——但最關鍵性的是要考慮到相似但不相同的雜湊。

比如,本文中的圖片僅僅是一小部分點重組了(依比例增大或減小)。如果一張圖片以一個較大的因素調整大小,或者縱橫比被改變了,對應的雜湊就會不同了。

然而,這些圖片應該仍然是相似的。

為了找到相似但不相同的圖片,我們需要計算漢明距離(Hamming distance).漢明距離被用於計算一個雜湊中的不同位元。因此,雜湊中只有一位不同的兩張圖片自然比有10位不同的圖片更相似。

然而,我們遇到了第二個問題——演算法的可擴充性。

考慮一下:我們有一張輸入圖片,又被要求在資料庫中找到所有相似圖片。然後我們必須計算輸入圖片和資料庫中的每一張圖片之間的漢明距離。

隨著資料庫規模的增長,和資料庫比對的時間也隨著延長。最終,我們的雜湊資料庫會達到一個線性比對已經不實際的規模。

解決辦法,雖然已超出本文範圍,就是利用K-d trees和VP trees將搜尋問題的複雜度從線性減小到次線性。
總結

本文中我們學會了如何構建和使用圖片雜湊來完成相似圖片的檢測。這些圖片雜湊是使用圖片的視覺內容構建的。

正如一個指紋可以識別一個人,圖片雜湊也能唯一的識別一張圖片。

使用圖片指紋的知識,我們建立了一個僅使用圖片雜湊就能找到和識別具有相似內容的圖片的系統。

然後我們又示範了圖片雜湊是如何應用於快速找到有相似內容的圖片。

從repo目錄下下載代碼。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.