Py-Faster R-CNN可視化——網(wǎng)絡(luò)模型,圖像特征,Loss圖,PR曲線

可視化網(wǎng)絡(luò)模型

使用Netscope在線可視化

Netscope

Netscope能可視化神經(jīng)網(wǎng)絡(luò)體系結(jié)構(gòu)(或技術(shù)上說(shuō),Netscope能可視化任何有向無(wú)環(huán)圖)。目前Netscope能可視化Caffe的prototxt 文件。網(wǎng)址為:ethereon.github.io/netscope/#/…Netscope的使用非常簡(jiǎn)單,只需要將prototxt的文件復(fù)制到Netscope的編輯框,再按快捷鍵Shift+Enter即可得到網(wǎng)絡(luò)模型的可視化結(jié)構(gòu)。Netscope的優(yōu)點(diǎn)是顯示的網(wǎng)絡(luò)模型簡(jiǎn)潔,而且將鼠標(biāo)放在右側(cè)可視化的網(wǎng)絡(luò)模型的任意模塊上,會(huì)顯示該模塊的具體參數(shù)。圖1以Faster R-CNN中ZF模型的train.prototxt文件為例


可視化圖像特征

關(guān)于圖像的可視化,我也使用過(guò)兩種兩種方式:

修改demo.py代碼輸出中間層結(jié)果

使用可視化工具deep-visualization-toolbox

修改demo.py

該部分是參考薛開宇的《caffe學(xué)習(xí)筆記》中的逐層特征可視化部分,還是以ZFNet網(wǎng)絡(luò)訓(xùn)練Pascal VOC為例,修改demo.py文件后,代碼如下:

#!/usr/bin/env python

#-*-coding:utf-8-*-

import matplotlib

matplotlib.use('Agg')

import _init_paths

from fast_rcnn.config import cfg

from fast_rcnn.test import im_detect

from fast_rcnn.nms_wrapper import nms

from utils.timer import Timer

import matplotlib.pyplot as plt

import numpy as np

import scipy.io as sio

import caffe, os, sys, cv2

import argparse

CLASSES = ('__background__',

? ? ? ? ? 'aeroplane', 'bicycle', 'bird', 'boat',

? ? ? ? ? 'bottle', 'bus', 'car', 'cat', 'chair',

? ? ? ? ? 'cow', 'diningtable', 'dog', 'horse',

? ? ? ? ? 'motorbike', 'person', 'pottedplant',

? ? ? ? ? 'sheep', 'sofa', 'train', 'tvmonitor')

NETS = {'vgg16': ('VGG16',

? ? ? ? ? ? ? ? ? 'VGG16_faster_rcnn_final.caffemodel'),

? ? ? ? 'zf': ('ZF',

? ? ? ? ? ? ? ? ? 'zf_faster_rcnn_iter_2000.caffemodel')}

def vis_detections(im, class_name, dets, thresh=0.5):

? ? """Draw detected bounding boxes."""

? ? inds = np.where(dets[:, -1] >= thresh)[0]

? ? if len(inds) == 0:

? ? ? ? return

? ? im = im[:, :, (2, 1, 0)]

? ? fig, ax = plt.subplots(figsize=(12, 12))

? ? ax.imshow(im, aspect='equal')

? ? for i in inds:

? ? ? ? bbox = dets[i, :4]

? ? ? ? score = dets[i, -1]

? ? ? ? ax.add_patch(

? ? ? ? ? ? plt.Rectangle((bbox[0], bbox[1]),

? ? ? ? ? ? ? ? ? ? ? ? ? bbox[2] - bbox[0],

? ? ? ? ? ? ? ? ? ? ? ? ? bbox[3] - bbox[1], fill=False,

? ? ? ? ? ? ? ? ? ? ? ? ? edgecolor='red', linewidth=3.5)

? ? ? ? ? ? )

? ? ? ? ax.text(bbox[0], bbox[1] - 2,

? ? ? ? ? ? ? ? '{:s} {:.3f}'.format(class_name, score),

? ? ? ? ? ? ? ? bbox=dict(facecolor='blue', alpha=0.5),

? ? ? ? ? ? ? ? fontsize=14, color='white')

? ? ax.set_title(('{} detections with '

? ? ? ? ? ? ? ? ? 'p({} | box) >= {:.1f}').format(class_name, class_name,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? thresh),

? ? ? ? ? ? ? ? ? fontsize=14)

