基于圖像模板的卡扣拼接

下面介紹一種基于圖像模板的拼接方法坟奥,使用該方法有以下前提:

  1. 明確圖像之間存在重合區(qū)域。
  2. 圖像不存在明顯的尺度變化和畸變能耻。

我們可以考慮以下場景帆竹,一臺做網(wǎng)格化運動的攝像機绕娘,每若干毫秒在一個網(wǎng)格上拍攝一張照片,控制這些照片之間有視野重疊栽连。并且網(wǎng)格的數(shù)量在幾百乃至上千之間险领,使用特征匹配法性能有可能會達(dá)不到要求侨舆,但是可以嘗試下本文介紹的基于模板匹配的拼接方法。

兩張圖拼接方法:

假定圖像有先后順序绢陌,左圖在先挨下,右圖在后,并且兩張圖的重疊區(qū)域不小于下面將要設(shè)定的圖像模板的尺寸脐湾。

步驟如下:

  1. 從左圖右邊緣某個位置扣取一塊區(qū)域做為圖像模板臭笆。
  2. 使用模板匹配算法在右圖中執(zhí)行類卷積操作,計算相似性矩陣秤掌。
  3. 在相似性矩陣中定位最匹配的位置愁铺。
  4. 創(chuàng)建大圖,根據(jù)匹配位置闻鉴,分別將左圖和右圖粘貼到大圖上茵乱。
  5. 對重疊區(qū)域進行反向距離加權(quán)融合(待補充)。

測試代碼如下:

{
    cv::Mat matImgL = cv::imread("cat01.png");
    cv::Mat matImgR = cv::imread("cat02.png");

    // 對右圖模擬光照變化孟岛、模糊度變化
    //cv::convertScaleAbs(matImgR, matImgR, 1.0, 30);
    //cv::GaussianBlur(matImgR, matImgR, cv::Size(3, 3), 0);

    cv::Mat matImgLGray, matImgRGray;
    cv::cvtColor(matImgL, matImgLGray, cv::COLOR_BGR2GRAY);
    cv::cvtColor(matImgR, matImgRGray, cv::COLOR_BGR2GRAY);

    int64 llTickStart = cv::getTickCount();

    // 選取的圖像模板的尺寸
    const int nTemplateWidth = 50;
    const int nTemplateHeight = 50;

    // 從左圖右邊緣中部扣取一塊50x50的區(qū)域瓶竭,做為圖像模板
    cv::Rect rectTemplate(matImgLGray.cols - nTemplateWidth, matImgLGray.rows / 2 - nTemplateHeight/2, nTemplateWidth, nTemplateHeight);
    cv::Mat matTemplate = matImgLGray(rectTemplate);

    // 在右圖中執(zhí)行模板匹配
    cv::Mat matResult;
    cv::matchTemplate(matImgRGray, matTemplate, matResult, cv::TM_CCOEFF_NORMED);

    // 定位匹配系數(shù)最高的位置
    double fMaxVal = 0.0;
    cv::Point ptMaxLoc;
    cv::minMaxLoc(matResult, NULL, &fMaxVal, NULL, &ptMaxLoc);

    cv::rectangle(matImgL, rectTemplate, cv::Scalar(0, 255, 0), 2);
    cv::rectangle(matImgR, cv::Rect(ptMaxLoc.x, ptMaxLoc.y, nTemplateWidth, nTemplateHeight), cv::Scalar(0, 255, 0), 2);

    // 創(chuàng)建大圖
    cv::Mat matBig(matImgL.rows, rectTemplate.x + (matImgR.cols - ptMaxLoc.x), matImgL.type(), cv::Scalar::all(0));

    // 選擇左圖全部位置貼到大圖指定位置
    cv::Rect rectROILSrc(0, 0, matImgL.cols, matImgL.rows);
    cv::Rect rectROILDst(0, 0, matImgL.cols, matImgL.rows);
    matImgL(rectROILSrc).copyTo(matBig(rectROILDst));

    // 選擇右圖部分位置貼到大圖指定位置
    cv::Rect rectROIRSrc;
    cv::Rect rectROIRDst;
    if (ptMaxLoc.y - rectTemplate.y >= 0)
    {
        rectROIRSrc = cv::Rect(ptMaxLoc.x, (ptMaxLoc.y - rectTemplate.y), matImgR.cols - ptMaxLoc.x, matImgR.rows - (ptMaxLoc.y - rectTemplate.y));
        rectROIRDst = cv::Rect(rectTemplate.x, 0, rectROIRSrc.width, rectROIRSrc.height);
    }
    else
    {
        rectROIRSrc = cv::Rect(ptMaxLoc.x, 0, matImgR.cols - ptMaxLoc.x, std::min(matBig.rows, matImgR.rows + (rectTemplate.y - ptMaxLoc.y)));
        rectROIRSrc.height -= (rectTemplate.y - ptMaxLoc.y);
        rectROIRDst = cv::Rect(rectTemplate.x, (rectTemplate.y - ptMaxLoc.y), rectROIRSrc.width, rectROIRSrc.height);
    }
    matImgR(rectROIRSrc).copyTo(matBig(rectROIRDst));

    int64 llTickEnd = cv::getTickCount();
    printf("use %fs \n", (llTickEnd - llTickStart)/cv::getTickFrequency());

    cv::imwrite("matBig.png", matBig);
}

