美圖網上太多難以篩選,教你用Python挑選最合適的

前幾天,極光同學寫了篇下載王者榮耀皮膚的文章,可以輕鬆的獲取各種英雄背景圖,甚是激動,也想將桌面背景換成漂亮的,不過我對王者榮耀不感冒(曝露年齡啦),時常會被必應搜索主頁的背景圖所震撼,於是想到從必應獲取桌面壁紙,並且排除掉自己不喜歡的圖片,應該是個不錯的主意,說幹就幹

問題分析

必應每天都會有新的壁紙,大都是自然風光、人文地理等等,非常漂亮,在頁面上點擊右鍵,保存背景圖片,就能簡單的保存下來。但是總是這麼幹,不是個好辦法,最好能自動化。

自動化中,圖片下載很簡單,只要找到背景圖片鏈接,下載保存即可。

但並非所有的圖片都合乎自己的口味,怎麼才能只選自己喜歡的圖片呢?於是想到用圖像識別,再加對識別結果的判斷,從而得到是否合適的判斷

經過分析,大概的處理流程是:先下載圖片,進行圖像識別,對得到的結果進行分析,對於合乎要求的,保存到桌面背景文件中。

重點是圖像識別和結果判斷,先從核心問題開始。

圖像識別

圖像識別屬於機器學習或者人工智能的部分,需要相應的算法和資料庫來訓練識別模式,是個比較繁雜的工作,好在各大雲服務商都提供了圖像識別接口,可以輕鬆做到。最終選用百度的綜合圖像識別 API 來處理

申請服務

需要先創建帳號,然後選擇服務,購買服務,購買服務是免費的,使用到一定頻率是需要收費的,對於目前場景來說,免費的完全夠用 購買服務後,可以用服務創建一個項目,隨便寫些項目信息,創建好後,就能得到 App key 和 Secret ,即調用服務的憑證,需要妥善保管

編寫接口

調用識別 API 之前需要先得到 accessToken ,獲取代碼如下:

 
import httpx

def getAccessToken():
    clientId = '<換成你的 clientid>'
    clientSecret = '<換成你的 clientsecret>'
    host = 'https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=%s&client_secret=%s' % (clientId, clientSecret)
    response = httpx.get(host)
    if response.status_code == 200:
        ret = response.json()
        return ret.get('access_token')
    else:
        raise "獲取AccessToken失敗:" + str(response.status_code)
accesstoken
access_token
accessToken 的默認
加python學習qq羣:775690737  送python零基礎入門學習資料+99個源碼

有效期爲 30 天,所以沒必要每次請求都獲取,所以最好將 accessToken緩存起來,不必每次調用都獲取,鑑於當前的業務場景,每天查下一次,就沒必要緩存了,如果有興趣可以自己實踐下

有了 accessToken 就可以編寫圖像識別了,代碼如下:

import base64
import httpx

def imageRecognition(image):
    img = base64.b64encode(image)
    params = {"image":img}
    access_token = getAccessToken()
    request_url = "https://aip.baidubce.com/rest/2.0/image-classify/v2/advanced_general?access_token=" + access_token
    headers = {'content-type': 'application/x-www-form-urlencoded'}
    response = httpx.post(request_url, data=params, headers=headers)
    if response.status_code == 200:
        return response.json().get("result")
    else:
        raise "獲取AccessToken失敗:" + str(response.status_code)
加python學習qq羣:775690737  送python零基礎入門學習資料+99個源碼
  • 接收一個圖片參數,因爲圖片是從網站上下載的,所以參數類型爲二定製數據
  • 由於圖像識別接口需要需要上傳圖片,必須是 base64 格式,所以將圖片數據轉換爲 base64 數據格式
  • 在得到的結果中,提取 result 並返回

result 是 JSON 數據,結構如下:

 
[{
  'score': 0.819942,
  'root': '自然風景-天空',
  'keyword': '天空'
}, {
  'score': 0.64795,
  'root': '自然風景-海洋',
  'keyword': '海洋'
}, {
  'score': 0.475835,
  'root': '非自然圖像-圖像素材',
  'keyword': '風景矢量圖'
}, {
  'score': 0.265258,
  'root': '非自然圖像-圖像素材',
  'keyword': '雪花背景'
}, {
  'score': 0.08247,
  'root': '非自然圖像-圖像素材',
  'keyword': '氣泡'
}]
score 爲得分,表示百分比,就是識別程度,比如 0.82 表示確認度爲 82%
root 爲識別分類
keyword 爲識別關鍵字,即對圖像元素中識別定義
加python學習qq羣:775690737  送python零基礎入門學習資料+99個源碼
  •  

圖像判別

得到了圖像識別結果,如何從結果中得到是否符合要求呢?經過探索和實踐,用以下方法來解決:

先將自己喜歡的圖像整體識別一次,獲取到得分爲 0.5 以上的關鍵字,組成一個數組(這個過程相當於機器學習),去重後,得到一下特徵描述:

keywords = ['植物', '樹', '天空', '陽光','霞光', '晚霞', '海洋', '大海', '森林', '湖泊', '草原', '沙漠', '高山', '瀑布']

