如何用OpenCV在Python中实现人脸检测
選自towardsdatascience
作者:Ma?l Fabien
機器之心編譯
參與:高璇、張倩、淑婷
本教程將介紹如何使用 OpenCV 和 Dlib 在 Python 中創(chuàng)建和運行人臉檢測算法。同時還將添加一些功能,以同時檢測多個面部的眼睛和嘴巴。本文介紹了人臉檢測的最基本實現(xiàn),包括級聯(lián)分類器、HOG 窗口和深度學(xué)習(xí) CNN。我們將通過以下方法實現(xiàn)人臉檢測:
使用 OpenCV 的 Haar 級聯(lián)分類器
使用 Dlib 的方向梯度直方圖
使用 Dlib 的卷積神經(jīng)網(wǎng)絡(luò)
本文代碼的 Github 庫(以及作者其他博客的代碼)鏈接:
https://github.com/maelfabien/Machine_Learning_Tutorials?
我們將使用用于計算機視覺的開源庫 OpenCV,它用 C/C++編寫,有 C++、Python 和 Java 接口。同時支持 Windows、Linux、MacOS、iOS 和 Android 系統(tǒng)。同時我們還需要工具包 Dlib,它是一個包含機器學(xué)習(xí)算法和創(chuàng)建復(fù)雜軟件的 C++工具包。
步驟
第一步是安裝 OpenCV 和 Dlib。運行以下命令:
pip?install?dlib
文件生成的路徑如下(版本不同,路徑會稍有差別):
如果在使用 Dlib 時出現(xiàn)問題,請參見文章:https://www.pyimagesearch.com/2018/01/22/install-dlib-easy-complete-guide/
導(dǎo)入工具包和模型路徑
創(chuàng)建一個新的 Jupyter notebook/Python 文件,從以下代碼開始:
import?matplotlib.pyplot?as?plt
import?dlib
from?imutils?import?face_utils
font?=?cv2.FONT_HERSHEY_SIMPLEX
級聯(lián)分類器
首先研究級聯(lián)分類器。
理論
級聯(lián)分類器,即使用類 Haar 特征工作的級聯(lián)增強分類器,是集成學(xué)習(xí)的一種特殊情況,稱為 boost。它通常依賴于 Adaboost 分類器(以及其他模型,如 Real Adaboost、Gentle Adaboost 或 Logitboost)。
級聯(lián)分類器在包含檢測目標(biāo)的幾百個樣本圖像以及不包含檢測目標(biāo)的其他圖像上進行訓(xùn)練。
我們?nèi)绾螜z測圖上是否有人臉呢?有一種名為 Viola-Jones 的框架的算法,包括了實時人臉檢測所需的所有步驟:
提取 Haar 特征,特征來自 Haar 小波
創(chuàng)建圖像
Adaboost 訓(xùn)練
級聯(lián)分類器
Haar 特征選擇
人臉上最常見的一些共同特征如下:
與臉頰相比,眼部顏色較深
與眼睛相比,鼻梁區(qū)域較為明亮
眼睛、嘴巴、鼻子的位置較為固定......
這些特征稱為 Haar 特征。過程如下所示:
Haar 特征
在上圖中,第一個特征測量眼部和上臉頰之間的強度差異。特征值計算的方法很簡單,對黑色區(qū)域中的像素求和再減去白色區(qū)域中的像素即可。
然后,將這個矩形作為卷積核作用到整個圖像。為了不產(chǎn)生遺漏,我們需要用到每個卷積核的所有的維度和位置。簡單的 24 * 24 的圖像可能會產(chǎn)生超過 160000 個特征,每個特征由像素值的和/差組成。這樣在計算上無法實現(xiàn)實時人臉檢測。那么,該如何加快這個過程呢?
一旦通過矩形框識別到有用區(qū)域,則在與之完全不同的區(qū)域上就無需再做計算了。這一點可以通過 Adaboost 實現(xiàn)。
使用積分圖像原理計算矩形框特征的方法更快。我們將在下一節(jié)介紹這一點。
原始論文中提到幾種可用于 Haar 特征提取的矩形框:
雙矩形特征計算的是兩個矩形區(qū)域內(nèi)像素和的差,主要用于檢測邊緣 (a,b)
三矩形特征計算的是中心矩形和減去兩個外部矩形和的差,主要用于檢測線 (c,d)
四矩形特征計算的是矩形對角線對之間的差 (e)
Haar 矩形
特征提取完成后,使用 Adaboost 分類器將它們應(yīng)用于訓(xùn)練集,該分類器結(jié)合了一組弱分類器來創(chuàng)建準(zhǔn)確的集成模型。只需 200 個特征(最初是 16 萬個),實現(xiàn)了 95%的準(zhǔn)確率。該論文的作者提取了 6000 個特征。
積分圖像
以卷積核的形式計算特征需要花費很長時間。出于這個原因,作者 Viola 和 Jones 提出了圖像的中間表示:積分圖像。積分圖像的作用是僅使用四個值簡單地計算矩形和。我們來看看它是如何工作的!
假設(shè)我們想要確定一個坐標(biāo)為 (x,y) 的給定像素的矩形特征。然后,像素的積分圖像是給定像素的上方和左側(cè)的像素之和。
其中 ii(x,y) 是積分圖像,i(x,y) 是原始圖像。
當(dāng)計算整個積分圖像時,有一種只需要遍歷一次原始圖像的遞歸方法。實際上,我們可以定義以下一對遞歸形式:
其中 s(x,y) 是累積行和,而 s(x?1)=0, ii(?1,y)=0。
這是怎么實現(xiàn)的呢?假設(shè)我們想要估算區(qū)域 D 的像素總和。我們已經(jīng)定義了 3 個其他區(qū)域:A,B 和 C。
點 1 處的積分圖像的值是矩形 A 中的像素的總和。
點 2 處的值為 A + B。
點 3 處的值為 A + C。
點 4 處的值是 A + B + C + D。
因此,區(qū)域 D 中的像素之和可以簡單地計算為: 4+1?(2+3)。
這樣我們僅使用 4 個數(shù)組值就計算出了矩形 D 的值。
人們應(yīng)該知道矩形在實際中是非常簡單的特征,但對于人臉檢測已經(jīng)足夠了。當(dāng)涉及復(fù)雜問題時,可調(diào)濾波器往往更靈活多變。
可調(diào)濾波器
使用 Adaboost 學(xué)習(xí)分類函數(shù)
給定一組帶標(biāo)簽的訓(xùn)練圖像(正負(fù)樣本均有),Adaboost 用于:
提取一小部分特征
訓(xùn)練分類器
由于 16 萬個特征中的大多數(shù)特征與之極不相關(guān),因此我們設(shè)計一個增強模型的弱學(xué)習(xí)算法,用來提取單個矩形特征,將最好的正負(fù)樣本區(qū)分開。
級聯(lián)分類器
雖然上述過程非常有效,但仍存在一個重大問題。在圖像中,大部分圖像為非面部區(qū)域。對圖像的每個區(qū)域給予等同的注意力是沒有意義的,因為我們應(yīng)該主要關(guān)注最有可能包含人臉的區(qū)域。Viola 和 Jone 使用級聯(lián)分類器在減少了計算時間的同時,實現(xiàn)了更高的檢測率。
關(guān)鍵思想是在識別人臉區(qū)域時排除不含人臉的子窗口。由于任務(wù)是正確識別人臉,我們希望假陰率最小,即包含人臉卻未被識別的子窗口最少。
每個子窗口都使用一系列分類器。這些分類器是簡單的決策樹:
如果第一個分類器檢測為正樣本,繼續(xù)用第二個
如果第二個分類器檢測是正樣本,繼續(xù)用第三個
以此類推
雖然有時可能包含人臉的圖被認(rèn)成負(fù)樣本被子窗口漏檢。但初級分類器以較低的計算成本篩除了大多數(shù)負(fù)樣本,下圖的分類器可額外消除更多的負(fù)樣本,但需要更多的計算量。
使用 Adaboost 訓(xùn)練分類器,并調(diào)整閾值使錯誤率降到最低。在訓(xùn)練該模型時,變量如下:
每個階段分類器數(shù)量
每個階段的特征數(shù)量
每個階段的閾值
幸運的是,在 OpenCV 中,整個模型已經(jīng)經(jīng)過預(yù)訓(xùn)練,可直接用于人臉檢測。
如果想了解有關(guān) Boosting 技術(shù)的更多信息,歡迎查看作者關(guān)于 Adaboost 的文章:
https://maelfabien.github.io/machinelearning/adaboost
輸入
下一步是找到預(yù)訓(xùn)練的權(quán)重。我們將使用默認(rèn)的預(yù)訓(xùn)練模型來檢測人臉、眼睛和嘴巴。文件應(yīng)位于此路徑(python 版本不同,路徑略有不同):
確定路徑后,以此方式聲明級聯(lián)分類器:
eyePath?=?"/usr/local/lib/python3.7/site-packages/cv2/data/haarcascade_eye.xml"
smilePath?=?"/usr/local/lib/python3.7/site-packages/cv2/data/haarcascade_smile.xml"
faceCascade?=?cv2.CascadeClassifier(cascPath)
eyeCascade?=?cv2.CascadeClassifier(eyePath)
smileCascade?=?cv2.CascadeClassifier(smilePath)
檢測圖像中的人臉
在實現(xiàn)實時人臉檢測算法之前,讓我們先嘗試在圖像上簡單檢測一下。從加載測試圖像開始:
gray?=?cv2.imread('face_detect_test.jpeg',?0)
plt.figure(figsize=(12,8))
plt.imshow(gray,?cmap='gray')
plt.show()
測試圖像
然后開始檢測人臉,并將檢測到的人臉框起來。
faces?=?faceCascade.detectMultiScale(
gray,
scaleFactor=1.1,
minNeighbors=5,
flags=cv2.CASCADE_SCALE_IMAGE
)
#?For?each?face
for?(x,?y,?w,?h)?in?faces:?
????#?Draw?rectangle?around?the?face
????cv2.rectangle(gray,?(x,?y),?(x+w,?y+h),?(255,?255,?255),?3)
以下是 detectMultiScale 函數(shù)常見的參數(shù)列表:
scaleFactor:確定每個圖像縮放比例大小。
minNeighbors:確定每個候選矩形應(yīng)保留多少個相鄰框。
minSize:最小目標(biāo)的大小。小于該值的目標(biāo)將被忽略。
maxSize:最大目標(biāo)的大小。大于該值的目標(biāo)將被忽略。
最后,顯示結(jié)果:
plt.imshow(gray,?cmap='gray')
plt.show()
在測試圖像上成功檢測到人臉。現(xiàn)在開始實時檢測!
實時人臉檢測
下面繼續(xù)進行實時人臉檢測的 Python 實現(xiàn)。第一步是啟動攝像頭,并拍攝視頻。然后,將圖像轉(zhuǎn)換為灰度圖。這用于減小輸入圖像的維數(shù)。實際上,我們應(yīng)用了一個簡單的線性變換,而不是每個像素用三個點來描述紅、綠、藍。
這在 OpenCV 中是默認(rèn)實現(xiàn)的。
while?True:
????#?Capture?frame-by-frame
????ret,?frame?=?video_capture.read()
????gray?=?cv2.cvtColor(frame,?cv2.COLOR_BGR2GRAY)
現(xiàn)在我們使用上述定義的 faceCascade 變量,它包含一個預(yù)訓(xùn)練算法,現(xiàn)在將其用于灰度圖。
????????gray,
????????scaleFactor=1.1,
????????minNeighbors=5,
????????minSize=(30,?30),
????????flags=cv2.CASCADE_SCALE_IMAGE
????????)
對于檢測到的每個人臉,都加上一個矩形框:
????????if?w?>?250?:
????????????cv2.rectangle(frame,?(x,?y),?(x+w,?y+h),?(255,?0,?0),?3)
????????????roi_gray?=?gray[y:y+h,?x:x+w]
????????????roi_color?=?frame[y:y+h,?x:x+w]
對于檢測到的每張嘴,都加上一個矩形框:
????????roi_gray,
????????scaleFactor=?1.16,
????????minNeighbors=35,
????????minSize=(25,?25),
????????flags=cv2.CASCADE_SCALE_IMAGE
????)
????for?(sx,?sy,?sw,?sh)?in?smile:
????????cv2.rectangle(roi_color,?(sh,?sy),?(sx+sw,?sy+sh),?(255,?0,?0),?2)
????????cv2.putText(frame,'Smile',(x?+?sx,y?+?sy),?1,?1,?(0,?255,?0),?1)
對于檢測到的每雙眼睛,都加上一個矩形框:
????for?(ex,ey,ew,eh)?in?eyes:
????????cv2.rectangle(roi_color,(ex,ey),(ex+ew,ey+eh),(0,255,0),2)
????????cv2.putText(frame,'Eye',(x?+?ex,y?+?ey),?1,?1,?(0,?255,?0),?1)
然后計算人臉總數(shù),顯示整體圖像:
????#?Display?the?resulting?frame
????cv2.imshow('Video',?frame)
當(dāng)按下 q 鍵時,執(zhí)行退出選項。
if?cv2.waitKey(1)?&?0xFF?==?ord('q'):
????????break
最后當(dāng)所有操作完成后,關(guān)閉所有窗口。在 Mac 上關(guān)閉窗口存在一些問題,可能需要通過活動管理器退出 Python。
cv2.destroyAllWindows()
封裝
cascPath?=?"/usr/local/lib/python3.7/site-packages/cv2/data/haarcascade_frontalface_default.xml"
eyePath?=?"/usr/local/lib/python3.7/site-packages/cv2/data/haarcascade_eye.xml"
smilePath?=?"/usr/local/lib/python3.7/site-packages/cv2/data/haarcascade_smile.xml"
faceCascade?=?cv2.CascadeClassifier(cascPath)
eyeCascade?=?cv2.CascadeClassifier(eyePath)
smileCascade?=?cv2.CascadeClassifier(smilePath)
font?=?cv2.FONT_HERSHEY_SIMPLEX
video_capture?=?cv2.VideoCapture(0)
while?True:
????#?Capture?frame-by-frame
????ret,?frame?=?video_capture.read()
????gray?=?cv2.cvtColor(frame,?cv2.COLOR_BGR2GRAY)
????faces?=?faceCascade.detectMultiScale(
????????gray,
????????scaleFactor=1.1,
????????minNeighbors=5,
????????minSize=(200,?200),
????????flags=cv2.CASCADE_SCALE_IMAGE
????)
????#?Draw?a?rectangle?around?the?faces
????for?(x,?y,?w,?h)?in?faces:
????????cv2.rectangle(frame,?(x,?y),?(x+w,?y+h),?(255,?0,?0),?3)
????????????roi_gray?=?gray[y:y+h,?x:x+w]
????????????roi_color?=?frame[y:y+h,?x:x+w]
????????????cv2.putText(frame,'Face',(x,?y),?font,?2,(255,0,0),5)
????smile?=?smileCascade.detectMultiScale(
????????roi_gray,
????????scaleFactor=?1.16,
????????minNeighbors=35,
????????minSize=(25,?25),
????????flags=cv2.CASCADE_SCALE_IMAGE
????)
????for?(sx,?sy,?sw,?sh)?in?smile:
????????cv2.rectangle(roi_color,?(sh,?sy),?(sx+sw,?sy+sh),?(255,?0,?0),?2)
????????cv2.putText(frame,'Smile',(x?+?sx,y?+?sy),?1,?1,?(0,?255,?0),?1)
????eyes?=?eyeCascade.detectMultiScale(roi_gray)
????for?(ex,ey,ew,eh)?in?eyes:
????????cv2.rectangle(roi_color,(ex,ey),(ex+ew,ey+eh),(0,255,0),2)
????????cv2.putText(frame,'Eye',(x?+?ex,y?+?ey),?1,?1,?(0,?255,?0),?1)
????cv2.putText(frame,'Number?of?Faces?:?'?+?str(len(faces)),(40,?40),?font,?1,(255,0,0),2)??????
????#?Display?the?resulting?frame
????cv2.imshow('Video',?frame)
????if?cv2.waitKey(1)?&?0xFF?==?ord('q'):
??????break
#?When?everything?is?done,?release?the?capture
video_capture.release()
cv2.destroyAllWindows()
結(jié)果
我已經(jīng)制作了人臉檢測算法的 YouTube 視頻演示:
Dlib 的方向梯度直方圖(HOG)
第二種常用的人臉檢測工具由 Dlib 提供,它使用了方向梯度直方圖(HOG)的概念。論文《Histograms of Oriented Gradients for Human Detection》實現(xiàn)這一方案。
理論
HOG 背后的想法是將特征提取到一個向量中,并將其輸入到分類算法中,例如支持向量機,它將評估人臉(或?qū)嶋H想識別的任何對象)是否存在于某個區(qū)域中。
提取的特征是圖像梯度(方向梯度)方向的分布(直方圖)。梯度通常在邊緣和角落周圍較大,并允許我們檢測這些區(qū)域。
在原始論文中,該算法用于人體檢測,檢測過程如下:
預(yù)處理
首先,輸入圖像必須尺寸相同(可通過裁剪和縮放)。圖像長寬比要求為 1:2,因此輸入圖像的尺寸可能為 64x128 或 100x200。
計算梯度圖像
第一步是通過以下卷積核計算圖像的水平梯度和垂直梯度:
計算梯度的卷積核
圖像的梯度通常會消除非必要信息。
上面圖像的梯度可以通過下面的 python 語句找到:
im?=?np.float32(gray)?/?255.0
#?Calculate?gradient?
gx?=?cv2.Sobel(im,?cv2.CV_32F,?1,?0,?ksize=1)
gy?=?cv2.Sobel(im,?cv2.CV_32F,?0,?1,?ksize=1)
mag,?angle?=?cv2.cartToPolar(gx,?gy,?angleInDegrees=True)
繪制圖片:
plt.imshow(mag)
plt.show()
我們之前沒有預(yù)處理圖像。
計算 HOG
首先將圖像分成 8x8 個單元來提供緊湊表示,使 HOG 對噪聲更魯棒。然后,計算每個單元的 HOG。
為了估計區(qū)域內(nèi)的梯度方向,我們只需在每個區(qū)域內(nèi)的 64 個梯度方向值(8x8)及其大小(另外 64 個值)之間構(gòu)建直方圖。直方圖的類別對應(yīng)梯度的角度,從 0 到 180°。總共 9 類:0°,20°,40°...... 160°。
上面的代碼給了我們 2 個信息:
梯度方向
梯度大小
當(dāng)我們構(gòu)建 HOG 時,有 3 種情況:
角度小于 160°,且不介于兩類之間。在這種情況下,角度將添加到 HOG 的正確類中。
角度小于 160°,恰好在兩類之間。在這種情況下,像素被均分到左右兩側(cè)類中。
角度大于 160°。在這種情況下,我們認(rèn)為像素與 160°和 0°成比例。
每個 8x8 單元的 HOG 如下所示:
HOG
模塊歸一化
最后,可以用 16×16 的模塊對圖像進行歸一化,并使其對光照不變。這可以通過將大小為 8x8 的 HOG 的每個值除以包含它的 16x16 模塊的 HOG 的 L2 范數(shù)來實現(xiàn),這個模塊實際上是長度為 9*4 = 36 的簡單向量。
模塊歸一化
最后,將所有 36x1 向量連接成一個大向量。OK!現(xiàn)在有了特征向量,我們可以在上面訓(xùn)練一個軟 SVM 分類器(C=0.01)。
檢測圖像上的人臉
實現(xiàn)非常簡單:
rects?=?face_detect(gray,?1)
for?(i,?rect)?in?enumerate(rects):
(x,?y,?w,?h)?=?face_utils.rect_to_bb(rect)
????cv2.rectangle(gray,?(x,?y),?(x?+?w,?y?+?h),?(255,?255,?255),?3)
plt.figure(figsize=(12,8))
plt.imshow(gray,?cmap='gray')
plt.show()
實時人臉檢測
如前所述,該算法非常容易實現(xiàn)。我們還實現(xiàn)了一個更輕量的版本,只用來識別人臉。Dlib 讓人臉關(guān)鍵點的檢測更加容易,但這是另一個話題。
flag?=?0
while?True:
????ret,?frame?=?video_capture.read()
????gray?=?cv2.cvtColor(frame,?cv2.COLOR_BGR2GRAY)
????rects?=?face_detect(gray,?1)
????for?(i,?rect)?in?enumerate(rects):
????????(x,?y,?w,?h)?=?face_utils.rect_to_bb(rect)
????????cv2.rectangle(frame,?(x,?y),?(x?+?w,?y?+?h),?(0,?255,?0),?2)
????????cv2.imshow('Video',?frame)
????if?cv2.waitKey(1)?&?0xFF?==?ord('q'):
????????break
video_capture.release()
cv2.destroyAllWindows()
Dlib 中的卷積神經(jīng)網(wǎng)絡(luò)
最后一種方法基于卷積神經(jīng)網(wǎng)絡(luò)。為了增強結(jié)果,它還實現(xiàn)了最大邊緣目標(biāo)檢測(MMOD)。
理論
卷積神經(jīng)網(wǎng)絡(luò)是主要用于計算機視覺的前饋神經(jīng)網(wǎng)絡(luò)。它們提供自動圖像預(yù)處理以及密集的神經(jīng)網(wǎng)絡(luò)部分。CNN 還是用來處理帶有網(wǎng)格狀拓?fù)涞臄?shù)據(jù)的特殊神經(jīng)網(wǎng)絡(luò)。它的架構(gòu)靈感來自動物視覺皮層。
以前的方法中,很大一部分工作是選擇濾波器來創(chuàng)建特征,以便盡從圖像中可能多地提取信息。隨著深度學(xué)習(xí)和計算能力的提高,這項工作現(xiàn)在可以實現(xiàn)自動化。CNN 的名稱就來自我們用一組濾波器卷積初始圖像輸入的事實。需要選擇的參數(shù)仍是需要應(yīng)用的濾波器數(shù)量以及尺寸。濾波器的尺寸稱為步幅。一般步幅設(shè)置在 2 到 5 之間。
在這種特定情況下,CNN 的輸出是二分類,如果有人臉,則取值 1,否則取 0。
檢測圖像上的人臉
一些元素在實現(xiàn)中會發(fā)生變化。
第一步是下載預(yù)訓(xùn)練模型:https://github.com/davisking/dlib-models/blob/master/mmod_human_face_detector.dat.bz2
?將下載后的權(quán)重放到文件夾中,并定義 dnnDaceDetector:
然后,與之前做的相同:
for?(i,?rect)?in?enumerate(rects):
????x1?=?rect.rect.left()
????y1?=?rect.rect.top()
????x2?=?rect.rect.right()
????y2?=?rect.rect.bottom()
????#?Rectangle?around?the?face
????cv2.rectangle(gray,?(x1,?y1),?(x2,?y2),?(255,?255,?255),?3)
plt.figure(figsize=(12,8))
plt.imshow(gray,?cmap='gray')
plt.show()
實時人臉檢測
最后,實現(xiàn)實時 CNN 人臉檢測:
flag?=?0
while?True:
????#?Capture?frame-by-frame
????ret,?frame?=?video_capture.read()
????gray?=?cv2.cvtColor(frame,?cv2.COLOR_BGR2GRAY)
????rects?=?dnnFaceDetector(gray,?1)
????for?(i,?rect)?in?enumerate(rects):
????????x1?=?rect.rect.left()
????????y1?=?rect.rect.top()
????????x2?=?rect.rect.right()
????????y2?=?rect.rect.bottom()
????????#?Rectangle?around?the?face
????????cv2.rectangle(frame,?(x1,?y1),?(x2,?y2),?(0,?255,?0),?2)
????#?Display?the?video?output
????cv2.imshow('Video',?frame)
????#?Quit?video?by?typing?Q
????if?cv2.waitKey(1)?&?0xFF?==?ord('q'):
????????break
video_capture.release()
cv2.destroyAllWindows()
如何選擇模型
這是一個很難回答的問題,但我們只討論兩個重要指標(biāo):
計算時間
準(zhǔn)確率
在速度方面,HOG 是最快的算法,其次是 Haar 級聯(lián)分類器和 CNN。
但是,Dlib 中的 CNN 是準(zhǔn)確率最高的算法。HOG 表現(xiàn)也很好,但在識別較小的人臉時會有一些問題。Haar 級聯(lián)分類器的整體表現(xiàn)與 HOG 相似。
考慮到實時人臉檢測的速度,我在個人項目中使用了 HOG。
希望這個關(guān)于 OpenCV 和 Dlib 的人臉檢測的快速教程能對你有所幫助。
原文鏈接:https://towardsdatascience.com/a-guide-to-face-detection-in-python-3eab0f6b9fc1
總結(jié)
以上是生活随笔為你收集整理的如何用OpenCV在Python中实现人脸检测的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Dropout的前世与今生
- 下一篇: 45页的NAS神经网络搜索的综述,请查收