Arbitrary-Oriented Scene Text Detection via Rotation Proposals

Arbitrary-Oriented Scene Text Detection via Rotation Proposals

 

论文地址:https://arxiv.org/abs/1703.01086

github地址:https://github.com/mjq11302010044/RRPN

该论文是基于faster-rcnn框架,在场景文字识别领域的应用。

 

创新点:生成带文字角度信息的倾斜的proposal

1.RRPN(Rotation Region Proposal Networks):生成带角度信息的anchor,从而生成任意方向的proposals.

2.RRoI(The Rotation Region-of-Interest) pooling layer:将任意方向的proposals映射到feature map上,再进行max pooling.

 

RRPN部分:

数据预处理:

groud truth of a text region:(x,y,h,w,θ)

其中,x,y为bounding box的几何中心;h为bounding box的短边,w为bounding box的长边;θ为bounding box长边旋转的角度,范围为

 

Anchors:

1.angle:-pi/6, 0, pi/6, pi/3, pi/2 以及2pi/3

规定bounding box的旋转范围为[3pi/4,-pi/4),而anchor的旋转角度包括:-pi/6, 0, pi/6, pi/3, pi/2 以及2pi/3

要求每个anchor对应的target的旋转角度和anchor本身的旋转角度差不能超过pi/12,这称为fit domain。

因此,anchor对应的target的旋转角度范围如下:

 

anchor角度target旋转角度范围
-pi/6[-pi/4,-pi/12)
0[-pi/12,pi/12)
pi/6[pi/12,pi/4)
pi/3[pi/4,5pi/12)
pi/2[5pi/12,7pi/12)
2pi/3[7pi/12,3pi/4)

 

 

可以看到,anchor与对应的target的角度差不超过pi/12.

 

2.aspect ration: 1:2, 1:5, 1:8

 

3.scale:8,16,32

 

对于feature map上面的每一点,生成的anchors的数量为6*3*3=54个。

 

除了增加了角度信息,生成anchor的方式与faster-rcnn类似。

在过滤的时候使用了 Scale Jittering策略。

步骤:

1.给图像增加大小为原边长0.25倍的border-padding。

2.利用给定的anchor(x,y,w,h,θ)信息,生成anchor的四个顶点坐标。

3.判断四个顶点坐标是否在新边界内。

 

 

IoU:

 

思想:先算出两个矩阵边的交点,然后生成一个多变形,最后将多边形分割成三角形进行计算。

伪代码如下:

 

 

RRoI部分:

RRoI Pooling算法:

1)将每一个proposal分成7×7的sub regions。

2)对每一个sub region的四个点,进行affine Transformation(仿射变换),得到对应的平行四边形。

3)对feature map中的平行四边形进行max pooling。

伪代码:

 

 

Affine Transformation(仿射变换):

参考:https://www.cnblogs.com/ghj1976/p/5199086.html,思路很棒,但是其中列举的部分矩阵有错误,已在本文订正。

1)概念

仿射变换,就是允许图形任意倾斜,而且允许图形在两个方向上任意伸缩的变换。其可以保持原来的线共点,点共线的关系不变,保持原来相互平行的线仍然平行,保持原来的中点仍然是中点,保持原来在一直线上几段线段之间的比例关系不变。

但是,仿射变换不能保持原来的线段长度不变,也不能保持原来的夹角角度不变。

仿射变换可以用下面公式表示:

其中,(tx,ty)表示平移量,而参数ai则反映了图像旋转,缩放等变化。将参数tx,ty,ai(i=1~4)计算出,即可得到两幅图形的坐标变换关系。

2)RRPN中用到的变换举例:

a)平移变换(Translation)

将每一点移动到(x+tx, y+ty),变换矩阵为:

平移变换不会产生形变。

效果:

b)旋转变换(Rotation)

目标图形围绕原点顺时针旋转θ弧度,变换矩阵为:

效果:

c)  缩放变换(scale)

将每一点的横坐标放大(缩小)至sx倍,纵坐标放大(缩小)至sy倍,变换矩阵为:

效果:

d)组合

目标图形以(x,y)为轴心顺时针旋转θ弧度,变换矩阵为:

相当于两次平移变换与一次原点旋转变换的组合,也就是先移动到中心节点,然后旋转,然后再移动回去。

 

平移与旋转的变换效果如下:

 

RRoI Pooling 实现

相信有了前面affine Transformation(仿射变换)的铺垫,RRoI Pooling的关键代码相对而言可以较为容易的看懂。

