MMDetection v2 目標(biāo)檢測(3):配置修改

本文以 Faster R-CNN 為例奸笤,介紹如何修改 MMDetection v2 的配置文件晌梨,來訓(xùn)練 VOC 格式的自定義數(shù)據(jù)集中跌。

2021.9.1 更新:適配 MMDetection v2.16

目錄:

服務(wù)器的環(huán)境配置:

  • Ubuntu:18.04.5
  • CUDA:10.1.243
  • Python:3.7.9
  • PyTorch:1.5.1
  • MMDetection:2.16.0

1 修改基礎(chǔ)配置

./configs/_base_ 的目錄結(jié)構(gòu):

_base_
├─ datasets
├─ models
├─ schedules
└─ default_runtime.py

可以看出宦焦,包含四類配置:

  • datasets:定義數(shù)據(jù)集
  • models:定義模型架構(gòu)
  • schedules:定義訓(xùn)練計(jì)劃
  • default_runtime.py:定義運(yùn)行信息

打開 ./configs/faster_rcnn/faster_rcnn_r50_fpn_1x_coco.py

_base_ = [
    '../_base_/models/faster_rcnn_r50_fpn.py',
    '../_base_/datasets/coco_detection.py',
    '../_base_/schedules/schedule_1x.py',
    '../_base_/default_runtime.py'
]

修改數(shù)據(jù)集配置的路徑:

_base_ = [
    '../_base_/models/faster_rcnn_r50_fpn.py',
    '../_base_/datasets/voc0712.py',
    '../_base_/schedules/schedule_1x.py',
    '../_base_/default_runtime.py'
]

2 修改數(shù)據(jù)集配置

打開 ./configs/_base_/datasets/voc0712.py

