不同平台下载安装搭建OpenCV + Java环境

不同平台下载安装搭建OpenCV + Java环境

  • macos

  • windows

  • linux

Windows平台

太简单了,直接去官网 https://opencv.org/releases/ 下载OpenCV 的 windows 版本,安装完,就能找到jar包和dll动态库,然后直接看后面怎么使用吧。

MacOS 平台

系统版本:macOS Big Sur (11.3.1)

时间:2022年2月16日,时间很重要,因为各种库的版本都会更新,有可能在你看到这篇文章的时候,库已经更新了,再使用我的安装方法的话,可能会导致你安装失败,我只能描述我是如何安装的。

安装需要用到的工具

Homebrew

需要使用到Homebrew工具

以下是Homebrew工具的安装

ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

我本地已经安装过了,上面步骤不再重复

在执行后续安装前,可以先使用 brew update brew upgrade 进行了升级之后,再安装后面的依赖库

如果Homebrew安装失败,那就移步百度去找Homebrew怎么安装吧~ ^ __ ^

依赖库

参考,但不是完全照抄,我在安装过程中,也遇到了好多问题

主要针对ffmpeg做了调整,如果默认安装,以我当前时间,安装的这个库版本为5,而我实际安装的最新的OpenCV-4.5.4版本的话,它使用的还是4版本,如果安装错误,会导致安装过程中ffmpeg库找不到某些接口

调整前

brew install gcc git cmake pkg-config ffmpeg libgphoto2 libav libjpeg libpng libtiff libdc1394 ant

调整后

brew install gcc git cmake pkg-config ffmpeg@4 libgphoto2 libav libjpeg libpng libtiff libdc1394 ant zlib

JAVA 环境

我用的是Oracle JDK 1.8u291,怎么安装不赘述了。

➜  lib Java -version
java version "1.8.0_291"
Java(TM) SE Runtime Environment (build 1.8.0_291-b10)
Java HotSpot(TM) 64-Bit Server VM (build 25.291-b10, mixed mode)

OpenCV + Java 依赖环境配置

在依赖库安装之后,使用ant命令,检查ant是否安装成功,如果安装失败,请手动安装,传送门

➜  lib ant -version
Apache Ant(TM) version 1.10.12 compiled on October 13 2021
环境变量配置

我这里用的是~/.zshrc文件,除此之外,~/.bash_profile~/.bashrc/etc/profile 这些文件都可以添加环境变量,不用纠结我为什么在zshrc中添加(启动终端会自动加载的环境变量配置文件)

  • export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_291.jdk/Contents/Home (改成你的即可,已经配置过忽略)

  • export PATH=$JAVA_HOME/bin:$PATH (已经配置过可以忽略)
    ========================分割线-很重要========================

  • export JAVA_AWT_INCLUDE_PATH=$JAVA_HOME

  • export JAVA_AWT_LIBRARY=$JAVA_HOME

  • export JAVA_INCLUDE_PATH=$JAVA_HOME/indclude

  • export JAVA_INCLUDE_PATH2=$JAVA_HOME/include/darwin

  • export JAVA_JVM_LIBRARY=$JAVA_HOME
    ============================================================

分割线中间内容,就是折磨我一天的配置,我之前一直没有配置,也不知道怎么配置,导致我安装OpenCV之后,一直没有找到生成的Jar包和dylib动态库。所以要特别感谢这位 博主 给我的启发

配置完了保存,别忘了使用source命令重载配置,并检查是否配置成功

echo $JAVA_INCLUDE_PATH2 // 检查是否配置成功

如果红色字体不配置的话,Cmake之后的结果如下:

...
--   Java:                          
--     ant:                         /usr/local/bin/ant (ver 1.10.12)
--     JNI:                         NO
--     Java wrappers:               NO
--     Java tests:                  NO
...

配置之后,Cmake之后结果如下:

...
--   Java:                          
--     ant:                         /usr/local/bin/ant (ver 1.10.12)
--     JNI:                         /Library/Java/JavaVirtualMachines/jdk1.8.0_291.jdk/Contents/Home/include /Library/Java/JavaVirtualMachines/jdk1.8.0_291.jdk/Contents/Home/include/darwin /Library/Java/JavaVirtualMachines/jdk1.8.0_291.jdk/Contents/Home/include
--     Java wrappers:               YES
--     Java tests:                  YES
...