/home/crediks/Downloads/RRPN/caffe-fast-rcnn/src/caffe/layers/roi_pooling_layer.cpp:

 

 
  1. void RotateROIPoolingLayer<Dtype>::Forward_cpu(const vector<Blob<Dtype>*>& bottom,

  2. const vector<Blob<Dtype>*>& top) {

  3.  
  4. const Dtype* bottom_data = bottom[0]->cpu_data();

  5. const Dtype* bottom_rois = bottom[1]->cpu_data();

  6. const Dtype* image_info = bottom[2]->cpu_data();

  7. // Number of ROIs

  8. int num_rois = bottom[1]->num();

  9. int batch_size = bottom[0]->num();

  10. int top_count = top[0]->count();

  11. Dtype* top_data = top[0]->mutable_cpu_data();

  12. caffe_set(top_count, Dtype(-FLT_MAX), top_data);

  13. int* argmax_data = max_idx_.mutable_cpu_data();

  14. caffe_set(top_count, -1, argmax_data);

  15. int imageWidth = int(image_info[1]*spatial_scale_+0.5);

  16. int imageHeight = int(image_info[0]*spatial_scale_+0.5);

  17.  
  18. // For each ROI R = [batch_index Cx Cy height width angle]: max pool over R

  19. for (int n = 0; n < num_rois; ++n) {

  20. // Points

  21. int roi_batch_ind = bottom_rois[0];

  22. CHECK_GE(roi_batch_ind, 0);

  23. CHECK_LT(roi_batch_ind, batch_size);

  24. Dtype cx = bottom_rois[1];

  25. Dtype cy = bottom_rois[2];

  26. Dtype h = bottom_rois[3];

  27. Dtype w = bottom_rois[4];

  28. Dtype angle = bottom_rois[5]/180.0*3.1415926535;

  29.  
  30. //TransformPrepare

  31. Dtype dx = -pooled_width_/2.0;

  32. Dtype dy = -pooled_height_/2.0;

  33. //每一个sub region的大小

  34. Dtype Sx = w*spatial_scale_/pooled_width_;

  35. Dtype Sy = h*spatial_scale_/pooled_height_;

  36. Dtype Alpha = cos(angle);

  37. Dtype Beta = sin(angle);

  38. Dtype Dx = cx*spatial_scale_;

  39. Dtype Dy = cy*spatial_scale_;

  40.  
  41. Dtype M[2][3];

  42. M[0][0] = Alpha*Sx;

  43. M[0][1] = Beta*Sy;

  44. M[0][2] = Alpha*Sx*dx+Beta*Sy*dy+Dx;

  45. M[1][0] = -Beta*Sx;

  46. M[1][1] = Alpha*Sy;

  47. M[1][2] = -Beta*Sx*dx+Alpha*Sy*dy+Dy;

  48.  
  49. /*std::cout<<M[0][0]<<std::endl;

  50. std::cout<<M[0][1]<<std::endl;

  51. std::cout<<M[0][2]<<std::endl;

  52. std::cout<<M[1][0]<<std::endl;

  53. std::cout<<M[1][1]<<std::endl;

  54. std::cout<<M[1][2]<<std::endl;

  55. */

  56. const Dtype* batch_data = bottom_data + bottom[0]->offset(roi_batch_ind);

  57. for (int c = 0; c < channels_; ++c) {

  58. for (int ph = 0; ph < pooled_height_; ++ph) {

  59. for (int pw = 0; pw < pooled_width_; ++pw) {

  60. const int pool_index = ph * pooled_width_ + pw;

  61. Dtype P[8];

  62. P[0] = M[0][0]*pw+M[0][1]*ph+M[0][2];

  63. P[1] = M[1][0]*pw+M[1][1]*ph+M[1][2];

  64. P[2] = M[0][0]*pw+M[0][1]*(ph+1)+M[0][2];

  65. P[3] = M[1][0]*pw+M[1][1]*(ph+1)+M[1][2];

  66. P[4] = M[0][0]*(pw+1)+M[0][1]*ph+M[0][2];

  67. P[5] = M[1][0]*(pw+1)+M[1][1]*ph+M[1][2];

  68. P[6] = M[0][0]*(pw+1)+M[0][1]*(ph+1)+M[0][2];

  69. P[7] = M[1][0]*(pw+1)+M[1][1]*(ph+1)+M[1][2];

  70.  
  71. std::cout<<imageWidth<<imageHeight<<std::endl;

  72. int leftMost = int(max(round(min(min(P[0],P[2]),min(P[4],P[6]))) ,0.0));

  73. int rightMost= int(min(round(max(max(P[0],P[2]),max(P[4],P[6]))),imageWidth-1.0));

  74. int topMost= int(max(round(min(min(P[1],P[3]),min(P[5],P[7]))),0.0));

  75. int bottomMost= int(min(round(max(max(P[1],P[3]),max(P[5],P[7]))),imageHeight-1.0));

  76. //bool is_empty = (rightMost<= leftMost) || (bottomMost <= topMost);

  77.  
  78. //std::cout<<leftMost<<rightMost<<topMost<<bottomMost<<std::endl;

  79. Dtype AB[2];

  80. AB[0] = P[2] - P[0];

  81. AB[1] = P[3] - P[1];

  82.   Dtype ABAB = AB[0]*AB[0] +AB[1]*AB[1];

  83.  
  84.   Dtype AC[2];

  85. AC[0] = P[4] - P[0];

  86. AC[1] = P[5] - P[1];

  87. Dtype ACAC = AC[0]*AC[0] + AC[1]*AC[1];

  88.  
  89. top_data[pool_index] = 0;

  90. argmax_data[pool_index] = -1;

  91. for (int h = topMost; h < bottomMost+1; ++h) {

  92. for (int w = leftMost; w < rightMost+1; ++w) {

  93. Dtype AP[2];

  94. AP[0] = w - P[0];

  95. AP[1] = h - P[1];

  96. Dtype ABAP = AB[0]*AP[0] +AB[1]*AP[1];

  97. Dtype ACAP = AC[0]*AP[0] + AC[1]*AP[1];

  98. if(ABAB>ABAP&&ABAP>=0&&ACAC>ACAP&&ACAP>=0){

  99. const int index = h * width_ + w;

  100. if (batch_data[index] > top_data[pool_index]) {

  101. top_data[pool_index] = batch_data[index];

  102. argmax_data[pool_index] = index;

  103. }

  104. }

  105. }

  106. }

  107. }

  108. }

  109. // Increment all data pointers by one channel

  110. batch_data += bottom[0]->offset(0, 1);

  111. top_data += top[0]->offset(0, 1);

  112. argmax_data += max_idx_.offset(0, 1);

  113. }

  114. // Increment ROI data pointer

  115. bottom_rois += bottom[1]->offset(1);

  116. }

  117. }

 