? ? plt.axis('off')

? ? plt.tight_layout()

? ? plt.draw()

def demo(net, image_name):

? ? """Detect object classes in an image using pre-computed object proposals."""

? ? # Load the demo image

? ? im_file = os.path.join(cfg.DATA_DIR, 'demo', image_name)

? ? im = cv2.imread(im_file)

? ? # Detect all object classes and regress object bounds

? ? timer = Timer()

? ? timer.tic()

? ? scores, boxes = im_detect(net, im)

? ? timer.toc()

? ? print ('Detection took {:.3f}s for '

? ? ? ? ? '{:d} object proposals').format(timer.total_time, boxes.shape[0])

? ? # Visualize detections for each class

? ? CONF_THRESH = 0.8

? ? NMS_THRESH = 0.3

? ? for cls_ind, cls in enumerate(CLASSES[1:]):

? ? ? ? cls_ind += 1 # because we skipped background

? ? ? ? cls_boxes = boxes[:, 4*cls_ind:4*(cls_ind + 1)]

? ? ? ? cls_scores = scores[:, cls_ind]

? ? ? ? dets = np.hstack((cls_boxes,

? ? ? ? ? ? ? ? ? ? ? ? ? cls_scores[:, np.newaxis])).astype(np.float32)

? ? ? ? keep = nms(dets, NMS_THRESH)

? ? ? ? dets = dets[keep, :]

? ? ? ? vis_detections(im, cls, dets, thresh=CONF_THRESH)

def parse_args():

? ? """Parse input arguments."""

? ? parser = argparse.ArgumentParser(description='Faster R-CNN demo')

? ? parser.add_argument('--gpu', dest='gpu_id', help='GPU device id to use [0]',

? ? ? ? ? ? ? ? ? ? ? ? default=0, type=int)

? ? parser.add_argument('--cpu', dest='cpu_mode',

? ? ? ? ? ? ? ? ? ? ? ? help='Use CPU mode (overrides --gpu)',

? ? ? ? ? ? ? ? ? ? ? ? action='store_true')

? ? parser.add_argument('--net', dest='demo_net', help='Network to use [zf]',

? ? ? ? ? ? ? ? ? ? ? ? choices=NETS.keys(), default='zf')

? ? args = parser.parse_args()

? ? return args

if __name__ == '__main__':

? ? cfg.TEST.HAS_RPN = True? # Use RPN for proposals

? ? args = parse_args()

? ? prototxt = os.path.join(cfg.MODELS_DIR, NETS[args.demo_net][0],

? ? ? ? ? ? ? ? ? ? ? ? ? ? 'faster_rcnn_alt_opt', 'faster_rcnn_test.pt')

? ? caffemodel = os.path.join(cfg.DATA_DIR, 'faster_rcnn_models',

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? NETS[args.demo_net][1])

? ? if not os.path.isfile(caffemodel):

? ? ? ? raise IOError(('{:s} not found.\nDid you run ./data/script/'

? ? ? ? ? ? ? ? ? ? ? 'fetch_faster_rcnn_models.sh?').format(caffemodel))

? ? if args.cpu_mode:

? ? ? ? caffe.set_mode_cpu()

? ? else:

? ? ? ? caffe.set_mode_gpu()

? ? ? ? caffe.set_device(args.gpu_id)

? ? ? ? cfg.GPU_ID = args.gpu_id

? ? net = caffe.Net(prototxt, caffemodel, caffe.TEST)

#指定caffe路徑,以下是我的caffe路徑

? ? caffe_root='/home/ouyang/GitRepository/py-faster-rcnn/caffe-fast-rcnn/'

? ? # import sys

? ? sys.path.insert(0, caffe_root+'python')

? ? # import caffe

? ? # #顯示的圖表大小為 10,圖形的插值是以最近為原則,圖像顏色是灰色

? ? plt.rcParams['figure.figsize'] = (10, 10)

? ? plt.rcParams['image.interpolation'] = 'nearest'

? ? plt.rcParams['image.cmap'] = 'gray'

? ? image_file = caffe_root+'examples/images/vehicle_0000015.jpg'?

? ? # 載入模型

? ? npload = caffe_root+ 'python/caffe/imagenet/ilsvrc_2012_mean.npy'?


