Proposal method(RCNN-Fast RCNN-Faster RCNN-PVAnet-RFCN)

传统方法主要可以表示为:目标特征提取,目标识别,目标定位而且这些特征都是人为设定的如SIFT,HOG。最著名的是DPM模型,通过这些特征对目标进行识别,再结合相应的策略进行定位。但是在一些数据集上跑的结果并不好。

目前基于深度学习的算法主要有三类

1.基于区域建议的目标检测与识别,RCNN、FastRCNN、FasterRCNN、PVAnet、RFCN

2.基于回归的目标检测与识别,YOLO、SSD

3.基于搜索的目标检测与识别,AttentionNet、基于强化学习算法

基于区域建议region proposal的方法一般都是:

首先,通过例如Selective Search,Bing,EdgeBoxes这些目标候选区域生成算法,生成一系列目标区域,然后通过深度神经网络提取目标候选区域的特征,并利用这些特征进行分类,以及目标groudtruth的回归。经典的比如RBG大神的RCNN,Fast RCNN,以及renshaoping 的faster rcnn。RCNN是开创性的把目标候选区域和深度学习相结合,用于目标检测。

RCNN中单张图片所生成的候选区域过多,而且每判断一次候选区域,就必须就要将图片进行区域提取后再送入深度神经网络,使得算法效率不高,fast rcnn不需要再输入图片上提取候选区域图块,而是在相应的feature map上提取提取候选区域的特征图,并利用这些特征图进行后续的识别和object bbox回归,然而这两种算法的候选目标区域都是通过普通图像处理算法在原始图像上生成,这使得算法在time expensive上很大,为了改善renshaoping提出faster rcnn该算法的目标候选区域是通过region proposal network(RPN)来生成,这使得整个目标检测和识别过程全部包含在一个网络中,整个模型是一个端到端的过程,大大提高了算法速度。

Timeline:

RCNN——SPPnet——fast RCNN——faster RCNN——PVAnet——R-FCN

RCNN

RCNN算法分为4个步骤

- 一张图像生成1K~2K个候选区域

- 对每个候选区域,使用深度网络提取特征

- 特征送入每一类的SVM分类器,判别是否属于该类

- 使用回归器精细修正候选框位置

候选区域生成

使用了Selective Search方法从一张图像生成约2000-3000个候选区域。基本思路如下:

- 使用一种过分割手段,将图像分割成小区域

- 查看现有小区域,合并可能性最高的两个区域。重复直到整张图像合并成一个区域位置

- 输出所有曾经存在过的区域,所谓候选区域

候选区域生成和后续步骤相对独立,实际可以使用任意算法进行。

合并规则

优先合并以下四种区域:

1.颜色(颜色直方图)相近的

2.纹理(梯度直方图)相近的

3.合并后总面积小的

4.合并后,总面积在其BBOX中所占比例大的

其中第三条:保证合并操作的尺度较为均匀,避免一个大区域陆续“吃掉”其他小区域。

例:设有区域a-b-c-d-e-f-g-h。较好的合并方式是:ab-cd-ef-gh -> abcd-efgh -> abcdefgh。

不好的合并方法是:ab-c-d-e-f-g-h ->abcd-e-f-g-h ->abcdef-gh -> abcdefgh。

其中第四条:保证合并后形状规则。

例:左图适于合并,右图不适于合并。

上述四条规则只涉及区域的颜色直方图、纹理直方图、面积和位置。合并后的区域特征可以直接由子区域特征计算而来,速度较快。

多样化与后处理

为尽可能不遗漏候选区域,上述操作在多个颜色空间中同时进行(RGB,HSV,Lab等)。在一个颜色空间中,使用上述四条规则的不同组合进行合并。所有颜色空间与所有规则的全部结果,在去除重复后,都作为候选区域输出。作者提供了Selective Search的源码,内含较多.p文件和.mex文件,难以细查具体实现。

特征提取

预处理数据

使用深度网络提取特征之前,因为CNN需要的是固定大小的图片作为输入所以首先把候选区域归一化成同一尺寸227×227。

此处有一些细节可做变化:外扩的尺寸大小,形变时是否保持原比例,对框外区域直接截取还是补灰。会轻微影响性能。

网络结构

基本借鉴Hinton 2012年在Image Net上的分类网络2,略作简化3

类别判断

分类器

对每一类目标,使用一个线性SVM二类分类器进行判别。输入为深度网络输出的4096维特征,输出是否属于此类。

