iOS OpenCV:詳解人臉識(shí)別原理(二)

人臉識(shí)別

上篇 iOS-OpenCV筆記:實(shí)現(xiàn)簡(jiǎn)單的人臉識(shí)別(一)著重介紹了OpenCV的基本知識(shí)和在iOS上的編譯過程泊交,本篇將通過代碼和API了解整個(gè)人臉的識(shí)別過程市框。

人臉識(shí)別主要分兩部分:

我將這兩部分的功能分別實(shí)現(xiàn)在這兩個(gè)類下:

  • HVFaceDetectorUtil:負(fù)責(zé)檢測(cè)和收集人臉
  • HVFaceRecognizerUitl:負(fù)責(zé)識(shí)別人臉

一哈踱、檢測(cè)人臉

iPhone通過攝像頭獲取到視頻流望抽,對(duì)每一幀的圖片持續(xù)進(jìn)行檢測(cè)溪窒,來捕捉到圖片中人臉的區(qū)域夯接。

  1. 首先通過 HVFaceDetectorUtil 類的初始化獲取 CvVideoCamera *videoCamera 屬性的實(shí)例闲擦,并設(shè)置代理慢味,再加載工程中的訓(xùn)練好的 HaarCascade xml 文件,創(chuàng)建人臉和眼睛檢測(cè)的Haar分類器:
#ifdef __cplusplus
#import <opencv2/opencv.hpp>
#endif
@interface HVFaceDetectorUtil()<CvVideoCameraDelegate>
{
    cv::CascadeClassifier _faceDetector;
    cv::CascadeClassifier _eyesDetector;
    
    std::vector<cv::Rect> _faceRects;
    std::vector<cv::Mat> _faceImgs;
}

@property (nonatomic, retain) CvVideoCamera *videoCamera;
@property (nonatomic, assign) CGFloat scale;
@end

@implementation HVFaceDetectorUtil

- (instancetype)initWithParentView:(UIImageView *)parentView scale:(CGFloat)scale
{
    self = [super init];
    if (self) {
        
        _videoCamera = [[CvVideoCamera alloc] initWithParentView:parentView];
        _videoCamera.defaultAVCaptureDevicePosition = AVCaptureDevicePositionBack;
        _videoCamera.defaultAVCaptureSessionPreset = AVCaptureSessionPreset640x480;
        _videoCamera.defaultAVCaptureVideoOrientation = AVCaptureVideoOrientationPortrait;
        _videoCamera.defaultFPS = 30;
        _videoCamera.grayscaleMode = NO;
        _videoCamera.delegate = self;
        _scale = scale;
               
        //加載項(xiàng)目中訓(xùn)練好的Haar分類器
        //正面臉部Haar分類器
        NSString *faceCascadePath = [[NSBundle mainBundle]
                                     pathForResource:@"haarcascade_frontalface_alt2"
                                     ofType:@"xml"];
        
        _faceDetector.load([faceCascadePath UTF8String]);
        
        //眼睛部位Haar分類器
        NSString *eyesCascadePath = [[NSBundle mainBundle]
                                     pathForResource:@"haarcascade_eye_tree_eyeglasses"
                                     ofType:@"xml"];
        
        _eyesDetector.load([eyesCascadePath UTF8String]);
        
    }
    
    return self;
}

- (void)startCapture
{
    [self.videoCamera start];
}

- (void)stopCapture
{
    [self.videoCamera stop];
}
  • Haar Cascade常用來做人臉檢測(cè)佛致,其實(shí)它可以檢測(cè)任何對(duì)象贮缕。
  • OpenCV 項(xiàng)目源碼中有很多訓(xùn)練好的Haar分類器,它們?cè)?/oenncv/data/haarcascades 文件夾路徑中可以找到如下:


    Haar Cascade list
  1. 然后實(shí)現(xiàn) CvVideoCamera *videoCamera 的代理函數(shù)- (void)processImage:(cv::Mat&)image俺榆,對(duì)每一幀的圖片進(jìn)行檢測(cè):
  • 攝像頭的幀率被設(shè)置為30幀每秒感昼,實(shí)現(xiàn)的 processImage 函數(shù)將每秒被調(diào)用30次。
  • 因?yàn)橐掷m(xù)不斷地檢測(cè)人臉罐脊,所以在這個(gè)函數(shù)里實(shí)現(xiàn)人臉的檢測(cè)定嗓。
  • 要注意的是,如果對(duì)某一幀進(jìn)行人臉檢測(cè)的時(shí)間超過 1/30 秒萍桌,就會(huì)產(chǎn)生掉幀現(xiàn)象宵溅。
