基于Aidlux平臺的工業(yè)視覺缺陷檢測

工業(yè)視覺缺陷檢測是一種利用計算機(jī)視覺技術(shù)跛蛋,對工業(yè)制品進(jìn)行自動化檢測,以識別和分類可能存在的缺陷的方法痊硕。它是現(xiàn)代工業(yè)生產(chǎn)中的重要環(huán)節(jié)赊级,可以大大提高生產(chǎn)效率,降低產(chǎn)品缺陷率岔绸,提高產(chǎn)品質(zhì)量理逊。

成像質(zhì)量是根本,好的算法可以錦上添花

該項目為醫(yī)療注射器缺陷檢測盒揉,要求檢觀出汗射器是否有質(zhì)量缺路(缺件或者多件)晋被,檢測對象分別為,膠暴刚盈、推桿承部針尾品羡洛、針嘴、媒口扁掸、小較暴翘县,產(chǎn)品如下圖所示擺放最域,針對歪嘴情況單獨來增加一個類作為檢測項。

通過對樣本進(jìn)行分析锈麸,注射器擺放的位置略有差異镀脂,采用目標(biāo)檢測來檢測各個部件是否存在比較合適,最后綜合判斷每個注射器的最終結(jié)果忘伞,如果檢測某個注射器出現(xiàn)歪嘴或者缺件的直接輸出對應(yīng)的NG信號薄翅,方便剔除不合格產(chǎn)品。

采集圖片:使用相機(jī)采圖工具或者專用軟件對缺陷產(chǎn)品進(jìn)行采圖氓奈,本項目采集圖片1000張左右翘魄,為了模擬小樣本訓(xùn)練的效果,實際只使用200張左右舀奶。

標(biāo)注:標(biāo)注工程師按照要求將目標(biāo)檢測項分為7類暑竟,分別是膠塞、推桿尾部育勺、針尾部.

針嘴但荤、歪嘴、螺口涧至、小膠塞腹躁。

訓(xùn)練:使用改進(jìn)的yolov8進(jìn)行訓(xùn)練得到模型。

模型上線:模型轉(zhuǎn)換南蓬、量化等纺非,使模型滿足部署平臺的上線要求,這里使用Aidlux進(jìn)行部署赘方。

相機(jī):工業(yè)高清黑白相機(jī)600w

光源:紅外平板光源烧颖,背面補(bǔ)光

鏡頭:工業(yè)高清FA鏡頭35mm

算法:yolov8

部署:工控機(jī)顯卡RTX206012G

界面及通信:WPF或者C#或者OT (這里不做討論)

選擇yolov8模型的理由:集大成者之作,包括分類蒜焊、實例分割倒信、目標(biāo)檢測、關(guān)鍵點檢測泳梆、目標(biāo)跟蹤等鳖悠,更多的功能更快的推理速度、更高的精度优妙、更加易于訓(xùn)練和調(diào)整支持更多的平臺乘综、全新的SOTA模型

該項目使用volov8的目標(biāo)檢測,對該模型進(jìn)行兩點改進(jìn)在head中加入slimNeck

在優(yōu)化中加入Siou

改進(jìn)1:yolov8模型改進(jìn): slicNeck

將neck中的4個CSPLayer2Conv全部更換為VoVGSCSPC套硼。

相比與C2f卡辰,VoVGSCSPC在保持性能的同時具更少的有參數(shù)量,輕量化效果明顯。

改進(jìn)2:

yolov8模型改進(jìn): slicNeckslimNeck之GSConv

slimNeck的作者提出了一種新方法GSConv來減輕模型復(fù)雜度九妈,保持準(zhǔn)確性反砌。GSConv可以更好地平衡模型的準(zhǔn)確性和速度。并且萌朱,提供了一種設(shè)計范式Slim-Neck宴树,以實現(xiàn)檢測器更高的計算成本效益。實驗過程中晶疼,與原始網(wǎng)絡(luò)相比改進(jìn)方法獲得了最優(yōu)秀的檢測結(jié)果酒贬。