由于负样本很多,使用hard negative mining方法。

正样本

本类的真值标定框。

负样本

考察每一个候选框,如果和本类所有标定框的重叠都小于0.3,认定其为负样本

位置精修

目标检测问题的衡量标准是重叠面积:许多看似准确的检测结果,往往因为候选框不够准确,重叠面积很小。故需要一个位置精修步骤。回归器对每一类目标,使用一个线性脊回归器进行精修。正则项λ=10000。

输入为深度网络pool5层的4096维特征,输出为xy方向的缩放和平移。训练样本判定为本类的候选框中,和真值重叠面积大于0.6的候选框

SPPnet

R-CNN中,他们都要求输入固定大小的图片,这些图片或者经过裁切(Crop)或者经过变形缩放(Warp),都在一定程度上导致图片信息的丢失和变形,限制了识别精确度。两种方式如下所示。

基础框架:

CNN网络需要固定尺寸的图像输入,SPPNet将任意大小的图像池化生成固定长度的图像表示,提升R-CNN检测的速度24-102倍。事实上,在网络实现的过程中,卷积层是不需要输入固定大小的图片的,而且还可以生成任意大小的特征图,只是全连接层需要固定大小的输入。因此,固定长度的约束仅限于全连接层。在本文中提出了Spatial Pyramid Pooling layer 来解决这一问题,使用这种方式,可以让网络输入任意的图片,而且还会生成固定大小的输出。这样,整体的结构和之前的R-CNN有所不同。,因此提出了SPP层放到卷积层的后面,改进后的网络如下图所示:

对于Spatital Pyramid Pooling空域金字塔池化:

在解释什么是空间金字塔池化之前,先一下什么是空间金字塔。这里的理解就是以不同大小的块来对图片提取特征,比如下面这张图:

分别是4*4,2*2,1*1大小的块,将这三张网格放到下面这张特征图上,就可以得到16+4+1=21种不同的切割方式,分别在每一个区域取最大池化,那么就可以得到21组特征。这种以不同的大小格子的组合方式来池化的过程就是空间金字塔池化(SPP)

现在,再来看这张完整的图像,因为卷积层输入的任意大小的图片,所以Conv5计算出的feature map也是任意大小的,现在经过SPP之后,就可以变成固定大小的输出了,以上图为例,一共可以输出(16+4+1)*256的特征,16+4+1表示空间盒的数量(Spatial bins),256则表示卷积核的数量。带有SPP layer的网络叫做SPP-net,它在物体检测上跟R-CNN也有一定的区别。首先是特征提取上,速度提升了好多,R-CNN是直接从原始图片中提取特征,它在每张原始图片上提取2000个Region Proposal,然后对每一个候选区域框进行一次卷积计算,差不多要重复2000次,而SPP-net则是在卷积原始图像之后的特征图上提取候选区域的特征。所有的卷积计算只进行了一次,效率大大提高。

具体算法的大体流程如下:

1、首先通过选择性搜索,对待检测的图片进行搜索出2000个候选窗口。这一步和R-CNN一样。

2、特征提取阶段。这一步就是和R-CNN最大的区别了,同样是用卷积神经网络进行特征提取,但是SPP-Net用的是金字塔池化。这一步骤的具体操作如下:把整张待检测的图片,输入CNN中,进行一次性特征提取,得到feature maps,然后在feature maps中找到各个候选框的区域,再对各个候选框采用金字塔空间池化,提取出固定长度的特征向量。而R-CNN输入的是每个候选框,然后在进入CNN,因为SPP-Net只需要一次对整张图片进行特征提取,速度是大大地快啊。江湖传说可一个提高100倍的速度,因为R-CNN就相当于遍历一个CNN两千次,而SPP-Net只需要遍历1次。

3、最后一步也是和R-CNN一样,采用SVM算法进行特征向量分类识别。

算法细节说明:看完上面的步骤二,我们会有一个疑问,那就是如何在feature maps中找到原始图片中候选框的对应区域?因为候选框是通过一整张原始的图片进行检测得到的,而feature maps的大小和原始图片的大小是不同的,feature maps是经过原始图片卷积、下采样等一系列操作后得到的。那么我们要如何在feature maps中找到对应的区域呢?

APPENDIX A:Mapping a Window to Feature Maps。这个作者直接给出了一个很方便我们计算的公式:假设(x’,y’)表示特征图上的坐标点,坐标点(x,y)表示原输入图片上的点,那么它们之间有如下转换关系:

