C#人臉識別入門篇--提取人臉特征值及人臉識別

如何識別多個人臉

在開始之前衡载,先解決一個疑問,這個SDK可以識別多個人臉嗎隙袁。答案當(dāng)然是可以的痰娱。在上一章節(jié)中我們實現(xiàn)了識別單個人臉的功能弃榨。
如果要識別多個人臉,需要進行下面的設(shè)置梨睁。

定義人臉的識別數(shù)目范圍

   int nMaxFaceNum = 50;/*定義人臉識別的數(shù)目鲸睛,有效范圍為1-50*/

修改人臉識別的程序。

在上一章節(jié)中坡贺, 我們的方法是只取到識別到的第一個人臉官辈,因此我們只需要一個顯示人臉的地方就可以了。要識別多個人臉遍坟,首先就是修改視圖拳亿。

界面截圖

然后,修改程序為循環(huán)愿伴。


//識別每一幅圖像

for (int i = 0; i < faceRes.nFace; i++)
{
    MRECT rect = (MRECT)Marshal.PtrToStructure(faceRes.rcFace + Marshal.SizeOf(typeof(MRECT)) * i, typeof(MRECT));

   Image image = CutFace(bitmap, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);


   if (i == 0)
   {
    /*第一個識別到的人臉保存在原位置*/
       this.pictureBox2.Image = image;
        this.pictureBox2.Tag = faceImageName[i];
    }
   else
   {
        /*后面識別到的人臉按順序并排顯示在下面肺魁,使用臨時創(chuàng)建PictureBox控件的方式顯示圖片內(nèi)容*/


       PictureBox tempPicture = new PictureBox();
       tempPicture.Width = 100;
       tempPicture.Height = 120;
       tempPicture.SizeMode = PictureBoxSizeMode.Zoom;
       tempPicture.Location = new System.Drawing.Point(10 + ((i-1) % 7) * 120, 10 + ((i-1) / 7) * 120);
       tempPicture.Image = image;
       tempPicture.Tag =faceImageName[i];
       this.panel1.Controls.Add(tempPicture);
   }

一步步實現(xiàn)人臉識別

先來看一下我們這節(jié)的效果

效果圖

本節(jié)我們主要講解如何根據(jù)識別到的人臉信息提取人臉數(shù)據(jù)特征,并在此基礎(chǔ)上講解一下如何做人臉識別

在人臉識別領(lǐng)域隔节,首先是檢測是否有人臉鹅经,人臉的區(qū)域是哪里,然后對這個區(qū)域進行特征點提取怎诫,在提取結(jié)束后瘾晃,告訴計算機,這個人臉是誰幻妓。

image

計算機把這些特征信息和人臉的名稱保存下來蹦误,就形成了人臉庫,在識別人臉時涌哲,計算機通過一定的算法胖缤,檢索庫中是否有匹配到的人臉結(jié)果尚镰,給出相似度數(shù)據(jù)阀圾。當(dāng)人臉的相似度數(shù)據(jù)達到一定的數(shù)值時,就可以認為同一張人臉狗唉。

相似度通常是一個0-1的小數(shù)初烘。一般來說,數(shù)值越大分俯,表示兩個人越相近肾筐。

注:不同人臉引擎的人臉相似度不具有可比性,例如缸剪,我們從Face ++ 拿到的同一個人的人臉相似度可能會在0.8-0.9吗铐,虹軟的只能在0.6-0.8之間,這并不能說明Face ++ ,它們只是算法的標(biāo)準(zhǔn)不同杏节,例如唬渗,虹軟在不同人臉0.1-0.2的時候典阵,F(xiàn)ace++達到了0.3-0.5
人臉檢測并建立人臉庫的過程如下

建立人臉庫

通過人臉檢測或者人臉跟蹤,獲取到人臉信息并識別人臉的過程如下:

從人臉庫中識別

本次教程我們以目錄結(jié)構(gòu)作為人臉的存檔方式镊逝,每張人臉對應(yīng)一張人臉標(biāo)識和一個人臉特征壮啊。人臉標(biāo)識和特征使用同一個文件名稱來關(guān)聯(lián),例如人臉a.jpg的特征用a.dat來表示撑蒜。

好歹啼,我們開始我們的課程

集成人臉識別SDK庫

我們本次使用到的虹軟的SDK包中,提供了人臉識別的庫座菠,它的名字叫face_recongnition.dll狸眼,我們找到它的SDK文檔。
來建立各個結(jié)構(gòu)體和API的C#映射辈灼。

首先是結(jié)構(gòu)體

從本節(jié)開始份企,我們不再講解原始SDK文檔中的數(shù)據(jù)結(jié)構(gòu)和C#數(shù)組結(jié)構(gòu)如何映射的,也不再講解P/Invoke的知識巡莹,如果需要了解相關(guān)知識司志,請參考我們上篇文檔的相關(guān)內(nèi)容。

AFR_FSDK_FaceInput

    public struct AFR_FSDK_FaceInput
    {
        public  MRECT rcFace;
        public int lOrient;
    }

這個結(jié)構(gòu)體是FD識別的輸出結(jié)構(gòu)體降宅,我們在上一章節(jié)標(biāo)記人臉時使用了此結(jié)構(gòu)體骂远。

AFR_FSDK_FaceModel

  public  struct AFR_FSDK_FaceModel
    {
     public IntPtr pbFeature;
     public int lFeatureSize;
    }

這個結(jié)構(gòu)體是人臉模型數(shù)據(jù),也就是我們說的人臉特征腰根。人臉識別就基于這個結(jié)構(gòu)激才。

參數(shù)名 說明
pbFeature 提取到的臉部特征
lFeatureSize 特征信息長度

其中pbFeature是人臉數(shù)據(jù),虹軟當(dāng)前版本的人臉數(shù)據(jù)為一個20K大小的二進制數(shù)組额嘿,在使用時瘸恼,我們把它保存為byte[]數(shù)組。

AFR_FSDK_Version

   public struct AFR_FSDK_Version
    {
      
        public int lCodebase;
        public int lMajor;
        public int lMinor;
        public int lBuild;
        public int lFeatureLevel;
        public string Version;
        public string BuildDate;
        public string CopyRight;
    }

定義識別方法類

我們將SDK中的對應(yīng)方法提取到C#類中册养,和上面的章節(jié)保持一致东帅,我們稱之為AFRFunction。

   public class AFRFunction
    {
       /**
        *Init Engine 
        */
       [System.Runtime.InteropServices.DllImportAttribute("libarcsoft_fsdk_face_recognition.dll", EntryPoint = "AFR_FSDK_InitialEngine", CallingConvention = CallingConvention.Cdecl)]
       public static extern int AFR_FSDK_InitialEngine(string AppId, string SDKKey,  System.IntPtr pMem, int lMemSize, ref System.IntPtr phEngine);

        /**
        * 提取人臉特征值
        */
       [System.Runtime.InteropServices.DllImportAttribute("libarcsoft_fsdk_face_recognition.dll", EntryPoint = "AFR_FSDK_ExtractFRFeature", CallingConvention = CallingConvention.Cdecl)]
       public static extern int AFR_FSDK_ExtractFRFeature(System.IntPtr hEngine,  System.IntPtr pInputImage,  System.IntPtr pFaceRes, System.IntPtr pFaceModels);

        /*
        * 比較兩個人臉特征值之間的相似度
        **/
       [System.Runtime.InteropServices.DllImportAttribute("libarcsoft_fsdk_face_recognition.dll", EntryPoint = "AFR_FSDK_FacePairMatching", CallingConvention = CallingConvention.Cdecl)]
       public static extern int AFR_FSDK_FacePairMatching(System.IntPtr hEngine, ref System.IntPtr reffeature, ref System.IntPtr probefeature, ref float pfSimilScore);


        /**
        *銷毀引擎
        */
       [System.Runtime.InteropServices.DllImportAttribute("libarcsoft_fsdk_face_recognition.dll", EntryPoint = "AFR_FSDK_UninitialEngine", CallingConvention = CallingConvention.Cdecl)]
       public static extern int AFR_FSDK_UninitialEngine(System.IntPtr hEngine);


        /**
        *獲取人臉的版本號
        */

       [System.Runtime.InteropServices.DllImportAttribute("libarcsoft_fsdk_face_recognition.dll", EntryPoint = "AFR_FSDK_GetVersion", CallingConvention = CallingConvention.Cdecl)]
       public static extern System.IntPtr AFR_FSDK_GetVersion(System.IntPtr hEngine);

   
   }

開始之前的準(zhǔn)備

定義人臉庫的位置

本次我們使用簡單的基于目錄存儲人臉庫

 private String FaceLibraryPath = "G:\\Test\\";

定義人臉識別引擎的變量

IntPtr detectEngine = IntPtr.Zero;
//新增人臉識別引擎的定義
IntPtr recognizeEngine = IntPtr.Zero;

在構(gòu)造函數(shù)中我們對人臉識別引擎進行初始化

int detectSize = 40 * 1024 * 1024;
int nScale = 50;
int nMaxFaceNum = 50;
IntPtr pMem = Marshal.AllocHGlobal(detectSize);
IntPtr pMemRecongnize = Marshal.AllocHGlobal(detectSize);

注意:detectSize為人臉識別的內(nèi)存大小球拦,一般來說靠闭,你可以根據(jù)你的應(yīng)用程序的規(guī)模來設(shè)置一個適當(dāng)?shù)臄?shù)值,數(shù)值過小會報內(nèi)存不足的ERROR坎炼。