改進(jìn)3:

yolov8模型改進(jìn): slicNeckslimNeck之VoVGSCSPC

P1--P2--P3,依次增加殘差連接層的個數(shù)翠霍,使得到的特征層的維度更多锭吨,獲得抽象的語義特征信息越豐富。

改進(jìn)4:

yolov8模型改進(jìn): SIoU

源代碼修改:

進(jìn)入到ultralyticsyololutils metrics,py中寒匙,76行開始修改bbox_iou方法零如,增加和修改SIoU,EloU,Focal,alpha,gamma相關(guān)的代碼.

進(jìn)入到ultralyticsyoloutils loss.py中,43行開始修改bbox_iou, 并注釋掉以前的loss_iou蒋情,增加帶Fcal的loss_iouo

進(jìn)入到ultralyticslyoloutilsltalpy中埠况,157行修改bbox_iouo

注射器數(shù)據(jù)集:139張訓(xùn)練數(shù)據(jù)集,46張驗證集

訓(xùn)練方式:基于yolov8預(yù)訓(xùn)練權(quán)重進(jìn)行訓(xùn)練

新建或者拷貝對應(yīng)的yaml文件棵癣,設(shè)置nc=7,如果是自己的數(shù)據(jù)集夺衍,按照實際類別進(jìn)行設(shè)置狈谊。

訓(xùn)練yolov8n模型

使用main.py文件,訓(xùn)練模型或者轉(zhuǎn)換為onnx模型加載自己的yaml文件沟沙,再加載預(yù)訓(xùn)練模型河劝,將預(yù)訓(xùn)練權(quán)重移植到新模型中,這樣就可以開始訓(xùn)練yolov8模型了矛紫。

if __name__ == '__main__':

? ? mode="predict"

? ? if mode=="train":

? ? ? ? model=YOLO("E:\\Aidlux8\\2\\yolov8-aidlux\\yolov8-main\\yolov8n.yaml")

? ? ? ? model=YOLO("E:\\Aidlux8\\2\\yolov8-aidlux\\yolov8-main\\yolov8n.pt")

? ? ? ? # model.train(**{'cfg':'D:\\AI\\YOLO\\yolov8-main\\ultralytics\\yolo\\cfg\\keypoints.yaml'})

? ? ? ? model.train(data='ultralytics\\datasets\\keypoint.yaml',epochs=100,device=0,batch=4,workers=6)

? ? ? ? # path = model.export(format="onnx")


? ? if mode=="onnx" :

? ? ? ? #D:\\IROnnx\\best.pt

? ? ? ? model = YOLO('E:\\Aidlux8\\2\\yolov8-aidlux\\yolov8-main\\runs\\detect\\train8\\weights\\best.pt')

? ? ? ? # model = YOLO('D:\\AI\\Unet\\unet-pytorch\\logs\\best_epoch_weights.pth')

? ? ? ? model.export(format="onnx",opset=11,simplify=True)

? ? ? ? # path = model.export(format="onnx",opset=13,half=True,simplify=True)

? ? ? ? # path = model.export(format="onnx",opset=13,half=True,simplify=True)

訓(xùn)練過程:

將預(yù)訓(xùn)練權(quán)重的加載到了我們自己的模型中赎瞎,經(jīng)過3個batchsize,模型就開始收斂颊咬。

Aidlux平臺介紹

Aidlux安裝和環(huán)境配置