(x,y)=(S*x’,S*y’)

其中S的就是CNN中所有的strides的乘积。比如paper所用的ZF-5:

S=2*2*2*2=16

而对于Overfeat-5/7就是S=12,这个可以看一下下面的表格:

需要注意的是Strides包含了池化、卷积的stride。自己计算一下Overfeat-5/7(前5层)是不是等于12。反过来,我们希望通过(x,y)坐标求解(x’,y’),那么计算公式如下:

因此我们输入原图片检测到的windows,可以得到每个矩形候选框的四个角点,然后我们再根据公式:

left,top=

right,bottom=

FAST RCNN

Fast RCNN方法解决了RCNN方法三个问题:

问题一:测试时速度慢

RCNN一张图像内候选框之间大量重叠,提取特征操作冗余。

本文将整张图像归一化后直接送入深度网络。在邻接时,才加入候选框信息,在末尾的少数几层处理每个候选框。

问题二:训练时速度慢

原因同上。

在训练时,本文先将一张图像送入网络,紧接着送入从这幅图像上提取出的候选区域。这些候选区域的前几层特征不需要再重复计算。

问题三:训练所需空间大

RCNN中独立的分类器和回归器需要大量特征作为训练样本。

本文把类别判断和位置精调统一用深度网络实现,不再需要额外存储。

特征提取网络

基本结构

图像归一化为224×224直接送入网络。前五阶段是基础的conv+relu+pooling形式,在第五阶段结尾,输入P个候选区域(labe l1+bbox 4)

roi_pool层的测试(forward)

roi_pool层将每个候选区域均匀分成M×N块,对每块进行max pooling。将特征图上大小不一的候选区域转变为大小统一的数据,送入下一层。

roi_pool层的训练(backward)

首先考虑普通max pooling层。设xi为输入层的节点,yj为输出层的节点。

其中判决函数δ(i,j)表示i节点是否被j节点选为最大值输出。不被选中有两种可能:xi不在yj范围内,或者xi不是最大值。对于roi max pooling,一个输入节点可能和多个输出节点相连。设xi为输入层的节点,yrj为第r个候选区域的第j个输出节点。

判决函数δ(i,r,j)表示i节点是否被候选区域r的第j个节点选为最大值输出。代价对于xi的梯度等于所有相关的后一层梯度之和。

网络参数训练

参数初始化

网络除去末尾部分如上结构图,在ImageNet上训练1000类分类器。结果参数作为相应层的初始化参数。其余参数随机初始化

第五阶段的特征输入到两个并行的全连层中(称为multi-task)。

cls_score层用于分类,输出K+1维数组p,表示属于K类和背景的概率。

bbox_prdict层用于调整候选区域位置,输出4*K维数组t,表示分别属于K类时,应该平移缩放的参数

FASTER RCNN

从RCNN到fast RCNN,再到本文的faster RCNN,目标检测的四个基本步骤(候选区域生成,特征提取,分类,位置精修)终于被

统一到一个深度网络框架之内

。所有计算没有重复,完全在GPU中完成,大大提高了运行速度。

faster RCNN可以简单地看做“区域生成网络+fast RCNN“的系统,用区域生成网络代替fast RCNN中的Selective Search方法。本篇论文着重解决了这个系统中的三个问题:

1. 如何设计区域生成网络

2. 如何训练区域生成网络

3. 如何让区域生成网络和fast RCNN网络共享特征提取网络


区域生成网络:结构

基本设想是:在提取好的特征图上,对所有可能的候选框进行判别。由于后续还有位置精修步骤,所以候选框实际比较稀疏。

特征提取

原始特征提取(上图灰色方框)包含若干层conv+relu,直接套用ImageNet上常见的分类网络即可。本文试验了两种网络:5层的ZF[3],16层的VGG-16[4],具体结构不再赘述。

额外添加一个conv+relu层,输出51*39*256维特征(feature)。

候选区域(anchor)

特征可以看做一个尺度51*39的256通道图像,对于该图像的每一个位置,考虑9个可能的候选窗口:三种面积{128^2,256^2,512^2}×三种比例{1:1,1:2,2:1}。这些候选窗口称为anchors。下图示出51*39个anchor中心,以及9种anchor示例。

在整个faster RCNN算法中,有三种尺度。

原图尺度:原始输入的大小。不受任何限制,不影响性能。

