opencv dnn模块 示例(13) 自然场景文本检测 Scene Text Detector-EAST

一、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.
opencv dnn模块 示例(13) 自然场景文本检测 Scene Text Detector-EAST_第1张图片
网络结构
opencv dnn模块 示例(13) 自然场景文本检测 Scene Text Detector-EAST_第2张图片
(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)矩形框旋转角度
opencv dnn模块 示例(13) 自然场景文本检测 Scene Text Detector-EAST_第3张图片

二、示例代码

#include 
#include 
#include 

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、演示

opencv dnn模块 示例(13) 自然场景文本检测 Scene Text Detector-EAST_第4张图片opencv dnn模块 示例(13) 自然场景文本检测 Scene Text Detector-EAST_第5张图片

opencv dnn模块 示例(13) 自然场景文本检测 Scene Text Detector-EAST_第6张图片
opencv dnn模块 示例(13) 自然场景文本检测 Scene Text Detector-EAST_第7张图片

你可能感兴趣的:(OpenCV,深度神经网络,opencv实例源码演示)