[图像算法]-yolov5全解-一文通关

序言

yolov5的配置过程总体来说还算可以,但是网上大部分博客都没有仔细介绍具体步骤,本文将从最细节的层面记录windows10系统下的yolov5环境配置的全过程,以及yolov5使用的一些细节,以及如何制作和训练自己的数据集。

注:yolov5官网代码更新速度较快,相关依赖环境如pytorch,apex等也会采用更新的版本。博主上传了2021年2月配置成功的工程文件,如需要老版本代码可以自行下载或从官网下载。若配置遇到困难可以参考评论区的一些回答,若还未解决,欢迎评论区留言或私信,博主将尽力解决。


目录

  • [yolov5介绍]
    • [资料]
      • 一、基础部分:
      • 二、代码部分:
      • 三、优秀的实际应用:
  • [环境配置所需资源]
  • [第一步-下载源码]
  • [第二步-安装anaconda与pycharm]
  • [第三步-安装cuda10.2和cudnn-10.2-windows10-x64-v7.6.5.32]
  • [第四步-安装pytorch1.5.1以及其他库]
  • [第五步-下载权重文件]
  • [第六步-实际测试]
  • [第七步(可选操作)-安装apex]
  • [第八步-训练自制数据集]
    • [1.coco128数据集]
    • [2.coco2017数据集]
    • [3.真正的自制数据集]
    • [4.虚假的自制数据集]
      • [1.生成随机数据集的代码如下]
      • [2.创建yaml文件]
      • [3.更改train.py的设置]

yolov5介绍

先来介绍一下yolov5


在这里插入图片描述

yolov5的特点之一就是权重文件非常之小,可以搭载在配置更低的移动设备上


在这里插入图片描述

而且yolov5的速度较yolov4更快,准确度更高


在这里插入图片描述

此博客还包括一些在学习过程中的其他资料,包括:

资料

一、基础部分:

1.深入浅出Yolo系列之Yolov3&Yolov4&Yolov5核心基础知识完整讲解
2.深入浅出Yolo系列之Yolov5核心基础知识完整讲解
3.从V1到V4,让你读懂YOLO原理

二、代码部分:

4.YOLOv5代码详解(yolov5l.yaml部分)
5.YOLOv5代码详解(common.py部分)
6.YOLOv5代码详解(test.py部分)
7.YOLOv5代码详解(train.py部分)

三、优秀的实际应用:

8.实时吸烟目标检测

环境配置所需资源

使用到的工具有
1.anaconda,pycharm
2.cuda10.2+cudnn-10.2-windows10-x64-v7.6.5.32+pytorch1.5.1-gpu+。。。。。。。。
ps(对于本文使用的旧版代码pytorch只要大于等于1.5.1即可,新版代码需与官网要求相同--1.6.0+,本文将在之后说明安装步骤)

所需资源:
本博客免费提供所有win10的cuda和cudnn,百度云,提取码:78il
以及anaconda和pycharm的安装包百度云,提取码:95q7
以及权重文件百度云,提取码:9pfg
以及GitHub官方yolov5源码百度云(官网源码已更新,建议下载此源码),提取码:tyn6
以及官方提供的coco测试数据集百度云,提取码:ny9j

配置正式开始

第一步-下载源码

本文采用的是yolov5官网提供的pytorch框架下的源码(官方代码为最新修改,建议使用本文提供的的代码版本--7月31日更新,可从上文百度云链接中下载),点击红色区域即可下载源码压缩包

在这里插入图片描述

将其解压到一个不带中文字符的文件夹下(如果带有中文字符,会使OpenCV的cv2.imread()函数读取不了待检测图片或视频)

第二步-安装anaconda与pycharm

