TensorFlow 訓(xùn)練 Mask R-CNN 模型

????????前面的文章 TensorFlow 訓(xùn)練自己的目標(biāo)檢測(cè)器 寫作的時(shí)候刑赶,TensorFlow models 項(xiàng)目下的目標(biāo)檢測(cè)專題 object_detection 還沒有給出用于實(shí)例分割的預(yù)訓(xùn)練模型,但其實(shí)這個(gè)專題中的 Faster R-CNN 模型是按照 Mask R-CNN 來寫的休里,只要用戶在訓(xùn)練時(shí)傳入了 mask十性,則模型也會(huì)預(yù)測(cè) mask慧库,這可以從該專題下的文件

object_detection/meta_architectures/faster_rcnn_meta_arch.py

看出來旺芽。

????????現(xiàn)在,TensorFlow object_detection API 官方已經(jīng)放出 Mask R-CNN 預(yù)訓(xùn)練模型擅这,而且對(duì)目標(biāo)檢測(cè)的源代碼也做了一些改動(dòng)(主要是引入了 TensorFlow 的兩個(gè)高級(jí) API 模塊:tf.estimator 以及 tf.keras),為了與官方的迭代同步景鼠,以及作為文章 TensorFlow 訓(xùn)練自己的目標(biāo)檢測(cè)器 的補(bǔ)充仲翎,特別記錄一下利用 TensorFlow/Object Detection API 來訓(xùn)練 Mask R-CNN 模型的過程。

????????首先铛漓,來體驗(yàn)一下官方公布的預(yù)訓(xùn)練模型的分割效果(此處引用的預(yù)訓(xùn)練模型是 mask_rcnn_resnet101_atrous_coco):

image3_out.jpg

顯然溯香,效果還是不錯(cuò)的。

????????關(guān)于 TensorFlow/Object Detection API 的安裝以及其它信息請(qǐng)參考前面文章 TensorFlow 訓(xùn)練自己的目標(biāo)檢測(cè)器浓恶。如果你對(duì)此不陌生(且成功更新了該 API)的話玫坛,那么話不多說,馬上進(jìn)入正題包晰。

????????所有代碼見 github湿镀。如果不想自己標(biāo)注訓(xùn)練數(shù)據(jù),請(qǐng)使用下一篇文章生成的數(shù)據(jù) train.recordval.record 以及對(duì)應(yīng)的 shape_label_map.pbtxt 來訓(xùn)練 Mask R-CNN 模型伐憾。

一勉痴、數(shù)據(jù)準(zhǔn)備

????????因?yàn)橹皇窃攲?shí)的記錄一下訓(xùn)練過程,所以數(shù)據(jù)量不需要太多树肃,我們以數(shù)據(jù)集 Oxford-IIIT Pet 中的 阿比西尼亞貓(Abyssinian) 為例來說明蒸矛。數(shù)據(jù)集 Oxford-IIIT Pet 可以從 這里 下載,數(shù)據(jù)量不大胸嘴,只有 800M 不到雏掠。其中,阿比西尼亞貓的圖像只有 232 張劣像,這種貓的長(zhǎng)相如下:

Abyssinian_65.jpg

要訓(xùn)練 Mask R-CNN 實(shí)例分割模型乡话,我們首先要準(zhǔn)備圖像的掩模(mask),使用標(biāo)注工具 labelme(支持 Windows 和 Ubuntu驾讲,使用 (sudo) pip install labelme 安裝蚊伞,需要安裝依賴項(xiàng):(sudo) pip install pyqt5)來完成這一步。安裝完 labelme 之后吮铭,在命令行執(zhí)行 labelme 會(huì)彈出一個(gè)標(biāo)注窗口:

labelme 標(biāo)注界面

labelImg 幾乎一樣时迫。從 Open 打開一張圖像 Abyssinian_65.jpg,之后使用 Create Polygons 描出目標(biāo)所在的近似多邊形區(qū)域:

Abyssinian_65.png

點(diǎn)擊 Save 之后選擇路徑保存為一個(gè) json 文件:Abyssinian_65.json谓晌。

????????【將命令行來到 Abyssinian_65.json 文件所在的文件夾掠拳,執(zhí)行

labelme_json_to_dataset Abyssinian_65.json

會(huì)在當(dāng)前目錄下生成一個(gè)名叫 Abyssinian_65_json 的文件夾,里面包含如下文件:

Abyssinian_65_json 文件夾內(nèi)文件

其中的 label.png 圖像:

label.png

正是在公開數(shù)據(jù)集經(jīng)常見到的實(shí)例分割掩模纸肉∧缗罚】——這一段只用來描述 labelme 的完整功能喊熟,實(shí)際上本文不需要執(zhí)行這個(gè)過程!

????????但是 labelme 有一個(gè)很大的缺陷姐刁,即它只能標(biāo)注首尾相連的多邊形芥牌,如果一個(gè)目標(biāo)實(shí)例包含一個(gè)洞(如第二幅圖像 Abyssinian_65.jpg 的貓的兩腿之間的空隙),那么這個(gè)洞也會(huì)算成這個(gè)目標(biāo)實(shí)例的一部分聂使,而這顯然是不正確的壁拉。為了避免這個(gè)缺陷,在標(biāo)注目標(biāo)實(shí)例時(shí)柏靶,可以增加一個(gè)額外的類 hole(如上圖的 綠色 部分)弃理,實(shí)際使用時(shí)只要把 hole 部分去掉即可,如:

image.png

????????TensorFlow 訓(xùn)練時(shí)要求 mask 是跟原圖像一樣大小的二值(0-1)png 圖像(如上圖)屎蜓,而且數(shù)據(jù)輸入格式必須為 tfrecord 文件痘昌,所以還需要寫一個(gè)數(shù)據(jù)格式轉(zhuǎn)化的輔助 python 文件,該文件可以參考 TensorFlow 目標(biāo)檢測(cè)官方的文件 create_coco_tf_record.py 來寫炬转。

????????在寫之前辆苔,強(qiáng)調(diào)說明一下數(shù)據(jù)輸入的格式:對(duì)每張圖像中的每個(gè)目標(biāo),該目標(biāo)的 mask 是一張與原圖一樣大小的 0-1 二值圖像返吻,該目標(biāo)所在區(qū)域的值為 1姑子,其他區(qū)域全為 0(見 TensorFlow/object_detection 官方說明:Run an Instance Segmentation Model/PNG Instance Segmentation Masks)。也就是說测僵,同一張圖像中的所有目標(biāo)的 mask 都需要從單個(gè)標(biāo)注文件中分割出來街佑。這可以使用 OpenCV 的 cv2.fillPoly 函數(shù)來實(shí)現(xiàn),該函數(shù)將指定多邊形區(qū)域內(nèi)部的值都填充為用戶設(shè)定的值捍靠。

????????假設(shè)已經(jīng)準(zhǔn)備好了 mask 標(biāo)注數(shù)據(jù)沐旨,因?yàn)榘鼑總€(gè)目標(biāo)的 mask 的最小矩形就是該目標(biāo)的 boundingbox,所以目標(biāo)檢測(cè)的標(biāo)注數(shù)據(jù)也就同時(shí)有了榨婆。接下來磁携,只需要將這些標(biāo)注數(shù)據(jù)(原始圖像,以及 labelme 標(biāo)注生成的 json 文件)轉(zhuǎn)換成 TFRecord 文件即可良风,使用如下代碼完成這一步操作(命名為 create_tf_record.py谊迄,見 github):

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sun Aug 26 10:57:09 2018

@author: shirhe-lyh
"""

"""Convert raw dataset to TFRecord for object_detection.

Please note that this tool only applies to labelme's annotations(json file).