左圖和右圖分別為:


cat01.png

cat02.png

拼接后的大圖為:


matBig.png
網(wǎng)格化圖像組拼接方法:

拿前面的運動相機舉例,在網(wǎng)格化的運動系統(tǒng)作用下渠羞,攝像機不斷地拍取網(wǎng)格照片斤贰,并且這些照片之間有明顯的位置先后關(guān)系。因而我們可按倆張圖的拼接方法堵未,以串聯(lián)的方式腋舌,前面的圖先拼出結(jié)果,后面的圖再往前面結(jié)果上拼渗蟹。但是需要額外留意的是块饺,卡扣位置的選取,當(dāng)發(fā)生網(wǎng)格化換行時雌芽,卡扣應(yīng)取上一行的下邊緣授艰。

我們先將兩張圖的拼接過程封裝成函數(shù),在這些定義中世落,增加了水平和垂直拼接的實現(xiàn)淮腾。代碼如下:

namespace dakuang
{
    // 兩張圖的位置關(guān)系
    enum EClipRelation
    {
        CLIP_HORIZONTAL = 0, // 水平
        CLIP_VERTICALITY  // 垂直
    };

    // 卡扣位置信息
    struct SClipInfo
    {
        cv::Rect rectFirst;  // 第一張圖的卡扣位置
        cv::Rect rectSecond;  // 第二張圖的卡扣位置
    };