#pragma mark - Protocol CvVideoCameraDelegate
- (void)processImage:(cv::Mat &)image {
    // Do some OpenCV stuff with the image
    
    [self detectAndDrawFacesOn:image scale:self.scale];
}
- (void)detectAndDrawFacesOn:(cv::Mat&)img scale:(double) scale
{
    int i = 0;
    double t = 0;
    //劃線顏色數(shù)組
    const static cv::Scalar colors[] =  { CV_RGB(0,0,255),
        CV_RGB(0,128,255),
        CV_RGB(0,255,255),
        CV_RGB(0,255,0),
        CV_RGB(255,128,0),
        CV_RGB(255,255,0),
        CV_RGB(255,0,0),
        CV_RGB(255,0,255)} ;
  
    cv::Mat gray, smallImg( cvRound (img.rows/scale), cvRound(img.cols/scale), CV_8UC1 );
    
    //將圖片轉(zhuǎn)成灰度圖
    cvtColor( img, gray, cv::COLOR_BGR2GRAY );
    
    ////修改圖片尺寸,壓縮成小圖
    resize( gray, smallImg, smallImg.size(), 0, 0, cv::INTER_LINEAR );
    //直方圖均衡化︰ 在低光照條件下的人臉檢測(cè)是不可靠的上炎,所以我們應(yīng)該執(zhí)行直方圖均衡化
    equalizeHist( smallImg, smallImg );
    
    //開啟時(shí)間計(jì)時(shí)器
    t = (double)cvGetTickCount();
    
    //決定每次遍歷分類器后尺度會(huì)變大多少倍
    double scalingFactor = 1.1;
    
    //指定一個(gè)符合條件的人臉區(qū)域應(yīng)該有多少個(gè)符合條件的鄰居像素才被認(rèn)為是一個(gè)可能的人臉區(qū)域恃逻,
    //擁有少于 minNeighbors 個(gè)符合條件的鄰居像素的人臉區(qū)域會(huì)被拒絕掉。
    int minNeighbors = 2;
    
    //設(shè)定檢測(cè)人臉區(qū)域范圍的最小值
    cv::Size minSize(30,30);
    //設(shè)定檢測(cè)人臉區(qū)域范圍的最大值
    cv::Size maxSize(280,280);
    
    //通過檢測(cè)輸入不同大小的圖像藕施,獲取被檢測(cè)到的圖像列表 
    //圖像對(duì)象會(huì)作為一個(gè)矩形列表返回:self->_faceRects寇损。
    self->_faceDetector.detectMultiScale(smallImg, self->_faceRects,
                                         scalingFactor, minNeighbors, 0,
                                         minSize,maxSize);
    
    //計(jì)算檢測(cè)所花費(fèi)的時(shí)間
    t = (double)cvGetTickCount() - t;
    // printf( "detection time = %g ms\n", t/((double)cvGetTickFrequency()*1000.) );
    
    std::vector<cv::Mat> faceImages;
    
    for( std::vector<cv::Rect>::const_iterator r = _faceRects.begin(); r != _faceRects.end(); r++, i++ )
    {
        cv::Mat smallImgROI;
        cv::Point center;
        cv::Scalar color = colors[i%8];
        std::vector<cv::Rect> nestedObjects;
        
        //畫正方形
        rectangle(img,
                  cvPoint(cvRound(r->x*scale), cvRound(r->y*scale)),
                  cvPoint(cvRound((r->x + r->width-1)*scale), cvRound((r->y + r->height-1)*scale)),
                  color, 1, 8, 0);
        
        //eye detection is pretty low accuracy
        if(self->_eyesDetector.empty())
            continue;
        
        
        smallImgROI = smallImg(*r);
        
        faceImages.push_back(smallImgROI.clone());
        
        //檢測(cè)眼睛
        self->_eyesDetector.detectMultiScale( smallImgROI,
                                             nestedObjects,
                                             1.1, 2, 0,
                                             cv::Size(1, 1) );
        
        //將檢測(cè)到的眼睛畫圓
        for( std::vector<cv::Rect>::const_iterator nr = nestedObjects.begin(); nr != nestedObjects.end(); nr++ )
        {
            center.x = cvRound((r->x + nr->x + nr->width*0.5)*scale);
            center.y = cvRound((r->y + nr->y + nr->height*0.5)*scale);
            int radius = cvRound((nr->width + nr->height)*0.25*scale);
            circle( img, center, radius, color, 1, 8, 0 );
        }
     }
    @synchronized(self) {
        self->_faceImgs = faceImages;
    }
}
  1. 下面我們來詳細(xì)研究一下獲取檢測(cè)圖像列表的關(guān)鍵函數(shù) detectMultiScale ,以及它所需傳入的參數(shù)定義:
//通過檢測(cè)輸入不同大小的圖像裳食,獲取被檢測(cè)到的圖像列表 
//檢測(cè)出的對(duì)象會(huì)作為一個(gè)矩形列表返回:objects矛市。
CV_WRAP void detectMultiScale( InputArray image,
                          CV_OUT std::vector<Rect>& objects,
                          double scaleFactor = 1.1,
                          int minNeighbors = 3, int flags = 0,
                          Size minSize = Size(),
                          Size maxSize = Size() );
  • @param image:CV_8U類型的圖像矩陣,待檢測(cè)圖片诲祸,一般為灰度圖像浊吏,加快檢測(cè)速度而昨。
  • @param objects:包含所有被檢測(cè)出的圖像的矩形列表,這些矩形可能部分位于原始圖像之外找田。
  • @param scaleFactor:指定每次遍歷分類器后每張圖像尺度的縮放大小歌憨。
  • @param minNeighbors:指定符合條件的圖像區(qū)域應(yīng)該有多少個(gè)符合條件的相鄰像素,才被認(rèn)為是一個(gè)可能的圖像區(qū)域午阵。
  • @param flags:參數(shù) flags 是 OpenCV 1.x 版本 API 的遺留物躺孝,應(yīng)該始終把它設(shè)置為 0。
  • @param minSize: 檢測(cè)可能圖像的最小范圍底桂。小于該范圍的圖像會(huì)被忽略植袍。
  • @param maxSize:檢測(cè)可能圖像的最大范圍。超過該范圍的圖像被忽略籽懦。如果“maxSize == minSize”則視為同一個(gè)范圍于个。

二、識(shí)別人臉

上篇介紹過 OpenCV 自帶了三個(gè)人臉識(shí)別算法:Eigenfaces暮顺,F(xiàn)isherfaces 和LBPH(局部二值模式直方圖)厅篓。

下面我們看一下它們的關(guān)系:

Eigenfaces,F(xiàn)isherfaces 繼承自 BasicFaceRecognizer捶码,
BasicFaceRecognizer 再繼承自 FaceRecognizer羽氮,
而 LBPH 直接繼承自 FaceRecognizer,
cv::Algorithm 是這些算法的抽象基類惫恼。


3種算法關(guān)系圖
區(qū)別:
  • Eigenfaces档押,F(xiàn)isherfaces 直接使用所有的像素來進(jìn)行人臉識(shí)別,而 LBPH 采用的是提取局部特征祈纯。
  • Eigenfaces令宿,F(xiàn)isherfaces 為了獲取良好的識(shí)別率,至少每個(gè)人需要8張左右的圖像來訓(xùn)練腕窥。
  • LBPH 可以根據(jù)用戶的輸入自動(dòng)更新粒没,而不需要在每添加一個(gè)人或糾正一次出錯(cuò)的判斷的時(shí)候都要重新進(jìn)行一次徹底的訓(xùn)練

1. LBP理論基礎(chǔ)

Local Binary Patterns 的基本思想是通過比較每個(gè)像素與其鄰域來總結(jié)圖像中的局部結(jié)構(gòu)。以一個(gè)像素為中心簇爆,并對(duì)其鄰居進(jìn)行限制癞松。如果中心像素的強(qiáng)度大于等于其鄰居,那么用1表示它入蛆,否則用0表示响蓉。就像每個(gè)像素一樣,你最終會(huì)得到一個(gè)二進(jìn)制數(shù)安寺。
因此厕妖,對(duì)于8個(gè)周圍的像素首尼,最終會(huì)有2 ^ 8個(gè)可能的組合挑庶,稱為局部二進(jìn)制模式或有時(shí)稱為L(zhǎng)BP代碼言秸。