1.矩阵M定义了Affine Transformation的变换矩阵。包含了缩放,旋转,平移操作。

2.通过缩放,旋转,平移,找到sub region对应的平行四边形在feature map上的坐标。也就是p[0]~p[7],示意图如下:

3.由于θ的大小不一定,所以p[0],p[2],p[4],p[6]均有可能为最左边的坐标。最右,最上和最下同理。通过比较坐标的位置,找到最左,最右,最上和最下的坐标。

4.过滤掉无效位置(if(ABAB>ABAP&&ABAP>=0&&ACAC>ACAP&&ACAP>=0)...)

5.最后进行max pooling.

实验说明:

ICDAR2015数据集:利用数据集中给定的四个顶点,生成倾斜的Bounding box;

ICDAR2013数据集:生成水平方向的Bounding box。

调参技巧:前20万次迭代lr为0.0001

后10万次迭代lr为0.0005

过滤:由于生成的anchors的数量为原来的6倍,因此将超出边框的anchors进行过滤。

 

运行部分:

bug解决方案:

Segmentation fault,错误解决方案:

./lib/rotation/data_extractor.py:

1.将import cv2的顺序提前,也就是将import文件的顺序改为:

 

import cv2

import numpy as np

import os

from xml.dom.minidom import parse

import xml.dom.minidom

from PIL import Image

import pickle

 

./tools/train_net.py:

 

2.将from rotation.data_extractor import get_rroidb的顺序提前,也就是将import文件的顺序改为:

 

from rotation.data_extractor import get_rroidb

import _init_paths

#from fast_rcnn.train import get_training_roidb, train_net #D

from rotation.rt_train import get_training_roidb, train_net

from fast_rcnn.config import cfg, cfg_from_file, cfg_from_list, get_output_dir

from datasets.factory import get_imdb

from rotation.data_extractor import get_rroidb

import datasets.imdb

import caffe

import argparse

import pprint

import numpy as np

import sys

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值