一、opencv的示例模型文件
使用tensorflow實現模型frozen_east_text_detection.pb,下載地址:https://www.dropbox.com/s/r2ingd0l3zt8hxs/frozen_east_text_detection.tar.gz?dl=1 。
參考論文和開源代碼如下:EAST: An Efficient and Accurate Scene Text Detector ,github EAST 。
使用數據庫爲ICDAR。
相比已有模型
該模型直接預測全圖像中任意方向和四邊形形狀的單詞或文本行,消除不必要的中間步驟(例如,候選聚合和單詞分割)。通過下圖它與一些其他方式的步驟對比,可以發現該模型的步驟比較簡單,去除了中間一些複雜的步驟,所以符合它的特點,EAST, since it is an Efficient and Accuracy Scene Text detection pipeline.
網絡結構
(1) 特徵提取層:使用的基礎網絡結構是PVANet,分別從stage1,stage2,stage3,stage4抽出特徵,一種FPN(feature pyramid network)的思想。
(2) 特徵融合層:第一步抽出的特徵層從後向前做上採樣,然後concat
(3) 輸出層:輸出一個score map和4個迴歸的框+1個角度信息(RBOX),或者輸出,一個scoremap和8個座標信息(QUAD)。
這裏的程序代碼實現的基礎網絡不是pvanet網絡,而是resnet50-v1。
下圖是標籤生的處理,(a)黃色虛線四邊形爲文本邊框,綠色實線是收縮後的標註框(b)文本檢測score map(c)RBOX幾何關係圖(d)各像素到矩形框四個邊界的距離,四通道表示。(e)矩形框旋轉角度
二、示例代碼
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/dnn.hpp>
using namespace cv;
using namespace cv::dnn;
void decode(const Mat& scores, const Mat& geometry, float scoreThresh,
std::vector<RotatedRect>& detections, std::vector<float>& confidences);
int main()
{
float confThreshold = 0.5;
float nmsThreshold = 0.4;
int inpWidth =320;
int inpHeight = 320;
String model = "../../data/testdata/dnn/frozen_east_text_detection.pb";
// 加載模型
Net net = readNet(model);
auto names = net.getLayerNames();
// 測試視頻或圖片或圖片序列
VideoCapture cap;
cap.open("../../data/image/bp2.jpg");
static const std::string kWinName = "EAST: An Efficient and Accurate Scene Text Detector";
namedWindow(kWinName, WINDOW_NORMAL);
// 設定網絡提取層的數據
std::vector<Mat> outs;
std::vector<String> outNames(2);
outNames[0] = "feature_fusion/Conv_7/Sigmoid";
outNames[1] = "feature_fusion/concat_3";
Mat frame, blob;
while (1) {
cap >> frame;
if (frame.empty()) {
break;
}
// 輸入圖片、網絡前向計算
blobFromImage(frame, blob, 1.0, Size(inpWidth, inpHeight), Scalar(123.68, 116.78, 103.94), true, false);
net.setInput(blob);
net.forward(outs, outNames);
Mat scores = outs[0]; // 1x1x80x80
Mat geometry = outs[1]; // 1x5x80x80
// 輸出數據Blob轉換爲可操作的數據對象
std::vector<RotatedRect> boxes;
std::vector<float> confidences;
decode(scores, geometry, confThreshold, boxes, confidences);
// NMS處理檢測結果
std::vector<int> indices;
cv::dnn::NMSBoxes(boxes, confidences, confThreshold, nmsThreshold, indices);
// 繪製檢測結果
Point2f ratio((float)frame.cols / inpWidth, (float)frame.rows / inpHeight);
for (int indice : indices) {
RotatedRect& box = boxes[indice];
Point2f vertices[4];
box.points(vertices);
// 映射(inpWidth,inpHeight)到輸入圖像實際大小比例中
for (auto & vertice : vertices) {
vertice.x *= ratio.x;
vertice.y *= ratio.y;
}
for (int j = 0; j < 4; ++j)
line(frame, vertices[j], vertices[(j + 1) % 4], Scalar(0, 255, 0), 1);
}
// 相關檢測效率信息
std::vector<double> layersTimes;
double freq = getTickFrequency() / 1000;
double t = net.getPerfProfile(layersTimes) / freq;
std::string label = format("Inference time: %.2f ms", t);
putText(frame, label, Point(0, 15), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 255, 0));
imshow(kWinName, frame);
waitKey();
}
return 0;
}
void decode(const Mat& scores, const Mat& geometry, float scoreThresh,
std::vector<RotatedRect>& detections, std::vector<float>& confidences)
{
detections.clear();
CV_Assert(scores.dims == 4); CV_Assert(geometry.dims == 4);
CV_Assert(scores.size[0] == 1); CV_Assert(geometry.size[0] == 1);
CV_Assert(scores.size[1] == 1); CV_Assert(geometry.size[1] == 5);
CV_Assert(scores.size[2] == geometry.size[2]);
CV_Assert(scores.size[3] == geometry.size[3]);
const int height = scores.size[2];
const int width = scores.size[3];
for (int y = 0; y < height; ++y) {
// 各行像素點對應的 score、4個距離、角度的 數據指針
const auto* scoresData = scores.ptr<float>(0, 0, y);
const auto* x0_data = geometry.ptr<float>(0, 0, y);
const auto* x1_data = geometry.ptr<float>(0, 1, y);
const auto* x2_data = geometry.ptr<float>(0, 2, y);
const auto* x3_data = geometry.ptr<float>(0, 3, y);
const auto* anglesData = geometry.ptr<float>(0, 4, y);
for (int x = 0; x < width; ++x) {
float score = scoresData[x]; // score
if (score < scoreThresh)
continue;
// 輸入圖像經過網絡有4次縮小
float offsetX = x * 4.0f, offsetY = y * 4.0f;
float angle = anglesData[x]; // 外接矩形框旋轉角度
float cosA = std::cos(angle);
float sinA = std::sin(angle);
float h = x0_data[x] + x2_data[x]; // 外接矩形框高度
float w = x1_data[x] + x3_data[x]; // 外接矩形框寬度
// 通過外接矩形框,旋轉角度,建立旋轉矩形
Point2f offset(offsetX + cosA * x1_data[x] + sinA * x2_data[x],
offsetY - sinA * x1_data[x] + cosA * x2_data[x]);
Point2f p1 = Point2f(-sinA * h, -cosA * h) + offset;
Point2f p3 = Point2f(-cosA * w, sinA * w) + offset;
RotatedRect r(0.5f * (p1 + p3), Size2f(w, h), -angle * 180.0f / (float)CV_PI);
detections.push_back(r);
confidences.push_back(score);
}
}
}
3、演示