特點o安卓手機(jī)app市場下載并安裝AidLux應(yīng)用务甥。安裝包大小為1.2g打開AidLux,配置各種權(quán)限喳篇,手機(jī)-設(shè)置-關(guān)于手機(jī)-多次點擊系統(tǒng)版本號敞临,打開開發(fā)者模式重啟AidLux,選擇登陸或者免注冊登陸麸澜,應(yīng)用系統(tǒng)第一次打開需要加載AidLux挺尿,等待進(jìn)度條完成手機(jī)端進(jìn)入AidLux界面后,查看手機(jī)端桌面Cloud ip藍(lán)色云朵圖標(biāo),獲得ip和端口號PC端瀏覽器中輸入ip地址和端口號编矾,登錄界面為root用戶熟史,輸入aidlux(默認(rèn)密碼)登陸進(jìn)入到AidLux桌面打開終端更新和安裝所需工具包,默認(rèn)已安裝aidlite_gpu,也可以更新窄俏。

安裝vscode:

打開應(yīng)用中心蹂匹,選擇Linux-Aid源-vscode進(jìn)行安裝

進(jìn)入已安裝-點擊vscode-點擊添加到桌面-桌面上顯示vscode

可以使用直接在設(shè)備里直接調(diào)試運行vscode,也可以使用ssh連接vscode進(jìn)行遠(yuǎn)程調(diào)試裆操。


基于Aidlux的yolov8模型轉(zhuǎn)換

打開網(wǎng)站: http://aimo.aidlux.com/

輸入試用賬號和密碼:

賬號:AIMOTC001怒详,密碼:AIMOTCO01

選擇對應(yīng)的原模型(.onnx)->選擇目標(biāo)平臺(tensorflowlite)->參數(shù)設(shè)置默認(rèn)->轉(zhuǎn)換結(jié)果->下載模型


基于tflite的yolov8模型部署

images文件夾:推理圖像樣本

onnx原模型:yolov8_slimneck_SIOU.onnx

tflite: tflite模型和推理的py文件

手機(jī)設(shè)備打開AidLux

PC網(wǎng)頁登錄AidLux,默認(rèn)用戶為root踪区,密碼為aidlux將images昆烁、tflite模型文件以及tflite.py文件放入到home目錄中

打開vscode,打開tflite.py文件缎岗,右鍵在終端運行静尼,可在result中查找推理的結(jié)果

最后的推理源碼如下:

import aidlite_gpu

import cv2

from cvs import *

import numpy as np

import os

import time


# import ?matplotlib.pyplot as plt


model_path = "/home/yolov8_slimneck_SIOU_save_path_tflite/yolov8_slimneck_SIOU_fp32.tflite"

image_path = "/home/test"

NUMS_CLASS = 7


confThresh = 0.3

NmsThresh = 0.45


#輸入格式 (8400,11)

def postProcess(pres, confThresh, NmsThresh):

? ? boxes_out = []

? ? scores_out = []

? ? class_out = []

? ? for pred in pres:


? ? ? ? pred_class = pred[4:]

? ? ? ? box_ = pred[0:4]

? ? ? ? # pred_class=(pred_class-min(pred_class))/(max(pred_class)-min(pred_class))

? ? ? ? class_index = np.argmax(pred_class)

? ? ? ? if pred_class[class_index] > 0.3:

? ? ? ? ? ? # box=np.array([round(pred[2]-0.5*pred[0]),round(pred[3]-0.5*pred[1]),round(pred[0]),round(pred[1])])

? ? ? ? ? ? box_ = pred[0:4] ?# w,h,xc,yc

? ? ? ? ? ? box = np.array([round((pred[2] / 2 - pred[0])), round((pred[3] / 2 - pred[1])), round(pred[0] * 2),

? ? ? ? ? ? ? ? ? ? ? ? ? ? round(pred[1] * 2)])

? ? ? ? ? ? boxes_out.append(box)

? ? ? ? ? ? score = pred_class[class_index]

? ? ? ? ? ? scores_out.append(score)

? ? ? ? ? ? class_out.append(class_index)


? ? result_boxes = cv2.dnn.NMSBoxes(boxes_out, np.array(scores_out), confThresh, NmsThresh)

? ? # detections=[]

? ? boxes = []

? ? scores = []

? ? classes = []

? ? for result_box in result_boxes:

? ? ? ? index = int(result_box)

? ? ? ? box = boxes_out[index]

? ? ? ? score = scores_out[index]

? ? ? ? class_type = class_out[index]

? ? ? ? boxes.append(box)

? ? ? ? scores.append(score)

? ? ? ? classes.append(class_type)

? ? return boxes, scores, classes


def draw(img, xscale, yscale, boxes, scores, classes):

? ? width = img.shape[1]

? ? w1 = 1620

? ? w2 = 2350

? ? w3 = width

? ? S1 = []

? ? S2 = []

? ? S3 = []

? ? S1_res = [False for i in range(NUMS_CLASS)]

? ? S2_res = [False for i in range(NUMS_CLASS)]

? ? S3_res = [False for i in range(NUMS_CLASS)]

? ? S_res = [S1_res, S2_res, S3_res]


? ? img_ = img.copy()

#遍歷所有box,按照分割區(qū)域?qū)ox歸類

? ? for i in range(len(boxes)):

? ? ? ? # boxes=[x1,y1,w,h]

? ? ? ? box = boxes[i]

? ? ? ? score = scores[i]

? ? ? ? class_ = int(classes[i])

? ? ? ? # class_text=label[class_]

? ? ? ? # detect=[round(box[0]*xscale),round(box[1]*yscale),round((box[0]+box[2])*xscale),round((box[1]+box[3])*yscale)]

? ? ? ? detect = [round(box[0] * xscale), round(box[1] * yscale), round(box[0] * xscale + (box[2]) * xscale),

? ? ? ? ? ? ? ? ? round(box[1] * yscale + (box[3]) * yscale)]

? ? ? ? text = "{}:{:.2f}".format(label[class_], float(score))

? ? ? ? img_ = cv2.rectangle(img_, (detect[0], detect[1]), (detect[2], detect[3]), (0, 255, 0), 2)

? ? ? ? cv2.putText(img_, text, (detect[0], detect[1] + 10), cv2.FONT_HERSHEY_COMPLEX, 2, (0, 0, 255), 1)


#分割為三塊

? ? ? ? if (detect[0] <= w1):

? ? ? ? ? ? p1 = []

? ? ? ? ? ? p1.append(detect)

? ? ? ? ? ? p1.append(class_)

? ? ? ? ? ? p1.append(score)

? ? ? ? ? ? S1.append(p1)

? ? ? ? elif (w1 < detect[0] <= w2):

? ? ? ? ? ? p2 = []

? ? ? ? ? ? p2.append(detect)

? ? ? ? ? ? p2.append(class_)

? ? ? ? ? ? p2.append(score)

? ? ? ? ? ? S2.append(p2)

? ? ? ? elif (w2 < detect[0] <= w3):

? ? ? ? ? ? p3 = []

? ? ? ? ? ? p3.append(detect)

? ? ? ? ? ? p3.append(class_)

? ? ? ? ? ? p3.append(score)

? ? ? ? ? ? S3.append(p3)


#判斷每個分割圖像中的結(jié)果

? ? index = 0

? ? for S in [S1, S2, S3]:

? ? ? ? for i in range(len(S)):

? ? ? ? ? ? p1 = S[i]

? ? ? ? ? ? box_temp = p1[0]

? ? ? ? ? ? class_temp = p1[1]

? ? ? ? ? ? score_temp = p1[2]

? ? ? ? ? ? S_res[index][class_temp] = True

? ? ? ? index += 1


#最終分割輸出結(jié)果true or false

? ? S_out = [False, False, False]

? ? index_out = 0

? ? for s_r in S_res:

? ? ? ? c0 = s_r[0]

? ? ? ? c1 = s_r[1]

? ? ? ? c2 = s_r[2]

? ? ? ? c3 = s_r[3]

? ? ? ? c4 = s_r[4]

? ? ? ? c5 = s_r[5]

? ? ? ? c6 = s_r[6]