dataset_type = 'VOCDataset'
data_root = 'data/VOCdevkit/'
img_norm_cfg = dict(
    mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
train_pipeline = [
    dict(type='LoadImageFromFile'),
    dict(type='LoadAnnotations', with_bbox=True),
    dict(type='Resize', img_scale=(1000, 600), keep_ratio=True),
    dict(type='RandomFlip', flip_ratio=0.5),
    dict(type='Normalize', **img_norm_cfg),
    dict(type='Pad', size_divisor=32),
    dict(type='DefaultFormatBundle'),
    dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels']),
]
test_pipeline = [
    dict(type='LoadImageFromFile'),
    dict(
        type='MultiScaleFlipAug',
        img_scale=(1000, 600),
        flip=False,
        transforms=[
            dict(type='Resize', keep_ratio=True),
            dict(type='RandomFlip'),
            dict(type='Normalize', **img_norm_cfg),
            dict(type='Pad', size_divisor=32),
            dict(type='ImageToTensor', keys=['img']),
            dict(type='Collect', keys=['img']),
        ])
]
data = dict(
    samples_per_gpu=2,
    workers_per_gpu=2,
    train=dict(
        type='RepeatDataset',
        times=3,
        dataset=dict(
            type=dataset_type,
            ann_file=[
                data_root + 'VOC2007/ImageSets/Main/trainval.txt',
                data_root + 'VOC2012/ImageSets/Main/trainval.txt'
            ],
            img_prefix=[data_root + 'VOC2007/', data_root + 'VOC2012/'],
            pipeline=train_pipeline)),
    val=dict(
        type=dataset_type,
        ann_file=data_root + 'VOC2007/ImageSets/Main/test.txt',
        img_prefix=data_root + 'VOC2007/',
        pipeline=test_pipeline),
    test=dict(
        type=dataset_type,
        ann_file=data_root + 'VOC2007/ImageSets/Main/test.txt',
        img_prefix=data_root + 'VOC2007/',
        pipeline=test_pipeline))
evaluation = dict(interval=1, metric='mAP')  # epoch
  1. 修改數(shù)據(jù)集的路徑 data_root发钝、ann_fileimg_prefix波闹,重復(fù)次數(shù) times笼平,并添加標(biāo)簽類別 classes
dataset_type = 'VOCDataset'
data_root = 'data/VOCdevkit/MyDataset/'
classes = ('car', 'pedestrian', 'cyclist')

data = dict(
    train=dict(
        type=dataset_type,
        ann_file=data_root + 'ImageSets/Main/train.txt',
        img_prefix=data_root,
        pipeline=train_pipeline,
        classes=classes),
    val=dict(
        type=dataset_type,
        ann_file=data_root + 'ImageSets/Main/val.txt',
        img_prefix=data_root,
        pipeline=test_pipeline,
        classes=classes),
    test=dict(
        type=dataset_type,
        ann_file=data_root + 'ImageSets/Main/test.txt',
        img_prefix=data_root,
        pipeline=test_pipeline,
        classes=classes))

Tips:
data_root 中的 MyDataset 可改為任意自定義數(shù)據(jù)集的名字。

  1. 添加圖像增強(qiáng)方式舔痪,并修改圖像縮放比例 img_scale
img_norm_cfg = dict(
    mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
train_pipeline = [
    dict(type='LoadImageFromFile'),
    dict(type='LoadAnnotations', with_bbox=True),
    dict(
        type='AutoAugment',
        policies=[
            [dict(
                 type='Rotate',
                 level=5,
                 img_fill_val=(124, 116, 104),
                 prob=0.5,
                 scale=1)
            ],
            [dict(type='Rotate', level=7, img_fill_val=(124, 116, 104)),
             dict(
                 type='Translate',
                 level=5,
                 prob=0.5,
                 img_fill_val=(124, 116, 104))
            ],
        ]),
    # 單尺度
    dict(type='Resize', img_scale=(1333, 800), keep_ratio=True),
    # 多尺度
    ```
    dict(
        type='Resize',
        img_scale=[(1333, 640), (1333, 672), (1333, 704), (1333, 736),
                   (1333, 768), (1333, 800)],
        multiscale_mode="value",
        keep_ratio=True),
    ```
    dict(type='RandomFlip', flip_ratio=0.5),
    dict(type='Normalize', **img_norm_cfg),
    dict(type='Pad', size_divisor=32),
    dict(type='DefaultFormatBundle'),
    dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels']),
]
test_pipeline = [
    dict(type='LoadImageFromFile'),
    dict(
        type='MultiScaleFlipAug',
        img_scale=(1333, 800),
        flip=False,
        transforms=[
            dict(type='Resize', keep_ratio=True),
            dict(type='RandomFlip'),
            dict(type='Normalize', **img_norm_cfg),
            dict(type='Pad', size_divisor=32),
            dict(type='DefaultFormatBundle'),
            dict(type='Collect', keys=['img']),
        ])
]

Tips:

  1. 如果 img_scale 是一個(gè)浮點(diǎn)數(shù)寓调,則直接作為縮放比例。

    • scale = img\_scale
  2. 如果 img_scale 是一對整數(shù)锄码,則需要根據(jù)長短邊計(jì)算縮放比例夺英。

    • long\_scale = \frac{\max(img\_scale)}{\max(h, w)}
    • short\_scale = \frac{\min(img\_scale)}{\min(h, w)}
    • scale = \min(long\_scale, short\_scale)

    這樣可確保縮放后的長短邊滋捶,均不超過設(shè)置的尺寸痛悯。

  3. 之后再根據(jù)縮放比例,調(diào)整圖像尺寸重窟。

    • new\_w = int(w \times scale + 0.5)
    • new\_h = int(h \times scale + 0.5)
  4. 如果設(shè)置多組值载萌,可實(shí)現(xiàn)多尺度訓(xùn)練。

注意:
官方文檔建議將 test_pipeline 中的 ImageToTensor 替換為 DefaultFormatBundle巡扇。

3 修改模型架構(gòu)配置

打開 ./configs/_base_/models/faster_rcnn_r50_fpn.py

model = dict(
    type='FasterRCNN',
    pretrained='torchvision://resnet50',
    
    roi_head=dict(
        type='StandardRoIHead',
        bbox_roi_extractor=dict(
            type='SingleRoIExtractor',
            roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=0),
            out_channels=256,
            featmap_strides=[4, 8, 16, 32]),
        bbox_head=dict(
            type='Shared2FCBBoxHead',
            in_channels=256,
            fc_out_channels=1024,
            roi_feat_size=7,
            num_classes=80,  # 改為自定義數(shù)據(jù)集的類別個(gè)數(shù)
            bbox_coder=dict(
                type='DeltaXYWHBBoxCoder',
                target_means=[0., 0., 0., 0.],
                target_stds=[0.1, 0.1, 0.2, 0.2]),
            reg_class_agnostic=False,
            loss_cls=dict(
                type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),
            loss_bbox=dict(type='L1Loss', loss_weight=1.0))))

修改 roi_head 的類別個(gè)數(shù) num_classes

model = dict(
    type='FasterRCNN',
    pretrained='torchvision://resnet50',
    
    roi_head=dict(
        type='StandardRoIHead',
        bbox_roi_extractor=dict(
            type='SingleRoIExtractor',
            roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=0),
            out_channels=256,
            featmap_strides=[4, 8, 16, 32]),
        bbox_head=dict(
            type='Shared2FCBBoxHead',
            in_channels=256,
            fc_out_channels=1024,
            roi_feat_size=7,
            num_classes=3,
            bbox_coder=dict(
                type='DeltaXYWHBBoxCoder',
                target_means=[0., 0., 0., 0.],
                target_stds=[0.1, 0.1, 0.2, 0.2]),
            reg_class_agnostic=False,
            loss_cls=dict(
                type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),
            loss_bbox=dict(type='L1Loss', loss_weight=1.0))))