? ? transformer = caffe.io.Transformer({'data': net.blobs['data'].data.shape})

? ? transformer.set_transpose('data', (2,0,1))

? ? transformer.set_mean('data', np.load(npload).mean(1).mean(1))

? ? # 參考模型的灰度為0~255,而不是0~1

? ? transformer.set_raw_scale('data', 255)

? ? # 由于參考模型色彩是BGR,需要將其轉(zhuǎn)換為RGB

? ? transformer.set_channel_swap('data', (2,1,0))

? ? im=caffe.io.load_image(image_file)

? ? net.blobs['data'].reshape(1,3,224,224)

? ? net.blobs['data'].data[...] = transformer.preprocess('data',im)

? ? # 顯示出各層的參數(shù)和形狀,第一個(gè)是批次,第二個(gè)是feature map數(shù)目,第三和第四是每個(gè)神經(jīng)元中圖片的長(zhǎng)和寬

? ? print [(k,v.data.shape) for k,v in net.blobs.items()]

? ? #輸出網(wǎng)絡(luò)參數(shù)

? ? print [(k,v[0].data.shape) for k,v in net.params.items()]


? ? def show_image(im):

? ? ? ? if im.ndim==3:

? ? ? ? ? ? m=im[:,:,::-1]

? ? ? ? plt.imshow(im)

? ? ? ? #顯示圖片的方法

? ? ? ? plt.axis('off') # 不顯示坐標(biāo)軸

? ? ? ? plt.show()? ?

? ? # 每個(gè)可視化的都是在一個(gè)由一個(gè)個(gè)網(wǎng)格組成

? ? def vis_square(data,padsize=1,padval=0):

? ? ? ? data-=data.min()

? ? ? ? data/=data.max()


? ? ? ? # force the number of filters to be square

? ? ? ? n=int(np.ceil(np.sqrt(data.shape[0])))

? ? ? ? padding=((0,n**2-data.shape[0]),(0,padsize),(0,padsize))+((0,0),)*(data.ndim-3)

? ? ? ? data=np.pad(data,padding,mode='constant',constant_values=(padval,padval))

? ? ? ? # 對(duì)圖像使用濾波器


? ? ? ? data=data.reshape((n,n)+data.shape[1:]).transpose((0,2,1,3)+tuple(range( 4,data.ndim+1)))

? ? ? ? data=data.reshape((n*data.shape[1],n*data.shape[3])+data.shape[4:])?


? ? ? ? #show_image(data)

? ? ? ? plt.imshow(data)

? ? ? ? plt.show()

? ? ? ? # 設(shè)置圖片的保存路徑,此處是我的路徑

? ? ? ? plt.savefig("./tools/Vehicle_2000/fc6.jpg")


? ? out = net.forward()

? ? image=net.blobs['data'].data[4].copy()

? ? image-=image.min()

? ? image/=image.max()

? ? # 顯示原始圖像

? ? show_image(image.transpose(1,2,0))

? ? #網(wǎng)絡(luò)提取conv1的卷積核

? ? filters = net.params['conv1'][0].data

? ? vis_square(filters.transpose(0, 2, 3, 1))

? ? #過(guò)濾后的輸出,96 張 featuremap

? ? feat =net.blobs['conv1'].data[0,:96]

? ? vis_square(feat,padval=1)

? ? #第二個(gè)卷積層,顯示全部的96個(gè)濾波器,每一個(gè)濾波器為一行。

? ? filters = net.params['conv2'][0].data

? ? vis_square(filters[:96].reshape(96**2, 5, 5))

? ? # #第二層輸出 256 張 featuremap

? ? feat = net.blobs['conv2'].data[0]

? ? vis_square(feat, padval=1)

? ? filters = net.params['conv3'][0].data

? ? vis_square(filters[:256].reshape(256**2, 3, 3))

? ? # 第三個(gè)卷積層:全部 384 個(gè) feature map

? ? feat = net.blobs['conv3'].data[0]

? ? vis_square(feat, padval=0.5)

? ? #第四個(gè)卷積層,我們只顯示前面 48 個(gè)濾波器,每一個(gè)濾波器為一行。

? ? filters = net.params['conv4'][0].data

? ? vis_square(filters[:384].reshape(384**2, 3, 3))

