基于虹软服务器版本(Linux Pro)的人脸识别服务搭建

项目背景

在人类社会的快速发展中,科技正深刻地影响着各行各业,其中人工智能技术尤为突出。特别是人脸识别技术,因其高效、直观和便捷的特性,在金融、安防、医疗、零售等多个领域得到了广泛应用。随着技术的逐步成熟,人脸识别正在从基础的身份验证功能,拓展到更深层次的场景需求中,例如行为分析、个性化服务以及高效管理等。
本项目的背景基于当下对智能化、高效化需求的日益增长。传统身份认证方式(如卡片、密码)存在安全性低、易被遗忘或伪造的问题,已无法满足某些场景对高安全性与无感化体验的双重要求。为此,本项目旨在开发一个服务端人脸识别系统,专注于实时、高效的人脸检测与识别能力,为目标行业(如智能安防、医疗管理或企业考勤)提供技术支持和服务优化。

环境

服务器:wsl Ubuntu 22.04.3
开发语言(框架):java SpringBoot
SDK版本:Linux Pro

集成

1、下载sdk
2、将arcsoft-sdk-face-server-1.0.0.0.jar 放入工程lib文件夹
3、引入jar包

    <dependency>
            <groupId>arcsoft-sdk-face-server</groupId>
            <artifactId>arcsoft-sdk-face-server</artifactId>
            <version>1.0.0.0</version>
            <scope>system</scope>
            <systemPath>${pom.basedir}/lib/arcsoft-sdk-face-server-1.0.0.0.jar</systemPath>
        </dependency>

4、将libarcsoft_face.so、libarcsoft_face_engine.so、libarcsoft_face_engine_jni.so放入服务器/usr/local/arcsoft_lib目录

引擎池

引擎池的优势:
(1)为了避免频繁创建和销毁引擎实例带来的开销
(2)支持高并发任务处理,减少系统瓶颈
(3)减少初始化时间,提高系统响应速度。
引擎池注意事项:
(1)根据系统的并发需求、可用资源(如内存和CPU)以及引擎实例的开销,合理设置池的最小值和最大值,避免浪费系统资源。
(2)每次使用完毕,需及时正确归还引擎,避免资源被长期占用。

1、创建AFaceEngine继承FaceEngine,用isCore来标识核心引擎,因为人脸注册到哪个引擎,识别就必须用哪个引擎,核心引擎即用于注册与识别。
(1)核心引擎:本文使用核心引擎来做人脸注册和比对,人脸注册时,使用哪个引擎注册的,人脸的特征数据就保存在哪个引擎中,注册人脸不适于随机使用引擎实例注册,否则识别时拿到的引擎若没有注册人脸,就会导致人脸识别不到的情况,本文示例核心引擎仅一个,如果需要多个核心引擎可根据本文逻辑自信扩展,注意多个核心引擎注册人脸时,每个核心引擎都需要注册。
(2)非核心引擎:非核心引擎可用于做一些与识别和注册无关的操作,例如人脸检测、人脸属性检测、特征值提取、图像质量检测、活体检测等操作。

public class AFaceEngine extends FaceEngine {
    private boolean isCore;

    public AFaceEngine(Boolean isCore,String libPath){
        super(libPath);
        this.isCore = isCore;
    }
    public AFaceEngine(Boolean isCore,String libPath,String appId, String sdkKey, String activeKey){
        super(libPath);
        this.isCore = isCore;
    }

    public boolean isCore() {
        return isCore;
    }
}