注意:
MMDetection 2.0 后的版本扭仁,類別個(gè)數(shù)不需要加 1

4 修改訓(xùn)練計(jì)劃配置

打開 ./configs/_base_/schedules/schedule_1x.py

# optimizer
optimizer = dict(
    type='SGD',  # 可設(shè)為 'SGD', 'Adadelta', 'Adagrad', 'Adam', 'RMSprop' 等
    lr=0.02,
    momentum=0.9,
    weight_decay=0.0001)
optimizer_config = dict(grad_clip=None)
# learning policy
lr_config = dict(
    policy='step',  # 可設(shè)為 'step', 'cyclic', 'poly', 'ConsineAnnealing' 等
    warmup='linear',  # 可設(shè)為 'constant', 'linear', 'exp', 'None'
    warmup_iters=500,
    warmup_ratio=0.001,
    step=[8, 11])
runner = dict(type='EpochBasedRunner', max_epochs=12)

修改學(xué)習(xí)率 lr 和迭代輪數(shù) total_epochs

# optimizer
optimizer = dict(
    type='SGD',
    lr=0.02 / 8,
    momentum=0.9,
    weight_decay=0.0001)
optimizer_config = dict(grad_clip=None)
# learning policy
lr_config = dict(
    policy='step',
    warmup='linear',
    warmup_iters=500,
    warmup_ratio=0.001,
    step=[7])
runner = dict(max_epochs=8)

Tips:
Faster R-CNN 的默認(rèn)學(xué)習(xí)率 lr=0.02 對應(yīng)批大小 batch_size=16厅翔。
因此需要根據(jù)實(shí)際情況乖坠,按比例縮放學(xué)習(xí)率。

  • batch\_size = num\_gpus \times samples\_per\_gpu
  • lr = 0.02 \times (batch\_size / 16)

5 修改運(yùn)行信息配置

打開 ./configs/_base_/default_runtime.py

checkpoint_config = dict(interval=1)  # epoch
# yapf:disable
log_config = dict(
    interval=50,  # iteration
    hooks=[
        dict(type='TextLoggerHook'),
        # dict(type='TensorboardLoggerHook')
    ])
