| 一年多以前我腦子一熱,想做一款移動(dòng)應(yīng)用:一款給學(xué)生朋友用的“錯(cuò)題集”應(yīng)用,可以將錯(cuò)題拍照,記錄圖像的同時(shí),還能自動(dòng)分類。比如拍個(gè)題目,應(yīng)用會(huì)把它自動(dòng)分類為”物理/力學(xué)/曲線運(yùn)動(dòng)”。 當(dāng)然,這個(gè)項(xiàng)目其實(shí)不靠譜,市場(chǎng)上已經(jīng)有太多“搜題”類應(yīng)用了。但過程很有趣,導(dǎo)致我過了一年多,清理磁盤垃圾時(shí),還舍不得刪掉這個(gè)項(xiàng)目的“成果”,所以干脆回收利用一下,寫篇文章圈圈粉。 源碼地址: 這個(gè)項(xiàng)目,核心要解決的問題就是文本分類。所以最初想到的方案是先 OCR 圖片轉(zhuǎn)文本,然后分詞,再計(jì)算 tf-idf,最后用 SVM 分類。但這個(gè)方案的問題是:開源 OCR 普遍需要自己訓(xùn)練,且需要做大量的優(yōu)化、調(diào)校和訓(xùn)練,才能在中文識(shí)別上有不錯(cuò)的效果,加上圖像上還會(huì)有公式、幾何圖形,這些特征也會(huì)決定分類,這又提高了對(duì) OCR 的要求。 所以我最終選擇的方案是,不使用 OCR,而是直接從圖像中尋找有區(qū)分性的、魯棒的特征,作為視覺詞匯。之后再通過傳統(tǒng)文本分類的方法,訓(xùn)練分類器。 下面將展示整個(gè)訓(xùn)練過程,訓(xùn)練的樣本來自《2016 B版 5年高考3年模擬:高考理數(shù)》,并手工標(biāo)注了14個(gè)分類,每個(gè)分類下約50個(gè)樣本,每個(gè)樣本為一個(gè)題目, 圖像為手機(jī)拍攝。 樣本數(shù)據(jù)下載地址: 本文中大部分算法庫(kù)來自numpy、scipy、opencv、skimage、sklearn。 為了獲取到穩(wěn)定的特征,我們需要對(duì)圖像進(jìn)行預(yù)處理,包括調(diào)整圖像大小,將圖像縮放到合適尺寸;旋轉(zhuǎn)圖像,或者說調(diào)整成水平;二值化,去除色彩信息,產(chǎn)生黑白圖像。1.1. 調(diào)整圖像大小 調(diào)整的目的是為了讓圖像中文字的尺寸保持大致相同的像素尺寸。這里做了一個(gè)簡(jiǎn)單假設(shè),即:圖像基本是一段完整的文本,比如一個(gè)段落,或者一頁(yè)文檔,那么不同的圖像中,每行文本的字?jǐn)?shù)相差不會(huì)很大。 這樣我就可以從我所了解的、少得可憐的圖像工具庫(kù)里找到一個(gè)工具了:直線擬合。即通過擬合的直線(線段)長(zhǎng)度與圖像寬度的比例,調(diào)整圖像的大小。下圖為兩張不同尺寸圖像,經(jīng)過多次擬合+調(diào)整大小后的結(jié)果,其中紅色算法檢查到的直線(線段)。 下面是使用 opencv 直線擬合的代碼: 1.2. 圖像二值化 二值算法選用skimage.filters.threshold_adaptive(局部自適應(yīng)閥值的二值化), 試下來針對(duì)這種場(chǎng)景,這個(gè)算法效果最好,其他算法可以去scikit-image文檔了解。下圖為全局閥值和局部自適應(yīng)閥值的效果對(duì)比: 文檔地址: http:///docs/dev/api/skimage.filters.html?highlight=threshold_adaptive#skimage.filters.threshold_adaptive 相關(guān)代碼如下: 1.3. 旋轉(zhuǎn)圖像 從第一步獲取到的直線,可以計(jì)算出圖像的傾斜角度,針對(duì)只是輕微傾斜的圖像,可以反向旋轉(zhuǎn)進(jìn)行調(diào)整。由于可能存在干擾線條,所以這里取所有直線傾斜角度的中值比平均值更合適。下圖展示了圖像旋轉(zhuǎn)跳轉(zhuǎn)前后的效果: 相關(guān)代碼如下: 這里的思路是,首先通過形態(tài)學(xué)處理,可以分割出文本行(的圖像),再?gòu)奈谋拘兄蟹指畛鲈~匯(的圖像),然后從”詞匯”中提取特征。但這里的需要克服的困難是: 
 針對(duì)以上問題的解決方案是: 
 下面將介紹具體實(shí)現(xiàn)。2.1. 提取文本行 由于預(yù)處理過程中已經(jīng)將樣本的圖像尺寸基本調(diào)整一致,所以可以比較容易的利用形態(tài)學(xué)的處理方法,分割出文本行。過程如下: 下圖展示了每一步的變化: 接下來可以利用scipy庫(kù)中的measurements.label方法,標(biāo)記出不同的的區(qū)域,下圖展示了標(biāo)注后的效果,不同區(qū)域以不同的灰度表示。  相關(guān)代碼如下:  接下來根據(jù)標(biāo)記的區(qū)域,可從圖像中裁剪出每行的數(shù)據(jù),如下圖:  相關(guān)代碼如下:  2.2. 提取特征(視覺詞匯) 裁剪出單行文本圖像后,我們可以將圖像中各列的像素的值各自累加,得到一個(gè)一緯數(shù)組,此數(shù)組中的每個(gè)局部最小值所在的位置,即為文字間的空隙。如下圖所示,其中藍(lán)色線為像素值的累加值,綠色線為其通過高斯濾波平滑后的效果,紅色線為最終檢測(cè)到的分割點(diǎn)。 詳細(xì)過程見下面代碼:  將單行的圖像按上述方法獲取的分割點(diǎn)進(jìn)行裁剪,裁剪出單個(gè)字符,然后再把相鄰的單個(gè)字符進(jìn)行組合,得到最終的特征數(shù)據(jù)。組合相鄰字符是為了使特征中保留詞匯信息,同時(shí)增加魯棒性。下圖為最終獲得的特征信息:  本文中使用的所有樣本,最終能提取出約30萬個(gè)特征。2.3. 選擇特征描述子 選擇合適的特征描述子通常需要直覺+運(yùn)氣+不停的嘗試(好吧我承認(rèn)這里沒有什么經(jīng)驗(yàn)可分享),經(jīng)過幾次嘗試,最終選中了HOG(方向梯度直方圖)描述子。HOG 最讓人熟悉的應(yīng)用領(lǐng)域應(yīng)該是行人檢測(cè)了,它很適合描述鋼性物體的邊緣特征(方向),而印刷字體首先是剛性的,其次其關(guān)鍵信息都包含在邊緣的方向上,所以理論上也適合用 HOG 描述。下圖為文字圖像及其 HOG 描述子的可視化: 更多關(guān)于HOG的介紹請(qǐng)點(diǎn)擊: http:///docs/dev/auto_examples/features_detection/plot_hog.html#sphx-glr-auto-examples-features-detection-plot-hog-py  代碼如下:  對(duì)詞匯進(jìn)行人工標(biāo)注工作量太大,所以最好能做到自動(dòng)分類。我的做法是先聚類,再基于聚類的結(jié)果訓(xùn)練分類器。 但有個(gè)問題,主流的聚類算法中,除了 K-Means 外,其他都不適合處理大量樣本(目前有30萬+樣本),但 K-Means 在這個(gè)場(chǎng)景上聚類效果不佳,高頻但不相關(guān)的詞匯容易被聚成一類,而 DBSCAN 效果很好,但樣本數(shù)一多,所需時(shí)間幾何級(jí)增長(zhǎng)(在我的機(jī)器上,超過兩萬個(gè)樣本就需要耗費(fèi)數(shù)個(gè)小時(shí))。 下圖來自sklearn 文檔,對(duì)各聚類算法做了比較:   為解決這一問題,我的做法是: 1. 先對(duì)每類樣本下的詞匯用 DBSCAN 聚類(約1萬個(gè)詞匯樣本),得到一級(jí)分類。 2. 聚類后,計(jì)算每個(gè)一級(jí)分類的中心,然后以所有中心為樣本再用DBSCAN聚類,得到二級(jí)分類。完成后,原一級(jí)分類中心的新分類,即代表其原一級(jí)分類下所有元素的分類。 聚類的過程為,使用前面提取的HOG特征,先 PCA 降緯,再 DBSCAN 聚類。這里注意,計(jì)算二級(jí)分類時(shí),PCA應(yīng)使用全局樣本計(jì)算。 分類器使用SGDClassifier,原因是其支持分批計(jì)算,不至于導(dǎo)致內(nèi)存不足。 本文中使用的樣本,最終得到3000+詞匯類型。下圖為分類效果,其中每一行為一個(gè)分類:  有了詞匯分類器,我們終于可以識(shí)別出每個(gè)文本樣本上所包含的詞匯了(事實(shí)上前面步驟的中間過程也能得到每個(gè)樣本的詞匯信息),于是我們可以給每個(gè)樣本計(jì)算一個(gè)詞袋模型(即用每個(gè)詞出現(xiàn)的次數(shù)表示一篇文本),再通過池袋模型計(jì)算TF-IDF模型(即用每個(gè)詞的 TF*IDF 值表示一篇文本),并最終訓(xùn)練 SVM 分類器。 下面展示了此過程的主要代碼:  執(zhí)行結(jié)果如下:  測(cè)試集上正確率81%,召回率 78%。個(gè)別分類正確率較低,可能是因?yàn)闃颖緮?shù)太少,另外訓(xùn)練過程大多使用默認(rèn)參數(shù),若進(jìn)行細(xì)致調(diào)校,應(yīng)該還有提高空間。 此項(xiàng)目完整代碼及樣本數(shù)據(jù)均可下載,地址為: 任何想在實(shí)際項(xiàng)目中使用此方法的朋友請(qǐng)注意,以上方法目前只在一個(gè)樣本庫(kù)中測(cè)試過,在其他樣本庫(kù)中表現(xiàn)如何還不知道,但愿沒把你帶坑里。 | 
|  |