2、引擎工厂FaceEngineFactory,用于创建引擎。
创建引擎时,创建一个核心引擎(可根据业务量控制核心引擎数量)与多个普通引擎,并激活与初始化所有引擎,核心引擎用于注册、识别,普通引擎用于人脸检测、图像质量检测、活体检测、属性识别、特征提取等操作

  @Override
    public PooledObject<AFaceEngine> makeObject() {
        AFaceEngine faceEngine = new AFaceEngine(true,"/usr/local/arcsoft_lib");  // 创建核心对象
        faceEngine.activeOnline(appId,sdkKey,activeKey);
        faceEngine.setLivenessParam(0.5f, 0.7f);
        if (!coreObjectCreated) {
            //因为核心引擎仅做注册与识别,为了节省资源,故此只初始化最近本识别检测功能
            faceEngine.init(coreEngineConfiguration);
            coreObjectCreated = true;
            System.out.println("Created core FaceEngine.");
        } else {
            //非核心引擎要做人脸各项功能,故单独初始化所有功能
            faceEngine.init(engineConfiguration);
            System.out.println("Created regular FaceEngine.");
        }
        return new DefaultPooledObject<>(faceEngine);
    }

完整的FaceEngineFactory引擎工厂

class FaceEngineFactory implements PooledObjectFactory<AFaceEngine> {
    private boolean coreObjectCreated = false;
    private EngineConfiguration engineConfiguration;
    private EngineConfiguration coreEngineConfiguration;
    private String appId;
    private String sdkKey;
    private String activeKey;

    public FaceEngineFactory(EngineConfiguration engineConfiguration, EngineConfiguration coreEngineConfiguration, String appId, String sdkKey, String activeKey){
        super();
        this.engineConfiguration = engineConfiguration;
        this.coreEngineConfiguration = coreEngineConfiguration;
        this.appId = appId;
        this.sdkKey = sdkKey;
        this.activeKey = activeKey;
    }

    @Override
    public PooledObject<AFaceEngine> makeObject() {
        AFaceEngine faceEngine = new AFaceEngine(true,"/usr/local/arcsoft_lib");  // 创建核心对象
        faceEngine.activeOnline(appId,sdkKey,activeKey);
        faceEngine.setLivenessParam(0.5f, 0.7f);
        if (!coreObjectCreated) {
            //因为核心引擎仅做注册与识别,为了节省资源,故此只初始化最近本识别检测功能
            faceEngine.init(coreEngineConfiguration);
            coreObjectCreated = true;
            System.out.println("Created core FaceEngine.");
        } else {
            //非核心引擎要做人脸各项功能,故单独初始化所有功能
            faceEngine.init(engineConfiguration);
            System.out.println("Created regular FaceEngine.");
        }
        return new DefaultPooledObject<>(faceEngine);
    }

    @Override
    public void destroyObject(PooledObject<AFaceEngine> p) {
        p.getObject().unInit();
    }

    @Override
    public boolean validateObject(PooledObject<AFaceEngine> p) {
        return true;
    }

    @Override
    public void activateObject(PooledObject<AFaceEngine> p) {
        System.out.println("Activating FaceEngine.");
    }

    @Override
    public void passivateObject(PooledObject<AFaceEngine> p) {
        System.out.println("Passivating FaceEngine.");
    }

3、创建引擎池CustomFaceEnginePool,用于管理引擎,自定义borrowCoreObject方法,用于借用核心引擎,并设置maxWaitMillis超时时间。

 // 阻塞等待核心对象归还
    public AFaceEngine borrowCoreObject(long maxWaitMillis) throws Exception {
        AFaceEngine coreObject = null;
        long startTime = System.currentTimeMillis();
        // 不断尝试从池中借用对象
        while (System.currentTimeMillis() - startTime < maxWaitMillis) {
            coreObject = this.borrowObject();
            if (coreObject.isCore()) {
                return coreObject;  // 找到核心对象,返回
            } else {
                // 不是核心对象,立即归还并继续等待
                this.returnObject(coreObject);
            }
            Thread.sleep(100);
        }
        throw new Exception("Timed out waiting for core object.");
    }

为了避免引擎被借出未被正确归还,需设计一个最大借出时间,超时后系统强制归还引擎
记录借出时间:

 private final Map<AFaceEngine, Long> borrowedObjects = new ConcurrentHashMap<>();
    private static final long MAX_BORROW_TIME = 10000; // 最大借出时间 10s

  public AFaceEngine borrowObject() throws Exception {
        AFaceEngine engine = super.borrowObject();
        // 记录借出时间
        borrowedObjects.put(engine, System.currentTimeMillis());
        return engine;
    }

检查借出引擎是否超时,如果超时强制归还:

 /**
     * 监控借出的对象,检测超时
     */
    private void monitorBorrowedObjects() {
        while (true) {
            try {
                Thread.sleep(1000); // 每秒扫描一次
                long currentTime = System.currentTimeMillis();
                for (Map.Entry<AFaceEngine, Long> entry : borrowedObjects.entrySet()) {
                    long borrowedTime = currentTime - entry.getValue();
                    if (borrowedTime > MAX_BORROW_TIME) {
                        // 超时强制归还
                        AFaceEngine engine = entry.getKey();
                        System.err.println("Engine timed out, forcing return: " + engine);
                        borrowedObjects.remove(engine); // 从记录中移除
                        super.returnObject(engine); // 强制归还到池中
                    }
                }
            } catch (Exception e) {
                System.err.println("Error in monitor thread: " + e.getMessage());
            }
        }
    }

完整的引擎池CustomFaceEnginePool代码:

public class CustomFaceEnginePool extends GenericObjectPool<AFaceEngine> {
    // 记录借出对象及其借出时间
    private final Map<AFaceEngine, Long> borrowedObjects = new ConcurrentHashMap<>();
    private static final long MAX_BORROW_TIME = 10000; // 最大借出时间 10s

    public CustomFaceEnginePool(PooledObjectFactory<AFaceEngine> factory, GenericObjectPoolConfig<AFaceEngine> config) {
        super(factory, config);
        // 启动监控线程
        new Thread(this::monitorBorrowedObjects).start();
    }

    @Override
    public AFaceEngine borrowObject() throws Exception {
        AFaceEngine engine = super.borrowObject();
        // 记录借出时间
        borrowedObjects.put(engine, System.currentTimeMillis());
        return engine;
    }

    @Override
    public void returnObject(AFaceEngine obj) {
        // 移除借出记录
        borrowedObjects.remove(obj);
        super.returnObject(obj);
    }

    /**
     * 阻塞等待核心对象归还
     */
    public AFaceEngine borrowCoreObject(long maxWaitMillis) throws Exception {
        AFaceEngine coreObject = null;
        long startTime = System.currentTimeMillis();
        while (System.currentTimeMillis() - startTime < maxWaitMillis) {
            coreObject = this.borrowObject();
            if (coreObject.isCore()) {
                borrowedObjects.put(coreObject, System.currentTimeMillis());
                return coreObject;
            } else {
                this.returnObject(coreObject);
            }
            Thread.sleep(100);
        }
        throw new Exception("Timed out waiting for core object.");
    }

