聊聊答題應用題庫的建立

NealCaffery發表於2018-02-10

前段時間,答題 APP 如火如荼的發展,各大網際網路公司都加入了撒幣大戰,包括像衝頂大會,百萬英雄,芝士英雄等等。隨之而來的也是各個答題應用輔助的興起。

網上已經有不少答題應用的輔助,一般來說包括兩個步驟,即獲取題目選項以及搜尋答案。對於題目以及選項的獲取包括利用 adb 抓取手機螢幕截圖,然後使用 ocr(optical character recognization) 的方式去識別題目和選項。大多數使用的 ocr 工具有谷歌開源的 tesseract-ocr以及百度的 ocr API。谷歌的 tesseract-ocr 可以在本地進行安裝,軟體下載地址是 https://digi.bib.uni-mannheim.de/tesseract/tesseract-ocr-setup-3.05.01.exe , 安裝的時候注意選擇增加中文簡體語言包,否則無法識別中文。另外一種方法就是利用百度的 ocr API,可以免費申請,使用起來比較方便,識別率相對來說也更加準確。百度 API 還有一個優點是圖片無需處理就可以進行識別,而 tesseract-ocr 一般還需要對圖片進行簡單的處理。獲取題目以及選項的另外一種方式就是使用抓包工具去抓取 APP 請求從而獲取題目以及選項資訊。

另一方面,對於題目答案的搜尋。常見的幾種做法是直接用題目作為搜尋關鍵字開啟瀏覽器,或者是問題加選項搜尋,獲取搜尋引擎搜尋的結果數量。通過結果數量來判斷問題和選項的相關性從而判斷問題的答案,一般來說這種方式獲取的答案都是不太準確的,一是因為現在題目的出題方式越來越詭異,二是相關性越大並不一定就意味著是正確答案。本來對於題目和選項的判斷就是很難的一件事情,除非你能做出很完美的語意理解,否則很難判斷出正確的選項。還有一種比較直白的方式就是建立題庫。在本文中,我們討論一種建立題庫的方式,這裡只是做一個簡單的探索,未必在實際中就能夠使用,因為題庫必須足夠全才能夠發揮威力。

使用 elasticsearch 建立題庫

本文主要講解關於題庫的建立方面的很小的一方面進行探索,對於答題輔助的使用可以閱讀原文檢視完整介紹,程式碼主要是基於TopSup 做了一些調整。Elasticsearch 將被用於題庫的建立,對於 es 的安裝可以檢視第一篇文章。有人可能會覺得用 es 來做題庫,簡直就是高射炮打蚊子——小題大做。但我覺得 es 安裝和使用都很方便,得益於其強大的 RESTFUL介面,幾乎可以用任何工具操控 es。Talk is cheap, show me the code.

from elasticsearch import Elasticsearch

def write_quetion():
  question = {
    'question': '誰是世界上最帥的人',
    'answer': 'Neal'
  }
  es = Elasticsearch({'localhost'})
  es.index(index='question-index', doc_type='question', id=1, body=question)
複製程式碼

上面是一個簡單的像索引中寫入一條記錄的程式碼片段,其實 es 可以算是一種非關係型資料庫,在 DB-Engines 的最新排名中,es 已經躥到了第 9 名。Elasticsearch 中的某些概念可以和關係型資料庫進行類比:

關係型資料庫 Elasticsearch
database index
table type
row document
column field

那麼在 es 中搜尋問題時應該這樣:

def search_question(key_words):
  es = Elasticsearch({'localhost'})
  res = es.search(index='question-index', body={
    "query": {
      "match": {
        "question": key_words,
        "minimum_should_match": "75%"
        }
      }
    }
  })
  if res['hits'['total'] > 0:
    for hit in res['hits']['hits']:
      print(hit['_source']['question'] + ':' + hit['_source']['answer'])
   else:
     print('未搜尋到類似結果')
複製程式碼

從圖片中獲取問題和答案

題庫的建立可以使用文字的方式或者直接使用答題應用的手機截圖,毫無疑問後者是更有價值的。假設我們現在有一張這樣的截圖:

聊聊答題應用題庫的建立

這張圖片中已經包含了正確的選項,但我們如何識別這個圖片並且知道這個正確答案呢?使用選項後面的數字麼,不可行,正確答案並不一定是選擇的最多的選項。感謝影象處理這門課程,裡面有一個非常基礎的概念幫我解決了這個問題。一般來說將彩色圖片轉化為灰度圖片就是通過一個確定的函式將彩色空間對映到灰度空間。以 matlab 中將 RGB 圖(可以理解為一張彩色圖)轉化為灰度圖的 rgb2gray 函式為例,假設一個彩色畫素的 RGB 值是 (R, G, B),那麼它的灰度值 G 的計算方法應該是:

G = 0.2989 * R + 0.5870 * G + 0.1140 * B

業界的通用做法就是將按照一定的權重來計算彩色畫素的灰度值。通過取色筆可以獲取上圖正確答案背景顏色的 RGB 值是(80, 215, 216),而錯誤答案背景顏色的 RGB 值是(194, 194, 194)。

936LqI.md.png

今天教大家的是乘法分配律,秀了一波小學數學。言歸正傳,可以看出,彩色影象對映的灰度值更低。這對於我們區分正確選項和錯誤選項就有了重大的幫助。首先我們對選項區域進行裁剪,避免右邊的數字影響識別結果。通過二值化演算法,我們可以把問題選項圖使用不同的閾值將圖片轉換成兩張不同的圖片,小於閾值的畫素點變成黑色畫素點,大於閾值的畫素點程式設計白色畫素點。二值化轉換的演算法非常簡單:

def binarizing(img, threshold):
    pixdata = img.load()
    w, h = img.size
    for y in range(h):
        for x in range(w):
            if pixdata[x, y] < threshold:
                pixdata[x, y] = 0
            else:
                pixdata[x, y] = 255
    return img
複製程式碼

通過閾值 120 和閾值 180(175到194之間的任意值都是可以的) 來獲取二值化圖片,結果分別為:

93c8dx.png

93clLR.png

這下答案就呼之欲出了吧。我們將這兩張圖通過 ocr 的方式去識別,第一張圖可以獲取所有的選項,而第二張圖只能獲取錯誤的選項,那麼二者的差異之處不正就是正確選項了嘛!是不是骨骼精奇,是不是沒想到!

結語

本文就到此為止,本文主要是從一個很小的角度講述一種建立題庫的方式,使用一種影象處理的簡單技術來獲取正確的選項。是不是覺得學的課程還是有價值的。當然本文只是作為一種技術的探討,並不一定保證實際中的可操作性,詳細程式碼可以閱讀原文檢視。

以上。

歡迎搜尋微訊號 mad_coder 或者掃描二維碼關注公眾號:

93cfyj.jpg

相關文章