Example usage:
    python3 create_tf_record.py \
        --images_dir=your absolute path to read images.
        --annotations_json_dir=your path to annotaion json files.
        --label_map_path=your path to label_map.pbtxt
        --output_path=your path to write .record.
"""

import cv2
import glob
import hashlib
import io
import json
import numpy as np
import os
import PIL.Image
import tensorflow as tf

import read_pbtxt_file


flags = tf.app.flags

flags.DEFINE_string('images_dir', None, 'Path to images directory.')
flags.DEFINE_string('annotations_json_dir', 'datasets/annotations', 
                    'Path to annotations directory.')
flags.DEFINE_string('label_map_path', None, 'Path to label map proto.')
flags.DEFINE_string('output_path', None, 'Path to the output tfrecord.')

FLAGS = flags.FLAGS


def int64_feature(value):
    return tf.train.Feature(int64_list=tf.train.Int64List(value=[value]))


def int64_list_feature(value):
    return tf.train.Feature(int64_list=tf.train.Int64List(value=value))


def bytes_feature(value):
    return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value]))


def bytes_list_feature(value):
    return tf.train.Feature(bytes_list=tf.train.BytesList(value=value))


def float_list_feature(value):
    return tf.train.Feature(float_list=tf.train.FloatList(value=value))


def create_tf_example(annotation_dict, label_map_dict=None):
    """Converts image and annotations to a tf.Example proto.
    
    Args:
        annotation_dict: A dictionary containing the following keys:
            ['height', 'width', 'filename', 'sha256_key', 'encoded_jpg',
             'format', 'xmins', 'xmaxs', 'ymins', 'ymaxs', 'masks',
             'class_names'].
        label_map_dict: A dictionary maping class_names to indices.
            
    Returns:
        example: The converted tf.Example.
        
    Raises:
        ValueError: If label_map_dict is None or is not containing a class_name.
    """
    if annotation_dict is None:
        return None
    if label_map_dict is None:
        raise ValueError('`label_map_dict` is None')
        
    height = annotation_dict.get('height', None)
    width = annotation_dict.get('width', None)
    filename = annotation_dict.get('filename', None)
    sha256_key = annotation_dict.get('sha256_key', None)
    encoded_jpg = annotation_dict.get('encoded_jpg', None)
    image_format = annotation_dict.get('format', None)
    xmins = annotation_dict.get('xmins', None)
    xmaxs = annotation_dict.get('xmaxs', None)
    ymins = annotation_dict.get('ymins', None)
    ymaxs = annotation_dict.get('ymaxs', None)
    masks = annotation_dict.get('masks', None)
    class_names = annotation_dict.get('class_names', None)
    
    labels = []
    for class_name in class_names:
        label = label_map_dict.get(class_name, None)
        if label is None:
            raise ValueError('`label_map_dict` is not containing {}.'.format(
                class_name))
        labels.append(label)
            
    encoded_masks = []
    for mask in masks:
        pil_image = PIL.Image.fromarray(mask.astype(np.uint8))
        output_io = io.BytesIO()
        pil_image.save(output_io, format='PNG')
        encoded_masks.append(output_io.getvalue())
        
    feature_dict = {
        'image/height': int64_feature(height),
        'image/width': int64_feature(width),
        'image/filename': bytes_feature(filename.encode('utf8')),
        'image/source_id': bytes_feature(filename.encode('utf8')),
        'image/key/sha256': bytes_feature(sha256_key.encode('utf8')),
        'image/encoded': bytes_feature(encoded_jpg),
        'image/format': bytes_feature(image_format.encode('utf8')),
        'image/object/bbox/xmin': float_list_feature(xmins),
        'image/object/bbox/xmax': float_list_feature(xmaxs),
        'image/object/bbox/ymin': float_list_feature(ymins),
        'image/object/bbox/ymax': float_list_feature(ymaxs),
        'image/object/mask': bytes_list_feature(encoded_masks),
        'image/object/class/label': int64_list_feature(labels)}
    example = tf.train.Example(features=tf.train.Features(
        feature=feature_dict))
    return example


def _get_annotation_dict(images_dir, annotation_json_path):  
    """Get boundingboxes and masks.
    
    Args:
        images_dir: Path to images directory.
        annotation_json_path: Path to annotated json file corresponding to
            the image. The json file annotated by labelme with keys:
                ['lineColor', 'imageData', 'fillColor', 'imagePath', 'shapes',
                 'flags'].
            
    Returns:
        annotation_dict: A dictionary containing the following keys:
            ['height', 'width', 'filename', 'sha256_key', 'encoded_jpg',
             'format', 'xmins', 'xmaxs', 'ymins', 'ymaxs', 'masks',
             'class_names'].