? ? ? ? if (c0 & c1 & c2 & c3 & (~c4) & (~c5) & (~c6)):

? ? ? ? ? ? S_out[index_out] = True

? ? ? ? elif (c0 & c1 & c2 & (~c3) & (~c4) & c5 & (~c6)):

? ? ? ? ? ? S_out[index_out] = True

? ? ? ? index_out += 1


#打印分割結(jié)果

? ? cv2.putText(img_, "OK" if S_out[0] == True else "NG", (w1 - 200, 100), cv2.FONT_HERSHEY_COMPLEX, 2, (255, 0, 0), 1)

? ? cv2.putText(img_, "OK" if S_out[1] == True else "NG", (w2 - 200, 100), cv2.FONT_HERSHEY_COMPLEX, 2, (255, 0, 0), 1)

? ? cv2.putText(img_, "OK" if S_out[2] == True else "NG", (w3 - 200, 100), cv2.FONT_HERSHEY_COMPLEX, 2, (255, 0, 0), 1)


? ? return img_


label = ["rubber stopper", "push rod tail", "needle tail", "mouth", "crooked mouth", "screw mouth", "small rubber plug"]


if __name__ == "__main__":


# 1.初始化aidlite類并創(chuàng)建aidlite對象

? ? aidlite = aidlite_gpu.aidlite()

? ? print("ok")


# 2.加載模型

? ? value = aidlite.ANNModel(model_path, [640 * 640 * 3 * 4], [8400 * 11 * 4], 4, 0)

? ? print("gpu:", value)

? ? # file_names=os.listdir(image_path)

? ? # root,dirs,files = os.walk(image_path)

? ? for root, dirs, files in os.walk(image_path):

? ? ? ? num = 0

? ? ? ? for file in files:

? ? ? ? ? ? file = os.path.join(root, file)

? ? ? ? ? ? frame = cv2.imread(file)

? ? ? ? ? ? x_scale = frame.shape[1] / 640

? ? ? ? ? ? y_scale = frame.shape[0] / 640


? ? ? ? ? ? img = cv2.resize(frame, (640, 640))

? ? ? ? ? ? # img_copy=img.co

? ? ? ? ? ? img = img / 255.0

? ? ? ? ? ? img = np.expand_dims(img, axis=0)

? ? ? ? ? ? img = img.astype(dtype=np.float32)

? ? ? ? ? ? print(img.shape)


# 3.傳入模型輸入數(shù)據(jù)

? ? ? ? ? ? aidlite.setInput_Float32(img)


# 4.執(zhí)行推理

? ? ? ? ? ? start = time.time()


? ? ? ? ? ? aidlite.invoke()


? ? ? ? ? ? end = time.time()

? ? ? ? ? ? timerValue = 1000 * (end - start)

? ? ? ? ? ? print("infer time(ms):{0}", timerValue)


# 5.獲取輸出

? ? ? ? ? ? pred = aidlite.getOutput_Float32(0)

? ? ? ? ? ? # print(pred.shape)

? ? ? ? ? ? pred = np.array(pred)

? ? ? ? ? ? print(pred.shape)

? ? ? ? ? ? pred = np.reshape(pred, (8400, 11))

? ? ? ? ? ? # pred=np.reshape(pred,(11,8400)).transpose()

? ? ? ? ? ? print(pred.shape) ?# shape=(8400,11)


# 6.后處理,解析輸出

? ? ? ? ? ? boxes, scores, classes = postProcess(pred, confThresh, NmsThresh)


# 7.繪制保存圖像

? ? ? ? ? ? ret_img = draw(frame, x_scale, y_scale, boxes, scores, classes)


? ? ? ? ? ? ret_img = ret_img[:, :, ::-1]

? ? ? ? ? ? num += 1

? ? ? ? ? ? image_file_name = "/home/result/res" + str(num) + ".jpg"

# 8.保存圖片

? ? ? ? ? ? cv2.imwrite(image_file_name, ret_img)