    // 計算兩張圖的卡扣位置信息
    void calcClipInfo(const cv::Mat& matImgFirst, const cv::Mat& matImgSecond, const EClipRelation& emRelation, SClipInfo& stClipInfo)
    {
        cv::Mat matImgFirstGray, matImgSecondGray;
        cv::cvtColor(matImgFirst, matImgFirstGray, cv::COLOR_BGR2GRAY);
        cv::cvtColor(matImgSecond, matImgSecondGray, cv::COLOR_BGR2GRAY);

        // 選取的圖像模板的尺寸
        const int nTemplateWidth = 50;
        const int nTemplateHeight = 50;

        // 選取第一張圖的卡扣位置
        cv::Rect rectTemplate;
        if (emRelation == CLIP_HORIZONTAL)
        {
            // 水平拼接,選取右邊緣中部扣取一塊區(qū)域
            rectTemplate = cv::Rect(matImgFirstGray.cols - nTemplateWidth, matImgFirstGray.rows / 2 - nTemplateHeight / 2, nTemplateWidth, nTemplateHeight);
        }
        else
        {
            // 垂直拼接屉佳,選取下邊緣中部扣取一塊區(qū)域
            rectTemplate = cv::Rect(matImgFirstGray.cols/2 - nTemplateWidth/2, matImgFirstGray.rows - nTemplateHeight, nTemplateWidth, nTemplateHeight);
        }

        // 將卡扣位置的圖像做為模板
        cv::Mat matTemplate = matImgFirstGray(rectTemplate);

        // 在第二張圖中執(zhí)行模板匹配
        cv::Mat matResult;
        cv::matchTemplate(matImgSecondGray, matTemplate, matResult, cv::TM_CCOEFF_NORMED);

        // 定位匹配系數(shù)最高的位置
        double fMaxVal = 0.0;
        cv::Point ptMaxLoc;
        cv::minMaxLoc(matResult, NULL, &fMaxVal, NULL, &ptMaxLoc);

        // 輸出卡扣位置信息
        stClipInfo.rectFirst = rectTemplate;
        stClipInfo.rectSecond = cv::Rect(ptMaxLoc.x, ptMaxLoc.y, nTemplateWidth, nTemplateHeight);
    }