原始的LBP算子定義為一個(gè)固定的3×3鄰域,鄰域內(nèi)的8個(gè)點(diǎn)經(jīng)比較可產(chǎn)生8位二進(jìn)制數(shù)(通常轉(zhuǎn)換為十進(jìn)制數(shù)即LBP碼迎捺,共256種)举畸,即得到該鄰域中心像素點(diǎn)的LBP值,并用這個(gè)值來反映該區(qū)域的紋理特征凳枝。如下圖所示:

原始的LBPg
LBP的改進(jìn)版本:

原始的LBP提出后抄沮,研究人員不斷對(duì)其提出了各種改進(jìn)和優(yōu)化。

1.1 圓形LBP算子

基本的 LBP算子的最大缺陷在于它只覆蓋了一個(gè)固定半徑范圍內(nèi)的小區(qū)域岖瑰,這顯然不能滿足不同尺寸和頻率紋理的需要叛买。為了適應(yīng)不同尺度的紋理特征,Ojala等對(duì)LBP算子進(jìn)行了改進(jìn)蹋订,將3×3鄰域擴(kuò)展到任意鄰域率挣,并用圓形鄰域代替了正方形鄰域,改進(jìn)后的LBP算子允許在半徑為R的圓形鄰域內(nèi)有任意多個(gè)像素點(diǎn)露戒,從而得到了諸如半徑為R的圓形區(qū)域內(nèi)含有P個(gè)采樣點(diǎn)的LBP算子椒功,OpenCV中正是使用圓形LBP算子,下圖示意了圓形LBP算子:

圓形LBP算子
1.2 旋轉(zhuǎn)不變模式

從LBP的定義可以看出智什,LBP算子是灰度不變的动漾,但卻不是旋轉(zhuǎn)不變的,圖像的旋轉(zhuǎn)就會(huì)得到不同的LBP值荠锭。Maenpaa等人又將LBP算子進(jìn)行了擴(kuò)展旱眯,提出了具有旋轉(zhuǎn)不變性的LBP算子,即不斷旋轉(zhuǎn)圓形鄰域得到一系列初始定義的LBP值节沦,取其最小值作為該鄰域的LBP值键思。下圖給出了求取旋轉(zhuǎn)不變LBP的過程示意圖,圖中算子下方的數(shù)字表示該算子對(duì)應(yīng)的LBP值甫贯,圖中所示的8種LBP模式吼鳞,經(jīng)過旋轉(zhuǎn)不變的處理,最終得到的具有旋轉(zhuǎn)不變性的LBP值為15叫搁。也就是說赔桌,圖中的8種LBP模式對(duì)應(yīng)的旋轉(zhuǎn)不變的LBP碼值都是00001111。

旋轉(zhuǎn)不變LBP

根據(jù)定義渴逻,LBP算子對(duì)單調(diào)灰度變換具有健壯性疾党。我們可以通過查看人工修改圖像的LBP圖像來輕松驗(yàn)證這一點(diǎn):
lbp_yale.jpg

二·、使用LBPH識(shí)別人臉

LBPH 繼承自 FaceRecognizer惨奕,F(xiàn)aceRecognizer 實(shí)際是通過生成本地的 model.xml 文件進(jìn)行 read雪位, write,update梨撞,predict雹洗。我們可以在#import <opencv2/face.hpp>頭文件看到這幾個(gè)主要的函數(shù)的具體使用香罐。

  1. 首先我們創(chuàng)建 HVFaceRecognizerUitl 初始化函數(shù),在函數(shù)中創(chuàng)建實(shí)例 Ptr<LBPHFaceRecognizer> _faceRecognizer
  2. 創(chuàng)建和實(shí)現(xiàn) read时肿, write庇茫,update,predict 函數(shù)螃成。
    具體代碼實(shí)現(xiàn)如下:
#ifdef __cplusplus
#import <opencv2/opencv.hpp>
#import <opencv2/face.hpp>
#endif
using namespace cv;
using namespace face;
@interface HVFaceRecognizerUtil()
{
    Ptr<LBPHFaceRecognizer> _faceRecognizer;
}
@property (nonatomic,strong) NSMutableDictionary *labelsDic;
@end

@implementation HVFaceRecognizerUtil