? ? # 第四個(gè)卷積層:全部 384 個(gè) feature map

? ? feat = net.blobs['conv4'].data[0]

? ? vis_square(feat, padval=0.5)

? ? # 第五個(gè)卷積層:全部 256 個(gè) feature map

? ? filters = net.params['conv5'][0].data

? ? vis_square(filters[:384].reshape(384**2, 3, 3))

? ? feat = net.blobs['conv5'].data[0]

? ? vis_square(feat, padval=0.5)

? ? #第五個(gè) pooling 層

? ? feat = net.blobs['fc6'].data[0]

? ? vis_square(feat, padval=1)

? ? 第六層輸出后的直方分布

? ? feat=net.blobs['fc6'].data[0]

? ? plt.subplot(2,1,1)

? ? plt.plot(feat.flat)

? ? plt.subplot(2,1,2)

? ? _=plt.hist(feat.flat[feat.flat>0],bins=100)

? ? # #顯示圖片的方法

? ? #plt.axis('off') # 不顯示坐標(biāo)軸

? ? plt.show()?

? ? plt.savefig("fc6_zhifangtu.jpg")

? ? # 第七層輸出后的直方分布

? ? feat=net.blobs['fc7'].data[0]

? ? plt.subplot(2,1,1)

? ? plt.plot(feat.flat)

? ? plt.subplot(2,1,2)

? ? _=plt.hist(feat.flat[feat.flat>0],bins=100)

? ? plt.show()

? ? plt.savefig("fc7_zhifangtu.jpg")

? ? #看標(biāo)簽

? ? #執(zhí)行測(cè)試?

? ? image_labels_filename=caffe_root+'data/ilsvrc12/synset_words.txt'

? ? #try:

? ? labels=np.loadtxt(image_labels_filename,str,delimiter='\t')

? ? top_k=net.blobs['prob'].data[0].flatten().argsort()[-1:-6:-1]

? ? #print labels[top_k]

? ? for i in np.arange(top_k.size):

? ? ? ? print top_k[i], labels[top_k[i]]

下面貼幾張檢測(cè)結(jié)果


圖3 原始檢測(cè)圖片


圖4 conv1參數(shù)可視化


圖5 conv1特征可視化

deep-visualization-toolbox

deep-visualization-toolbox是Jason Yosinsk出版在Computer Science上的一篇論文的源代碼,改論文主要講述的是卷積神經(jīng)網(wǎng)絡(luò)的可視化,感興趣的朋友可以看看這篇論文(論文地址)。B站上有個(gè)講怎么使用該工具的視頻,這里附上鏈接www.bilibili.com/video/av740…。 該工具的源碼在github:github.com/yosinski/de…。該github下有完整的安裝配置步驟,還是以圖2中的馬為例,貼幾張檢測(cè)結(jié)果圖。


圖6 ToolBox conv1特征可視化


圖7 ToolBox conv2特征可視化

從檢測(cè)效果上看,還是挺簡(jiǎn)潔的。圖片左側(cè)的一列圖片左上角是輸入圖片,中間部分是圖片經(jīng)過(guò)網(wǎng)絡(luò)前向傳播得到的特征圖可視化,左下角是其特征可視化。


Loss可視化

網(wǎng)絡(luò)訓(xùn)練過(guò)程中Loss值的可視化可以幫助分析該網(wǎng)絡(luò)模型的參數(shù)是否合適。在使用Faster R-CNN網(wǎng)絡(luò)訓(xùn)練模型時(shí),訓(xùn)練完成后的日志文件中保存了網(wǎng)絡(luò)訓(xùn)練各個(gè)階段的loss值,如圖8所示。只用寫簡(jiǎn)單的python程序,讀取日志文件中的迭代次數(shù),以及需要的損失值,再畫圖即可完成Loss的可視化。

圖8 模型的訓(xùn)練日志

在下面貼出Loss可視化的代碼:

#!/usr/bin/env python?

import os?

import sys?

import numpy as np?

import matplotlib.pyplot as plt?

import math?

import re?

import pylab?

from pylab import figure, show, legend?

from mpl_toolkits.axes_grid1 import host_subplot?


# 日志文件名

fp = open('faster_rcnn_end2end_ZF_.txt.2018-04-13_19-46-23', 'r',encoding='UTF-8')


train_iterations = []?

train_loss = []?