    // 使用卡扣信息谷朝,拼接兩張圖
    void joinTwoImage(const cv::Mat& matImgFirst, const cv::Mat& matImgSecond, const EClipRelation& emRelation, const SClipInfo& stClipInfo, cv::Mat& matResult)
    {
        // 水平拼接
        if (emRelation == CLIP_HORIZONTAL)
        {
            // 創(chuàng)建大圖(高度以第一張圖為準(zhǔn))
            cv::Mat matBig(matImgFirst.rows, stClipInfo.rectFirst.x + (matImgSecond.cols - stClipInfo.rectSecond.x), matImgFirst.type(), cv::Scalar::all(0));

            // 選擇第一張圖全部位置貼到大圖指定位置
            cv::Rect rectROIFirstSrc(0, 0, matImgFirst.cols, matImgFirst.rows);
            cv::Rect rectROIFirstDst(0, 0, matImgFirst.cols, matImgFirst.rows);
            matImgFirst(rectROIFirstSrc).copyTo(matBig(rectROIFirstDst));

            // 計算第二張圖部分位置貼到大圖指定位置
            cv::Rect rectROISecondSrc;
            cv::Rect rectROISecondDst;
            if (stClipInfo.rectSecond.y - stClipInfo.rectFirst.y >= 0)
            {
                rectROISecondSrc = cv::Rect(stClipInfo.rectSecond.x, (stClipInfo.rectSecond.y - stClipInfo.rectFirst.y), 
                        (matImgSecond.cols - stClipInfo.rectSecond.x), std::min(matBig.rows, matImgSecond.rows - (stClipInfo.rectSecond.y - stClipInfo.rectFirst.y)));
                rectROISecondDst = cv::Rect(stClipInfo.rectFirst.x, 0, rectROISecondSrc.width, rectROISecondSrc.height);
            }
            else
            {
                rectROISecondSrc = cv::Rect(stClipInfo.rectSecond.x, 0,
                        (matImgSecond.cols - stClipInfo.rectSecond.x), std::min(matBig.rows, matImgSecond.rows + (stClipInfo.rectFirst.y - stClipInfo.rectSecond.y)));
                rectROISecondSrc.height -= (stClipInfo.rectFirst.y - stClipInfo.rectSecond.y);

                // 另一種表示法
                //rectROISecondSrc = cv::Rect(stClipInfo.rectSecond.x, 0, (matImgSecond.cols - stClipInfo.rectSecond.x), 0);
                //int nMoreHeight = (stClipInfo.rectFirst.y - stClipInfo.rectSecond.y) + matImgSecond.rows - matBig.rows;
                //rectROISecondSrc.height = (nMoreHeight > 0) ? (matImgSecond.rows - nMoreHeight) : matImgSecond.rows;

                rectROISecondDst = cv::Rect(stClipInfo.rectFirst.x, (stClipInfo.rectFirst.y - stClipInfo.rectSecond.y), rectROISecondSrc.width, rectROISecondSrc.height);
            }
            matImgSecond(rectROISecondSrc).copyTo(matBig(rectROISecondDst));

            matResult = matBig;
        }
        // 垂直拼接
        else
        {
            // 創(chuàng)建大圖(寬度以第一張圖為準(zhǔn))
            cv::Mat matBig(stClipInfo.rectFirst.y + (matImgSecond.rows - stClipInfo.rectSecond.y), matImgFirst.cols, matImgFirst.type(), cv::Scalar::all(0));

            // 選擇第一張圖全部位置貼到大圖指定位置
            cv::Rect rectROIFirstSrc(0, 0, matImgFirst.cols, matImgFirst.rows);
            cv::Rect rectROIFirstDst(0, 0, matImgFirst.cols, matImgFirst.rows);
            matImgFirst(rectROIFirstSrc).copyTo(matBig(rectROIFirstDst));

            // 計算第二張圖部分位置貼到大圖指定位置
            cv::Rect rectROISecondSrc;
            cv::Rect rectROISecondDst;
            if (stClipInfo.rectSecond.x - stClipInfo.rectFirst.x >= 0)
            {
                rectROISecondSrc = cv::Rect((stClipInfo.rectSecond.x - stClipInfo.rectFirst.x), stClipInfo.rectSecond.y,
                        std::min(matBig.cols, matImgSecond.cols - (stClipInfo.rectSecond.x - stClipInfo.rectFirst.x)), (matImgSecond.rows - stClipInfo.rectSecond.y));
                rectROISecondDst = cv::Rect(0, stClipInfo.rectFirst.y, rectROISecondSrc.width, rectROISecondSrc.height);
            }
            else
            {
                rectROISecondSrc = cv::Rect(0, stClipInfo.rectSecond.y,
                        std::min(matBig.cols, matImgSecond.cols + (stClipInfo.rectFirst.x - stClipInfo.rectSecond.x)), (matImgSecond.rows - stClipInfo.rectSecond.y));
                rectROISecondSrc.width -= (stClipInfo.rectFirst.x - stClipInfo.rectSecond.x);

                // 另一種表示法
                //rectROISecondSrc = cv::Rect(0, stClipInfo.rectSecond.y, 0, (matImgSecond.rows - stClipInfo.rectSecond.y));
                //int nMoreWidth = (stClipInfo.rectFirst.x - stClipInfo.rectSecond.x) + matImgSecond.cols - matBig.cols;
                //rectROISecondSrc.width = (nMoreWidth > 0) ? (matImgSecond.cols - nMoreWidth) : matImgSecond.cols;

                rectROISecondDst = cv::Rect((stClipInfo.rectFirst.x - stClipInfo.rectSecond.x), stClipInfo.rectFirst.y, rectROISecondSrc.width, rectROISecondSrc.height);
            }
            matImgSecond(rectROISecondSrc).copyTo(matBig(rectROISecondDst));

            matResult = matBig;
        }
    }
}

接下來我們找一張圖,把它分割為3x3的小圖武花,并模擬網(wǎng)格化拍攝圆凰,保證每張圖有相同的視野,相同的重疊區(qū)域体箕。分割的代碼如下:

{
    cv::Mat matImgBase = cv::imread("full.jpg");

    // 模擬相機专钉,切分出3x3的視野圖
    int nPerKernelWidth = (matImgBase.cols - 400) / 3;
    int nPerKernelHeight = (matImgBase.rows - 400) / 3;

    // 截圖
    for (int i = 0; i < 3; ++i)
    {
        for (int j = 0; j < 3; ++j)
        {
            cv::Rect rectROI;
            rectROI.x = 100 * (j + 1) + j * nPerKernelWidth - 100;
            rectROI.y = 100 * (i + 1) + i * nPerKernelHeight - 100;
            rectROI.width = 100 + nPerKernelWidth + 100;
            rectROI.height = 100 + nPerKernelHeight + 100;

            char sbuf[32] = { 0 };
            snprintf(sbuf, sizeof(sbuf), "part%dx%d.jpg", i, j);
            cv::imwrite(sbuf, matImgBase(rectROI));
        }
    }
}

大圖如下:


full.jpg

分割的小圖效果如下:


image.png

然后應(yīng)用卡扣拼接算法挑童,對這3x3的小圖進行拼接,代碼如下:

{
    // 加載網(wǎng)格化小圖
    std::vector< std::vector<cv::Mat> > matrixMat;
    matrixMat.resize(3);
    for (int i = 0; i < 3; ++i)
    {
        matrixMat[i].resize(3);
        for (int j = 0; j < 3; ++j)
        {
            char sbuf[32] = { 0 };
            snprintf(sbuf, sizeof(sbuf), "part%dx%d.jpg", i, j);

            matrixMat[i][j] = cv::imread(sbuf);
        }
    }

    // 網(wǎng)格化遍歷
    cv::Mat matFull;
    for (int i = 0; i < 3; ++i)
    {
        cv::Mat matLine;

        // 行內(nèi)遍歷
        for (int j = 0; j < 3; ++j)
        {
            if (matLine.empty())
            {
                matLine = matrixMat[i][j];
                continue;
            }

            // 行內(nèi)水平卡扣
            dakuang::SClipInfo stClipInfo;
            dakuang::calcClipInfo(matLine, matrixMat[i][j], dakuang::CLIP_HORIZONTAL, stClipInfo);
            dakuang::joinTwoImage(matLine, matrixMat[i][j], dakuang::CLIP_HORIZONTAL, stClipInfo, matLine);
        }

        if (matFull.empty())
        {
            matFull = matLine;
            continue;
        }

        // 行間垂直卡扣
        dakuang::SClipInfo stClipInfo;
        dakuang::calcClipInfo(matFull, matLine, dakuang::CLIP_VERTICALITY, stClipInfo);
        dakuang::joinTwoImage(matFull, matLine, dakuang::CLIP_VERTICALITY, stClipInfo, matFull);
    }

    cv::imwrite("full2.jpg", matFull);
}

拼接后的效果如下:


full2.jpg
性能優(yōu)化:
1. 關(guān)于雙圖拼接時的卡扣計算優(yōu)化

在卡扣計算函數(shù)calcClipInfo()中,如果第一張圖的尺寸過大,如水平拼接時長度過長充易,會消耗無端的資源來計算灰度值,可以優(yōu)化為先扣出模板尽楔,再對模板進行灰度運算,并且對第二張圖玉雾,也只對左端/上端感興趣區(qū)域進行灰度運算翔试,然后執(zhí)行模板匹配轻要。

2. 關(guān)于雙圖拼接過程中圖像矩陣的內(nèi)存分配問題

隨著拼接過程不斷地進行复旬,會反復(fù)執(zhí)行下面的過程:
a) 創(chuàng)建兩張圖的合并矩陣
b) 復(fù)制第一張圖到合并矩陣
c) 復(fù)制第二張圖到合并矩陣
圖示如下:


image.png

在這些過程中,每個過程都會重新創(chuàng)建新的矩陣內(nèi)存冲泥,而且隨著拼接的進行驹碍,內(nèi)存會越來越大,系統(tǒng)開銷會越來越重凡恍。優(yōu)化思路為:
一次性創(chuàng)建出所有合并后大圖尺寸志秃,由于不清楚真實的尺寸大小,可以創(chuàng)建得大一些嚼酝,以后每次的合并結(jié)果不再創(chuàng)建新的矩陣浮还,而是在這個大矩陣上做ROI映射。因此需修改calcClipInfo()和joinTwoImage()這兩個函數(shù)闽巩,傳能時同時傳入第一張圖的ROI矩陣和整個大矩陣钧舌,并且在拼接處理時就不再需要復(fù)制第一張圖了,只需要把第二張圖復(fù)制上去涎跨,最后返回合并后的感興趣矩陣洼冻。
圖示如下:


image.png

這種方法也可以推廣到整個網(wǎng)格大圖上,大致思路是先分配網(wǎng)格大矩陣隅很,然后逐行分配行矩陣執(zhí)行行拼接撞牢,一行完成后再粘巾到網(wǎng)格大內(nèi)存上。

內(nèi)存優(yōu)化:

在上面的過程中叔营,由于將整個網(wǎng)格完整地分配了內(nèi)存屋彪,如果網(wǎng)格數(shù)量比較大,且網(wǎng)格圖的分辨率比較高绒尊,例如2048x2048三通道格式畜挥,網(wǎng)格數(shù)為100x100,粗略計算需要120G以上的內(nèi)存垒酬,這是非常嚇人的數(shù)字砰嘁。有什么辦法可以解決這個問題呢件炉?
我們可以想想在做網(wǎng)絡(luò)協(xié)議解幀的情景,每當(dāng)緩沖區(qū)收到新的數(shù)據(jù)時矮湘,我們就檢查是否收滿一幀斟冕,如果有就立即提取這個幀,使緩沖區(qū)永遠(yuǎn)保持在低水平長度缅阳。借鑒這個方法磕蛇,我們也可以檢查拼圖過程中,當(dāng)前已經(jīng)完成的拼接圖高度是否滿足一定條件十办,若滿足則把這部分提走秀撇,也使后續(xù)拼圖的高度維持在低水位。按照這個方法計算向族,如果設(shè)定每拼出完整一行就提取走呵燕,那么對內(nèi)存的要求將下降到3G以內(nèi)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末件相,一起剝皮案震驚了整個濱河市再扭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌夜矗,老刑警劉巖泛范,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異紊撕,居然都是意外死亡罢荡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進店門对扶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來区赵,“玉大人,你說我怎么就攤上這事辩稽【宓眩” “怎么了?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵逞泄,是天一觀的道長患整。 經(jīng)常有香客問我,道長喷众,這世上最難降的妖魔是什么各谚? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮到千,結(jié)果婚禮上昌渤,老公的妹妹穿的比我還像新娘。我一直安慰自己憔四,他們只是感情好膀息,可當(dāng)我...
    茶點故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布般眉。 她就那樣靜靜地躺著,像睡著了一般潜支。 火紅的嫁衣襯著肌膚如雪甸赃。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天冗酿,我揣著相機與錄音埠对,去河邊找鬼。 笑死裁替,一個胖子當(dāng)著我的面吹牛项玛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播弱判,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼襟沮,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了裕循?” 一聲冷哼從身側(cè)響起臣嚣,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎剥哑,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體淹父,經(jīng)...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡株婴,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了暑认。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片困介。...
    茶點故事閱讀 38,716評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖蘸际,靈堂內(nèi)的尸體忽然破棺而出座哩,到底是詐尸還是另有隱情,我是刑警寧澤粮彤,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布根穷,位于F島的核電站,受9級特大地震影響导坟,放射性物質(zhì)發(fā)生泄漏屿良。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一惫周、第九天 我趴在偏房一處隱蔽的房頂上張望尘惧。 院中可真熱鬧,春花似錦递递、人聲如沸喷橙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽贰逾。三九已至饥臂,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間似踱,已是汗流浹背隅熙。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留核芽,地道東北人囚戚。 一個月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓,卻偏偏與公主長得像轧简,于是被迫代替她去往敵國和親驰坊。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,612評論 2 350

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