Java版人臉檢測詳解下篇:編碼

歡迎訪問我的GitHub

https://github.com/zq2599/blog_demos

內(nèi)容:所有原創(chuàng)文章分類匯總及配套源碼种冬,涉及Java客燕、Docker庄撮、Kubernetes帕涌、DevOPS等;

本篇概覽

  • 如果您看過《三分鐘極速體驗:Java版人臉檢測》一文蔽豺,甚至動手實際操作過朦肘,您應(yīng)該會對背后的技術(shù)細(xì)節(jié)感興趣砍濒,開發(fā)這樣一個應(yīng)用,咱們總共要做以下三件事:
  1. 準(zhǔn)備好docker基礎(chǔ)鏡像
  2. 開發(fā)java應(yīng)用
  3. 將java應(yīng)用打包成package文件叫潦,集成到基礎(chǔ)鏡像中蝇完,得到最終的java應(yīng)用鏡像

版本信息

  • 這個java應(yīng)用的涉及的版本信息如下:
  1. springboot:2.4.8
  2. javacpp:1.4.3
  3. javacv:1.4.3

源碼下載

名稱 鏈接 備注
項目主頁 https://github.com/zq2599/blog_demos 該項目在GitHub上的主頁
git倉庫地址(https) https://github.com/zq2599/blog_demos.git 該項目源碼的倉庫地址,https協(xié)議
git倉庫地址(ssh) git@github.com:zq2599/blog_demos.git 該項目源碼的倉庫地址傻咖,ssh協(xié)議
  • 這個git項目中有多個文件夾朋魔,本篇的源碼在<font color="blue">javacv-tutorials</font>文件夾下,如下圖紅框所示:
在這里插入圖片描述

編碼

  • 為了統(tǒng)一管理源碼和jar依賴卿操,項目采用了maven父子結(jié)構(gòu)警检,父工程名為<font color="blue">javacv-tutorials</font>,其pom.xml如下害淤,可見主要是定義了一些jar的版本:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.bolingcavalry</groupId>
    <artifactId>javacv-tutorials</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>face-detect-demo</module>
    </modules>

    <properties>
        <java.version>1.8</java.version>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <maven-compiler-plugin.version>3.6.1</maven-compiler-plugin.version>
        <springboot.version>2.4.8</springboot.version>

        <!-- javacpp當(dāng)前版本 -->
        <javacpp.version>1.4.3</javacpp.version>
        <!-- opencv版本 -->
        <opencv.version>3.4.3</opencv.version>
        <!-- ffmpeg版本 -->
        <ffmpeg.version>4.0.2</ffmpeg.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.18</version>
            </dependency>

            <dependency>
                <groupId>org.bytedeco</groupId>
                <artifactId>javacv-platform</artifactId>
                <version>${javacpp.version}</version>
            </dependency>
            <dependency>
                <groupId>org.bytedeco</groupId>
                <artifactId>javacv</artifactId>
                <version>${javacpp.version}</version>
            </dependency>
            <!-- javacpp -->
            <dependency>
                <groupId>org.bytedeco</groupId>
                <artifactId>javacpp</artifactId>
                <version>${javacpp.version}</version>
            </dependency>
            <!-- ffmpeg -->
            <dependency>
                <groupId>org.bytedeco.javacpp-presets</groupId>
                <artifactId>ffmpeg-platform</artifactId>
                <version>${ffmpeg.version}-${javacpp.version}</version>
            </dependency>
            <dependency>
                <groupId>org.bytedeco.javacpp-presets</groupId>
                <artifactId>ffmpeg</artifactId>
                <version>${ffmpeg.version}-${javacpp.version}</version>
            </dependency>
        </dependencies>

    </dependencyManagement>