就是这个差别,会影响最后到底有没有生成Jar包和动态库

安装OpenCV

Homebrew安装

参考

brew edit opencv

In the text editor that will open, change the line -DBUILD_opencv_java=OFF to -DBUILD_opencv_java=ON, and save the file.

修改-DBUILD_opencv_java=OFF 为 -DBUILD_opencv_java=ON

brew install --build-from-source opencv

等待安装结束

查看目录:/usr/local/Cellar/opencv/{version}

不过这个方式我只是把库装好了,没有找到对应的jar

安装完了我没删除,这样不用去配置opencv的环境了,直接就有了。

源码安装

去官网 https://opencv.org/releases/ 下载OpenCV

我下载的是 OpenCV – 4.5.4 版本(4.5.5版本我试过,没成功,为什么选用4.5.4版本是因为我尝试过),下载完成后解压,通过Terminal进入到解压后的目录,并创建build目录

cd Downloads/opencv-4.5.4
mkdir build

执行cmake命令进行安装

cd Downloads/opencv-4.5.4
mkdir build
cd build
cmake -DBUILD_SHARED_LIBS=OFF -DWITH_IPP=OFF -DCMAKE_INSTALL_PREFIX={你的路径}/opencv-4.5.4 ../


如果遇到了(表示你Cmake太多次了)
1.FATAL:In-source builds are not allowed.
You should create separate directory for build files.
解决方法:1)先删除刚才在当前目录下创建的CMakeCache.txt文件和CMakeFIles目录;2)再新建目录,比如build目录,在build目录执行cmake.

... 开始进行配置/编译

注意检查最后:
--   Java:                          
--     ant:                         
--     JNI:                         
--     Java wrappers:               
--     Java tests:    

这后面的值有没有,如果没有说明环境变量没有配置成功

等待片刻后在当前目录下会出现一堆文件,这时使用命令

make -j 8

等待编译完成,然后进行安装,等待结束

make install

遇到报错太正常了

中间遇到了很多报错,其中一个就是ffmpeg库导致的报错,我去翻了百度、谷歌,都没找到很好的办法,后来我就想到了时间,因为库会更新,所以,很有可能是版本不同导致的有些东西找不到,只要环境一样,理论上都可以安装成功,一定要耐心一些。

为了解决ffmpeg的报错,我使用brew search ffmpeg去找别的版本,果然发现了,还有别的版本,抱着试试的态度,安装了ffmpeg@4再次make,结果这个报错就没了,别提多开心了。

后来遇到了找不到zlib包,所以我在优化后的依赖库中加了zlib包。

再后来遇到了xxx_dnn的报错,当时我用的是opencv_4.5.5版本,这个死活没办法,后来我通过homebrew安装了一次opencv,安装成功了,但是没有jar包,但是我好奇的看了一下opencv的版本,发现是4.5.4版本,索性我又去下载了4.5.4版本重新cmake,make,make install一顿操作之后,居然成功安装了。特别开心,但是死活找不到jar包。

为了解决jar包没生成的问题,才找到了上面说的,环境变量的问题。

所以,心态要好,一定别慌。我没有任何人可以问,只能一步一步自己弄。我就是一个搞Java的,由于工作原因要用到OpenCV,没学过C++,C就懂点皮毛,连入门都不算,所以也走了很多很多弯路。

找Jar包和dylib动态库

安装结束后,可以找到安装目录,找到对应的动态链接库以及对应的Jar文件

cd ~/Downloads/opencv-4.5.4/build/bin
能找到 opencv-454.jar 文件

cd ~/Downloads/opencv-4.5.4/build/lib
能找到 libopencv_java454.dylib 文件

至此,OpenCV的安装结束

OpenCV环境变量配置
  1. 如果使用了Homebrew完成了OpenCV安装,则忽略这一步

  2. 如果直接使用的源码安装,则需要配置环境变量

  • export OPENCV_HOME={你的目录}

  • export PATH=$OPENCV_HOME/build/bin:$PATH

别忘了source加载

➜ opencv_version
4.5.4

看到这个环境变量就配置成功了

IDEA + Java 使用OpenCV

必备:opencv-454.jarlibopencv_java454.dylib (在windows环境下,应该为dll文件,在Linux环境下,应该为.so文件)

将jar包放入项目根目录./lib目录下

