吴恩达Deep Learning第四课作业(第三周 Tensorflow2.0 keras搭建YOLO3)

目录链接:吴恩达Deep Learning学习笔记目录

参考资料:YOLO系列之yolo v1YOLO系列之yolo v2yolo系列之yolo v3【深度解析】
论文:
YOLO1:You Only Look Once:Unified, Real-Time Object Detection
YOLO2:YOLO9000: Better, Faster, Stronger
YOLO3:YOLOv3: An Incremental Improvement

  1.YOLO1
  2.YOLO2
  3.YOLO3
  4.tensorflow实现YOLO3

1.YOLO1

 1.1 Unified Detection


  YOLO算法在一个神经网络中对各个目标进行统一的检测。从整张图片提取特征,同时预测每个类的各个bounding boxes。
 ①YOLO将输入图片分割为SXS大小个格子,如果一个目标的中心点在这个格子内,那么这个格子将检测到这个目标(这并不意味着目标只有格子大,如上图,狗的中心在格子(5,2)内,但狗的bounding box远比这个格子大)。

 ②每个格子将预测B个bounding boxes,并预测出每个boxes的confidence score:

如果没有目标的中心落在格子里,那么这个格子的confidence score=0,否则应该为预测box和真实box(标签box)之间的IOU值(交并比)。
 ③每个bounding box由5个预测值:x,y,w,h,confidence score,分别代表对象坐标,大小和置信分数。

 ④每个格子还需要预测所属类别的条件概率C:
即基于检测到对象后,确认这个对象的类别,每个格子只给出一类的概率(一个格子预测出多个box,挑选最符合(IOU最大)的box用于预测该目标所属类别)
 ⑤在测试时,获得的概率为类别的条件概率乘以box的confidence score:
  如上图,将图片划分为7X7的格子,每个格子预测2个box,设类别有20种,则输出为7x7x(2x5+20)=7x7x30,每个格子的输出长度为30。

1.2 Network

  YOLO算法神经网络,一共有24层卷积和来层全连接,输出为7x7x30。

1.3 Training

  采用如上图所示的网络的前20卷积层连接一个平均池化层对ImageNet-1000数据进行预训练,预训练后的模型在交叉验证集上(ImageNet2012)top-5准确率达88%。然后将预训好的模型用于检测,在预训练好的模型后连接4个卷积层和两个个全连接层。由于检测通常需要比较细微的纹理,所以将输入图片的大小由224x224x3放大到448x448x448.。
  最后一层用于预测类别概率和bouding box的坐标。box的大小标准化到0-1之间(以整张图片大小为基准),box坐标也标准化到0-1之间(以格子大小为基准)。
  激活函数:最后一层采用线性激活,其他层采用leaky ReLu,系数为0.1。
  loss函数采用方差和:

如果直接采用方差和,存在三个问题:①对于定位的误差和分类的误差权重相同,②对于一张图片来说,大多数格子里都是没有目标的(目标中心不在这个格子),这将会导致整体confidence score趋向于0,这会导致模型不稳定。为解决这个问题,增加了坐标预测损失的权重,减小了不含目标的box的confidence score误差权重。分别为λcoord=5,λnoobj=0.5;③对于大box和小box的误差权重相同,如果误差很小,在大box容易被忽略,这里将w和h进行开方,以弱化这个问题。
  训练时,为避免过拟合,还采用了dropout和数据增强。

2.YOLO2

  YOLO2是对YOLO1的改进,在速度和精度上进行了权衡。由于含标记的检测数据获取的代价较为昂贵,一般规模较小。最常见的检测数据集包含数千到数十万张图像和数十到数百个类别,而分类数据集包含数以百万计的图像和成千上万的类别。YOLO2使用层次图将不同的数据集组合在一起(world tree)。通过有目标标记的数据集(COCO)来学习精确定位,通过图片分类(ImageNet)来提升词表(图像类别)和泛化性能。

 2.1 Better

  YOLO1相对于Fast-R-CNN,定位误差较大;相对于区域提议法的召回率较低。YOLO2聚焦于在维持分类准确性、快速的基础上改善召回率和定位性能。