</project>
  • 在<font color="blue">javacv-tutorials</font>下面新建名為<font color="red">face-detect-demo</font>的子工程扇雕,這里面是咱們今天要開發(fā)的應(yīng)用,其pom.xml如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>javacv-tutorials</artifactId>
        <groupId>com.bolingcavalry</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>face-detect-demo</artifactId>
    <packaging>jar</packaging>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${springboot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <!--FreeMarker模板視圖依賴-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>javacv-platform</artifactId>
        </dependency>
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>javacv</artifactId>
        </dependency>
        <!-- javacpp -->
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>javacpp</artifactId>
        </dependency>
        <!-- ffmpeg -->
        <dependency>
            <groupId>org.bytedeco.javacpp-presets</groupId>
            <artifactId>ffmpeg-platform</artifactId>
        </dependency>
        <dependency>
            <groupId>org.bytedeco.javacpp-presets</groupId>
            <artifactId>ffmpeg</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- 如果父工程不是springboot窥摄,就要用以下方式使用插件镶奉,才能生成正常的jar -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <mainClass>com.bolingcavalry.facedetect.FaceDetectApplication</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>
  • 配置文件如下,要重點關(guān)注前段模板、文件上傳大小腮鞍、模型文件目錄等配置:
### FreeMarker 配置
spring.freemarker.allow-request-override=false
#Enable template caching.啟用模板緩存值骇。
spring.freemarker.cache=false
spring.freemarker.check-template-location=true
spring.freemarker.charset=UTF-8
spring.freemarker.content-type=text/html
spring.freemarker.expose-request-attributes=false
spring.freemarker.expose-session-attributes=false
spring.freemarker.expose-spring-macro-helpers=false
#設(shè)置面板后綴
spring.freemarker.suffix=.ftl

# 設(shè)置單個文件最大內(nèi)存
spring.servlet.multipart.max-file-size=100MB
# 設(shè)置所有文件最大內(nèi)存
spring.servlet.multipart.max-request-size=1000MB
# 自定義文件上傳路徑
web.upload-path=/app/images
# 模型路徑
opencv.model-path=/app/model/haarcascade_frontalface_default.xml
  • 前端頁面文件只有一個<font color="blue">index.ftl</font>,請原諒欣宸不入流的前端水平移国,前端只有一個頁面吱瘩,可以提交頁面,同時也是展示處理結(jié)果的頁面:
<!DOCTYPE html>
<head>
    <meta charset="UTF-8" />
    <title>圖片上傳Demo</title>
</head>
<body>
<h1 >圖片上傳Demo</h1>
<form action="fileUpload" method="post" enctype="multipart/form-data">
    <p>選擇檢測文件: <input type="file" name="fileName"/></p>
    <p>周圍檢測數(shù)量: <input type="number" value="32" name="minneighbors"/></p>
    <p><input type="submit" value="提交"/></p>
</form>
<#--判斷是否上傳文件-->
<#if msg??>
    <span>${msg}</span><br><br>
<#else >
    <span>${msg!("文件未上傳")}</span><br>
</#if>
<#--顯示圖片迹缀,一定要在img中的src發(fā)請求給controller使碾,否則直接跳轉(zhuǎn)是亂碼-->
<#if fileName??>
<#--<img src="/show?fileName=${fileName}" style="width: 100px"/>-->
<img src="/show?fileName=${fileName}"/>
<#else>
<#--<img src="/show" style="width: 200px"/>-->
</#if>
</body>
</html>
  • 再來看后臺代碼,先是最常見的應(yīng)用啟動類:
package com.bolingcavalry.facedetect;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class FaceDetectApplication {

    public static void main(String[] args) {
        SpringApplication.run(FaceDetectApplication.class, args);
    }
}
  • 前端上傳圖片后祝懂,后端要做哪些處理呢票摇?先不貼代碼,咱們把后端要做的事情捋一遍砚蓬,如下圖:
在這里插入圖片描述
  • 接下來是最核心的業(yè)務(wù)類<font color="blue">UploadController.java</font>矢门,web接口和業(yè)務(wù)邏輯處理都在這里面,是按照上圖的流程順序執(zhí)行的灰蛙,有幾處要注意的地方稍后會提到:
package com.bolingcavalry.facedetect.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ResourceLoader;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.util.Map;
import org.opencv.core.*;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.CascadeClassifier;

import java.util.UUID;

import static org.bytedeco.javacpp.opencv_objdetect.CV_HAAR_DO_CANNY_PRUNING;

@Controller
@Slf4j
public class UploadController {

    static {
        // 加載 動態(tài)鏈接庫
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
    }

    private final ResourceLoader resourceLoader;

    @Autowired
    public UploadController(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    @Value("${web.upload-path}")
    private String uploadPath;

    @Value("${opencv.model-path}")
    private String modelPath;

    /**
     * 跳轉(zhuǎn)到文件上傳頁面
     * @return
     */
    @RequestMapping("index")
    public String toUpload(){
        return "index";
    }

    /**
     * 上次文件到指定目錄
     * @param file 文件
     * @param path 文件存放路徑
     * @param fileName 源文件名
     * @return
     */
    private static boolean upload(MultipartFile file, String path, String fileName){
        //使用原文件名
        String realPath = path + "/" + fileName;

        File dest = new File(realPath);

        //判斷文件父目錄是否存在
        if(!dest.getParentFile().exists()){
            dest.getParentFile().mkdir();
        }

        try {
            //保存文件
            file.transferTo(dest);
            return true;
        } catch (IllegalStateException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return false;
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return false;
        }
    }

    /**
     *
     * @param file 要上傳的文件
     * @return
     */
    @RequestMapping("fileUpload")
    public String upload(@RequestParam("fileName") MultipartFile file, @RequestParam("minneighbors") int minneighbors, Map<String, Object> map){
        log.info("file [{}], size [{}], minneighbors [{}]", file.getOriginalFilename(), file.getSize(), minneighbors);

        String originalFileName = file.getOriginalFilename();
        if (!upload(file, uploadPath, originalFileName)){
            map.put("msg", "上傳失斔钐蕖!");
            return "forward:/index";
        }

        String realPath = uploadPath + "/" + originalFileName;

        Mat srcImg = Imgcodecs.imread(realPath);

        // 目標(biāo)灰色圖像
        Mat dstGrayImg = new Mat();
        // 轉(zhuǎn)換灰色
        Imgproc.cvtColor(srcImg, dstGrayImg, Imgproc.COLOR_BGR2GRAY);
        // OpenCv人臉識別分類器
        CascadeClassifier classifier = new CascadeClassifier(modelPath);
        // 用來存放人臉矩形
        MatOfRect faceRect = new MatOfRect();

        // 特征檢測點的最小尺寸
        Size minSize = new Size(32, 32);
        // 圖像縮放比例,可以理解為相機(jī)的X倍鏡
        double scaleFactor = 1.2;
        // 執(zhí)行人臉檢測
        classifier.detectMultiScale(dstGrayImg, faceRect, scaleFactor, minneighbors, CV_HAAR_DO_CANNY_PRUNING, minSize);
        //遍歷矩形,畫到原圖上面
        // 定義繪制顏色
        Scalar color = new Scalar(0, 0, 255);

        Rect[] rects = faceRect.toArray();

        // 沒檢測到
        if (null==rects || rects.length<1) {
            // 顯示圖片
            map.put("msg", "未檢測到人臉");
            // 文件名
            map.put("fileName", originalFileName);

            return "forward:/index";
        }

        // 逐個處理
        for(Rect rect: rects) {
            int x = rect.x;
            int y = rect.y;
            int w = rect.width;
            int h = rect.height;
            // 單獨框出每一張人臉
            Imgproc.rectangle(srcImg, new Point(x, y), new Point(x + w, y + w), color, 2);
        }

        // 添加人臉框之后的圖片的名字
        String newFileName = UUID.randomUUID().toString() + ".png";

        // 保存
        Imgcodecs.imwrite(uploadPath + "/" + newFileName, srcImg);

        // 顯示圖片
        map.put("msg", "一共檢測到" + rects.length + "個人臉");
        // 文件名
        map.put("fileName", newFileName);

        return "forward:/index";
    }
    /**
     * 顯示單張圖片
     * @return
     */
    @RequestMapping("show")
    public ResponseEntity showPhotos(String fileName){
        if (null==fileName) {
            return ResponseEntity.notFound().build();
        }

        try {
            // 由于是讀取本機(jī)的文件摩梧,file是一定要加上的物延, path是在application配置文件中的路徑
            return ResponseEntity.ok(resourceLoader.getResource("file:" + uploadPath + "/" + fileName));
        } catch (Exception e) {
            return ResponseEntity.notFound().build();
        }
    }
}
  • <font color="blue">UploadController.java</font>的代碼,有以下幾處要關(guān)注:
  1. 在靜態(tài)方法中通過<font color="blue">System.loadLibrary</font>加載本地庫函仅父,實際開發(fā)過程中叛薯,這里是最容易報錯的地方,一定要確保<font color="red">-Djava.library.path</font>參數(shù)配置的路徑中的本地庫是正丑舷耍可用的耗溜,前文制作的基礎(chǔ)鏡像中已經(jīng)準(zhǔn)比好了這些本地庫,因此只要確保<font color="red">-Djava.library.path</font>參數(shù)配置正確即可省容,這個配置在稍后的Dockerfile中會提到
  2. <font color="blue">public String upload</font>方法是處理人臉檢測的代碼入口强霎,內(nèi)部按照前面分析的流程順序執(zhí)行
  3. <font color="blue">new CascadeClassifier(modelPath)</font>是根據(jù)指定的模型來實例化分類器,模型文件是從GitHub下載的蓉冈,opencv官方提前訓(xùn)練好的模型城舞,地址是:https://github.com/opencv/opencv/tree/master/data/haarcascades
  4. 看似神奇的人臉檢測功能,實際上只需一行代碼<font color="blue">classifier.detectMultiScale</font>寞酿,就能得到每個人臉在原圖中的矩形位置家夺,接下來,咱們只要按照位置在原圖上添加矩形框即可
  • 現(xiàn)在代碼已經(jīng)寫完了伐弹,接下來將其做成docker鏡像

docker鏡像制作

  • 首先是編寫Dockerfile:
# 基礎(chǔ)鏡像集成了openjdk8和opencv3.4.3
FROM bolingcavalry/opencv3.4.3:0.0.3

# 創(chuàng)建目錄
RUN mkdir -p /app/images && mkdir -p /app/model

# 指定鏡像的內(nèi)容的來源位置
ARG DEPENDENCY=target/dependency

# 復(fù)制內(nèi)容到鏡像
COPY ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY ${DEPENDENCY}/META-INF /app/META-INF
COPY ${DEPENDENCY}/BOOT-INF/classes /app

# 指定啟動命令
ENTRYPOINT ["java","-Djava.library.path=/opencv-3.4.3/build/lib","-cp","app:app/lib/*","com.bolingcavalry.facedetect.FaceDetectApplication"]
  • 上述Dockerfile內(nèi)容很簡單拉馋,就是一些復(fù)制文件的處理,只有一處要格外注意:啟動命令中有個參數(shù)<font color="blue">-Djava.library.path=/opencv-3.4.3/build/lib</font>,指定了本地so庫的位置煌茴,前面的java代碼中随闺,<font color="blue">System.loadLibrary</font>加載的本地庫就是從這個位置加載的,咱們用的基礎(chǔ)鏡像是<font color="blue">bolingcavalry/opencv3.4.3:0.0.3</font>蔓腐,已經(jīng)在該位置準(zhǔn)備好了opencv的所有本地庫

  • 在父工程目錄下執(zhí)行<font color="blue">mvn clean package -U</font>矩乐,這是個純粹的maven操作,和docker沒有任何關(guān)系

  • 進(jìn)入<font color="blue">face-detect-demo</font>目錄回论,執(zhí)行以下命令散罕,作用是從jar文件中提取class、配置文件傀蓉、依賴庫等內(nèi)容到target/dependency目錄:

mkdir -p target/dependency && (cd target/dependency; jar -xf ../*.jar)
  • 最后欧漱,在Dockerfile文件所在目錄執(zhí)行命令<font color="blue">docker build -t bolingcavalry/facedetect:0.0.1 .</font>(命令的最后有個點,不要漏了)葬燎,即可完成鏡像制作

  • 如果您有hub.docker.com的賬號误甚,還可以通過docker push命令把鏡像推送到中央倉庫,讓更多的人用到:

  • 最后谱净,再來回顧一下《三分鐘極速體驗:Java版人臉檢測》一文中啟動docker容器的命令靶草,如下可見,通過兩個-v參數(shù)岳遥,將宿主機(jī)的目錄映射到容器中,因此裕寨,容器中的/app/images和/app/model可以保持不變浩蓉,只要能保證宿主機(jī)的目錄映射正確即可:

docker run \
--rm \
-p 18080:8080 \
-v /root/temp/202107/17/images:/app/images \
-v /root/temp/202107/17/model:/app/model \
bolingcavalry/facedetect:0.0.1

需要重點注意的地方

  • 請大家關(guān)注pom.xml中和javacv相關(guān)的幾個庫的版本宾袜,這些版本是不能隨便搭配的捻艳,建議按照文中的來,就算要改庆猫,也請在maven中央倉庫檢查您所需的版本是否存在认轨;

  • 至此,《Java版人臉檢測》從體驗到開發(fā)詳解都完成了月培,小小的功能涉及到不少知識點嘁字,也讓我們體驗到了javacv的便捷和強(qiáng)大,借助docker將環(huán)境配置和應(yīng)用開發(fā)分離開來杉畜,降低了應(yīng)用開發(fā)和部署的難度(不再花時間到j(luò)dk和opencv的部署上)纪蜒,如果您正在尋找簡單易用的javacv開發(fā)和部署方案,希望本文能給您提供參考此叠;

你不孤單纯续,欣宸原創(chuàng)一路相伴

  1. Java系列
  2. Spring系列
  3. Docker系列
  4. kubernetes系列
  5. 數(shù)據(jù)庫+中間件系列
  6. DevOps系列

歡迎關(guān)注公眾號:程序員欣宸

微信搜索「程序員欣宸」,我是欣宸,期待與您一同暢游Java世界...
https://github.com/zq2599/blog_demos

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末猬错,一起剝皮案震驚了整個濱河市窗看,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌倦炒,老刑警劉巖显沈,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異析校,居然都是意外死亡构罗,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門智玻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來遂唧,“玉大人,你說我怎么就攤上這事吊奢「桥恚” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵页滚,是天一觀的道長召边。 經(jīng)常有香客問我,道長裹驰,這世上最難降的妖魔是什么隧熙? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮幻林,結(jié)果婚禮上贞盯,老公的妹妹穿的比我還像新娘。我一直安慰自己沪饺,他們只是感情好躏敢,可當(dāng)我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著整葡,像睡著了一般件余。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上遭居,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天啼器,我揣著相機(jī)與錄音,去河邊找鬼俱萍。 笑死镀首,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的鼠次。 我是一名探鬼主播更哄,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼芋齿,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了成翩?” 一聲冷哼從身側(cè)響起觅捆,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎麻敌,沒想到半個月后栅炒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡术羔,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年赢赊,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片级历。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡释移,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出寥殖,到底是詐尸還是另有隱情玩讳,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布嚼贡,位于F島的核電站熏纯,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏粤策。R本人自食惡果不足惜樟澜,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望叮盘。 院中可真熱鬧秩贰,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽尸变。三九已至,卻和暖如春艇棕,著一層夾襖步出監(jiān)牢的瞬間蝌戒,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工沼琉, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留北苟,地道東北人。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓打瘪,卻偏偏與公主長得像友鼻,于是被迫代替她去往敵國和親傻昙。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,901評論 2 345

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