1. maven项目引入jar包

<dependency>
    <groupId>org.opencv</groupId>
    <artifactId>opencv</artifactId>
    <version>4.5.4</version>
    <scope>system</scope>
    <systemPath>${project.basedir}/lib/opencv-454.jar</systemPath>
</dependency>

2. 编写测试类

public class OpenCVTest {

    static {
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
    }

    public static void main(String[] args) {
        Mat imread = Imgcodecs.imread("WechatIMG26763.jpeg");
        System.out.println(imread);
    }
}

点击执行,这个时候会报错,不要管

点开IDEA的运行Configurations,找到OpenCVTest选项,添加VM参数

-Djava.library.path={你的libopencv_java454.dylib所在目录}
例如:/Home/Users/my/Download/opencv_4.5.4/build/lib

再次点击运行:

Mat [ 873*1920*CV_8UC3, isCont=true, isSubmat=false, nativeObj=0x7fb3524176b0, dataAddr=0x7fb356aaa000 ]

至此,开发环境搭建完成。

3. 关于打包执行问题

参考

主要是需要把我们独立引入的lib下的包打包到jar中

<build>
  <plugins>
    <plugin>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-maven-plugin</artifactId>
      <configuration>
        <includeSystemScope>true</includeSystemScope>
        <fork>true</fork>
      </configuration>
    </plugin>
  </plugins>
</build>

打包后,所有依赖包都会打包到jar中

4. 建议把Jar包上传至Maven仓库,方便正常打包使用

mvn install:install-file -Dfile={jar地址} -DgroupId=org.opencv -DartifactId=opencv-454-for-mac -Dversion4.5.4 -Dpackaging=jar

Linux 平台

Centos 7 安装必要依赖库

yum install epel-release git gcc gcc-c++ cmake3 qt5-qtbase-devel \
 python python-devel python-pip cmake python-devel numpy \
 python34-numpy gtk2-devel libpng-devel jasper-devel \
 openexr-devel libwebp-devel libjpeg-turbo-devel libtiff-devel \
 libdc1394-devel tbb-devel eigen3-devel gstreamer-plugins-base-devel \
 freeglut-devel mesa-libGL mesa-libGL-devel  boost boost-thread \
 boost-devel libv4l-devel ant -y
环境变量配置

我这里用的是~/.zshrc文件,除此之外,~/.bash_profile~/.bashrc/etc/profile 这些文件都可以添加环境变量,不用纠结我为什么在zshrc中添加(启动终端会自动加载的环境变量配置文件)

  • export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_291.jdk/Contents/Home (改成你的即可,已经配置过忽略)

  • export PATH=$JAVA_HOME/bin:$PATH (已经配置过可以忽略)

  • export JAVA_AWT_INCLUDE_PATH=$JAVA_HOME

  • export JAVA_AWT_LIBRARY=$JAVA_HOME

  • export JAVA_INCLUDE_PATH=$JAVA_HOME/indclude

  • export JAVA_INCLUDE_PATH2=$JAVA_HOME/include/darwin

  • export JAVA_JVM_LIBRARY=$JAVA_HOME

下载源码

去官网 https://opencv.org/releases/ 下载OpenCV

我下载的是 OpenCV – 4.5.4 版本(4.5.5版本我试过,没成功,为什么选用4.5.4版本是因为我尝试过),下载完成后解压,通过Terminal进入到解压后的目录,并创建build目录

cd Downloads/opencv-4.5.4
 mkdir build

执行cmake命令进行安装

cd Downloads/opencv-4.5.4
mkdir build
cd build
cmake -DBUILD_SHARED_LIBS=OFF -DWITH_IPP=OFF -DCMAKE_INSTALL_PREFIX={你的路径}/opencv-4.5.4 ../

如果遇到了(表示你Cmake太多次了)
1.FATAL:In-source builds are not allowed.
You should create separate directory for build files.
解决方法:1)先删除刚才在当前目录下创建的CMakeCache.txt文件和CMakeFIles目录;2)再新建目录,比如build目录,在build目录执行cmake.

... 开始进行配置/编译

注意检查最后:
-- Java:
-- ant:
-- JNI:
-- Java wrappers:
-- Java tests:

这后面的值有没有,如果没有说明环境变量没有配置成功</pre>

等待片刻后在当前目录下会出现一堆文件,这时使用命令