两个开发工具从官网均可下载。
anaconda是一个管理用于python开发的包含不同库的虚拟环境的平台,可以高效的管理和创建适用于多个不同项目的project interpreter。安装完成自带一个根环境,路径在conda的安装目录下。进入后可以在环境管理页面创建新环境,新环境的路径在安装目录下的envs中存储,在pycharm中设置interpreter时需要找到所需环境的存储位置,interpreter设置选择conda enviroment,填写python.exe的路径即可完成编译环境与项目的绑定。之后进行虚拟环境设置,安装新的库的时候只需打开cmd,输入activate 环境名,即可进入环境目录,之后pip安装所需库即可。

首先从官网上下载anaconda-py3.7-64版本,勾选这两项方便环境配置,如果安装时没有勾选这两项建议重新安装,并!重!启!,涉及到环境变量的改动都建议在修改后重启

在这里插入图片描述

等待anaconda安装完进入environment界面,点击create,将新环境命名为yolov5test,python版本选择python3.7,点击ok,等待自动生成初始环境,之后即可关闭anaconda。
ps(如果出现anaconda卡load application的情况,可以参考Anaconda创建新的python环境使用cmd创建新环境与操作包的安装)

在这里插入图片描述

完成anaconda安装后,从官网下载pycharm,解压安装完成后创建工程,路径选择到之前解压源码的无中文字符路径,注意!!!!,创建完成后此处有红框标记的文件即为路径正确(也可先创建再解压到工程文件中)

在这里插入图片描述

之后进入pycharm中选择工程所需的interpreter


在这里插入图片描述

第三步-安装cuda10.2和cudnn-10.2-windows10-x64-v7.6.5.32

在这里插入图片描述

因为本次使用pytorch1.5.1(如果使用最新版的官网代码,请按照requirement.txt中的环境要求安装,将pytorch升级为1.6即可),对应的cuda为10.1(其实cuda≥10.1即可,只需将cudnn与cuda版本对应上就行),ps(本人之前使用tf=1.14+cuda10.0进行深度学习开发,但是pytorch1.5.1需要安装新的cuda,故安装cuda10.2。下面给出这种情况的解决方法)
第一步:下载双击运行,选自定义


在这里插入图片描述

第二步:不要选择visual studio integration,否则无法安装


在这里插入图片描述

等待安装完成--约2分钟,期间若有GeForce experience失败则重复上述步骤即可

第三步:更改环境变量
进入环境变量编辑页面,此时已经将cuda10.2安装到默认的NVIDIA cumputing toolkit路径下,需要将系统的环境变量修改为cuda10.2,把10.0的变量去掉。同一台电脑可以拥有多个不同版本的cuda,如果需要用低版本的cuda,只需要将系统环境变量修改为低版本,将高版本的删除即可。


在这里插入图片描述

新建环境变量


在这里插入图片描述
C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.2\lib\x64
C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.2\include
C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.2\extras\CUPTI\lib64
C:\ProgramData\NVIDIA Corporation\CUDA Samples\v10.2\bin\win64
C:\ProgramData\NVIDIA Corporation\CUDA Samples\v10.2\common\lib\x64

此时应该有如下两个变量,如果以前安装的有残存的cuda10.0的路径,删除即可


在这里插入图片描述

第四步:安装cudnn
将cudnn解压后,将其中的所有内容复制到以下路径中,ps(默认路径,可以直接用)

C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.2

第五步:验证

打开cmd,输入nvcc -V验证cuda是否安装成功,出现如下界面即为成功
在这里插入图片描述

第四步-安装pytorch1.5.1以及其他库

以管理员身份进入cmd,输入activate yolov5test进入环境目录下,输入

pip install torch===1.5.1 torchvision===0.6.1 -f https://download.pytorch.org/whl/torch_stable.html -i https://pypi.douban.com/simple

使用豆瓣源安装,很多是使用清华源,但是清华源安装时容易超时而导致安装失败,有条件的可以翻墙直接安装,效果非常好。
安装完成后再pycharm中创建文件,输入验证pytorch安装是否成功

import torch
print(torch.cuda.is_available())
print(torch.__version__)

出现以下结果即为安装成功


在这里插入图片描述

