精品文章,第一時間送達
來源:pyimagesearch
編譯:三石、大明
轉載自:新智元,未經允許不得二次轉載
【導讀】衆所周知,自然場景下的文本檢測是極具挑戰性的。本文便使用OpenCV和EAST文本檢測器在自然場景下對文本進行了檢測,包括圖像中的文本檢測,以及視頻中的文本檢測,並對其原理與實現過程做了詳盡的描述。
在本教程中,您將學習如何使用EAST文本檢測器在自然場景下檢測文本。
本教程的主要目的是教讀者利用OpenCV和EAST文本檢測器來檢測文本。
運行環境:
EAST文本檢測器需要OpenCV3.4.2或更高版本,有需要的讀者可以先安裝OpenCV。
主要內容:
- 教程第一部分分析爲何在自然場景下進行文本檢測的挑戰性是如此之高。
- 接下來簡要探討EAST文本檢測器,爲何使用,算法新在何處,並附上相關論文供讀者參考。
- 最後提供 Python + OpenCV文本檢測實現方式,供讀者在自己的應用中使用。
爲何在自然場景下進行文本檢測的挑戰性是如此之高
由於光照條件、圖片質量以及目標非線性排列等因素的限制,自然場景下的文本檢測任務難度較大
受約束的受控環境中的文本檢測任務通常可以使用基於啓發式的方法來完成,比如利用梯度信息或文本通常被分成段落呈現,並且字符一般都是成直線排列等信息。
但自然場景下文本檢測則不同,而且更具挑戰性。
由於廉價數碼相機和智能手機的普及,我們需要高度關注圖像拍攝時的條件。Celine Mancas-Thillou和Bernard Gosselin在其2017年發表的優秀論文《自然場景文本理解》中描述了的自然場景文本檢測面對的主要挑戰:
- 圖像/傳感器噪音:手持式相機的傳感器噪音通常要高於傳統掃描儀。此外,廉價相機通常會介入原始傳感器的像素以產生真實的顏色。
- 視角:自然場景中的文本存在不平行的觀測角度問題,使文本更難以識別。
- 模糊:不受控制的環境下,文本往往會變模糊,尤其是如果最終用戶使用的智能手機的拍攝穩定性不足時,問題就更明顯。
- 照明條件:我們無法對自然場景圖像中的照明條件做出任何假設。可能在接近黑暗的條件下,相機上的閃光燈可能會亮起,也可能在豔陽高照的條件下,使整個圖像趨於飽和。
- 分辨率:每臺圖像捕捉設備都是不同的,可能存在分辨率過低的攝像機拍出的圖像。
- 非紙質對象:大多數(但不是全部)紙張是不反光的。而自然場景中的文字可能是反光的,比如徽標,標誌等。
- 非平面目標:想象文字印在瓶子上的情況,瓶子表面上的文本會扭曲和變形。雖然我們自己仍可以輕鬆地“檢測”並閱讀文本,但算法做起來就會很困難。我們需要能夠處理這種情況的用例。
- 處理條件未知:我們不能使用任何先驗信息來爲算法提供關於文本所在位置的“線索”。
OpenCV’sEAST文本檢測器甚至可以識別模糊圖片中的文字
EAST深度學習文本檢測器
EAST文本檢測器全卷積網絡結構
EAST是一種基於深度學習的文本探測器,即高效、準確的場景文本檢測(Efficient and Accurate Scene Text detectionpipeline)。更重要的是,深度學習模型是端對端的,因此可能繞開一般文本識別器用的計算成本高昂的子算法,比如候選對象聚合和詞彙分割等。
項目結構
首先使用Tree終端命令來瀏覽項目結構:
1$ tree --dirsfirst 2. 3├── images 4│ ├── car_wash.png 5│ ├── lebron_james.jpg 6│ └── sign.jpg 7├── frozen_east_text_detection.pb 8├── text_detection.py 9└── text_detection_video.py 10 111 directory, 6 files
在images/ 目錄下已有三張樣圖,讀者可以自己添加更多圖片。
我們使用兩個.py 文件:
- text_detection.py : 檢測靜態圖像中的文本
- text_detection_video.py : 檢測網絡攝像頭或輸入圖像文件中的文本
兩個腳本都使用EAST模型 ( frozen_east_text_detection.pb )
注意事項
本文中介紹的實例基於OpenCV的官方C++實例,在轉換爲Python的過程中可能會遇見一些問題。
比如,Python中沒有Point2f 和 RotatedRect函數,所以不能完全再現C++環境下的實現。
其次,NMSBoxes函數不返回Python綁定的任何值,最終導致OpenCV報錯。 NMSBoxes函數可以在OpenCV3.4.2中使用,但我無法對其進行詳盡的測試。
使用OpenCV實現文本檢測器的構建
在開始之前,我想再次指出,您至少需要在系統上安裝OpenCV 3.4.2(或OpenCV 4)才能使用OpenCV的EAST文本檢測器,因此如果您還沒有安裝OpenCV 3.4.2或更高版本,請參閱後文的OpenCV安裝指南。
接下來,安裝或升級你的系統中的imutils 。
1$ pip install --upgrade imutils
此時,系統設置已經完成,打開 text_detection.py ,輸入以下代碼:
1# import the necessary packages 2from imutils.object_detection import non_max_suppression 3import numpy as np 4import argparse 5import time 6import cv2 7 8# construct the argument parser and parse the arguments 9ap = argparse.ArgumentParser() 10ap.add_argument("-i", "--image", type=str, 11 help="path to input image") 12ap.add_argument("-east", "--east", type=str, 13 help="path to input EAST text detector") 14ap.add_argument("-c", "--min-confidence", type=float, default=0.5, 15 help="minimum probability required to inspect a region") 16ap.add_argument("-w", "--width", type=int, default=320, 17 help="resized image width (should be multiple of 32)") 18ap.add_argument("-e", "--height", type=int, default=320, 19 help="resized image height (should be multiple of 32)") 20args = vars(ap.parse_args())
首先,我們在第2-6行導入所需的包和模塊。注意,我們從imutils.object_detection導入NumPy,OpenCV和non_max_suppression實現。
然後我們繼續解析第9-20行的五個命令行參數:
- --image:輸入圖像的路徑。
- --east:EAST場景文本檢測器模型文件路徑。
- --min-confidence:確定文本的概率閾值。可選,默認值= 0.5。
- --width:調整後的圖像寬度 - 必須是32的倍數。可選,默認值= 320。
- --height:調整後的圖像高度 - 必須是32的倍數。可選,默認值= 320。
重要提示:EAST文本要求輸入圖像尺寸爲32的倍數,因此如果您選擇調整圖像的寬度和高度值,請確保這兩個值是32的倍數!
然後加載圖像並調整大小:
22# load the input image and grab the image dimensions 23image = cv2.imread(args["image"]) 24orig = image.copy() 25(H, W) = image.shape[:2] 26 27# set the new width and height and then determine the ratio in change 28# for both the width and height 29(newW, newH) = (args["width"], args["height"]) 30rW = W / float(newW) 31rH = H / float(newH) 32 33# resize the image and grab the new image dimensions 34image = cv2.resize(image, (newW, newH)) 35(H, W) = image.shape[:2]
第23和24行加載並複製輸入圖像。
第30行和第31行確定原始圖像尺寸與新圖像尺寸的比率(基於爲--width和--height提供的命令行參數)。
然後我們調整圖像大小,忽略縱橫比(第34行)。
爲了使用OpenCV和EAST深度學習模型執行文本檢測,我們需要提取兩層的輸出特徵映射:
37# define the two output layer names for the EAST detector model that 38# we are interested -- the first is the output probabilities and the 39# second can be used to derive the bounding box coordinates of text 40layerNames = [ 41 "feature_fusion/Conv_7/Sigmoid", 42 "feature_fusion/concat_3"]
我們在40-42行構建了layerNames的表:
- 第一層是我們的輸出sigmoid激活,它給出了包含文本或不包含文本的區域的概率。
- 第二層是表示圖像“幾何”的輸出要素圖。我們使用它來導出輸入圖像中文本的邊界框座標。
加載OpenCV的EAST文本檢測器:
44# load the pre-trained EAST text detector 45print("[INFO] loading EAST text detector...") 46net = cv2.dnn.readNet(args["east"]) 47 48# construct a blob from the image and then perform a forward pass of 49# the model to obtain the two output layer sets 50blob = cv2.dnn.blobFromImage(image, 1.0, (W, H), 51 (123.68, 116.78, 103.94), swapRB=True, crop=False) 52start = time.time() 53net.setInput(blob) 54(scores, geometry) = net.forward(layerNames) 55end = time.time() 56 57# show timing information on text prediction 58print("[INFO] text detection took {:.6f} seconds".format(end - start))
我們使用cv2.dnn.readNet將神經網絡加載到內存中,方法是將路徑傳遞給EAST檢測器作爲第46行的參數。
然後我們通過將其轉換爲第50行和第51行的blob來準備我們的圖像。要了解有關此步驟的更多信息,請參閱深度學習:OpenCV的blobFromImage如何工作。
要預測文本,我們可以簡單地將blob設置爲輸入並調用net.forward(第53和54行)。這些行被抓取時間戳包圍,以便我們可以在第58行打印經過的時間。
通過將layerNames作爲參數提供給net.forward,我們指示OpenCV返回我們感興趣的兩個特徵映射:
- 輸出幾何圖用於導出輸入圖像中文本的邊界框座標
- 類似地,分數圖包含文本的給定區域的概率:
我們需要逐一循環這些值:
60# grab the number of rows and columns from the scores volume, then 61# initialize our set of bounding box rectangles and corresponding 62# confidence scores 63(numRows, numCols) = scores.shape[2:4] 64rects = [] 65confidences = [] 66 67# loop over the number of rows 68for y in range(0, numRows): 69 # extract the scores (probabilities), followed by the geometrical 70 # data used to derive potential bounding box coordinates that 71 # surround text 72 scoresData = scores[0, 0, y] 73 xData0 = geometry[0, 0, y] 74 xData1 = geometry[0, 1, y] 75 xData2 = geometry[0, 2, y] 76 xData3 = geometry[0, 3, y] 77 anglesData = geometry[0, 4, y]
我們首先抓取score的維度(第63行),然後初始化兩個列表:
- rects:存儲文本區域的邊界框(x,y)座標
- 置信度:存儲與每個邊界框相關的概率
我們稍後將對這些區域使用non-maximasuppression。
在第68行開始循環。
第72-77行提取當前行的分數和幾何數據y。
接下來,我們遍歷當前所選行的每個列索引:
79# loop over the number of columns 80 for x in range(0, numCols): 81 # if our score does not have sufficient probability, ignore it 82 if scoresData[x] < args["min_confidence"]: 83 continue 84 85 # compute the offset factor as our resulting feature maps will 86 # be 4x smaller than the input image 87 (offsetX, offsetY) = (x * 4.0, y * 4.0) 88 89 # extract the rotation angle for the prediction and then 90 # compute the sin and cosine 91 angle = anglesData[x] 92 cos = np.cos(angle) 93 sin = np.sin(angle) 94 95 # use the geometry volume to derive the width and height of 96 # the bounding box 97 h = xData0[x] + xData2[x] 98 w = xData1[x] + xData3[x] 99 100 # compute both the starting and ending (x, y)-coordinates for 101 # the text prediction bounding box 102 endX = int(offsetX + (cos * xData1[x]) + (sin * xData2[x])) 103 endY = int(offsetY - (sin * xData1[x]) + (cos * xData2[x])) 104 startX = int(endX - w) 105 startY = int(endY - h) 106 107 # add the bounding box coordinates and probability score to 108 # our respective lists 109 rects.append((startX, startY, endX, endY)) 110 confidences.append(scoresData[x])
對於每一行,我們開始循環第80行的列。
我們需要通過忽略概率不高的區域來過濾弱文本檢測(第82行和第83行)。
當圖像通過網絡時,EAST文本檢測器自然地減少了體積大小——我們的體積實際上比輸入圖像小4倍,所以我們乘以4,使座標回到原始圖像。
我已經包含了如何在第91-93行提取角度數據;然而,正如我在前一節中提到的,不能像在C++中那樣構造一個旋轉的邊界框——如果你想要處理這個任務,那麼從第91行角度開始將是你的第一步。
第97-105行派生出文本區域的邊框座標。
然後我們分別更新rects和confi數據庫列表(第109行和第110行)。
最後一步是將非最大值抑制應用於我們的邊界框以抑制弱重疊邊界框,然後顯示結果文本預測:
112# apply non-maxima suppression to suppress weak, overlapping bounding 113# boxes 114boxes = non_max_suppression(np.array(rects), probs=confidences) 115 116# loop over the bounding boxes 117for (startX, startY, endX, endY) in boxes: 118 # scale the bounding box coordinates based on the respective 119 # ratios 120 startX = int(startX * rW) 121 startY = int(startY * rH) 122 endX = int(endX * rW) 123 endY = int(endY * rH) 124 125 # draw the bounding box on the image 126 cv2.rectangle(orig, (startX, startY), (endX, endY), (0, 255, 0), 2) 127 128# show the output image 129cv2.imshow("Text Detection", orig) 130cv2.waitKey(0)
正如我在上一節中提到的,我無法在我的OpenCV 4安裝(cv2.dnn.NMSBoxes)中使用非最大值抑制,因爲Python綁定沒有返回值,最終導致OpenCV出錯。我無法完全在OpenCV 3.4.2中進行測試,因此它可以在v3.4.2中運行。
相反,我使用了imutils包中提供的非最大值抑制實現(第114行)。結果仍然很好;但是,我無法將我的輸出與NMSBoxes函數進行比較,看它們是否相同。
第117-126行循環遍歷邊界框,將座標縮放到原始圖像尺寸,並將輸出繪製到orig圖像。直到按下一個按鍵爲止,原始圖像將一直顯示(129-130行)。
最後一個實驗需要注意的是,我們的兩個嵌套for循環用於循環第68-110行上的分數和幾何體(geometry volume),這是一個很好的例子,說明你可以利用Cython極大地加快pipeline的速度。我已經用OpenCV和Python演示了Cython在快速優化“for”像素循環中的強大功能。
OpenCV文本檢測器結果
在終端可以執行一下命令(注意兩個命令行參數):
1$ python text_detection.py --image images/lebron_james.jpg \ 2 --east frozen_east_text_detection.pb 3[INFO] loading EAST text detector... 4[INFO] text detection took 0.142082 seconds
結果應該如下圖所示:
文本檢測器成功識別出籃球巨星勒布朗·詹姆斯球衣上的文字
詹姆斯身上有三個文本區域。
現在讓我們嘗試檢測業務標誌的文本:
1$ python text_detection.py --image images/car_wash.png \ 2 --east frozen_east_text_detection.pb 3[INFO] loading EAST text detector... 4[INFO] text detection took 0.142295 seconds
使用EAST文本檢測器很容易識別出路邊洗車店的招牌文字
最後,我們將嘗試一個路標:
1$ python text_detection.py --image images/sign.jpg \ 2 --east frozen_east_text_detection.pb 3[INFO] loading EAST text detector... 4[INFO] text detection took 0.141675 seconds
基於Python和OpenCV的場景文本檢測器和EAST文本檢測器成功檢測出西班牙語的停車指示路牌
該場景中包含一個西班牙的停車標誌。“ALTO”可以準確的被OpenCV和EAST識別出來。
如你所知,EAST非常精確,且相對較快,平均每張圖片耗時約0.14秒。
OpenCV在視頻中進行文本檢測
我們可以基於上述工作,進一步使用OpenCV在視頻中進行文本檢測。
開啓text_detection_video.py,然後插入如下代碼:
1# import the necessary packages 2from imutils.video import VideoStream 3from imutils.video import FPS 4from imutils.object_detection import non_max_suppression 5import numpy as np 6import argparse 7import imutils 8import time 9import cv2
首先,我們導入一些包。我們將使用VideoStream訪問網絡攝像頭並用FPS來爲這個腳本測試每秒幀數。其他內容與前一節相同。
爲方便起見,定義一個新函數來爲我們的預測函數進行解碼 - 它將被重用於每個幀並使循環更清晰:
11def decode_predictions(scores, geometry): 12 # grab the number of rows and columns from the scores volume, then 13 # initialize our set of bounding box rectangles and corresponding 14 # confidence scores 15 (numRows, numCols) = scores.shape[2:4] 16 rects = [] 17 confidences = [] 18 19 # loop over the number of rows 20 for y in range(0, numRows): 21 # extract the scores (probabilities), followed by the 22 # geometrical data used to derive potential bounding box 23 # coordinates that surround text 24 scoresData = scores[0, 0, y] 25 xData0 = geometry[0, 0, y] 26 xData1 = geometry[0, 1, y] 27 xData2 = geometry[0, 2, y] 28 xData3 = geometry[0, 3, y] 29 anglesData = geometry[0, 4, y] 30 31 # loop over the number of columns 32 for x in range(0, numCols): 33 # if our score does not have sufficient probability, 34 # ignore it 35 if scoresData[x] < args["min_confidence"]: 36 continue 37 38 # compute the offset factor as our resulting feature 39 # maps will be 4x smaller than the input image 40 (offsetX, offsetY) = (x * 4.0, y * 4.0) 41 42 # extract the rotation angle for the prediction and 43 # then compute the sin and cosine 44 angle = anglesData[x] 45 cos = np.cos(angle) 46 sin = np.sin(angle) 47 48 # use the geometry volume to derive the width and height 49 # of the bounding box 50 h = xData0[x] + xData2[x] 51 w = xData1[x] + xData3[x] 52 53 # compute both the starting and ending (x, y)-coordinates 54 # for the text prediction bounding box 55 endX = int(offsetX + (cos * xData1[x]) + (sin * xData2[x])) 56 endY = int(offsetY - (sin * xData1[x]) + (cos * xData2[x])) 57 startX = int(endX - w) 58 startY = int(endY - h) 59 60 # add the bounding box coordinates and probability score 61 # to our respective lists 62 rects.append((startX, startY, endX, endY)) 63 confidences.append(scoresData[x]) 64 65 # return a tuple of the bounding boxes and associated confidences 66 return (rects, confidences)
在第11行,我們定義decode_prediction函數。該函數用於提取:
- 文本區域的邊界框座標;
- 文本區域檢測的概率。
這個專用函數將使代碼更易於閱讀和管理。
讓我們來解析命令行參數:
68# construct the argument parser and parse the arguments 69ap = argparse.ArgumentParser() 70ap.add_argument("-east", "--east", type=str, required=True, 71 help="path to input EAST text detector") 72ap.add_argument("-v", "--video", type=str, 73 help="path to optinal input video file") 74ap.add_argument("-c", "--min-confidence", type=float, default=0.5, 75 help="minimum probability required to inspect a region") 76ap.add_argument("-w", "--width", type=int, default=320, 77 help="resized image width (should be multiple of 32)") 78ap.add_argument("-e", "--height", type=int, default=320, 79 help="resized image height (should be multiple of 32)") 80args = vars(ap.parse_args())
69-80行代碼中命令行參數解析:
- --east:EAST場景文本檢測器模型文件路徑。
- --video:輸入視頻的路徑(可選)。如果提供了視頻路徑,那麼網絡攝像頭將不會被使用。
- --Min-confidence:確定文本的概率閾值(可選)。default=0.5。
- --width:調整圖像寬度(必須是32的倍數,可選)。default=320。
- --Height:調整圖像高度(必須是32的倍數,可選)。default=320。
與上一節中僅使用圖像的腳本(就命令行參數而言)的不同之處在於,用視頻替換了圖像參數。
接下里,我們將進行重要的初始化工作:
82# initialize the original frame dimensions, new frame dimensions, 83# and ratio between the dimensions 84(W, H) = (None, None) 85(newW, newH) = (args["width"], args["height"]) 86(rW, rH) = (None, None) 87 88# define the two output layer names for the EAST detector model that 89# we are interested -- the first is the output probabilities and the 90# second can be used to derive the bounding box coordinates of text 91layerNames = [ 92 "feature_fusion/Conv_7/Sigmoid", 93 "feature_fusion/concat_3"] 94 95# load the pre-trained EAST text detector 96print("[INFO] loading EAST text detector...") 97net = cv2.dnn.readNet(args["east"])
第84-86行上的高度、寬度和比率初始化將允許我們稍後適當地縮放邊界框。
我們定義了輸出層的名稱,並在第91-97行加載了預先訓練好的EAST文本檢測器。
下面的代碼設置了我們的視頻流和每秒幀數計數器:
99# if a video path was not supplied, grab the reference to the web cam 100if not args.get("video", False): 101 print("[INFO] starting video stream...") 102 vs = VideoStream(src=0).start() 103 time.sleep(1.0) 104 105# otherwise, grab a reference to the video file 106else: 107 vs = cv2.VideoCapture(args["video"]) 108 109# start the FPS throughput estimator 110fps = FPS().start()
我們的視頻流設置爲:
- 一個攝像頭(100-103行)
- 或一個視頻文件(106-107行)
我們在第110行初始化每秒幀計數器,並開始循環傳入幀:
112# loop over frames from the video stream 113while True: 114 # grab the current frame, then handle if we are using a 115 # VideoStream or VideoCapture object 116 frame = vs.read() 117 frame = frame[1] if args.get("video", False) else frame 118 119 # check to see if we have reached the end of the stream 120 if frame is None: 121 break 122 123 # resize the frame, maintaining the aspect ratio 124 frame = imutils.resize(frame, width=1000) 125 orig = frame.copy() 126 127 # if our frame dimensions are None, we still need to compute the 128 # ratio of old frame dimensions to new frame dimensions 129 if W is None or H is None: 130 (H, W) = frame.shape[:2] 131 rW = W / float(newW) 132 rH = H / float(newH) 133 134 # resize the frame, this time ignoring aspect ratio 135 frame = cv2.resize(frame, (newW, newH))
我們從113行開始在視頻/攝像頭框架上進行循環。
我們的框架調整了大小,保持了縱橫比(第124行)。從129-132行中獲取維度並計算比例。然後我們再次調整幀的大小(必須是32的倍數),這一次忽略了長寬比,因爲我們已經存儲了用於安全維護(safe keeping)的比率(第135行)。
推理和繪製文本區域邊框發生在以下幾行:
137# construct a blob from the frame and then perform a forward pass 138 # of the model to obtain the two output layer sets 139 blob = cv2.dnn.blobFromImage(frame, 1.0, (newW, newH), 140 (123.68, 116.78, 103.94), swapRB=True, crop=False) 141 net.setInput(blob) 142 (scores, geometry) = net.forward(layerNames) 143 144 # decode the predictions, then apply non-maxima suppression to 145 # suppress weak, overlapping bounding boxes 146 (rects, confidences) = decode_predictions(scores, geometry) 147 boxes = non_max_suppression(np.array(rects), probs=confidences) 148 149 # loop over the bounding boxes 150 for (startX, startY, endX, endY) in boxes: 151 # scale the bounding box coordinates based on the respective 152 # ratios 153 startX = int(startX * rW) 154 startY = int(startY * rH) 155 endX = int(endX * rW) 156 endY = int(endY * rH) 157 158 # draw the bounding box on the frame 159 cv2.rectangle(orig, (startX, startY), (endX, endY), (0, 255, 0), 2)
在這一代碼塊中:
創建一個blob並通過網絡傳遞文本區域(第139-142行);
解碼預測並應用NMS(第146行和第147行)。使用之前在這個腳本中定義的decode_forecasts函數和imutils non_max_suppression函數。
循環包圍框並在框架上繪製它們(150-159行)。這涉及到按前面收集的比率縮放方框。
而後我們將關閉框架處理循環以及腳本本身:
161# update the FPS counter 162 fps.update() 163 164 # show the output frame 165 cv2.imshow("Text Detection", orig) 166 key = cv2.waitKey(1) & 0xFF 167 168 # if the `q` key was pressed, break from the loop 169 if key == ord("q"): 170 break 171 172# stop the timer and display FPS information 173fps.stop() 174print("[INFO] elasped time: {:.2f}".format(fps.elapsed())) 175print("[INFO] approx. FPS: {:.2f}".format(fps.fps())) 176 177# if we are using a webcam, release the pointer 178if not args.get("video", False): 179 vs.stop() 180 181# otherwise, release the file pointer 182else: 183 vs.release() 184 185# close all windows 186cv2.destroyAllWindows()
我們在循環的每次迭代中更新fps計數器(第162行),以便當我們跳出循環時可以計算和顯示計時(第173-175行)。
我們在第165行顯示了EAST文本檢測的輸出,並處理按鍵(第166-170行)。如果“q”鍵代表“退出”,並被按下,我們將跳出循環,繼續清理和釋放指針。
視頻文本檢測結果
要使用OpenCV對視頻進行文本檢測,請務必點擊本文底部“下載內容”鏈接獲取相應資源。
而後,打開終端並執行以下命令(將會開啓攝像頭,因爲通過命令行參數不提供- -video):
1$ python text_detection_video.py --east frozen_east_text_detection.pb 2[INFO] loading EAST text detector... 3[INFO] starting video stream... 4[INFO] elasped time: 59.76 5[INFO] approx. FPS: 8.85