    /**
     * 监控借出的对象,检测超时
     */
    private void monitorBorrowedObjects() {
        while (true) {
            try {
                Thread.sleep(1000); // 每秒扫描一次
                long currentTime = System.currentTimeMillis();
                for (Map.Entry<AFaceEngine, Long> entry : borrowedObjects.entrySet()) {
                    long borrowedTime = currentTime - entry.getValue();
                    if (borrowedTime > MAX_BORROW_TIME) {
                        // 超时强制归还
                        AFaceEngine engine = entry.getKey();
                        System.err.println("Engine timed out, forcing return: " + engine);
                        borrowedObjects.remove(engine); // 从记录中移除
                        super.returnObject(engine); // 强制归还到池中
                    }
                }
            } catch (Exception e) {
                System.err.println("Error in monitor thread: " + e.getMessage());
            }
        }
    }
}

引擎准备完成,接下来就是使用引擎进行各项人脸操作,本示例实现了人脸注册、人脸识别、人脸属性检测三个功能,其余扩展功能都可根据本文提供的代码进行扩展,本文提供的各项人脸操作均为原子性接口,可根据实际需求自由组合。

引擎操作类

1、引擎初始化,根据文档配置引擎的各项功能与参数,调用引擎池进行初始化操作

public void init() {
        try {
            //引擎配置
            EngineConfiguration engineConfiguration = new EngineConfiguration();
            EngineConfiguration coreEngineConfiguration = new EngineConfiguration();

            //设置检测模式为image
            engineConfiguration.setDetectMode(DetectMode.ASF_DETECT_MODE_IMAGE);
            coreEngineConfiguration.setDetectMode(DetectMode.ASF_DETECT_MODE_IMAGE);
            //设置人脸角度为全角度
            engineConfiguration.setDetectFaceOrientPriority(DetectOrient.ASF_OP_ALL_OUT);
            coreEngineConfiguration.setDetectFaceOrientPriority(DetectOrient.ASF_OP_ALL_OUT);
            //设置可检测最大人脸数
            engineConfiguration.setDetectFaceMaxNum(1);
            coreEngineConfiguration.setDetectFaceMaxNum(1);
            //设置人脸识别的模型,ASF_REC_MIDDLE:中等模型,ASF_REC_LARGE:大模型
            engineConfiguration.setFaceModel(FaceModel.ASF_REC_MIDDLE);
            coreEngineConfiguration.setFaceModel(FaceModel.ASF_REC_MIDDLE);
            //功能配置
            FunctionConfiguration functionConfiguration = new FunctionConfiguration();
            //年龄检测
            functionConfiguration.setSupportAge(true);
            //人脸检测
            functionConfiguration.setSupportFaceDetect(true);
            //人脸识别
            functionConfiguration.setSupportFaceRecognition(true);
            //性别检测
            functionConfiguration.setSupportGender(true);
            //活体检测
            functionConfiguration.setSupportLiveness(true);
            //ir活体检测
            functionConfiguration.setSupportIRLiveness(true);
            //图像质量检测
            functionConfiguration.setSupportImageQuality(true);
            //口罩检测
            functionConfiguration.setSupportMaskDetect(true);
            engineConfiguration.setFunctionConfiguration(functionConfiguration);

            GenericObjectPoolConfig<AFaceEngine> config = new GenericObjectPoolConfig<>();
            config.setMaxTotal(5);
            facePool = new CustomFaceEnginePool(new FaceEngineFactory(engineConfiguration,coreEngineConfiguration,appId,sdkKey,activeKey), config);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

2、加载数据库中所有人脸到引擎

    public void loadAllFace(List<FaceFeatureInfo> faceFeatureInfoList) throws Exception {
        AFaceEngine faceEngine = facePool.borrowCoreObject(5000);
        int i = faceEngine.registerFaceFeature(faceFeatureInfoList);
        log.info("load all face  count:{}", +faceFeatureInfoList.size());
        log.info("load all face  res:{}", +i);
        facePool.returnObject(faceEngine);
    }

注意:此处使用的是facePool.borrowCoreObject(5000),借用核心引擎进行load face,将数据控中所有人脸注册到核心引擎中,使用完毕后立即调用facePool.returnObject(faceEngine);归还核心引擎。

3、人脸检测(普通引擎)

  //人脸检测
    public ArrayList<FaceInfo> detectFaces(ImageInfo imageInfo) throws Exception {
        ArrayList<FaceInfo> faceInfos = new ArrayList<>();
        AFaceEngine faceEngine = facePool.borrowObject();
        int detectFacesCode = faceEngine.detectFaces(imageInfo, faceInfos);
        log.info("人脸属性检测 res:{}", detectFacesCode);
        facePool.returnObject(faceEngine);
        return faceInfos;
    }

4、人脸图像质量检测,有些图像虽然可检测到人脸,但因为图像质量较差,无法提取到有效的人脸特征值,所以,提取特征值之前,建议检测图像质量,仅对达标的图像进行特征值提取,提高效率。

 //图像质量检测
    public float imageQuality(ImageInfo imageInfo, FaceInfo faceInfo) throws Exception {
        ImageQuality imageQuality = new ImageQuality();
        AFaceEngine faceEngine = facePool.borrowObject();
        int imageQualityCode =faceEngine.imageQualityDetect(imageInfo, faceInfo, 0, imageQuality);
        facePool.returnObject(faceEngine);
        log.info("图像质量检测 res:{}", imageQualityCode);
        log.info("图像质量检测分数:{}", imageQuality.getFaceQuality());
        return imageQuality.getFaceQuality();
    }

5、人脸属性检测

public FaceAttributesResponse faceAttributes(ImageInfo imageInfo, FaceInfo faceInfo) throws Exception {
        FaceAttributesResponse faceAttributesResponse = new FaceAttributesResponse();
        FunctionConfiguration configuration = new FunctionConfiguration();
        configuration.setSupportAge(true);
        configuration.setSupportGender(true);
        configuration.setSupportLiveness(true);
        configuration.setSupportMaskDetect(true);
        ArrayList<FaceInfo> faceInfos = new ArrayList<>();
        faceInfos.add(faceInfo);
        AFaceEngine faceEngine = facePool.borrowObject();
        int faceAttributesCode = faceEngine.process(imageInfo, faceInfos, configuration);
        log.info("图像属性处理errorCode:{}", faceAttributesCode);
        //性别检测
        List<GenderInfo> genderInfoList = new ArrayList<GenderInfo>();
        int genderCode = faceEngine.getGender(genderInfoList);
        log.info("性别 res:{}", +genderCode);
        log.info("性别:{}", +genderInfoList.get(0).getGender());
        faceAttributesResponse.setSex(genderInfoList.get(0).getGender() == 0?"男":"女");
        //年龄检测
        List<AgeInfo> ageInfoList = new ArrayList<AgeInfo>();
        int ageCode = faceEngine.getAge(ageInfoList);
        log.info("年龄 res:{}", +ageCode);
        log.info("年龄:{}", ageInfoList.get(0).getAge());
        faceAttributesResponse.setAge(String.valueOf(ageInfoList.get(0).getAge()));
        //rgb活体检测
        List<LivenessInfo> livenessInfoList = new ArrayList<LivenessInfo>();
        int livenCode = faceEngine.getLiveness(livenessInfoList);
        log.info("RGB活体 res:{}", +livenCode);
        log.info("活体:{}", livenessInfoList.get(0).getLiveness());
        faceAttributesResponse.setLive(livenessInfoList.get(0).getLiveness()==0?"非真人":"真人");
        //口罩检测
        List<MaskInfo> maskInfoList = new ArrayList<MaskInfo>();
        int maskCode = faceEngine.getMask(maskInfoList);
        log.info("口罩 res:{}", +maskCode);
        log.info("口罩:{}", +maskInfoList.get(0).getMask());
        faceAttributesResponse.setMask(maskInfoList.get(0).getMask()==0?"无口罩":"有口罩");
        facePool.returnObject(faceEngine);
        return faceAttributesResponse;
    }

6、IR活体检测,需要前端传入双目摄像头数据,包括RGB图像与IR图像,对IR图像进行人脸检测、活体检测,得到活体结果后需要判断与RGB图像人脸位置的重合度用于初步确认两张图像为同一张人脸(未实现)。

 public int irLivingDetect(ImageInfo irImg) throws Exception {
        AFaceEngine faceEngine = facePool.borrowObject();
        //IR属性处理
        List<FaceInfo> faceInfoListGray = new ArrayList<FaceInfo>();
        int irFaceDetectCode = faceEngine.detectFaces(irImg, faceInfoListGray);
        log.info("ir图像人脸检测 res:{}", +irFaceDetectCode);
        FunctionConfiguration configuration2 = new FunctionConfiguration();
        configuration2.setSupportIRLiveness(true);
        int irLivingCode = faceEngine.processIr(irImg, faceInfoListGray, configuration2);
        log.info("ir活体检测 res:{}", +irLivingCode);
        //IR活体检测
        List<IrLivenessInfo> irLivenessInfo = new ArrayList<>();
        int errorCode = faceEngine.getLivenessIr(irLivenessInfo);
        log.info("获取ir活体检测 res:{}", +errorCode);
        facePool.returnObject(faceEngine);
        return irLivenessInfo.get(0).getLiveness();
    }

7、注册人脸,此操作需使用核心引擎

  public int registerFace(FaceFeatureInfo faceFeatureInfo) throws Exception {
        AFaceEngine faceEngine = facePool.borrowCoreObject(5000);
        int i = faceEngine.registerFaceFeature(faceFeatureInfo);
        facePool.returnObject(faceEngine);
        return i;
    }

8、移除人脸,此处应增加数据库的移除操作

  public int removeFace(int searchId) throws Exception {
        AFaceEngine faceEngine = facePool.borrowCoreObject(5000);
        int i = faceEngine.removeFaceFeature(searchId);
        facePool.returnObject(faceEngine);
        return i;
    }

9、更新人脸,此处应新增数据的更新操作

public int updateFace(FaceFeatureInfo faceFeatureInfo) throws Exception {
        AFaceEngine faceEngine = facePool.borrowCoreObject(5000);
        int i = faceEngine.updateFaceFeature(faceFeatureInfo);
        facePool.returnObject(faceEngine);
        return i;
    }

10、人脸识别(搜索),此操作需使用核心引擎

 public SearchResult searchFace(FaceFeature faceFeature) throws Exception {
        AFaceEngine faceEngine = facePool.borrowCoreObject(5000);
        FaceSearchCount faceSearchCount = new FaceSearchCount();
        faceEngine.getFaceCount(faceSearchCount);
        log.info("引擎库face count:{}", +faceSearchCount.getCount());
        SearchResult searchResult = new SearchResult();
        int searchCode = faceEngine.searchFaceFeature(faceFeature, CompareModel.LIFE_PHOTO, searchResult);
        log.info("人脸搜索 res:{}", +searchCode);
        log.info("人脸搜索 sim:{}", +searchResult.getMaxSimilar());
        facePool.returnObject(faceEngine);
        return searchResult;
    }

注意:人脸识别需传入CompareModel识别模式,CompareModel.LIFE_PHOTO为生活照识别,即通用的人脸识别;CompareModel.ID_PHOTO为身份证照片识别,针对身份证照片进行了特殊优化,用于人证比对。
11、提取特征值

  public FaceFeature extractFaceFeature(ImageInfo imageInfo, FaceInfo faceInfo, ExtractType type) throws Exception {
        AFaceEngine faceEngine = facePool.borrowObject();
        FaceFeature faceFeature = new FaceFeature();
        int extractCode = faceEngine.extractFaceFeature(imageInfo, faceInfo, type, 0, faceFeature);
        log.info("特征提取 res:{}", extractCode);
        facePool.returnObject(faceEngine);
        return faceFeature;
    }

注意:特征值提取需要传入ExtractType,ExtractType.REGISTER表示此特征值将用于注册操作,ExtractType.RECOGNIZE表示此特征值将用于识别操作。

核心的引擎操作已经完成了,接下来就可以自由组合进行业务上的处理了

人脸注册(效果图)

image.png

人脸识别(效果图)

image.png

人脸属性检测(效果图)

image.png

项目地址:https://gitee.com/wanmaomao/arc-face-for-linux

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,458评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,030评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,879评论 0 358
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,278评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,296评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,019评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,633评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,541评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,068评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,181评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,318评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,991评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,670评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,183评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,302评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,655评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,327评论 2 358

推荐阅读更多精彩内容