其他库的安装建议根据requirement.txt文件逐个安装。。。清华源太容易崩了
下面给出requirement.txt的内容

# pip install -U -r requirements.txt
Cython
numpy>=1.18.5
opencv-python
torch>=1.5.1
matplotlib
pillow
tensorboard
PyYAML>=5.3
torchvision>=0.6
scipy
tqdm
# pycocotools>=2.0

# Nvidia Apex (optional) for mixed precision training --------------------------
# git clone https://github.com/NVIDIA/apex && cd apex && pip install -v --no-cache-dir --global-option="--cpp_ext" --global-option="--cuda_ext" . --user && cd .. && rm -rf apex

# Conda commands (in place of pip) ---------------------------------------------
# conda update -yn base -c defaults conda
# conda install -yc anaconda numpy opencv matplotlib tqdm pillow ipython
# conda install -yc conda-forge scikit-image pycocotools tensorboard
# conda install -yc spyder-ide spyder-line-profiler
# conda install -yc pytorch pytorch torchvision
# conda install -yc conda-forge protobuf numpy && pip install onnx==1.6.0  # https://github.com/onnx/onnx#linux-and-macos

可以根据下面的指令,依次输入以下命令安装相应的库:

pip install Cython
pip install numpy
pip install opencv-python
pip install matplotlib
pip install pillow
pip install tensorboard
pip install PyYAML
pip install torchvision
pip install scipy
pip install tqdm

第五步-下载权重文件

下载的权重文件放入项目文件夹中!!!!注意是项目文件夹,而不是weights文件夹!!!!!!

在这里插入图片描述

第六步-实际测试

打开项目文件夹,找到inference,这里images存储测试数据,output存储测试结果,同样,测试数据名称不能带有中文字符


在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

基本测试需要运行detect.py,pycharm中运行结果如下


在这里插入图片描述

说明:测试数据可以是图片或视频,也可以是本机摄像头。
使用方法为在detect文件最后找到

d_argument('--source', type=str, default='0', help='source')  # file/folder, 0 for webcam

默认值改成0即可
本人使用i7-8750+1050Ti跑手机录像30~50FPS,效果还可以

ps:如果出现页面文件太小,无法完成操作,这样的错误是因为虚拟内存不足(虚拟内存定义参考操作系统的虚拟内存),参考修改页面文件即可解决

第七步(可选操作)-安装apex

官网地址

介绍:
这个存储库包含nvidia维护的实用程序,以简化Pytorch中的混合精度和分布式培训。这里的一些代码将包括在上游Pytorch最终。Apex的目的是尽可能快地为用户提供最新的实用程序。

Windows support is experimental, and Linux is recommended.
pip install -v --no-cache-dir --global-option="--cpp_ext" --global-option="--cuda_ext" .may work if you were able to build Pytorch from source on your system. pip install -v --no-cache-dir .(without CUDA/C++ extensions) is more likely to work. If you installed Pytorch in a Conda environment, make sure to install Apex in that same environment.

1.从官网上下载源码,降里面的内容解压到yolov5工程文件夹下
2.打开pycharm的terminal界面,输入

pip install -v --no-cache-dir --global-option="--cpp_ext" --global-option="--cuda_ext"

但是感觉貌似不太对。。输入会报

ERROR: You must give at least one requirement to install (see "pip help install")

可以尝试在terminal中输入

python setup.py install --cpp_ext --cuda_ext

等待1-2分钟即可完成安装

第八步-训练自制数据集

1.coco128数据集

训练数据集需要下载coco数据集,解压到与工程文件并列的位置,运行train.py
注:如果使用自制训练集训练自己的网络模型且GPU不太给力,可以调小网络参数和训练规模。(因为coco128数据集只有128张图片,且标记的种类较多,所以训练出来的网络最终效果不太理想,只是用来测试是否可以训练)

2.coco2017数据集

因为数据集较大,且标注为json格式。。。。(听说要转xml再转txt才能训练yolov5?),顾再研究研究,有大佬知道的可以评论去补充一波
感谢Thanks♪(・ω・)ノ