归一化尺度:输入特征提取网络的大小,在测试时设置,源码中opts.test_scale=600。anchor在这个尺度上设定。这个参数和anchor的相对大小决定了想要检测的目标范围。

网络输入尺度:输入特征检测网络的大小,在训练时设置,源码中为224*224。

窗口分类和位置精修

分类层(cls_score)输出每一个位置上,9个anchor属于前景和背景的概率;窗口回归层(bbox_pred)输出每一个位置上,9个anchor对应窗口应该平移缩放的参数。

对于每一个位置来说,分类层从256维特征中输出属于前景和背景的概率;窗口回归层从256维特征中输出4个平移缩放参数。

就局部来说,这两层是全连接网络;就全局来说,由于网络在所有位置(共51*39个)的参数相同,所以实际用尺寸为1×1的卷积网络实现。

需要注意的是:并没有显式地提取任何候选窗口,完全使用网络自身完成判断和修正。

区域生成网络:训练

样本

考察训练集中的每张图像:

a. 对每个标定的真值候选区域,与其重叠比例最大的anchor记为前景样本

b. 对a)剩余的anchor,如果其与某个标定重叠比例大于0.7,记为前景样本;如果其与任意一个标定的重叠比例都小于0.3,记为背景样本

c. 对a),b)剩余的anchor,弃去不用。

d. 跨越图像边界的anchor弃去不用

代价函数

同时最小化两种代价:

a. 分类误差

b. 前景样本的窗口位置偏差,其中如主体结构中rpn-data生成proposal region 的target数据 feat_stride为16,因为经过VGG16 size下降4x4

具体参看fast RCNN中的“分类与位置调整”段落

超参数

原始特征提取网络使用ImageNet的分类样本初始化,其余新增层随机初始化。

每个mini-batch包含从一张图像中提取的256个anchor,前景背景样本1:1.

前60K迭代,学习率0.001,后20K迭代,学习率0.0001。

momentum设置为0.9,weight decay设置为0.0005。[5]

共享特征

区域生成网络(RPN)和fast RCNN都需要一个原始特征提取网络(下图灰色方框)。这个网络使用ImageNet的分类库得到初始参数W0,但要如何精调参数,使其同时满足两方的需求呢?本文讲解了三种方法。

轮流训练

a. 从W0开始,训练RPN。用RPN提取训练集上的候选区域

b. 从W0开始,用候选区域训练Fast RCNN,参数记为W1

c. 从W1开始,训练RPN…

具体操作时,仅执行两次迭代,并在训练时冻结了部分层。论文中的实验使用此方法。

如Ross Girshick在ICCV 15年的讲座Training R-CNNs of various velocities中所述,采用此方法没有什么根本原因,主要是因为”实现问题,以及截稿日期“。

近似联合训练

直接在上图结构上训练。在backward计算梯度时,把提取的ROI区域当做固定值看待;在backward更新参数时,来自RPN和来自Fast RCNN的增量合并输入原始特征提取层。

此方法和前方法效果类似,但能将训练时间减少20%-25%。公布的python代码中包含此方法。

联合训练

直接在上图结构上训练。但在backward计算梯度时,要考虑ROI区域的变化的影响。推导超出本文范畴。

实验

除了开篇提到的基本性能外,还有一些值得注意的结论

与Selective Search方法(黑)相比,当每张图生成的候选区域从2000减少到300时,本文RPN方法(红蓝)的召回率下降不大。说明RPN方法的目的性更明确

PVAnet

Pipeline:

CNN feature  extraction + region proposal区域建议+ RoI classification

innovations.:

redesign the feature extraction part 因为region proposal并不是computationally expensive在于classification可以利用如截断SVD等技术高效压缩。设计思想是少通道多layers,也采用一些building blocks包括concatenated ReLU,Inception以及HyperNet.总之,最终的网络呈现deep and thin,通过batch normalization, residual connections, and learning rate scheduling based on plateau detection来帮助训练.VOC2007 2012 很不错的mAP,和ResNet101相比computational cost只有12.3%。

但是并没有验证一下所有的都是effectiveness:

1.C.RELU用于CNN网络的前期层如输入层后面的几层,降低计算量但没有降低准确度。

2.Inception用于the remaining of our feature generation sub-network. AnInceptionmodule produces output activations of different sizes of receptivefields, so that increases the variety of receptive field sizes in the previouslayer. We observed that stacking up Inception modules can capture widelyvarying-sized objects more effectively than a linear chain of convolutions.