# yapf:enable
custom_hooks = [dict(type='NumClassCheckHook')]

dist_params = dict(backend='nccl')
log_level = 'INFO'
load_from = None
resume_from = None
workflow = [('train', 1)]  # 也可設(shè)為 [('train', 1), ('val', 1)]

修改 log_config 的日志記錄間隔 interval刀闷,并開啟 TensorBoard 記錄器:

log_config = dict(
    interval=100,
    hooks=[
        dict(type='TextLoggerHook'),
        dict(type='TensorboardLoggerHook')
    ])

6 創(chuàng)建自定義配置

另外熊泵,也可以將上面步驟 1-5 修改的配置寫在一個(gè)文件中。

這樣就能夠更方便地管理不同的配置文件甸昏,避免因頻繁修改導(dǎo)致出錯(cuò)顽分。

  1. 打開 configs 目錄:
cd configs
  1. 新建自定義配置目錄:
mkdir myconfig
  1. ./myconfig 目錄下,新建 faster_rcnn_r50_fpn_1x_mydataset.py
# 修改基礎(chǔ)配置
_base_ = [
    '../_base_/models/faster_rcnn_r50_fpn.py',
    '../_base_/datasets/voc0712.py',
    '../_base_/schedules/schedule_1x.py',
    '../_base_/default_runtime.py'
]

# 修改數(shù)據(jù)集配置
dataset_type = 'VOCDataset'
data_root = 'data/VOCdevkit/MyDataset/'
classes = ('car', 'pedestrian', 'cyclist')