coco2017标注为json格式

在这里插入图片描述

目标检测只需要用到instances_train2017.json和instances_val2017.json两个标注文件,一个是训练集的标注,一个是验证集的标注

在此给出已经完成转换的两种格式的压缩包xml,txt

在这里插入图片描述

在这里插入图片描述

并给出将json->xml->txt格式标注的源码

json2xml.py

# 使用时仅需修改21、22、24行路径文件
import os
import time
import json
import pandas as pd
from tqdm import tqdm
from pycocotools.coco import COCO

def trans_id(category_id):
    names = []
    namesid = []
    for i in range(0, len(cats)):
        names.append(cats[i]['name'])
        namesid.append(cats[i]['id'])
    index = namesid.index(category_id)
    return index

root = 'coco2017'  # 你下载的 COCO 数据集所在目录
dataType = 'train2017'
anno = '{}/annotations/instances_{}.json'.format(root, dataType)
xml_dir = '{}/xml/{}_xml'.format(root, dataType)

coco = COCO(anno)  # 读文件
cats = coco.loadCats(coco.getCatIds())  # 这里loadCats就是coco提供的接口,获取类别

# Create anno dir
dttm = time.strftime("%Y%m%d%H%M%S", time.localtime())
if os.path.exists(xml_dir):
    os.rename(xml_dir, xml_dir + dttm)
os.mkdir(xml_dir)

with open(anno, 'r') as load_f:
    f = json.load(load_f)

imgs = f['images']

df_cate = pd.DataFrame(f['categories'])
df_cate_sort = df_cate.sort_values(["id"], ascending=True)
categories = list(df_cate_sort['name'])
print('categories = ', categories)
df_anno = pd.DataFrame(f['annotations'])

for i in tqdm(range(len(imgs))):
    xml_content = []
    file_name = imgs[i]['file_name']
    height = imgs[i]['height']
    img_id = imgs[i]['id']
    width = imgs[i]['width']

    xml_content.append("<annotation>")
    xml_content.append("    <folder>VOC2007</folder>")
    xml_content.append("    <filename>" + file_name + "</filename>")
    xml_content.append("    <size>")
    xml_content.append("        <width>" + str(width) + "</width>")
    xml_content.append("        <height>" + str(height) + "</height>")
    xml_content.append("    </size>")
    xml_content.append("    <segmented>0</segmented>")
    # 通过img_id找到annotations
    annos = df_anno[df_anno["image_id"].isin([img_id])]

    for index, row in annos.iterrows():
        bbox = row["bbox"]
        category_id = row["category_id"]
        cate_name = categories[trans_id(category_id)]

        # add new object
        xml_content.append("    <object>")
        xml_content.append("        <name>" + cate_name + "</name>")
        xml_content.append("        <pose>Unspecified</pose>")
        xml_content.append("        <truncated>0</truncated>")
        xml_content.append("        <difficult>0</difficult>")
        xml_content.append("        <bndbox>")
        xml_content.append("            <xmin>" + str(int(bbox[0])) + "</xmin>")
        xml_content.append("            <ymin>" + str(int(bbox[1])) + "</ymin>")
        xml_content.append("            <xmax>" + str(int(bbox[0] + bbox[2])) + "</xmax>")
        xml_content.append("            <ymax>" + str(int(bbox[1] + bbox[3])) + "</ymax>")
        xml_content.append("        </bndbox>")
        xml_content.append("    </object>")
    xml_content.append("</annotation>")

    x = xml_content
    xml_content = [x[i] for i in range(0, len(x)) if x[i] != "\n"]
    ### list存入文件
    xml_path = os.path.join(xml_dir, file_name.replace('.jpg', '.xml'))
    with open(xml_path, 'w+', encoding="utf8") as f:
        f.write('\n'.join(xml_content))
    xml_content[:] = []

xml2txt.py

import os.path
import xml.etree.ElementTree as ET

class_names = ['lip']