#            
#    Raises:
#        ValueError: If images_dir or annotation_json_path is not exist.
    """
#    if not os.path.exists(images_dir):
#        raise ValueError('`images_dir` is not exist.')
#    
#    if not os.path.exists(annotation_json_path):
#        raise ValueError('`annotation_json_path` is not exist.')
    
    if (not os.path.exists(images_dir) or
        not os.path.exists(annotation_json_path)):
        return None
    
    with open(annotation_json_path, 'r') as f:
        json_text = json.load(f)
    shapes = json_text.get('shapes', None)
    if shapes is None:
        return None
    image_relative_path = json_text.get('imagePath', None)
    if image_relative_path is None:
        return None
    image_name = image_relative_path.split('/')[-1]
    image_path = os.path.join(images_dir, image_name)
    image_format = image_name.split('.')[-1].replace('jpg', 'jpeg')
    if not os.path.exists(image_path):
        return None
    
    with tf.gfile.GFile(image_path, 'rb') as fid:
        encoded_jpg = fid.read()
    image = cv2.imread(image_path)
    height = image.shape[0]
    width = image.shape[1]
    key = hashlib.sha256(encoded_jpg).hexdigest()
    
    xmins = []
    xmaxs = []
    ymins = []
    ymaxs = []
    masks = []
    class_names = []
    hole_polygons = []
    for mark in shapes:
        class_name = mark.get('label')
        class_names.append(class_name)
        polygon = mark.get('points')
        polygon = np.array(polygon)
        if class_name == 'hole':
            hole_polygons.append(polygon)
        else:
            mask = np.zeros(image.shape[:2])
            cv2.fillPoly(mask, [polygon], 1)
            masks.append(mask)
            
            # Boundingbox
            x = polygon[:, 0]
            y = polygon[:, 1]
            xmin = np.min(x)
            xmax = np.max(x)
            ymin = np.min(y)
            ymax = np.max(y)
            xmins.append(float(xmin) / width)
            xmaxs.append(float(xmax) / width)
            ymins.append(float(ymin) / height)
            ymaxs.append(float(ymax) / height)
    # Remove holes in mask
    for mask in masks:
        mask = cv2.fillPoly(mask, hole_polygons, 0)
        
    annotation_dict = {'height': height,
                       'width': width,
                       'filename': image_name,
                       'sha256_key': key,
                       'encoded_jpg': encoded_jpg,
                       'format': image_format,
                       'xmins': xmins,
                       'xmaxs': xmaxs,
                       'ymins': ymins,
                       'ymaxs': ymaxs,
                       'masks': masks,
                       'class_names': class_names}
    return annotation_dict


def main(_):
    if not os.path.exists(FLAGS.images_dir):
        raise ValueError('`images_dir` is not exist.')
    if not os.path.exists(FLAGS.annotations_json_dir):
        raise ValueError('`annotations_json_dir` is not exist.')
    if not os.path.exists(FLAGS.label_map_path):
        raise ValueError('`label_map_path` is not exist.')
        
    label_map = read_pbtxt_file.get_label_map_dict(FLAGS.label_map_path)
    
    writer = tf.python_io.TFRecordWriter(FLAGS.output_path)
        
    num_annotations_skiped = 0
    annotations_json_path = os.path.join(FLAGS.annotations_json_dir, '*.json')
    for i, annotation_file in enumerate(glob.glob(annotations_json_path)):
        if i % 100 == 0:
            print('On image %d', i)
            
        annotation_dict = _get_annotation_dict(
            FLAGS.images_dir, annotation_file)
        if annotation_dict is None:
            num_annotations_skiped += 1
            continue
        tf_example = create_tf_example(annotation_dict, label_map)
        writer.write(tf_example.SerializeToString())
    
    print('Successfully created TFRecord to {}.'.format(FLAGS.output_path))


if __name__ == '__main__':
    tf.app.run()

假設(shè)你的所有原始圖像的路徑為 path_to_images_dir,使用 labelme 標(biāo)注產(chǎn)生的所有用于 訓(xùn)練 的 json 文件的路徑為 path_to_train_annotations_json_dir烟央,所有用于 驗(yàn)證 的 json 文件的路徑為 path_to_val_annotaions_json_dir统诺,在終端先后執(zhí)行如下指令:

$ python3 create_tf_record.py \
    --images_dir=path_to_images_dir \   
    --annotations_json_dir=path_to_train_annotations_json_dir \ 
    --label_map_path=path_to_label_map.pbtxt \
    --output_path=path_to_train.record
$ python3 create_tf_record.py \
    --images_dir=path_to_images_dir \   
    --annotations_json_dir=path_to_val_annotations_json_dir \ 
    --label_map_path=path_to_label_map.pbtxt \
    --output_path=path_to_val.record

其中,以上所有路徑都支持相對(duì)路徑疑俭。output_path 為輸出的 train.record 以及 val.record 的路徑粮呢,label_map_path 是所有需要檢測(cè)的類名及類標(biāo)號(hào)的配置文件,該文件的后綴名為 .pbtxt,寫法很簡(jiǎn)單啄寡,假如你要檢測(cè) ’person' 豪硅, 'car' ,'bicycle' 等類目標(biāo)挺物,則寫入如下內(nèi)容:

item {
????????id: 1
????????name: 'person'
}

item {
????????id: 2
????????name: 'car'
}

item {
????????id: 3
????????name: 'bicycle'
}

...

這里我們只檢測(cè) 阿比西尼亞貓(Abyssinian)一個(gè)類懒浮,所以只需要寫入:

item {
????????id: 1
????????name: 'Abyssinian'
}

即可,命名為 Abyssinian_label_map.pbtxt姻乓。

???????通過以上源代碼的部分內(nèi)容:

    feature_dict = {
        'image/height': int64_feature(height),
        'image/width': int64_feature(width),
        'image/filename': bytes_feature(filename.encode('utf8')),
        'image/source_id': bytes_feature(filename.encode('utf8')),
        'image/key/sha256': bytes_feature(sha256_key.encode('utf8')),
        'image/encoded': bytes_feature(encoded_jpg),
        'image/format': bytes_feature(image_format.encode('utf8')),
        'image/object/bbox/xmin': float_list_feature(xmins),
        'image/object/bbox/xmax': float_list_feature(xmaxs),
        'image/object/bbox/ymin': float_list_feature(ymins),
        'image/object/bbox/ymax': float_list_feature(ymaxs),
        'image/object/mask': bytes_list_feature(encoded_masks),
        'image/object/class/label': int64_list_feature(labels)}

我們知道嵌溢,寫入 tfrecord 的文件內(nèi)容包括:原始圖像的寬高,圖像保存名蹋岩,圖像本身,圖像格式学少,目標(biāo)邊框(boundingbox)剪个,掩模(mask),以及類標(biāo)號(hào)(label)等版确。而且還需要注意的是:boungingbox 必須是正規(guī)化坐標(biāo)(除以圖像寬或高扣囊,0-1 之間取值)。到此绒疗,訓(xùn)練數(shù)據(jù)準(zhǔn)備完畢侵歇,進(jìn)入訓(xùn)練時(shí)間。

二吓蘑、訓(xùn)練 Mask R-CNN 模型

???????訓(xùn)練過程完全類似文章 TensorFlow 訓(xùn)練自己的目標(biāo)檢測(cè)器惕虑,只需要下載一個(gè) Mask R-CNN 的預(yù)訓(xùn)練模型,以及配置一下訓(xùn)練參數(shù)即可磨镶。預(yù)訓(xùn)練模型下載請(qǐng)前往鏈接 Mask R-CNN 預(yù)訓(xùn)練模型溃蔫,使用預(yù)訓(xùn)練模型(以及下面精調(diào)后的模型)可以參考 官方示例代碼注意要將 sess 當(dāng)作函數(shù) run_inference_for_single_image 的參數(shù)傳入琳猫,否則預(yù)測(cè)每幅圖像都要重新生成會(huì)話會(huì)消耗大量時(shí)間伟叛,此時(shí)原來的 for 循環(huán)應(yīng)該這么寫:

def run_inference_for_single_image(image, graph, sess):
    with graph.as_default():
        # Get handles to input and output tensors
        ops = tf.get_default_graph().get_operations()
        ...
        return return output_dict

with tf.Session(graph=detection_graph) as sess:
    for image_path in TEST_IMAGE_PATHS:
        ...
        output_dict = run_inference_for_single_image(image_np, detection_graph, sess)

???????接下來是使用預(yù)訓(xùn)練模型和第一步標(biāo)注的數(shù)據(jù)來微調(diào)模型了,假如下載的預(yù)訓(xùn)練模型是 mask_rcnn_inception_v2_coco脐嫂,那么復(fù)制

TensorFlow models/research/object_detection/samples/configs/mask_rcnn_inception_v2_coco.config

文件到你的訓(xùn)練項(xiàng)目下统刮,將其中的 num_classes : 90 改為你要檢測(cè)的目標(biāo)總類目數(shù),比如账千,因?yàn)檫@里我們只檢測(cè) 阿比西尼亞貓 一個(gè)類侥蒙,所以改為 1。另外蕊爵,還需要將該文件中 5 個(gè) PATH_TO_BE_CONFIGURED 改為相應(yīng)文件的路徑辉哥,詳情參考文章 TensorFlow 訓(xùn)練自己的目標(biāo)檢測(cè)器

???????所有配置都完成后,在 models/research/object_detection 目錄的終端下執(zhí)行:

$ python3 model_main.py \
    --model_dir=path/to/save/directory \
    --pipeline_config_path=path/to/mask_rcnn_inception_v2_xxx.config

開始訓(xùn)練醋旦。訓(xùn)練成功開始后恒水,新開一個(gè)終端,可以使用 tensorboard 在瀏覽器上實(shí)時(shí)監(jiān)督訓(xùn)練過程饲齐,如果想要提前終止訓(xùn)練钉凌,請(qǐng)用 Ctrl + C 中斷。訓(xùn)練結(jié)束后捂人,模型轉(zhuǎn)換以及使用請(qǐng)參考文章 TensorFlow 訓(xùn)練自己的目標(biāo)檢測(cè)器 或者 官方文檔御雕。如果執(zhí)行以上訓(xùn)練指令時(shí),有 TensorFlow 本身代碼報(bào)錯(cuò)滥搭,請(qǐng)使用:

$ sudo pip/pip3 install --upgrade tensorflow-gpu

升級(jí) TensorFlow 版本酸纲。除此之外买喧,如果說缺乏 pycocotools 模塊亿驾,可以使用

$ sudo pip/pip3 install Cython pycocotools

安裝。

說明
???????1.有關(guān)本文章代碼以及數(shù)據(jù)都在 github迅脐,文件夾 datasets/images 下有 232 張 阿比西尼亞貓 的圖像愁溜,文件夾 datasets/annotations 下有其中 10 張圖像的 mask 標(biāo)注數(shù)據(jù)疾嗅,可以將該文件夾一分為二,比如挑選其中 8 張用于生成 train.record冕象,其它 2 張用于生成 val.record代承。然后,修改文件夾 training 下的配置文件 mask_rcnn_inception_v2_coco.config (假如對(duì)應(yīng)的預(yù)訓(xùn)練模型是 mask_rcnn_inception_v2_coco渐扮。其它預(yù)訓(xùn)練模型對(duì)應(yīng)的配置文件請(qǐng)到文件夾 models/research/object_detection/samples/configs 內(nèi)復(fù)制)论悴,之后就可以開始訓(xùn)練了。因?yàn)橹皇峭暾菔菊麄€(gè)訓(xùn)練過程席爽,所以數(shù)據(jù)多少無所謂意荤。

???????2.為了防止訓(xùn)練過程中出現(xiàn)大概某某 groundtruth 已經(jīng)加入將忽略的問題,請(qǐng)確保訓(xùn)練集和驗(yàn)證集圖像的名字不要重名只锻。

TensorFlow bug】:訓(xùn)練過程如果報(bào)如下錯(cuò)誤:TypeError: can't pickle dict_values objects玖像,則將 models/research/object_detection/model_lib.py 中第 418 行的 category_index.values() 改成 list(category_index.values()) 即可。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末齐饮,一起剝皮案震驚了整個(gè)濱河市捐寥,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌祖驱,老刑警劉巖握恳,帶你破解...
    沈念sama閱讀 221,576評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異捺僻,居然都是意外死亡乡洼,警方通過查閱死者的電腦和手機(jī)崇裁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來束昵,“玉大人拔稳,你說我怎么就攤上這事∏鲁” “怎么了巴比?”我有些...
    開封第一講書人閱讀 168,017評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)礁遵。 經(jīng)常有香客問我轻绞,道長(zhǎng),這世上最難降的妖魔是什么佣耐? 我笑而不...
    開封第一講書人閱讀 59,626評(píng)論 1 296
  • 正文 為了忘掉前任政勃,我火速辦了婚禮,結(jié)果婚禮上兼砖,老公的妹妹穿的比我還像新娘稼病。我一直安慰自己,他們只是感情好掖鱼,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著援制,像睡著了一般戏挡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上晨仑,一...
    開封第一講書人閱讀 52,255評(píng)論 1 308
  • 那天褐墅,我揣著相機(jī)與錄音,去河邊找鬼洪己。 笑死妥凳,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的答捕。 我是一名探鬼主播逝钥,決...
    沈念sama閱讀 40,825評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼拱镐!你這毒婦竟也來了艘款?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,729評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤沃琅,失蹤者是張志新(化名)和其女友劉穎哗咆,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體益眉,經(jīng)...
    沈念sama閱讀 46,271評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡晌柬,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評(píng)論 3 340
  • 正文 我和宋清朗相戀三年姥份,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片年碘。...
    茶點(diǎn)故事閱讀 40,498評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡澈歉,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出盛泡,到底是詐尸還是另有隱情闷祥,我是刑警寧澤,帶...
    沈念sama閱讀 36,183評(píng)論 5 350
  • 正文 年R本政府宣布傲诵,位于F島的核電站凯砍,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏拴竹。R本人自食惡果不足惜悟衩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望栓拜。 院中可真熱鬧座泳,春花似錦、人聲如沸幕与。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)啦鸣。三九已至潮饱,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間诫给,已是汗流浹背香拉。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留中狂,地道東北人凫碌。 一個(gè)月前我還...
    沈念sama閱讀 48,906評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像胃榕,于是被迫代替她去往敵國(guó)和親盛险。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容