test_iterations = []?

#test_accuracy = []?


for ln in fp:?

? # get train_iterations and train_loss?

? if '] Iteration ' in ln and 'loss = ' in ln:?

? ? arr = re.findall(r'ion \b\d+\b,',ln)?

? ? train_iterations.append(int(arr[0].strip(',')[4:]))?

? ? train_loss.append(float(ln.strip().split(' = ')[-1]))?


fp.close()?


host = host_subplot(111)?

plt.subplots_adjust(right=0.8) # ajust the right boundary of the plot window?

#par1 = host.twinx()?

# set labels?

host.set_xlabel("iterations")?

host.set_ylabel("RPN loss")?

#par1.set_ylabel("validation accuracy")?


# plot curves?

p1, = host.plot(train_iterations, train_loss, label="train RPN loss")?

.?

host.legend(loc=1)?


# set label color?

host.axis["left"].label.set_color(p1.get_color())?

host.set_xlim([-1000, 60000])?

host.set_ylim([0., 3.5])?


plt.draw()?

plt.show()?

可視化效果如下圖所示


圖9 Loss可視化


畫PR圖

參考:https://github.com/rbgirshick/py-faster-rcnn/issues/670

在pascal_voc.py里添加幾行代碼即可:

1,文件頭部:

importmatplotlib.pyplot as plt

importpylab as pl

from sklearn.metricsimportprecision_recall_curve

from itertoolsimportcycle

2,_do_python_eval函數(shù):

def _do_python_eval(self, output_dir='output'):

? ? annopath = os.path.join(

self._devkit_path,

'VOC'+self._year,

'Annotations',

'{:s}.xml')

? ? imagesetfile = os.path.join(

self._devkit_path,

'VOC'+self._year,

'ImageSets',

'Main',

self._image_set +'.txt')

cachedir = os.path.join(self._devkit_path,'annotations_cache')

? ? aps = []

# The PASCAL VOC metric changed in 2010

use_07_metric =Trueifint(self._year) <2010elseFalse

print('VOC07 metric? '+ ('Yes'ifuse_07_metricelse'No'))

ifnot os.path.isdir(output_dir):

? ? ? os.mkdir(output_dir)

fori, cls in enumerate(self._classes):

ifcls =='__background__':

continue

filename =self._get_voc_results_file_template().format(cls)

? ? ? rec, prec, ap = voc_eval(

filename, annopath, imagesetfile, cls, cachedir, ovthresh=0.5,

? ? ? ? use_07_metric=use_07_metric)

? ? ? aps += [ap]

pl.plot(rec, prec, lw=2,

label='Precision-recall curve of class {} (area = {:.4f})'

''.format(cls, ap))

print(('AP for {} = {:.4f}'.format(cls, ap)))

with open(os.path.join(output_dir, cls +'_pr.pkl'),'wb')asf:

pickle.dump({'rec': rec,'prec': prec,'ap': ap}, f)

pl.xlabel('Recall')

pl.ylabel('Precision')

plt.grid(True)

pl.ylim([0.0,1.05])

pl.xlim([0.0,1.0])

pl.title('Precision-Recall')

pl.legend(loc="upper right")

? ? plt.show()

print(('Mean AP = {:.4f}'.format(np.mean(aps))))

print('~~~~~~~~')

print('Results:')

forap in aps:

print(('{:.3f}'.format(ap)))

print(('{:.3f}'.format(np.mean(aps))))

print('~~~~~~~~')

print('')

print('--------------------------------------------------------------')

print('Results computed with the **unofficial** Python eval code.')

print('Results should be very close to the official MATLAB eval code.')

print('Recompute with `./tools/reval.py --matlab ...` for your paper.')

print('-- Thanks, The Management')

print('--------------------------------------------------------------')

然后運(yùn)行test_net.py,就可以得到如下圖的PR曲線。如果想比較多條曲線,可以先把rec, prec數(shù)據(jù)存起來(lái)再畫圖。


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,533評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,055評(píng)論 3 414
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,365評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,561評(píng)論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,346評(píng)論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,889評(píng)論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,978評(píng)論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,118評(píng)論 0 286
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,637評(píng)論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,558評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,739評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,246評(píng)論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 43,980評(píng)論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,619評(píng)論 1 280
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,347評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,702評(píng)論 2 370

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