xmlpath = 'F:\\share\\标注\\xml\\' # xml文件的位置
txtpath = 'F:\\share\\标注\\txt\\' # 导出txt的位置

files=[]

for root, dirs, files in os.walk(xmlpath):
    None

number = len(files)
i = 0
while i < number:

    name = files[i][0:-4]
    xml_name = name + ".xml"
    txt_name = name + ".txt"
    xml_file_name = xmlpath + xml_name
    txt_file_name = txtpath + txt_name

    xml_file = open(xml_file_name)
    tree = ET.parse(xml_file)
    root = tree.getroot()
    filename = root.find('filename').text

    image_name = root.find('filename').text
    w = int(root.find('size').find('width').text)
    h = int(root.find('size').find('height').text)

    f_txt = open(txt_file_name, 'w+')
    content = ""

    first = True

    for obj in root.iter('object'):

        name = obj.find('name').text
        class_num = class_names.index(name)

        xmlbox = obj.find('bndbox')

        x1 = int(xmlbox.find('xmin').text)
        x2 = int(xmlbox.find('xmax').text)
        y1 = int(xmlbox.find('ymin').text)
        y2 = int(xmlbox.find('ymax').text)

        if first:
            content += str(class_num) + " " + \
                       str((x1+x2)/2/w) + " " + str((y1+y2)/2/h) + " " + \
                       str((x2-x1)/w) + " " + str((y2-y1)/h)
            first=False
        else:
            content += "\n" + \
                       str(class_num) + " " + \
                       str((x1 + x2) / 2 / w) + " " + str((y1 + y2) / 2 / h) + " " + \
                       str((x2 - x1) / w) + " " + str((y2 - y1) / h)

    print(str(i/(number - 1) * 100) + "%\n")
    f_txt.write(content)
    f_txt.close()
    xml_file.close()
    i += 1

print("done!")

3.真正的自制数据集

使用各种目标检测的标注工具都行,我用的LabelImg

在这里插入图片描述

可以下载源码安装,也可以直接pip安装

安装方法参考:
https://blog.csdn.net/qq_34809033/article/details/80589868?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.edu_weight&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.edu_weight

使用方法参考:
https://zhuanlan.zhihu.com/p/90834296
https://blog.csdn.net/python_pycharm/article/details/85338801

4.虚假的自制数据集

尝试训练了coco128数据集,但是效果不理想。
可以采用了pygame绘图自制数据集训练的方法。

1.生成随机数据集的代码如下

import math
import random
import pygame
pygame.init()

windowSize = [640, 640]
screen = pygame.display.set_mode(windowSize)
clock = pygame.time.Clock()

color = pygame.color.Color('#57B0F6')
black = pygame.color.Color('#000000')

count = 0
done = False
fileNo = 0

round = 1

while not done and round<1000:
    screen.fill(black)

    x = 100 + random.randint(0, 400)
    y = 100 + random.randint(0, 400)
    pygame.draw.ellipse(screen, color, [x, y, 100, 100])

    pygame.display.flip()

    zero=""
    if round>=1 and round<=9:
        zero="00000000000"
    if round>=10 and round<=99:
        zero="0000000000"
    if round>=100 and round<=999:
        zero="000000000"
    pygame.image.save(screen, "images\\" + zero + str(round) + ".jpg") #这句话保存图片

    f=open("labels\\" + zero +str(round)+".txt",'w+')
    f.write("0 "+str((x+50)/640)+" "+str((y+50)/640)+" 0.15625 0.15625")
    f.close()

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            done = True

    round+=1
pygame.quit()

通过pygame在黑色背景下绘制一个蓝色圆形图案,并根据yolov5标记的导入规则--每张图片一个txt文件,每一行表示一个object。第一个数表示种类在yaml中定义的下标(只有一个就写0),第二三个数表示矩形框中心的横纵坐标(位于正中间,则都为0.5),第四五个数表示矩形的宽高(若矩宽高都为图片的一半,则都为0.5)