int retCode2 = AFR.AFRFunction.AFR_FSDK_InitialEngine(appId, sdkFRKey, pMemRecongnize, detectSize, ref recognizeEngine);


 if (retCode2 != 0)
{
    MessageBox.Show("引擎FR初始化失敗:錯誤碼為:" + retCode2);
    this.Close();
}

這里需要注意FR Key愧膀,虹軟這次開源了1:1和1:N的SDK,不同的SDK谣光,其對應(yīng)的KEY是不一樣的檩淋。

提取人臉特征值

我們來提取人臉特征值。打開我們的checkAndMarkFace方法萄金。

人臉特征值是一個二進制的byte數(shù)組蟀悦,其內(nèi)容對虹軟來說是屬于技術(shù)機密碳柱,里面保存了人臉的特征。這里的特征可以在人臉相似度比較時用到熬芜,人臉的特征包含了人臉的關(guān)鍵點信息莲镣。可惜的是涎拉,虹軟這方面并沒有開源瑞侮。同樣的,人臉的相似度比較算法也沒有開源鼓拧。不過不開源也有不開源的好處半火,至少我們用起來不用擔(dān)心這里面的細節(jié)。

首先季俩,我們定義一個變量數(shù)組钮糖,用于保存圖片名稱的數(shù)組。 這里我們簡單的對每個識別到的人臉酌住,用GUID命名店归。