+ (HVFaceRecognizerUtil *)faceRecWithFile:(NSString *)path
{
  //OpenCV 3.X 之后的版本創(chuàng)建 LBPH 的實(shí)例由舊的方法
    createLBPHFaceRecognizer() 改為: 
    LBPHFaceRecognizer::create()旦签。
  
    HVFaceRecognizerUtil *faceRec = [HVFaceRecognizerUtil new];
    faceRec->_faceRecognizer = LBPHFaceRecognizer::create();
    
    NSFileManager *fm = [NSFileManager defaultManager];
    if (path && [fm fileExistsAtPath:path isDirectory:nil]) {
        [faceRec readFaceRecParamatersFromFile:path];
    }else
    {
        faceRec.labelsDic = [[NSMutableDictionary alloc]init];
        NSLog(@"could not load paramaters file: %@", path);
    }
    return faceRec;
}

#pragma mark - FaceRec read/write
- (BOOL)readFaceRecParamatersFromFile:(NSString *)path
{
    self->_faceRecognizer->read(path.UTF8String);
    
    NSDictionary *unarchiverNames = [NSKeyedUnarchiver
                                     unarchiveObjectWithFile:[path stringByAppendingString:@".names"]];
    
    self.labelsDic = [NSMutableDictionary dictionaryWithDictionary:unarchiverNames];
    return YES;
}

- (BOOL)writeFaceRecParamatersToFile:(NSString *)path
{
    self->_faceRecognizer->write(path.UTF8String);
    [NSKeyedArchiver archiveRootObject:self.labelsDic toFile:[path stringByAppendingString:@".names"]];
    return YES;
}


#pragma mark - FaceRec predict/update
//根據(jù)臉部圖片的灰度圖匹配出對(duì)應(yīng)的標(biāo)簽,通過對(duì)應(yīng)的標(biāo)簽獲取人名
- (NSString *)predict:(UIImage *)image confidence:(double *)confidence
{
    //原圖轉(zhuǎn)成灰度圖
    cv::Mat src = [UIImage cvMatGrayFromUIImage:image];
    int label;
    
    //@param src:樣本圖像得到一個(gè)預(yù)測(cè)寸宏。
    //@param label:給定的圖像標(biāo)記預(yù)測(cè)的標(biāo)簽宁炫。
    //@param confidence:預(yù)測(cè)的置信度(例如距離)。
    self->_faceRecognizer->predict(src, label, *confidence);
    
    //返回標(biāo)簽對(duì)應(yīng)的人名
    return self.labelsDic[@(label)];
}

- (void)updateFace:(UIImage *)faceImg name:(NSString *)name
{
    //原圖轉(zhuǎn)成灰度圖
    cv::Mat src = [UIImage cvMatGrayFromUIImage:faceImg];
    
    NSSet *keys = [self.labelsDic keysOfEntriesPassingTest:^BOOL(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
        return [name isEqual:obj];
    }];
    
    NSInteger label;
    if (keys.count) {
        label = [[keys anyObject] integerValue];
    }else
    {
        label = self.labelsDic.allKeys.count;
        [self.labelsDic setObject:name forKey:@(label)];
    }
    
    std::vector<Mat> newImages = std::vector<cv::Mat>();;
    std::vector<int> newLabels = std::vector<int>();
    newImages.push_back(src);
    newLabels.push_back((int)label);
    
    _faceRecognizer->update(newImages, newLabels);
    
    [self labels];
}

- (NSArray *)labels
{
    cv::Mat labels = _faceRecognizer->getLabels();
    if (labels.total() == 0) {
        return @[];
    }
    else {
        NSMutableArray *mutableArray = [NSMutableArray array];
        for (MatConstIterator_<int> itr = labels.begin<int>(); itr != labels.end<int>(); ++itr ) {
            int lbl = *itr;
            [mutableArray addObject:@(lbl)];
        }
        return [NSArray arrayWithArray:mutableArray];
    }
}
  1. HVFaceRecognizerUitl 實(shí)現(xiàn)在識(shí)別人臉的視圖 HVFaceRecViewController氮凝,通過按鈕對(duì)識(shí)別的結(jié)果確認(rèn)和修正:
@interface HVFaceRecViewController ()

@property (weak, nonatomic) IBOutlet UILabel *nameLabel;
@property (weak, nonatomic) IBOutlet UILabel *confidenceLabel;
@property (weak, nonatomic) IBOutlet UIImageView *inputImageView;
@property (nonatomic, strong) HVFaceRecognizerUtil *faceModel;
@end