個人體會:

筆者是在Aidlux團(tuán)隊以及龍哥的訓(xùn)練營中學(xué)習(xí)而來传泊,期間龍哥區(qū)別以往的視頻課鼠渺,以一種更加直觀的方式展現(xiàn)出整個項目的流程與細(xì)節(jié)。不管是AI算法小白還是AI算法的老手都在這次訓(xùn)練營受益匪淺眷细。Aidlux工程實踐內(nèi)容全是干貨拦盹,同時過程也遇見了很多問題,但是龍哥和訓(xùn)練營的其他同學(xué)們都很認(rèn)真為其他學(xué)員解決溪椎,耐心輔導(dǎo)普舆,對我來言,剛剛接觸這一領(lǐng)域校读,以及Aidlux平臺的使用沼侣,讓我耳目一新。整個流程下歉秫,我已經(jīng)學(xué)會了如何在Aidlux進(jìn)行模型部署蛾洛,令我也感覺到成就感,在此特別感謝張子豪老師和Aidlux團(tuán)隊的貢獻(xiàn)雁芙,希望他們以后在AI算法開發(fā)的道路事業(yè)更加順利轧膘。

最后放上本次基于Aidlux平臺實現(xiàn)工業(yè)視覺缺陷檢測的效果視頻的地址。

https://www.bilibili.com/video/BV1GN4y197cD/

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末却特,一起剝皮案震驚了整個濱河市扶供,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌裂明,老刑警劉巖椿浓,帶你破解...
    沈念sama閱讀 222,627評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件太援,死亡現(xiàn)場離奇詭異,居然都是意外死亡扳碍,警方通過查閱死者的電腦和手機(jī)提岔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來笋敞,“玉大人碱蒙,你說我怎么就攤上這事『幌铮” “怎么了赛惩?”我有些...
    開封第一講書人閱讀 169,346評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長趁餐。 經(jīng)常有香客問我喷兼,道長,這世上最難降的妖魔是什么后雷? 我笑而不...
    開封第一講書人閱讀 60,097評論 1 300
  • 正文 為了忘掉前任季惯,我火速辦了婚禮,結(jié)果婚禮上臀突,老公的妹妹穿的比我還像新娘勉抓。我一直安慰自己,他們只是感情好候学,可當(dāng)我...
    茶點故事閱讀 69,100評論 6 398
  • 文/花漫 我一把揭開白布藕筋。 她就那樣靜靜地躺著,像睡著了一般梳码。 火紅的嫁衣襯著肌膚如雪念逞。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,696評論 1 312
  • 那天边翁,我揣著相機(jī)與錄音,去河邊找鬼硕盹。 笑死符匾,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的瘩例。 我是一名探鬼主播啊胶,決...
    沈念sama閱讀 41,165評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼垛贤!你這毒婦竟也來了焰坪?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,108評論 0 277
  • 序言:老撾萬榮一對情侶失蹤聘惦,失蹤者是張志新(化名)和其女友劉穎某饰,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,646評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡黔漂,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,709評論 3 342
  • 正文 我和宋清朗相戀三年诫尽,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片炬守。...
    茶點故事閱讀 40,861評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡牧嫉,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出减途,到底是詐尸還是另有隱情酣藻,我是刑警寧澤,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布鳍置,位于F島的核電站辽剧,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏墓捻。R本人自食惡果不足惜抖仅,卻給世界環(huán)境...
    茶點故事閱讀 42,196評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望砖第。 院中可真熱鬧撤卢,春花似錦、人聲如沸梧兼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,698評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽羽杰。三九已至渡紫,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間考赛,已是汗流浹背惕澎。 一陣腳步聲響...
    開封第一講書人閱讀 33,804評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留颜骤,地道東北人唧喉。 一個月前我還...
    沈念sama閱讀 49,287評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像忍抽,于是被迫代替她去往敵國和親八孝。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,860評論 2 361

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