在我們上一節(jié)的,輸出識別到的人臉數(shù)據(jù)之前酪我,我們增加一下我們的業(yè)務(wù)邏輯消痛。找到下面的代碼

if (faceRes.nFace > 0)

我們在后面增加定義

//定義用到保存識別到的圖片的名稱的數(shù)組
List<string> faceImageName = new List<string>(faceRes.nFace);

for (int i = 0; i < faceRes.nFace; i++)
{
    faceImageName.Add(Guid.NewGuid().ToString());
}

在識別到的每個人臉以后,我們把識別到的人臉保存下來

Image image = CutFace(bitmap, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);

image.Save(FaceLibraryPath+faceImageName[i]+".jpg",ImageFormat.Jpeg);

如何進行人臉特征值的讀取

人臉特征值依賴于人臉識別的結(jié)果都哭,其原理是利用識別到的人臉區(qū)域信息秩伞,在原圖中對人臉部分進行運算,輸出人臉的特征數(shù)據(jù)欺矫。
通過前面的定義纱新,可以知道人臉特征提取函數(shù)的需要的參數(shù)信息如下

  • recognizeEngine:人臉識別引擎
  • offInputPtr:輸入的圖像信息,和FD的信息相同穆趴。同為ASVLOFFSCREEN結(jié)構(gòu)體脸爱,我們可以直接使用上一步已經(jīng)定義好的這個變量。
  • faceInputPtr:人臉區(qū)域信息毡代,包括人臉的角度信息阅羹,以及人臉的坐標(biāo)范圍勺疼,對應(yīng)的參數(shù)類型為MRECT教寂,也就是在FD中識別到的人臉的區(qū)域坐標(biāo),
  • 輸出參數(shù)為faceModel結(jié)構(gòu)體执庐。包括長度信息和人臉特征數(shù)組

我們來一步步解決酪耕。

定義faceInput結(jié)構(gòu)體并指定它的引用互操作類型

AFR_FSDK_FaceInput faceinput = new AFR_FSDK_FaceInput();
faceinput.lOrient =(int)Marshal.PtrToStructure( faceRes.lfaceOrient,typeof(int));
  
MRECT rect = (MRECT)Marshal.PtrToStructure(faceRes.rcFace + Marshal.SizeOf(typeof(MRECT)) * i, typeof(MRECT));
faceinput.rcFace = rect;

IntPtr faceInputPtr = Marshal.AllocHGlobal(Marshal.SizeOf(faceinput));
                Marshal.StructureToPtr(faceinput, faceInputPtr, false);

定義faceModel變量用于保存識別到的特征值信息

AFR_FSDK_FaceModel faceModel = new AFR_FSDK_FaceModel();
IntPtr faceModelPtr = Marshal.AllocHGlobal(Marshal.SizeOf(faceModel));