3.3.multi-scale representation like HyperNet [4] that combines several intermediate(中间)outputs so that multiple levels of details and non-linearities can be considered simultaneously(同时)

C.ReLU building block

Negation multiplies -1 to the output of Convolution,Scale / Shift applies trainable weight and bias to each channel, allowing activations in the negated part to be adaptive.CNN网络的前几层学习到的feature map中存在负相关,从而negationand  concatenate both them,From this observation, C.ReLU reduces the number ofoutput channels by half,and doubles it by simply concatenating the same outputswith negation,add在concatenating后缩放和移位,是为了允许每个通道的slope斜率和激活阈值可以与其相反的渠道不同。

Inception:

Remaining building blocks in feature generation,Inception can be one of the most cost-effective building block for capturing both small and large objects in an input image.,for capturing large object, output features of CNNs should correspond to sufficiently large receptive fields, which can be easily fulfilled by stacking up convolutions of 3x3 or larger kernels.capturing small-sized objects, output features should correspond to sufficiently small receptive fields to localize small regions of interest precisely.

Exampleof a distribution of (expected) receptive field sizes of intermediate outputsin a chainof 3 Inception modules. Each module concatenates 3 convolutional layers ofdifferent kernel sizes,1x1, 3x3 and 5x5, respectively. The number of output channels in each module is set tof1=2;1=4;1=4 g of the number of channels from theprevious module, respectively. A latter Inception module can Inception can fulfill both requirements.1x1 convolution plays the key role to this end, bypreserving the receptive fieldof the previous layer. Just increasing the nonlinearity ofinput patterns, it slows down the growth of receptive fields for some outputfeatures so that small-sized objects can be capturedprecisely.

5x5convolution is replaced with two 3x3 convolutional layers for efficiency.(Right) Inception for reducing feature map size by half

HyperNet:关联多尺度中间输出

Multi-scale representation及其组合在很多deep learning中都是有效的,他将fine-grained细粒度(coarse-grained粗粒度)的细节和feature extraction layer特征生成层的高度抽象的信息结合起来以帮助紧随其后的region proposal网络检测物体的不同scales。由于直接将所有抽象层关联起来可能会产生很多冗余的信息造成不必要的的计算资源浪费,因此我们要小心的设计不同抽象层的数量和抽象层的层序。如果你选择过早的层来object proposal和classification,考虑到增加的计算复杂度,将不会有什么帮助,我们和前人一样:即组合1)最后一层和2)两个中间层,其尺度分别为最后一层的2x和4x层。我们选择中等大小的图层作为参考比例(= 2x),并连接4x层和最后一层来分别down-scaling(pooling)和up-scaling(线性插值)。

Deep network training

1.随着网络越来越深入,网络训练越来越多麻烦。我们通过采用residual structure残差结构来解决这个问题。不像原来的residual training思想,我们在inception添加残留连接,并稳定我们的深层网络架构的后期部分。

2.我们还在所有ReLU激活层之前添加了batch normalization layer批归一化层。小批量样品在训练前使用统计数据,之后使用moving-averaged statistic移动平均统计量作为固定的缩放和移位参数。

3.learning rate policy学习率策略对于成功训练网络也很重要。我们的策略是控制基于plateau detection动态化学习率。我们衡量moving average loss移动平均线的损失,如果在一段时间迭代内其改善程度低于门槛,则on-plateau。每当检测到plateau时,学习率都会降低一个常数因子。从而达到显着的accuracy准确性增益。

PVAnet结构

表1显示了PVANET的整体结构。在早期阶段(conv1 1,...,conv3 4),C.ReLU适用于卷积层,将KxK卷积的计算成本降低一半。1x1卷积层在KxK conv之前和之后添加,以减少输入size,然后分别放大representation代表能力。来自conv3_4(wth down-scaling),conv4_4和conv5_4(with up-scaling)的三个中间输出被合并到512通道的multi-scale多尺度输出特征(convf)中,这些特征被输入fastrerR-CNN模块:为了计算效率,只有第一个在conf的128个通道被馈送到该proposal region net(RPN)。我们的RPN是“3x3conv(384通道)- 1x1conv(25x(2 + 4)= 150通道)”的序列层,以产生感兴趣区域(RoIs)R-CNN采用全部512个通道。对于每个RoI,6x6x512tensor由RoIpooling,然后通过一系列FC layer给4096 - 4096 -(21 + 84)输出节点

.............有空再补充,欢迎推荐补充...............

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容