@implementation HVFaceRecViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    _inputImageView.image = _inputImage;
    NSString *modelPath = [self faceModelFilePath];
    self.faceModel = [HVFaceRecognizerUtil faceRecWithFile:modelPath];
    
    if (_faceModel.labels.count == 0) {
        [_faceModel updateFace:_inputImage name:@"朱茵"];
    }
    
    double confidence;
    NSString *name = [_faceModel predict:_inputImage confidence:&confidence];
    
    _nameLabel.text = name;
    _confidenceLabel.text = [@(confidence) stringValue];
}

- (NSString *)faceModelFilePath {
    NSString *modelPath = [NSString pathFromFlieName:@"face-model.xml"];
    NSLog(@">>> modelPath[face-model.xml] = %@ ",modelPath);
    return modelPath;
}

- (IBAction)didTapCorrect:(id)sender {
    //Positive feedback for the correct prediction
    [_faceModel updateFace:_inputImage name:_nameLabel.text];
    [_faceModel writeFaceRecParamatersToFile:[self faceModelFilePath]];
    [self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
}

- (IBAction)didTapWrong:(id)sender {
    //Update our face model with the new person
//    NSString *name = [@"Person " stringByAppendingFormat:@"%lu", (unsigned long)_faceModel.labels.count];
    
    NSString *name = @"至尊寶";
    [_faceModel updateFace:_inputImage name:name];
    [_faceModel writeFaceRecParamatersToFile:[self faceModelFilePath]];
    [self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
}
詳細(xì)代碼已上傳到我的GitHub:

OpenCV-iOS-FaceRecDemo

注:Demo不包含opencv2.framework淋淀,我手動(dòng)編譯的 Opencv+Contrib 庫版本為 3.4.1,大約407MB上傳不了覆醇,Git上傳單個(gè)文件只允許<100MB朵纷,所以你可以在這個(gè)地址下載我編譯好的庫:Opencv+Contrib-3.4.1,如有遇到問題永脓,請(qǐng)留言袍辞。

上一篇: iOS-OpenCV筆記:實(shí)現(xiàn)簡(jiǎn)單的人臉識(shí)別(一)

參考資料:
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市常摧,隨后出現(xiàn)的幾起案子搅吁,更是在濱河造成了極大的恐慌,老刑警劉巖落午,帶你破解...
    沈念sama閱讀 221,198評(píng)論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谎懦,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡溃斋,警方通過查閱死者的電腦和手機(jī)界拦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來梗劫,“玉大人享甸,你說我怎么就攤上這事∈崆龋” “怎么了蛉威?”我有些...
    開封第一講書人閱讀 167,643評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)走哺。 經(jīng)常有香客問我蚯嫌,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,495評(píng)論 1 296
  • 正文 為了忘掉前任择示,我火速辦了婚禮妒牙,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘对妄。我一直安慰自己,他們只是感情好敢朱,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評(píng)論 6 397
  • 文/花漫 我一把揭開白布剪菱。 她就那樣靜靜地躺著,像睡著了一般拴签。 火紅的嫁衣襯著肌膚如雪孝常。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,156評(píng)論 1 308
  • 那天蚓哩,我揣著相機(jī)與錄音构灸,去河邊找鬼。 笑死岸梨,一個(gè)胖子當(dāng)著我的面吹牛喜颁,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播曹阔,決...
    沈念sama閱讀 40,743評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼半开,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了赃份?” 一聲冷哼從身側(cè)響起寂拆,我...
    開封第一講書人閱讀 39,659評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎抓韩,沒想到半個(gè)月后纠永,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,200評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡谒拴,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評(píng)論 3 340
  • 正文 我和宋清朗相戀三年尝江,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片英上。...
    茶點(diǎn)故事閱讀 40,424評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡茂装,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出善延,到底是詐尸還是另有隱情少态,我是刑警寧澤,帶...
    沈念sama閱讀 36,107評(píng)論 5 349
  • 正文 年R本政府宣布易遣,位于F島的核電站彼妻,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜侨歉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評(píng)論 3 333
  • 文/蒙蒙 一屋摇、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧幽邓,春花似錦炮温、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至畸颅,卻和暖如春担巩,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背没炒。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評(píng)論 1 271
  • 我被黑心中介騙來泰國打工涛癌, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人送火。 一個(gè)月前我還...
    沈念sama閱讀 48,798評(píng)論 3 376
  • 正文 我出身青樓拳话,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親种吸。 傳聞我的和親對(duì)象是個(gè)殘疾皇子假颇,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評(píng)論 2 359