調(diào)用FR引擎進行特征信息提取

   int ret = AFRFunction.AFR_FSDK_ExtractFRFeature(recognizeEngine, offInputPtr, faceInputPtr,
                        faceModelPtr);

如果ret=0,則提取成功轨淌,我們再調(diào)用Marshal的方法將對應(yīng)的信息取出來

faceModel = (AFR_FSDK_FaceModel) Marshal.PtrToStructure(faceModelPtr, typeof (AFR_FSDK_FaceModel));
Marshal.FreeHGlobal(faceModelPtr);



byte[] featureContent = new byte[faceModel.lFeatureSize];
Marshal.Copy(faceModel.pbFeature, featureContent, 0, faceModel.lFeatureSize);

保存獲取到的結(jié)果迂烁,為了后面的匹配方便看尼,和圖片命名保持一致

System.IO.File.WriteAllBytes(FaceLibraryPath+faceImageName[i]+".dat",featureContent);

通過圖像庫識別圖像中的特征

現(xiàn)在我們要做的是人臉識別功能呢,我們想要的功能是盟步,打開一張照片藏斩,如果里面有人臉钓账,那么我們就識別這個人臉是否已經(jīng)在我們的人臉庫中出現(xiàn)過嚎卫,如果已經(jīng)出現(xiàn) ,就顯示人臉的圖像編號依鸥。

依然打開項目黄橘,增加一個按鈕兆览。識別人臉,并增加一個pictureBox用于保存匹配到的人臉的對應(yīng)的人臉信息塞关。雙擊剛才新加的按鈕進入事件處理代碼編輯窗口抬探。

為了不增加重新提取特征臉的工作量,我們將上一步獲取到的特征臉重用帆赢。在上一步中小压,對識別到的人臉的第一個保存在了pictureBox中,并把相關(guān)的特征信息保存在對應(yīng)命名的dat文件中椰于。在保存時场航,使用

         this.pictureBox2.Tag = faceImageName[i];

保存圖像特征數(shù)據(jù)的文件名,因此在這里我們使用

string faceFeaturePath = pictureBox2.Tag as string;

獲取圖像文件名廉羔。

這里我們需要讀文件溉痢,讀取這個特征信息。

C# 讀取二進制文件和寫二進制文件都相當(dāng)?shù)姆奖惚锼憧梢允褂肅#的序列化操作把變量保為dat文件孩饼,然后使用反操作把文件重新讀取以初始化對象。這里使用的是簡單的二進制讀取的方法竹挡,當(dāng)然你也可以嘗試序列化來完成這個操作镀娶。

byte[] sourceFeature = System.IO.File.ReadAllBytes(FaceLibraryPath + faceFeaturePath + ".dat");

接下來我們要使用人臉匹配的方法來進行匹配。這里使用的方法是AFR_FSDK_FacePairMatching
方法揪罕。再來看一下這個方法的定義

參數(shù)名稱 輸入輸出 說明
hEngine [in] 引擎 handle
reffeature [in] 已有臉部特征信息
probefeature [in] 被比較的臉部特征信息
pfSimilScore [out] 相似程度數(shù)值

我們先來定義被比較的臉部信息梯码。這里原來的參數(shù)名稱有點拗口,我們使用localFaceModel來定義本地的

AFR_FSDK_FaceModel localFaceModels = new AFR_FSDK_FaceModel();
IntPtr sourceFeaturePtr = Marshal.AllocHGlobal(sourceFeature.Length);
Marshal.Copy(sourceFeature, 0, sourceFeaturePtr, sourceFeature.Length);
localFaceModels.lFeatureSize = sourceFeature.Length;
localFaceModels.pbFeature = sourceFeaturePtr;

由于使用了文件保存人臉特征信息好啰,因此我們的人臉遍歷算法就變得很簡單了轩娶。我們這里使用1:1的方法。

我們直接使用存儲的人臉信息來進行搜索框往,方法自然是先遍歷讀取所有特征數(shù)據(jù),提取特征值并進行比較

     foreach (var b in System.IO.Directory.GetFiles(FaceLibraryPath,"*.dat"))
                {
                    byte[] libaryFeature = System.IO.File.ReadAllBytes(b);
                    float result=0f;

                    //TODO:構(gòu)造AFR_FSDK_FaceModel鳄抒,調(diào)用API,獲取比較結(jié)果

                    if (result>0.7&&result<0.99)
                    {
                        //  MessageBox.Show(b);

                        Image image = Image.FromFile(b.Replace(".dat",".jpg"));

                        this.pictureBox3.Image = new Bitmap(image);
                        MessageBox.Show(result.ToString());
                        break;

                    }
                }