make -j 8

等待编译完成,然后进行安装,等待结束

make install

代码演示

public class SliderUnlock {
 
  static {
  System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
  }
 
  /**
  * 解析两个图片之间的像素距离百分比
  * @param templateUrl
  * @param imageUrl
  * @return
  */
  public static int offsetDistance(String templateUrl, String imageUrl, int webImageWidth) throws IOException {
  long currentTime = System.currentTimeMillis();
  Mat slideBgMat = backgroundHandle(saveNetImage(imageUrl, currentTime, false));
  Mat slideBlockMat = templateHandle(saveNetImage(templateUrl, currentTime, true));
 
  Mat result = new Mat();
  /*
  * matchTemplate:在模板和输入图像之间寻找匹配,获得匹配结果图像
  * result:保存匹配的结果矩阵
  * TM_CCOEFF_NORMED标准相关匹配算法
  */
  Imgproc.matchTemplate(slideBgMat, slideBlockMat, result, Imgproc.TM_CCOEFF_NORMED);
 
  /*
  反馈匹配结果
  */
  /*Point ml = new Point();
  Core.MinMaxLocResult mmlr = Core.minMaxLoc(result);
  ml = mmlr.maxLoc;
  Imgproc.rectangle(slideBgMat, ml,
  new Point(ml.x + slideBlockMat.cols(), ml.y + slideBlockMat.rows()),
  new Scalar(0, 0, 0, 0));
  new ImageViewer(slideBgMat).imshow();
  System.out.println(slideBgMat.width());*/
  /*
  * minMaxLoc:在给定的结果矩阵中寻找最大和最小值,并给出它们的位置
  * maxLoc最大值
  */
  Point matchLocation = Core.minMaxLoc(result).maxLoc;
  // 返回匹配点的横向距离 / 原图宽度,得到偏移量,把结果再乘以页面上的图片宽度就得到了偏移像素了。
  return (int) Math.round(matchLocation.x * webImageWidth / slideBgMat.width());
  }
 
  /**
  * 匹配滑块处理
  * @param filePath
  * @return
  */
  private static Mat templateHandle(String filePath) {
  Mat slidBlockMat = Imgcodecs.imread(filePath);
  // 删除图片
  new File(filePath).delete();
  // 1。 灰度
  Imgproc.cvtColor(slidBlockMat, slidBlockMat, Imgproc.COLOR_BGR2GRAY);
  // 2\. 去除周围黑边
  for (int row = 0; row < slidBlockMat.height(); row++) {
  for (int col = 0; col < slidBlockMat.width(); col++) {
  if (slidBlockMat.get(row, col)[0] == 0) {
  slidBlockMat.put(row, col, 96);
  }
  }
  }
  // 3\. inRange二值化转黑白图
  Core.inRange(slidBlockMat, Scalar.all(96), Scalar.all(96), slidBlockMat);
  return slidBlockMat;
  }
 
  /**
  * 背景图处理
  * @param filePath
  * @return
  */
  private static Mat backgroundHandle(String filePath) {
  Mat slideBgMat = Imgcodecs.imread(filePath);
  // 删除图片
  new File(filePath).delete();
  // 1\. 灰度化图片
  Imgproc.cvtColor(slideBgMat, slideBgMat, Imgproc.COLOR_BGR2GRAY);
  // 2\. 二值化
  Imgproc.threshold(slideBgMat, slideBgMat, 127, 255, Imgproc.THRESH_BINARY);
  return slideBgMat;
  }
 
  /**
  * 保存网络上的图片至本地
  * @param url 图片地址
  * @param currentTime 当前时间,作为文件名
  * @param isTemplate 是否是模板, 如果是则会添加template作为结尾
  * @return
  */
  private static String saveNetImage(String url, long currentTime, boolean isTemplate) throws IOException {
  BufferedImage buffer = ImageIO.read(new URL(url));
  String fileName = isTemplate ? currentTime + "_template.png" : currentTime + ".jpeg";
  File file = new File("/tmp/" + fileName);
  String absolutePath = file.getAbsolutePath();
  ImageIO.write(buffer, isTemplate ? "png" : "jpeg", new FileImageOutputStream(file));
  return absolutePath;
  }
 