(1)Batch Normalization
  在所有卷积层上添加Batch Noremalization使得YOLO在mAP上提升2%,同时Batch Normalization也具有一定的正则化作用,所以YOLO2删除了dropout
(2)High Resolution Classifier
  许多检测方法都是基于ImageNet数据集来预训练的,AlexNet输入图片分辨率小于256x256,YOLO1将输入的分辨率提升到448,这意味着,在学习检测时,模型还要同时去适应这种转化。而在YOLO2中,在分类网络上先采用448x448的图片训练10 epoches,让网络的filters调整到能够适应高分辨率输入,然后再调整网络用于检测。这使得YOLO的mAP提升了4%。
(3)Convolutional With Anchor Boxes
  YOLO1采用全连接层直接预测bounding box的坐标。而YOLO从Faster R-CNN中获取了灵感(训练前人工确定bounding box的形状,即Anchor Boxes,然后通过RPN预测Anchor Boxes的偏差和置信评分)。
  YOLO2删除了全连接层,采用Anchor Boxes来预测bounding boxes。同时还删除了一层池化层(应该是指在预训练时的最后那一层池化?)保证输出图片的分辨率更高。但在检测网络中,图片输入的分辨率不再是448x448而是416(为了卷积后的输出是是奇数乘奇数的,确保在输出的中心是一个格子,一般目标趋于占据中间位置,在中心有个单独的格子有利于预测),在YOLO2的网络中416经过一系列卷积后,输出大小刚好是13x13,也就是要在13x13个格子中做bouding box预测。
  当移动anchor box时,通过预测每个anchor box的类别和目标来代替空间位置和类别的预测机制(空间位置和类别相互作用,这里相对于分开来解决)。采用anchor box后虽然mAP有所降低,但在召回率和每张图片能预测的box数量上提升较大。(实际上最后没没没采用anchor box)
(4)Dimension Clusters
  在YOLO中采用anchor box时遇到了两个问题:①anchor box 的大小是手选的(网络只能学习到相近的box,如果先期就选择一个好的anchor box(文中为priors),那么更有利益学习和预测);
  YOLO2采用K-means聚类方法来对训练集的bounding boxes来进行自动挑选priors,标准的k-means方法采用的欧几里得距离,这会使得较大的box误差较大,作者想要的是这个priors具有较好的IOU分数,所以在k-means中采用的距离为:

质心centriod是指一类priors中的一个作为质心,而不是一个点。如果手动指定anchor box,由于图片中预测的box我们不确定,所以必然要多指定更多的anchor box,才能尽可能的使得平均IOU更大。而通过聚类的方法,能够自己对预测出来的box进行分类,box类型相近才会被归为一类,而不是强行去贴合指定的box,这样能减少priors(anchor box的数量)。
(5)Direct loction prediction
  在YOLO中采用anchor box时遇到的第二个问题:②模型不稳定,尤其在迭代早期。这种不稳定来源于box坐标(x,y)的预测,在RPN网络中,预测 tx 和 ty来计算(x,y):
这会导致预测的坐标点可以出现在图片中任意位置(如 tx =1时,预测坐标之间向右移动了一个个anchor的宽度),需要很长时间才能够使得偏移量稳定。所以YOLO2继续采用了YOLO1中的做法,预测相对偏移量:
YOLO2对每个bounding box预测5个值 tx、 tx、 tw、 th和 to,( cx, cx)是指每个格子相对图片左上角的偏移量。所以坐标点是直接预测的,而box 的大小则依赖于priors的( pw, ph)来计算。

(6)Fine-Grained Features
  YOLO2在13x13的特征映射上进行预测检测。这对于小目标的检测效果提升较大。Faster R-CNN和SSD等通过在不同的特征映射上运行RPN来获取不同的分辨率,YOLO2仅是添加一层passthrough层从前面的层提取26x26分辨率的特征。类似于ResNet中的恒等映射。
