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

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

使用Netscope在線可視化

Netscope

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


可視化圖像特征

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

修改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ù)和形狀策幼,第一個是批次邑时,第二個是feature map數(shù)目,第三和第四是每個神經(jī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') # 不顯示坐標軸

? ? ? ? plt.show()? ?

? ? # 每個可視化的都是在一個由一個個網(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))

? ? ? ? # 對圖像使用濾波器


? ? ? ? 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))

? ? #過濾后的輸出,96 張 featuremap

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

? ? vis_square(feat,padval=1)

? ? #第二個卷積層,顯示全部的96個濾波器,每一個濾波器為一行刁愿。

? ? 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))

? ? # 第三個卷積層:全部 384 個 feature map

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

? ? vis_square(feat, padval=0.5)

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

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

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

? ? # 第四個卷積層:全部 384 個 feature map

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

? ? vis_square(feat, padval=0.5)

? ? # 第五個卷積層:全部 256 個 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)

? ? #第五個 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') # 不顯示坐標軸

? ? 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")

? ? #看標簽

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

? ? 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]]

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


圖3 原始檢測圖片


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


圖5 conv1特征可視化

deep-visualization-toolbox

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


圖6 ToolBox conv1特征可視化


圖7 ToolBox conv2特征可視化

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


Loss可視化

網(wǎng)絡(luò)訓(xùn)練過程中Loss值的可視化可以幫助分析該網(wǎng)絡(luò)模型的參數(shù)是否合適。在使用Faster R-CNN網(wǎng)絡(luò)訓(xùn)練模型時涩笤,訓(xùn)練完成后的日志文件中保存了網(wǎng)絡(luò)訓(xùn)練各個階段的loss值嚼吞,如圖8所示。只用寫簡單的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('--------------------------------------------------------------')

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


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末俏脊,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子肤晓,更是在濱河造成了極大的恐慌爷贫,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件补憾,死亡現(xiàn)場離奇詭異漫萄,居然都是意外死亡,警方通過查閱死者的電腦和手機盈匾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門腾务,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人削饵,你說我怎么就攤上這事岩瘦。” “怎么了窿撬?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵启昧,是天一觀的道長。 經(jīng)常有香客問我劈伴,道長密末,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任跛璧,我火速辦了婚禮严里,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘追城。我一直安慰自己刹碾,他們只是感情好,可當我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布漓柑。 她就那樣靜靜地躺著教硫,像睡著了一般。 火紅的嫁衣襯著肌膚如雪辆布。 梳的紋絲不亂的頭發(fā)上瞬矩,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天,我揣著相機與錄音锋玲,去河邊找鬼景用。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的伞插。 我是一名探鬼主播割粮,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼媚污!你這毒婦竟也來了舀瓢?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤耗美,失蹤者是張志新(化名)和其女友劉穎京髓,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體商架,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡堰怨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了蛇摸。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片备图。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖赶袄,靈堂內(nèi)的尸體忽然破棺而出揽涮,到底是詐尸還是另有隱情,我是刑警寧澤饿肺,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布绞吁,位于F島的核電站,受9級特大地震影響唬格,放射性物質(zhì)發(fā)生泄漏家破。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一购岗、第九天 我趴在偏房一處隱蔽的房頂上張望汰聋。 院中可真熱鬧,春花似錦喊积、人聲如沸烹困。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽髓梅。三九已至,卻和暖如春绎签,著一層夾襖步出監(jiān)牢的瞬間枯饿,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工诡必, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留奢方,地道東北人。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像蟋字,于是被迫代替她去往敵國和親稿蹲。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,925評論 2 344

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