在这里插入图片描述

images文件夹


在这里插入图片描述
在这里插入图片描述

labels文件夹


在这里插入图片描述
在这里插入图片描述

共随机生成1000张图片,位置均随机,并生成对应的标注。

2.创建yaml文件

在yolov5的工程文件中找到data文件夹,创建circle.yaml文件,将下面的代码输进去

train: ../creat_image/images/ #改为自己的训练文件的路径
val: ../creat_image/images/ #改为自己的测试文件的路径

# number of classes
nc: 1

# class names
names: ['circle']

3.更改train.py的设置

训练属性设置说明

    # 存储模型结构的配置文件
    parser.add_argument('--cfg', type=str, default='models/yolov5s.yaml', help='model.yaml path')

    # 存储训练、测试的数据的文件 coco128.yaml->circle.yaml
    parser.add_argument('--data', type=str, default='data/circle.yaml', help='data.yaml path')

    #
    parser.add_argument('--hyp', type=str, default='', help='hyp.yaml path (optional)')

    # 指的就是训练过程中整个数据集将被迭代多少次,显卡不行你就调小点
    parser.add_argument('--epochs', type=int, default=100)

    # batch-size:一次看完多少张图片才进行权重更新,梯度下降的mini-batch,显卡不行你就调小点。
    parser.add_argument('--batch-size', type=int, default=30, help="Total batch size for all gpus.")

    # 输入图片宽高,显卡不行你就调小点。-------必须是32的倍数,输入250会自动调整成256
    parser.add_argument('--img-size', nargs='+', type=int, default=[320, 320], help='train,test sizes')

    # 进行矩形训练
    parser.add_argument('--rect', action='store_true', help='rectangular training')

    # 恢复最近保存的模型开始训练
    parser.add_argument('--resume', nargs='?', const='get_last', default=False,
                        help='resume from given path/to/last.pt, or most recent run if blank.')

    # 仅保存最终checkpoint
    parser.add_argument('--nosave', action='store_true', help='only save final checkpoint')

    # 仅测试最后的epoch
    parser.add_argument('--notest', action='store_true', help='only test final epoch')

    #
    parser.add_argument('--noautoanchor', action='store_true', help='disable autoanchor check')

    # 进化超参数
    parser.add_argument('--evolve', action='store_true', help='evolve hyperparameters')

    # gsutil bucket
    parser.add_argument('--bucket', type=str, default='', help='gsutil bucket')

    # 缓存图像以加快训练速度
    parser.add_argument('--cache-images', action='store_true', help='cache images for faster training')

    # 权重文件路径,用于迁移训练,可以以官方提供的几个训练好的模型为基础进行训练
    # default='' ----> default='yolov5s.pt'
    parser.add_argument('--weights', type=str, default='', help='initial weights path')

    # 重命名results.txt to results_name.txt
    parser.add_argument('--name', default='', help='renames results.txt to results_name.txt if supplied')

    # cuda device, i.e. 0 or 0,1,2,3 or cpu
    parser.add_argument('--device', default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')

    # 多尺度训练,img-size +/- 50%
    parser.add_argument('--multi-scale', action='store_true', help='vary img-size +/- 50%%')

    # 单类别的训练集
    parser.add_argument('--single-cls', action='store_true', help='train as single-class dataset')

打开train.py,找到后面位置的这个语句,将default改为自己的circle.yaml的路径

parser.add_argument('--data', type=str, default='data/circle.yaml', help='data.yaml path')

本人使用1050Ti+i7-8750的笔记本电脑,采用320,320图片输入,训练100轮次,训练时间大约1.5小时

完成训练后会在yolov5文件夹的run下保存有训练模型以及训练参数的图表

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

使用detect.py检测训练结果需要更改模型路径,改为run/exp.../weights/best.py(完成训练后会提示生成的权重文件在哪)
之后与前文步骤类似,将从数据集中挑几张图片输入,运行即可得到输出


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

推荐阅读更多精彩内容