然後,需要一個計算公式對圖片做整體打分,設置一個分值作爲判定條件,就可以挑選出符合自己胃口的圖片了,算法如下:

import difflib

score = 0
for r in result:
    # 進行對比
    for k in keywords:
        root = r.get('keyword', '')
        ratio = difflib.SequenceMatcher(None, root, k).ratio()
        mscore = r.get('score')
        score += mscore*ratio
加python學習qq羣:775690737  送python零基礎入門學習資料+99個源碼

部分識別過程如下,標記了的是得分項:

主要過程是,從結果中提取 keyword,和每個自己喜歡的描述去比較,得到一個相似度,然後與識別得分相乘,得到一個特徵相對於一個關鍵描述的得分,將所有特徵的分值相加,就得到了這個圖像的整體得分,從而得到判斷依據

這裏的關鍵是如何比較兩個描述的相似程度,相似程度並非是否相等,相似程度是一個 0 ~ 1 之間的數,用 Python 自帶 difflib 模塊 SequenceMatcher 來解決

SequenceMatcher 有三個參數,第一個參數是一個 lambda 表達式,可以理解成一個簡寫的回調函數,作用是排除掉不需要對比的字符串,例如排除掉空格的寫法爲 x:x==' ' ,不用排除的話,傳入 None

通過對測試圖片的對比,符合胃口的圖片綜合得分值在 0.8 到 9.9 之間,分值越大,表示有多個元素符合要求,爲了照顧圖片識別的失誤,以及數值化過程中的誤差,將判斷值設定爲 0.5, 也就是綜合評分大於等於 0.5 的就認爲是自己喜歡的,當然這只是個經驗值,可以根據自己的經驗調整

如果符合要求,就對圖片進行保存,代碼如下:

with open(r"C:\Users\alisx\Pictures\Saved Pictures\bing_%s.jpg" % datetime.date.today(), 'wb') as f:
    f.write(image)
加python學習qq羣:775690737  送python零基礎入門學習資料+99個源碼

圖像獲取

解決了圖像識別問題,獲取圖像就是小菜一碟,直接上代碼:

def grabImage(file=None):
    if file:
        image = Image.open(file)
        output_buffer = io.BytesIO()
        image.save(output_buffer, format='JPEG')
        return output_buffer.getvalue()
    else:
        rsp = httpx.get("https://cn.bing.com/")
        bs = BS(rsp.content, "html.parser")
        bglink = bs.find("link").get("href")
        url = str(rsp.url) + bglink
        image = httpx.get(url).content
        return image
加python學習qq羣:775690737  送python零基礎入門學習資料+99個源碼
  • 參數 file,是爲了測試方便,測試是提供本地圖像的路徑,grabImage 方法會將其轉換爲二定製數據,以到達抓取圖片的效果
  • 當不提供 file 參數時,會從必應中獲取,通過解析頁面數據,獲取背景圖片的 url
  • 將圖片通過 httpx 下載後,返回圖片的二進制數據

這裏並沒有將圖片存儲到硬盤上,因爲獲取到的圖片需要經過判定,只有符合要求的纔會被保存,不符合要求的直接丟棄掉

識別效率

通過上面的過程就可以獲得自己喜歡的背景圖片了,但背景圖片一般比較大,可能會導致識別效率較低,所以需要將圖片處理下,以提高上傳和識別效率

經過測試,將圖片設置爲原來圖片的一半比較合適,所以在下載到圖片後,需要對圖片進行加工處理,代碼如下:

from PIL import Image
# 壓縮圖片
img = Image.open(io.BytesIO(image))
x, y = img.size
x_s = round(x/2)
y_s = int(y * x_s / x)
out = img.resize((x_s, y_s), Image.ANTIALIAS)

# 圖片轉碼
output_buffer = io.BytesIO()
out.save(output_buffer, format='JPEG')
byte_data = output_buffer.getvalue()
  • 下載的圖片是二進制數據,所以將其轉換爲 IO 序列,使之可以像文件一樣打開
  • 打開得到 image 對象後,獲取圖片高度 (y) 和寬度 (x)
  • 計算出寬度的一半,並且計算出等比例的高度
  • 最後用 resize 方法對圖像進行壓縮
  • 壓縮完後得到新的 image 對象,再將內容轉換爲 IO 序列,最後獲得圖片的二進制碼

經過轉換後,再調用圖像識別接口,會快很多

這個壓縮過程比較簡單,如果更精細一些,可以對圖像的大小進行判斷,對於不同大小的圖像做不同的處理,不過在此場景下,作爲背景的圖像都差不多,所以採用固定的處理方式

總結

今天的實踐雖然很小,但涉及到爬蟲、圖像識別、機器學習(雖然只是手工過程)、圖像評價等,將很多單獨的技術組合起來,就能解決個性化圖像篩選問題,就像 SpaceX 一樣,每個技術都不是業內頂尖的,但合理的組合,就能發揮更大的價值。文章中爲了符合思維習慣,對代碼做了解構,代碼示例中有完整的代碼,歡迎參考研究,有問題歡迎加入企鵝羣

python學習qq羣:775690737  送python零基礎入門學習資料+99個源碼

進行交流,

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章