(7)Multi-Scale Training
  由于YOLO2都是卷积层和池化层,对于输入的大小都可以动态的调整,为了使模型更健壮,YOLO2在不同大小的图片上进行训练。每10 epochs改变一次输入图片大小。
(8)Darknet19

  darknet19含19层卷积、5层最大池化,相比于VGG-16,运行更快,且能达到同等级准确率。在检测的时候,与YOLO1有所不同,每个格子预测5个box,每个box含5个预测值和20个类别,所以每个格子的长度是125,即filter有125个。YOLO2可以检测多个目标,而在YOLO1一个格子只给出一类的概率。

 2.2Stronger

  YOLO2通过带标记的数据集来学习bounding box的定位与大小,通过带类别标签的数据集来扩展可检测的类别数量。当网络遇到检测标签数据时,采用YOLO2的loss函数来后向传播,遇到类别标签数据则采用结构中特定的分类部分用于损失传递。
  在检测标签类数据中,其标签只是简单的“dog”或“boat”,但在分类数据中,其标签的语义更深,如在ImageNet中,“Norfilk terrier”、“Yorkshire terrier”等都归属于狗类,这里需要一个方法来将这些标签进行合并。
  大多数分类方法采用softmax层来对所有类别计算概率分布。这种方法是基于各个类别相互独立的假设上的。但在融合的数据集中,不能采用这种方法来计算概率,因为两个数据集的类别不独立,如“Norfolk terrier”和“dog”不独立。作者采用多类别标签来融合数据集,这种方法不用假设类别独立。
(1)Hierarchical classification
  ImageNet的类别标签来自于WordNet(一个语言数据集)。在WordNet中“Norfilk terrier”、“Yorkshire terrier”、都属于“terrier”的下位词,属于“hunting dog”,属于“dog”,属于“canine”等等。而在大多数分类任务中,类别的假设是平坦的(所有类别处于一个层级),而对于融合的数据集,层级化的结构才是我们所需要的。
  WordNet是一个有向图结构,而不是树结构,因为语言是复杂的。例如“dog”即属于“canine”也属于“domestic animal”,这在WordNet中是同义词集。作者简化了这种图结构,来用于YOLO2数据集的融合,称为WordTree。
  为了构建WordTree,需要检索ImageNet中的类别标签,并在WordNet中沿着路径一直回溯到根节点(physical object)。一些同义词集只有一条路径,所以首先将这些路径添加到WordTree中,然后将剩下的概念词添加到树结构中,并尽量减少路径的数量。例如,如果一个概念词到达根节点需要添加3条边,但另一路径仅仅需要添加一条边,这里会选择较短的路径。
  为了将WordTree用于分类,需要预测给定节点同义词集下的每个下位词的条件概率,例如“terrier”节点:

  单个节点的绝对概率为
为了分类,设Pr(physical object)= 1 。
  为了验证这个方法,作者用ImageNet-1000来构建WordTree,通过Darknet19来训练。构建WordTree时添加了所有需要的中间节点,标签空间从1000上升到1369。在训练时,会在WordTree上传递标签值,如果一张图片的标签是“Norkfolk terrier”,那么它将获得“dog”、“mammal”等标签。为了计算条件概率,模型将预测出一个向量(长度为1369),将每个节点(同义词集)当作一个数据集,通过softmax计算其下位词基于同义词集的概率分布,如下图所示:
  采用之前在ImageNet 1000上训练的参数,Top-5准确率仅下降2.9%,达90.4%。采用这个方法同样具有一定优点,比如,如果网络中输入一张没见遇到过的类型的狗的照片,网络将预测为dog,尽管在dog的下位词上的置信度降低。这个方法同样用于目标检测,在目标检测中给出Pr(physical object)值,给出bounding box和树的概率,沿着树向下搜索,找到置信度最高的路径来预测这个目标所属类。
  通过WordTree可以将多个数据集融合起来,仅需要将数据集的类别映射到WordTree的同义词集下:

3.YOLO3

 3.1 Bounding Box

  YOLO3采用同YOLO2的方法来预测bounding box的坐标和大小,但做出了一点改善:在对每个bounding box进行逻辑回归获得预测分数后,如果一个prior与真正box的重叠部分大于其他prior,那么这个prior的置信分数为1,如果一个prior覆盖了真box,但不是最好的,那么通过一个阈值来将其过滤掉(这里取0.5)。如果一个prior没有被分配到任何一个目标,那么它将不会产生关于坐标和类别的loss,仅有是否是目标的loss

 3.2 class prediction

  在类别预测中不再使用softmax,而是逻辑回归分类器,同时,采用binary cross-entropy作为类别的loss。这样做的原因是,当将模型用于更复杂的数据集如Open Images,这些数据集有相当多的重叠标签(如women和person),如果采用softmax会强制将每个box分配一个类,而实际上并不是。

 3.3 prediction across scales

  YOLO3在三种尺寸类型上预测box,最后一层用于预测一个3-d tensor,用于编码bounding box、objectness、和类别,在每个尺寸上预测3个box,所以这个tensor为N x N x(3*(4+1+80)),即4个box的高宽及坐标、1个object、80个类别。
  随后,从将倒数两层的输出分辨率 x 2,再和倒数4层的输出进行结合,后输入给另一个尺寸的预测器,这样做的好处是,可以通过上采样提取一些有意义的特征和低层的细微纹理特征。再重复操作一次,获得第三种尺寸的预测。通过聚类的方法总共获取9个prior,每种尺寸上3个。

4.tensorflow2.0 实现YOLO

下文中代码和图片都参照YunYang1994大神的分享YOLOv3 算法的一点理解

 4.1 YOLO模型结构

  YOLO模型结构如下图所示:


YOLO model
YOLO block

4.2 代码说明

文件目录

├─data
│  ├─anchors             # prior存放文件夹
│  ├─classes             # 类别名存放文件夹
│  └─dataset             # 训练数据存放文件夹
├─model_dir              # 模型存放文件夹,存放权重数据
│  └─log                 # 训练日志存放文件夹
├─__init__.py  
├─backbone.py            # 主干网络结构
├─common.py              # 构建网络共用层结构
├─config.ini             # 配置文件
├─dataset.py             # 构建datasei迭代器
├─generate_data_txt.py   # 生成图片路径及lablel对应txt文件
├─getConfig.py           # 配置信息获取
├─image_detect.py        # 测试图片
├─train.py               # 训练
├─utils.py               # 工具函数
├─yolo3.py               # yolo结构
└─yolov3.weights         # yolov3官方权重文件

4.2.1 配置文件及读取

config.ini:配置文件
  为方便解析,将配置文件中含4个section,分别为strings、ints、floats、lists。

getConfig.py:读取配置信息文件
  读取配置信息后返回一个dict,key-value对为配置文件中各键值对。注意:configparser读取出来的value都是str,想要获取整数、列表想要进行转换。

4.2.3 utils文件

  内置函数为:read_class_names()读取类别名称,get_anchors()获取anchors,image_preprocess()图片预处理,draw_bbox()绘制带box的图片,bboxes_iou()计算bounding boxes之间的IoU,nms()非极大值限制函数,load_weights()官方权重文件加载,load_partial_weights()部分权重加载,postprocess_boxes()预测box后处理。

read_class_names():读取类别名文件,生成一个字典,key=索引,value=类别名

get_anchors():读取prior anchors文件,文件中为一行以逗号分割的数字,记录box的高宽。返回一个np.array,reshape为(3,3,2),指三种类型prior anchors,每种类型下又有三种。

image_preprocess():用于将图片预处理为模型输入大小。在处理过程中,是将图片按照原图片高宽与目标大小高宽之间的最小比例缩放,在无像素值的位置填充灰度值128。如下图所示

然后再归一化。如果是训练数据则需要按照相同比例转化label box的坐标。返回值为np.array