img_norm_cfg = dict(
    mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
train_pipeline = [
    dict(type='LoadImageFromFile'),
    dict(type='LoadAnnotations', with_bbox=True),
    dict(
        type='AutoAugment',
        policies=[
            [dict(
                 type='Rotate',
                 level=5,
                 img_fill_val=(124, 116, 104),
                 prob=0.5,
                 scale=1)
            ],
            [dict(type='Rotate', level=7, img_fill_val=(124, 116, 104)),
             dict(
                 type='Translate',
                 level=5,
                 prob=0.5,
                 img_fill_val=(124, 116, 104))
            ],
        ]),
    # 單尺度
    dict(type='Resize', img_scale=(1333, 800), keep_ratio=True),
    # 多尺度
    ```
    dict(
        type='Resize',
        img_scale=[(1333, 640), (1333, 672), (1333, 704), (1333, 736),
                   (1333, 768), (1333, 800)],
        multiscale_mode="value",
        keep_ratio=True),
    ```
    dict(type='RandomFlip', flip_ratio=0.5),
    dict(type='Normalize', **img_norm_cfg),
    dict(type='Pad', size_divisor=32),
    dict(type='DefaultFormatBundle'),
    dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels']),
]
test_pipeline = [
    dict(type='LoadImageFromFile'),
    dict(
        type='MultiScaleFlipAug',
        img_scale=(1333, 800),
        flip=False,
        transforms=[
            dict(type='Resize', keep_ratio=True),
            dict(type='RandomFlip'),
            dict(type='Normalize', **img_norm_cfg),
            dict(type='Pad', size_divisor=32),
            dict(type='DefaultFormatBundle'),
            dict(type='Collect', keys=['img']),
        ])
]

data = dict(
    train=dict(
        type=dataset_type,
        ann_file=data_root + 'ImageSets/Main/train.txt',
        img_prefix=data_root,
        pipeline=train_pipeline,
        classes=classes),
    val=dict(
        type=dataset_type,
        ann_file=data_root + 'ImageSets/Main/val.txt',
        img_prefix=data_root,
        pipeline=test_pipeline,
        classes=classes),
    test=dict(
        type=dataset_type,
        ann_file=data_root + 'ImageSets/Main/test.txt',
        img_prefix=data_root,
        pipeline=test_pipeline,
        classes=classes))

# 修改模型架構(gòu)配置
model = dict(
    roi_head=dict(
        bbox_head=dict(num_classes=3)))

# 修改訓(xùn)練計(jì)劃配置
# optimizer
optimizer = dict(
    type='SGD',
    lr=0.02 / 8,
    momentum=0.9,
    weight_decay=0.0001)
optimizer_config = dict(grad_clip=None)
# learning policy
lr_config = dict(
    policy='step',
    warmup='linear',
    warmup_iters=500,
    warmup_ratio=0.001,
    step=[7])
runner = dict(max_epochs=8)

# 修改運(yùn)行信息配置
checkpoint_config = dict(interval=1)
log_config = dict(
    interval=100,
    hooks=[
        dict(type='TextLoggerHook'),
        dict(type='TensorboardLoggerHook')
    ])
evaluation = dict(interval=1, metric='mAP')

7 修改其他信息

在訓(xùn)練和測試時(shí)施蜜,遇到的一些容易報(bào)錯(cuò)的地方卒蘸,這里做下記錄。

7.1 標(biāo)簽類別

  1. 打開 ./mmdet/datasets/voc.py

修改 VOCDataset() 的標(biāo)簽類別 CLASSES

class VOCDataset(XMLDataset):

    CLASSES = ('car', 'pedestrian', 'cyclist')
    ```
    CLASSES = ('aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus', 'car',
               'cat', 'chair', 'cow', 'diningtable', 'dog', 'horse',
               'motorbike', 'person', 'pottedplant', 'sheep', 'sofa', 'train',
               'tvmonitor')
    ```
  1. 打開 ./mmdet/core/evaluation/class_names.py

修改 voc_classes() 返回的標(biāo)簽類別:

def voc_classes():

    return [
        'car', 'pedestrian', 'cyclist'
    ]
    ```
    return [
        'aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus', 'car', 'cat',
        'chair', 'cow', 'diningtable', 'dog', 'horse', 'motorbike', 'person',
        'pottedplant', 'sheep', 'sofa', 'train', 'tvmonitor'
    ]
    ```

注意:
以上代碼花墩,如果只有一個(gè)類別悬秉,需要在類別后加一個(gè)逗號澄步,否則會報(bào)錯(cuò)。

7.2 其他信息

  1. 打開 ./mmdet/datasets/voc.py

如果是自定義數(shù)據(jù)集的名字和泌,需要注釋報(bào)錯(cuò)信息 ValueError村缸,并將 self.year 設(shè)為 None

class VOCDataset(XMLDataset):

    def __init__(self, **kwargs):
        super(VOCDataset, self).__init__(**kwargs)
        if 'VOC2007' in self.img_prefix:
            self.year = 2007
        elif 'VOC2012' in self.img_prefix:
            self.year = 2012
        else:
            self.year = None
            # raise ValueError('Cannot infer dataset year from img_prefix')

Tips:
這里的年份主要是為了區(qū)分計(jì)算 AP 時(shí)采用的標(biāo)準(zhǔn)。
VOC2007 采用 11 points 計(jì)算(即:[0, 0.1, ..., 1])武氓,而其他數(shù)據(jù)集則采用 AUC 計(jì)算梯皿。

  1. 打開 ./mmdet/datasets/xml_style.py

如果圖像文件不是 jpg 格式,需要將 filenameimg_path 的后綴名改為相應(yīng)格式:

def load_annotations(self, ann_file):

    data_infos = []
    img_ids = mmcv.list_from_file(ann_file)
    for img_id in img_ids:
        # filename = f'JPEGImages/{img_id}.jpg'
        filename = f'JPEGImages/{img_id}.png'
        xml_path = osp.join(self.img_prefix, 'Annotations',
                            f'{img_id}.xml')
        tree = ET.parse(xml_path)
        root = tree.getroot()
        size = root.find('size')
        width = 0
        height = 0
        if size is not None:
            width = int(size.find('width').text)
            height = int(size.find('height').text)
        else:
            # img_path = osp.join(self.img_prefix, 'JPEGImages',
            #                     '{}.jpg'.format(img_id))
            img_path = osp.join(self.img_prefix, 'JPEGImages',
                                '{}.png'.format(img_id))
            img = Image.open(img_path)
            width, height = img.size
        data_infos.append(
            dict(id=img_id, filename=filename, width=width, height=height))

    return data_infos

如果標(biāo)注文件中不存在 difficult 標(biāo)簽县恕,需要將 difficult 設(shè)為 0

def get_ann_info(self, idx):

    img_id = self.data_infos[idx]['id']
    xml_path = osp.join(self.img_prefix, 'Annotations', f'{img_id}.xml')
    tree = ET.parse(xml_path)
    root = tree.getroot()

    for obj in root.findall('object'):
        name = obj.find('name').text
        if name not in self.CLASSES:
            continue
        label = self.cat2label[name]
        # difficult = int(obj.find('difficult').text)
        try:
            difficult = int(obj.find('difficult').text)
        except AttributeError:
            difficult = 0

注意:
目前最新的版本东羹,已經(jīng)修復(fù)了這個(gè)問題,可以忽略忠烛。

  1. 打開 ./tools/robustness_eval.py

results 中的 20 改為自定義數(shù)據(jù)集的類別個(gè)數(shù):

def get_voc_style_results(filename, prints='mPC', aggregate='benchmark'):

    eval_output = mmcv.load(filename)

    num_distortions = len(list(eval_output.keys()))
    # results = np.zeros((num_distortions, 6, 20), dtype='float32')
    results = np.zeros((num_distortions, 6, 3), dtype='float32')

8 結(jié)語

有幫助的話属提,點(diǎn)個(gè)贊再走吧,謝謝~

參考:

  1. 最新版本的mmdetection2.0 (v2.0.0版本)環(huán)境搭建美尸、訓(xùn)練自己的數(shù)據(jù)集冤议、測試以及常見錯(cuò)誤集合
  2. Learn about Configs
  3. Finetuning Models
  4. Prepare a config
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市师坎,隨后出現(xiàn)的幾起案子恕酸,更是在濱河造成了極大的恐慌,老刑警劉巖胯陋,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蕊温,死亡現(xiàn)場離奇詭異,居然都是意外死亡遏乔,警方通過查閱死者的電腦和手機(jī)义矛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來按灶,“玉大人症革,你說我怎么就攤上這事⊙炫裕” “怎么了?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵量蕊,是天一觀的道長铺罢。 經(jīng)常有香客問我,道長残炮,這世上最難降的妖魔是什么韭赘? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮势就,結(jié)果婚禮上泉瞻,老公的妹妹穿的比我還像新娘脉漏。我一直安慰自己,他們只是感情好袖牙,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布侧巨。 她就那樣靜靜地躺著,像睡著了一般鞭达。 火紅的嫁衣襯著肌膚如雪司忱。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天畴蹭,我揣著相機(jī)與錄音坦仍,去河邊找鬼。 笑死叨襟,一個(gè)胖子當(dāng)著我的面吹牛繁扎,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播糊闽,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼梳玫,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了墓怀?” 一聲冷哼從身側(cè)響起汽纠,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎傀履,沒想到半個(gè)月后虱朵,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡钓账,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年碴犬,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片梆暮。...
    茶點(diǎn)故事閱讀 39,834評論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡服协,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出啦粹,到底是詐尸還是另有隱情偿荷,我是刑警寧澤,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布唠椭,位于F島的核電站跳纳,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏贪嫂。R本人自食惡果不足惜寺庄,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧斗塘,春花似錦赢织、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至朽合,卻和暖如春俱两,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背曹步。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工宪彩, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人讲婚。 一個(gè)月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓尿孔,卻偏偏與公主長得像,于是被迫代替她去往敵國和親筹麸。 傳聞我的和親對象是個(gè)殘疾皇子活合,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評論 2 354