  /**
  * 读取图片
  * @param filePath
  * @return
  */
  private static Mat readImage(String filePath) {
  return Imgcodecs.imread(filePath, Imgcodecs.IMREAD_UNCHANGED);
  }
 //
 //    public static void main(String[] args) throws IOException {
 ////        Mat imread = Imgcodecs.imread("/Users/wuyujia/Desktop/WechatIMG26763.jpeg");
 ////        System.out.println(imread);
 ////        saveNetImage("https://p9-catpcha.byteimg.com/tos-cn-i-188rlo5p4y/52642779215b43f0aa8b00add6156bcd~tplv-188rlo5p4y-2.jpeg",
 ////                System.currentTimeMillis(), false);
 ////        String path = saveNetImage("https://p9-catpcha.byteimg.com/tos-cn-i-188rlo5p4y/159aa92eae634f6d8221f213b7d2d87e~tplv-188rlo5p4y-1.png",
 ////                System.currentTimeMillis(), true);
 ////        System.out.println(path);
 //        String templateUrl = "https://p9-catpcha.byteimg.com/tos-cn-i-188rlo5p4y/a4b1042da716481cbac0df6a2e98cec3~tplv-188rlo5p4y-1.png";
 //        String imageUrl = "https://p9-catpcha.byteimg.com/tos-cn-i-188rlo5p4y/d9584766ad204220a1c86c7b2085bac5~tplv-188rlo5p4y-2.jpeg";
 //        double x = offsetDistance(templateUrl, imageUrl);
 //        System.out.println(x);
 ////        Mat mat = backgroundHandle(saveNetImage(imageUrl, System.currentTimeMillis(), false));
 ////        new ImageViewer(mat).imshow();
 //    }
 package org.spiderflow.custom.utils.opencv;
 
 import org.opencv.core.Mat;
 
 import javax.swing.*;
 import java.awt.*;
 import java.awt.image.BufferedImage;
 import java.awt.image.DataBufferByte;
 
 /**
  * Created by kofee on 2016/3/28.
  */
 public class ImageViewer {
  private JLabel imageView;
 
  private Mat image;
  private String windowName;
 
  /**
  * 如果使用junit测试时调用该方法,图像会一闪而过,可通过sleep()等方式暂时显示
  *
  * @param
  */
 
  public ImageViewer(Mat image) {
  this.image = image;
  }
 
 
  /**
  * @param image      要显示的mat
  * @param windowName 窗口标题
  */
  public ImageViewer(Mat image, String windowName) {
  this.image = image;
  this.windowName = windowName;
  }
 
  /**
  * 图片显示
  */
  public void imshow() {
  setSystemLookAndFeel();
  Image loadedImage = toBufferedImage(image);
  JFrame frame = createJFrame(windowName, image.width(), image.height());
  imageView.setIcon(new ImageIcon(loadedImage));
  frame.pack();
  frame.setLocationRelativeTo(null);
  frame.setVisible(true);
  frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);// 用户点击窗口关闭
  }
 
  private void setSystemLookAndFeel() {
  try {
  UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
  } catch (ClassNotFoundException e) {
  e.printStackTrace();
  } catch (InstantiationException e) {
  e.printStackTrace();
  } catch (IllegalAccessException e) {
  e.printStackTrace();
  } catch (UnsupportedLookAndFeelException e) {
  e.printStackTrace();
  }
  }
 
  private JFrame createJFrame(String windowName, int width, int height) {
  JFrame frame = new JFrame(windowName);
  imageView = new JLabel();
  final JScrollPane imageScrollPane = new JScrollPane(imageView);
  imageScrollPane.setPreferredSize(new Dimension(width, height));
  frame.add(imageScrollPane, BorderLayout.CENTER);
  frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
  return frame;
  }
 
 
  private Image toBufferedImage(Mat matrix) {
  int type = BufferedImage.TYPE_BYTE_GRAY;
  if (matrix.channels() > 1) {
  type = BufferedImage.TYPE_3BYTE_BGR;
  }
  int bufferSize = matrix.channels() * matrix.cols() * matrix.rows();
  byte[] buffer = new byte[bufferSize];
  matrix.get(0, 0, buffer); // 获取所有的像素点
  BufferedImage image = new BufferedImage(matrix.cols(), matrix.rows(), type);
  final byte[] targetPixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
  System.arraycopy(buffer, 0, targetPixels, 0, buffer.length);
  return image;
  }
 }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335