draw_bbox():绘制图片,对object进行bounding box标注,并在box左上角写出置信度和object所属类别。首先根据所有类别名构建不同颜色的映射(采用hsv转换),用于不同类别绘制不同颜色box。然后通过cv2库进行图片、box、文字标注绘制。如果是预测的box,应该进行处理还原到原图对应的坐标。

bboxes_iou():计算bounding boxes之间的IoU值,输入值为两个二维数组,每一行为一个box,4列分别为左上角和右下角坐标x_min,y_min,x_max,y_max。返回值为n行1列数组,对应每一行对应box之间iou。

nms():非极大值限制函数,输入值为一个二维数组,每一行为一个box,各列依次为x_min,y_min,x_max,y_max,prob,class,代码中若采用softnms,采用的高斯加权,nms和softnms之间区别参见:NMS和soft-nms算法

load_weights():官方权重文件为weights格式,不能直接加载到tf中,如果要将官方权重迁移到tf写的yolo中,需要写一个函数来进行加载。首先对Yolov3结构进行分析如下:

Yolov3结构
如上图所示,Yolov3有三个输出层(第58,66,74层),对应检测不同大小的目标。在Yolov3源码中,权重的保存(参见 darknet中weights文件存储格式)顺序为主版本号,次版本号,迭代次数,第1层权重参数,第2层权重参数...,每一层参数顺序为偏置,bn,卷积权重。存储时,每个参数以4个字节来存储,对于输出层,没有bn层。所以在读取参数时,需要跳过前5个参数,并对输出层进行特殊处理。此外,对于bn层参数,官方保存参数顺序为[beta,gamma,mean,variance],而在tf中则为[gamma,beta,mean,variance]。对于卷积权重,官方为[out_ch,in_ch,h,w],在tf中为[h,w,in_ch,out_ch],需要注意转换对应。

load_partial_weights():这个函数的目的是读取时忽略输出层参数,仅将其他层参数迁移到tf写的model中,以仅训练三个输出层。

postprocess_boxes():对于预测的bbox,还原到原图坐标系中去。输入预测信息(二维数组,行为预测的box,列依次为坐标,置信度,one-hot编码类别)、原图大小、模型输入大小。将预测box坐标由x_center,y_center,w,h转化为x_min,y_min,x_max,y_max。缩放回原图比例,修改box坐标超出原图左上角和右下角的坐标,剔除x_min > x_max或y_min > y_max的box。求各个box面积,筛选面积大于0小于inf的。筛选置信度大于设定阈值的。返回还原后的box,包含坐标、置信度、类别。

4.2.4 dataset文件

  用于构造一个迭代器,对模型输入数据。同时对图片进行数据增强、prior anchor标签绑定。包含__init__()load_annotation()random_horizotal_flip()random_crop()random_translate()parse_annotation()bbox_iou()preprocess_true_boxes()__iter__()__next__()__len__()

__init__():设置初始化参数

load_annotation():读取image_path - ground_truth. txt文件

random_horizotal_flip():随机水平翻转

random_crop():随机裁剪

random_translate():随机平移

parse_annotation():解析annotation,输入一行annotation(图片路径+真实box),读取图片并进行数据增强,BGR2RGB。通过utils.image_preprocess()处理为模型输入大小。

bbox_iou():输入两个二维数组,列为[x_center,y_center,h,w],先转化为[ x_min,y_min,x_max,y_max]再计算IoU。

preprocess_true_boxes():输入一张图片ground truth,为一个二维数组,列为[ x_min,y_min,x_max,y_max,class_id]。返回label_bbox(ground truth转化后三类绑定了prior的输出)和bboxes(ground truth转化后三类输出)。①构建一个label列表,含3类0值数组,每个数组对应模型的三个输出之一:

三个数组代表三类输出(和三类prior),每个格子中为(3,5+num_class)的数组,即每个格子有同一类下三种输出(三种prior)。②构建一个bboxes_xywh列表,也是含三类0值数组,每个数组维度(150,4),150自己指定的,是指每个尺寸下最大预测box的数量。③遍历ground truth,对每个bbox执行:i、对class_id进行one-hot编码,然后将bbox的xywh按比例缩小到三类输出大小,即由一个一维坐标向量变换为二维,每一行对应一类输出大小的坐标。ii、循环遍历三类bbox_xywh,将xy坐标向下取整后+0.5(即定位到格子中心点)以获得三类prior的坐标,并赋值三类三种prior的wh,即将9种prior的高宽转化为带坐标的三类输出大小上去。iii、计算每一类prior和ground truth的iou,将iou>0.3过滤,如果存在,同类三种尺寸之一满足iou>0.3则置信度标记为1,则将bbox_xywh及分类one-hot赋予label第i个数组中对应坐标。

__iter__():实现了 next() 方法并通过 StopIteration 异常标识迭代的完成

__next__():用于返回下一个迭代对象。①构造图片存储数组,label_bbox,bboxes存储数组。②每次返回一个batch的数据,首先获取每条annotation,并解析,通过preprocess_true_boxes()获取label_bbox,bboxes,追加到前述构造的数组中,每次返回一个batch的image,label_bbox,bboxes。

__len__():返回batch数量。

4.2.5 common文件

  主要用于定义基础卷积块、残差块、上采样块。含BatchNormalization()类方法重构、conv()res_block()upsample()

BatchNormalization()类方法重构:在keras.BatchNormalization源码中,有一个trainining参数,默认None;如果training=True/None,训练时,batch normalization依赖于当前batch的moving average和variance,但是在推理模式下,却依赖于整个训练集的average和variance;如果training=False,训练时,batch normalization依赖于每个batch的moving average和variance,但是在推理模式下,依赖于整个训练集的average和variance。显然我们希望实现后一种情况,但不能通过层的trainable参数来控制,这是两个概念,trainable控制了在归一化时是否更新beta和gamma,而training则控制了使用什么类型的均值和方差来进行归一化。所以需要将BatchNormalization中的call函数中training默认改为False。

conv():卷积块,整个网络用到的卷积块,从缩放上来看分为两种一种无缩放,一种缩小两倍。还设置了是否归一化、激活、添加偏置(如果没有归一化时添加偏置)

res_block():残差块,两个卷积块进行跳跃连接

upsample():采用两种方法,一种是直接resize,一种则是反卷积。

4.2.6 backbone文件

  定义darknet53网络

4.2.7 yolo3文件

  含yolo3()decode()bbox_iou()bbox_giou()loss_layer()

yolo3():定义yolo3神经网络,返回值为三种类型的预测输出,每个格子为长3*(num_class+5)的向量。

decode():首先将yolo3的输出转化为[batch_size,output_size,output_size,3,5+num_class],将预测得到的xy偏移量转化到输出大小的坐标上去,将,依据prior和预测得到的wh偏移量进行调整得到预测box的wh,同时对置信度和类别进行激活。

bbox_iou():输入两个二维数组,列为[x,y,w,h],计算IoU

bbox_giou():输入两个二维数组,列为[x,y,w,h],计算giou值,参见GIOU:对IOU的优化

loss_layer():计算loss。①获取yolo3网络输出和decode后的pred输出;②获取label中的xywh,类别概率分布,获取respond_bbox=label[...,4:5]这个表示格子中存在obj的话标记为1其余为0,与giou相乘即计算有obj的损失;③bbox_loss_scale作为一个放大系数,对于小目标而言,目标越小,这个值越大,与giou相乘后,giou框回归损失越大。④计算置信度损失:计算预测bbox和*bboxes的iou,并求最大值,如果最大值小于阈值则认为是背景。计算前景和背景的置信度损失。⑤计算分类损失。

4.2.8 train文件

  用于训练数据,训练时加载了官方权重,并锁定了一些层权重不可更新。

4.2.9 image_detect文件

  预测并绘图。

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