我們來完成TODO的部分
首先我們定義庫Model和本地Model的結(jié)構(gòu)體指針
定義庫的指針

IntPtr libaryFeaturePtr = Marshal.AllocHGlobal(libaryFeature.Length);

Marshal.Copy(libaryFeature, 0, libaryFeaturePtr, libaryFeature.Length);

AFR_FSDK_FaceModel libraryFaceModel = new AFR_FSDK_FaceModel();

libraryFaceModel.lFeatureSize = libaryFeature.Length;

libraryFaceModel.pbFeature = libaryFeaturePtr;
IntPtr firstPtr = Marshal.AllocHGlobal(Marshal.SizeOf(localFaceModels));

Marshal.StructureToPtr(localFaceModels, firstPtr, false);

定義本地Model的指針

IntPtr firstPtr = Marshal.AllocHGlobal(Marshal.SizeOf(localFaceModels));

Marshal.StructureToPtr(localFaceModels, firstPtr, false);

調(diào)用方法輸出匹配結(jié)果

int ret = AFRFunction.AFR_FSDK_FacePairMatching(recognizeEngine, firstPtr, secondPtr, ref result);

從這里可以看出,人臉識別并沒有特別高深的地方许溅,其基礎(chǔ)理論依然是特征值匹配搜索的理論瓤鼻,
雖然這里面的難點是特征值的提取和匹配算法,但因為虹軟已經(jīng)免費給我們提供了對應(yīng)的SDK贤重,我們只需要調(diào)用相關(guān)的接口就可能了茬祷。如果要提高人臉匹配的速度,除了可以聯(lián)系虹軟尋找技術(shù)支持以外并蝗,也可以利用我們在其它算法方面的積累來嘗試解決方案牲迫。

后記

本次我們學(xué)習(xí)了人臉特征的提取和人臉特征的保存,實際上借卧,在業(yè)務(wù)系統(tǒng)中盹憎,人臉通常是保存在數(shù)據(jù)庫中的,并且在匹配的時候铐刘,為了性能考慮陪每,更多的是把特征保存在內(nèi)存中,20K的特征值如果在2GB的業(yè)務(wù)系統(tǒng)中镰吵,可以很輕松的保存10W+的特征信息檩禾。人臉檢測和識別是CPU密集型和內(nèi)存密集型的應(yīng)用,保持良好的計算機配置有助于提高識別的性能疤祭,離線SDK的良好擴展性也為我們提高系統(tǒng)的性能提供了可行性盼产。

這兩章節(jié)都是從靜態(tài)圖像出發(fā)的人臉檢測和人臉識別,從下一章節(jié)開始勺馆,我們將先從視頻的人臉識別講起戏售。然后結(jié)合攝像頭的實時圖像采集,我們來講解一下簡單的人臉識別門禁系統(tǒng)的實現(xiàn)草穆。請繼續(xù)關(guān)注灌灾。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市悲柱,隨后出現(xiàn)的幾起案子锋喜,更是在濱河造成了極大的恐慌,老刑警劉巖豌鸡,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嘿般,死亡現(xiàn)場離奇詭異,居然都是意外死亡涯冠,警方通過查閱死者的電腦和手機炉奴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來功偿,“玉大人盆佣,你說我怎么就攤上這事⌒岛桑” “怎么了共耍?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長吨瞎。 經(jīng)常有香客問我痹兜,道長,這世上最難降的妖魔是什么颤诀? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任字旭,我火速辦了婚禮,結(jié)果婚禮上崖叫,老公的妹妹穿的比我還像新娘遗淳。我一直安慰自己,他們只是感情好心傀,可當(dāng)我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布屈暗。 她就那樣靜靜地躺著,像睡著了一般脂男。 火紅的嫁衣襯著肌膚如雪养叛。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天宰翅,我揣著相機與錄音弃甥,去河邊找鬼。 笑死汁讼,一個胖子當(dāng)著我的面吹牛淆攻,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播嘿架,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼卜录,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了眶明?” 一聲冷哼從身側(cè)響起艰毒,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎搜囱,沒想到半個月后丑瞧,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡蜀肘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年绊汹,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片扮宠。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡西乖,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情获雕,我是刑警寧澤薄腻,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站届案,受9級特大地震影響庵楷,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜楣颠,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一尽纽、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧童漩,春花似錦弄贿、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至豆拨,卻和暖如春直奋,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背施禾。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工脚线, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人弥搞。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓邮绿,卻偏偏與公主長得像,于是被迫代替她去往敵國和親攀例。 傳聞我的和親對象是個殘疾皇子船逮,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,700評論 2 354

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