LeqiangBian

记录精彩的程序人生

Open Source, Open Mind,
Open Sight, Open Future!
  menu
7 文章
4 评论
5652 浏览
0 当前访客
ღゝ◡╹)ノ❤️

搭建支持opencv的java环境

opencv是一个开发源的视觉识别库,附上github链接:https://github.com/opencv/opencv

opencv支持了opencl异构计算,支持图像的多种处理方式和训练建立模型。下面说几点硬核的东西吧,主要是环境搭建,基础api的使用,深度学习。

(1)环境搭建
环境主要就是开发环境,生产环境。环境主要包括这几类操作系统:
windows,mac,ios,android(arm linux),服务器(x86 linux,一般是centos),不同的操作系统指令集不一样,但是都支持c和c++,通过对opencv源码在不同平台上进行编译,可以编译出在不同平台上的动态库或者可执行文件。动态库需要放在特定的library path下面才能被运行的java程序找到,在不同平台上可以通过

System.out.println(System.getProperty("java.library.path"));

来寻找java library path
类unix系统的运行环境一般是/usr/lib。

知道了这么多我们可以去编译源码了。

yum -y install unzip gcc gcc-c++ gtk+-devel gimp-devel gimp-devel-tools gimp-help-browser zlib-devel libtiff-devel libjpeg-devel libpng-devel gstreamer-devel libavc1394-devel libraw1394-devel libdc1394-devel jasper-devel jasper-utils swig python libtool nasm build-essential ant epel-release clang

# 寻找一个目录做为工作空间用来编译
# 安装高版本cmake
wget https://cmake.org/files/v3.6/cmake-3.6.2.tar.gz
tar xvf cmake-3.6.2.tar.gz && cd cmake-3.6.2/
./bootstrap
gmake
gmake install
# 对安装的cmake文件做软链
ln -s /usr/local/bin/cmake /usr/bin/

# 安装opencv
wget https://codeload.github.com/opencv/opencv/zip/4.0.1

# 进入目录
mkdir build
cd build

cmake ../ -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local/opencv_make/release -DBUILD_TESTS=OFF ..

make -j8

# 进入lib目录下看看有没有libopencv_java401.so,没有的话是有问题的
make install

cp libopencv_java401.so /usr/local/opencv_make/release/lib64/

ln -s /usr/local/opencv_make/release/lib64/libopencv_java401.so /usr/lib64/

# 参考
https://www.52pojie.cn/thread-872736-1-1.html
https://blog.csdn.net/boom_man/article/details/87628287

到这里opencv的java环境就算是安装完成了,验证是否可用,可以倒入opencv的jar包到项目中,

System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
new Mat(); 
看看是否报错,如果不报错,证明可用。

(2)基础api的使用

public class OpencvService {

  /*
   * @author LeQiang.
   * ps: 当前类注释.
   * opencv所有常用方法的二次封装,带有注释.
   * opencv常用类处理的工具类.
   * 目的是为了方便统一写注释,并且改变c&c++代码指针函数返回值写法样式,符合java习惯.
   */
  private Logger log = LoggerFactory.getLogger(OpencvService.class);

  static {
    //System.load(PathConstants.OPENCV_NATIVE_PATH);
    System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
  }

  /**
   * mat旋转.
   *
   * @param degree 90,-90,180,270度数
   */
  public Mat rotateSatCard(Mat sourceMat, int degree) {
    switch (degree) {
      case -90:
        Core.transpose(sourceMat, sourceMat);
        Core.flip(sourceMat, sourceMat, 0);
        break;
      case 90:
        // 矩阵转置
        Core.transpose(sourceMat, sourceMat);
        /*
         * 0: 沿X轴翻转; >0: 沿Y轴翻转; <0: 沿X轴和Y轴翻转
         * 翻转模式,flipCode == 0垂直翻转(沿X轴翻转),
         * flipCode>0水平翻转(沿Y轴翻转),
         * flipCode<0水平垂直翻转(先沿X轴翻转,再沿Y轴翻转,等价于旋转180°)
         */
        Core.flip(sourceMat, sourceMat, 1);
        break;
      case 180:
        //0: 沿X轴翻转; >0: 沿Y轴翻转; <0: 沿X轴和Y轴翻转
        Core.flip(sourceMat, sourceMat, 0);
        /*
         * 翻转模式,flipCode == 0垂直翻转(沿X轴翻转),
         * flipCode>0水平翻转(沿Y轴翻转),
         * flipCode<0水平垂直翻转(先沿X轴翻转,再沿Y轴翻转,等价于旋转180°)
         */
        Core.flip(sourceMat, sourceMat, 1);
        break;
      case 270:
        //0: 沿X轴翻转; >0: 沿Y轴翻转; <0: 沿X轴和Y轴翻转
        Core.transpose(sourceMat, sourceMat);
        /*
         * 翻转模式,flipCode == 0垂直翻转(沿X轴翻转),
         * flipCode>0水平翻转(沿Y轴翻转),
         * flipCode<0水平垂直翻转(先沿X轴翻转,再沿Y轴翻转,等价于旋转180°)
         */
        Core.flip(sourceMat, sourceMat, 0);
        break;
      default:
        throw new UnsupportedOperationException("error.not support this degree.");

    }
    return sourceMat;
  }

  /**
   * opencv的修改尺寸方法.
   */
  public Mat resizeMat(Mat sourceMat, double width, double height) {
    Mat dstMat = new Mat();
    Size size = new Size(width, height);
    Imgproc.resize(sourceMat, dstMat, size);
    return dstMat;
  }

  /**
   * opecncv转灰度图像.
   */
  public Mat cvtColor(Mat sourceMat) {
    Mat dstMat = new Mat();
    //CV_BGR2GRAY 这个是什么意思需要好好研究一下.
    Imgproc.cvtColor(sourceMat, dstMat, Imgproc.COLOR_RGB2GRAY);
    return dstMat;
  }

  /**
   * opecncv转换色值图像.
   */
  public Mat cvtColor(Mat sourceMat, int code) {
    Mat dstMat = new Mat();
    Imgproc.cvtColor(sourceMat, dstMat, code);
    return dstMat;
  }

  /**
   * 读取图片.
   */
  public Mat readPic(String absFileName) {
    return Imgcodecs.imread(absFileName);
  }

  /**
   * 写入图片.
   */
  public boolean writePic(Mat sourceMat, String absFileName) {
    if (Constants.isDebugger()) {
      boolean b = Imgcodecs.imwrite(absFileName, sourceMat);
      log.info("info.result: " + (b ? "Write successfully" : "Write failure"));
      return b;
    } else {
      return Constants.isDebugger();
    }
  }

  /**
   * 输入流转mat.
   */
  public Mat inputStream2Mat(InputStream inputStream) throws IOException {
    BufferedInputStream bis = new BufferedInputStream(inputStream);
    ByteArrayOutputStream os = new ByteArrayOutputStream();
    byte[] buffer = new byte[1024];
    int bytesRead = 0;
    while ((bytesRead = bis.read(buffer)) != -1) {
      os.write(buffer, 0, bytesRead);
    }
    os.flush();
    os.close();
    bis.close();
    Mat encoded = new Mat(1, os.size(), 0);
    encoded.put(0, 0, os.toByteArray());
    Mat decoded = Imgcodecs.imdecode(encoded, -1);
    encoded.release();
    return decoded;
  }

  /**
   * byte[]转mat.
   */
  public Mat byte2Mat(byte[] bytes) throws IOException {
    Mat encoded = new Mat(1, bytes.length, 0);
    encoded.put(0, 0, bytes);
    Mat decoded = Imgcodecs.imdecode(encoded, -1);
    encoded.release();
    return decoded;
  }

  /**
   * mat转输入流.
   */
  public InputStream mat2InputStream(Mat mat) {
    MatOfByte mob = new MatOfByte();
    imencode(".jpg", mat, mob);
    byte[] byteArray = mob.toArray();
    return new ByteArrayInputStream(byteArray);
  }

  /**
   * mat转byte[].
   */
  public byte[] mat2ByteArray(Mat mat) {
    MatOfByte mob = new MatOfByte();
    imencode(".jpg", mat, mob);
    return mob.toArray();
  }

  /**
   * mat转base64字符串.
   */
  public String mat2Base64(Mat mat) {
    byte[] bytes = mat2ByteArray(mat);
    BASE64Encoder encoder = new BASE64Encoder();
    return encoder.encode(bytes);
  }

  /**
   * base64字符串转mat.
   */
  public Mat base642Mat(String str) throws IOException {
    BASE64Decoder decoder = new BASE64Decoder();
    byte[] b = decoder.decodeBuffer(str);
    return byte2Mat(b);
  }

  /**
   * base64字符串转InputStream.
   */
  public InputStream base642InputStream(String str) throws IOException {
    BASE64Decoder decoder = new BASE64Decoder();
    byte[] b = decoder.decodeBuffer(str);
    return new ByteArrayInputStream(b);
  }

  /**
   * countNonZero计算有效像素点个数.
   */
  public int countNonZero(Mat sourceMat) {
    return Core.countNonZero(sourceMat);
  }

  /**
   * (1)
   * 均值滤波相当于低通滤波.
   * 有将图像模糊化的趋势.
   * 对椒盐噪声基本无能为力.
   * (2)
   * 中值滤波的优点是可以很好的过滤掉椒盐噪声.
   * 缺点是易造成图像的不连续性.
   * 均值滤波.
   */
  public Mat meanBlur(Mat sourceMat) {
    // 加载时灰度
    Mat dstMat = new Mat();
    Imgproc.blur(sourceMat, dstMat, new Size(9, 9), new Point(-1, -1), Core.BORDER_DEFAULT);
    return dstMat;
  }

  /**
   * 中值滤波.
   */
  public Mat medianBlur(Mat sourceMat) {
    Mat dstMat = new Mat();
    Imgproc.medianBlur(sourceMat, dstMat, 7);
    return dstMat;
  }

  /**
   * 高斯滤波.
   */
  public Mat gaussianBlur(Mat sourceMat) {
    Mat dstMat = new Mat();
    Imgproc.GaussianBlur(sourceMat, dstMat, new Size(3, 3), 0);
    //Imgproc.GaussianBlur(sourceMat, dstMat, new Size(9, 9), 0, 0, Core.BORDER_DEFAULT);
    return dstMat;
  }

  /**
   * 边缘检测,必须是二值图像.
   */
  public Mat canny(Mat sourceMat) {
    Mat dstMat = new Mat();
    Imgproc.Canny(sourceMat, dstMat, 10, 150);
    return dstMat;
  }

  /**
   * 边缘检测,返回二值图像.
   * ps:注意和阙值处理threshold方法的区别是canny返回的是边缘,
   * threshold返回的是轮廓.
   */
  public Mat canny(Mat sourceMat, double threshold1, double threshold2) {
    Mat dstMat = new Mat();
    Imgproc.Canny(sourceMat, dstMat, threshold1, threshold2);
    return dstMat;
  }

  /**
   * 图片二值化,返回二值图像.
   */
  public Mat threshold(Mat sourceMat) {
    Mat dstMat = new Mat();
    Imgproc.threshold(sourceMat, dstMat, 200, 255, Imgproc.THRESH_BINARY_INV);
    return dstMat;
  }

  /**
   * 图片二值化,返回二值图像.
   */
  public Mat threshold(Mat sourceMat, double thresh, double maxval, int type) {
    Mat dstMat = new Mat();
    Imgproc.threshold(sourceMat, dstMat, thresh, maxval, type);
    return dstMat;
  }

  /**
   * 这个会显示真实图像的二值图像和轮廓是有区别的.
   * 他是实心的.
   * 图片二值化,阙值处理.
   *
   * @param maxValue       阙值最大值.
   * @param adaptiveMethod 1.ADAPTIVE_THRESH_MEAN_C(通过平均的方法取得平均值)
   *                       2.ADAPTIVE_THRESH_GAUSSIAN_C(通过高斯取得高斯值)
   *                       这两种方法最后得到的结果要减掉参数里面的C值
   * @param thresholdType  Int类型的,方法如下.
   *                       THRESH_BINARY 二进制阈值化 -> 大于阈值为1 小于阈值为0
   *                       THRESH_BINARY_INV 反二进制阈值化 -> 大于阈值为0 小于阈值为1
   *                       THRESH_TRUNC 截断阈值化 -> 大于阈值为阈值,小于阈值不变
   *                       THRESH_TOZERO 阈值化为0 -> 大于阈值的不变,小于阈值的全为0
   *                       THRESH_TOZERO_INV 反阈值化为0 -> 大于阈值为0,小于阈值不变
   * @param blockSize      blockSize:Int类型的,这个值来决定像素的邻域块有多大,这里的blockSize的值要为奇数.
   *                       当blockSize的值比较小的时候,两种方法得到的结果的差异不是很大
   *                       当blockSize的值比较大的时候,就会发现,平均的这种会将整体的轮廓加深的程度大于高斯
   */
  public Mat adaptiveThreshold(
      Mat sourceMat, double maxValue, int adaptiveMethod,
      int thresholdType, int blockSize, double c) {
    Mat dstMat = new Mat();
    Imgproc.adaptiveThreshold(sourceMat, dstMat, maxValue,
        adaptiveMethod, thresholdType, blockSize, c);
    return dstMat;
  }

  /**
   * 设置膨胀和腐蚀核的大小.
   *
   * @param shape Imgproc.MORPH_ELLIPSE 椭圆
   *              Imgproc.MORPH_RECT 矩形
   */
  public Mat getStructuringElement(int shape, double width, double height) {
    return Imgproc.getStructuringElement(shape, new Size(width, height));
  }

  /**
   * 腐蚀.
   */
  public Mat erode(Mat sourceMat, Mat element) {
    Mat dstMat = new Mat();
    Imgproc.erode(sourceMat, dstMat, element);
    return dstMat;
  }

  /**
   * 膨胀.
   */
  public Mat dilate(Mat sourceMat, Mat element) {
    Mat dstMat = new Mat();
    Imgproc.dilate(sourceMat, dstMat, element);
    return dstMat;
  }

  /**
   * 计算轮廓的垂直边界最小矩形,矩形是与图像上下边界平行的.
   */
  public Rect boundingRect(MatOfPoint matOfPoint) {
    return Imgproc.boundingRect(matOfPoint);
  }

  /**
   * 计算轮廓的垂直边界最小矩形,矩形是与图像上下边界平行的.
   */
  public Rect boundingRect(MatOfPoint2f matOfPoint) {
    return Imgproc.boundingRect(matOfPoint);
  }

  /**
   * 求最小包围圆的函数.
   *
   * @param radius 圆心半径,这里解释一下,看源码会发现方法只对radius[0]赋值,
   *               那为什么用数组,原因是c++要给你返回值半径...
   */
  public void minEnclosingCircle(MatOfPoint2f points, Point center, float[] radius) {
    Imgproc.minEnclosingCircle(points, center, radius);
  }

  /**
   * 区域寻找.
   */
  public Mat submat(Mat sourceMat, int x, int y, int width, int height) {
    return sourceMat.submat(new Rect(x, y, width, height));
  }

  /**
   * 找到所有的圆.
   * 这个api有空再研究.
   */
  public void houghCircles(Mat image, Mat circles, int method, double dp, double minDist) {
    Imgproc.HoughCircles(image, circles, Imgproc.CV_HOUGH_GRADIENT, 1, 1);
  }

  /**
   * 寻找圆形.
   */
  public Mat houghCircles(Mat sourceMat) {
    Mat dstMat = new Mat();
    return houghCircles(sourceMat, dstMat,
        Imgproc.HOUGH_GRADIENT, 1, 1, 100, 210, 0, 0);
  }

  /**
   * 寻找圆形.
   *
   * @param dp        累加器图像的分辨率。这个参数允许创建一个比输入图像分辨率低的累加器。
   *                  (这样做是因为有理由认为图像中存在的圆会自然降低到与图像宽高相同数量的范畴)。
   *                  如果dp设置为1,则分辨率是相同的;如果设置为更大的值(比如2),
   *                  累加器的分辨率受此影响会变小(此情况下为一半)。dp的值不能比1小。
   * @param minDist   该参数是让算法能明显区分的两个不同圆之间的最小距离
   * @param param1    用于Canny的边缘阀值上限,下限被置为上限的一半。
   * @param param2    累加器的阀值。
   * @param minRadius 最小圆半径。
   * @param maxRadius 最大圆半径。
   */
  public Mat houghCircles(
      Mat sourceMat, Mat circles, int method, double dp,
      double minDist, double param1, double param2, int minRadius, int maxRadius) {
    Imgproc.HoughCircles(sourceMat, circles,
        method, dp, minDist, param1, param2, minRadius, maxRadius);
    return circles;
  }

  /**
   * 获取中值point.
   * point[]是无序的
   * 项目中统一定义point[]顺序左上右下顺时针顺序.
   */
  public List<Point> getMedianPoint(List<Point> point1, List<Point> point2) {
    if (point1.size() != point2.size()) {
      throw new UnsupportedOperationException("error.point1 & point2 length not the same.");
    }
    pointSort(point1);
    pointSort(point2);
    ArrayList<Point> lists = new ArrayList<>();
    for (int i = 0; i < point1.size(); i++) {
      lists.add(new Point((point1.get(i).x + point2.get(i).x) / 2,
          (point1.get(i).y + point2.get(i).y) / 2));
    }
    return lists;

  }

  /**
   * points排序.
   * (1)左上
   * (2)右上
   * (3)右下
   * (4)左下
   */
  public void pointSort(List<Point> points) {
    //y升序
    points.sort((o1, o2) -> {
      if (o1.y > o2.y) {
        return 1;
      }
      if (o1.y == o2.y) {
        return 0;
      }
      if (o1.y < o2.y) {
        return -1;
      }
      return -1;
    });
    if (points.get(0).x > points.get(1).x) {
      swap(points, 0, 1);
    }
    if (points.get(2).x < points.get(3).x) {
      swap(points, 2, 3);
    }
  }

  /**
   * 为M矩阵定制的工具算法.
   */
  public MatOfPoint2f rectVertexSort(MatOfPoint2f matOfPoint2f) {
    List<Point> points = matOfPoint2f.toList();
    pointSort(points);
    Point[] pointsTemp = new Point[points.size()];
    Point[] objects = points.toArray(pointsTemp);
    return new MatOfPoint2f(objects);
  }

  /**
   * 制作三点或者四点的M矩阵,用于透射变换.
   *
   * @param src 原图像的要被变换点坐标构成的mat对象.
   * @param dst 变换结果坐标构成的mat对象.
   *            ps: 重点是两个mat对象的坐标顺序要一一对应,结果以dst为准.
   *            结果应该是盖在黑布上,我没测过.
   */
  public Mat getPerspectiveTransform(Mat src, Mat dst) {
    return Imgproc.getPerspectiveTransform(src, dst);
  }

  /**
   * 由四对点计算透射变换.
   *
   * @param sourceMat 原图
   * @param m         getPerspectiveTransform 方法构造的M矩阵.
   * @param size      结果图像的大小
   */
  public Mat warpPerspective(Mat sourceMat, Mat m, Size size) {
    Mat dstMat = new Mat();
    Imgproc.warpPerspective(sourceMat, dstMat, m, size);
    return dstMat;
  }

  /**
   * 复制图像并且制作边界.
   */
  public Mat copyMakeBorder(Mat sourceMat) {
    Mat dstMat = new Mat();
    Core.copyMakeBorder(sourceMat, dstMat, 5, 5, 5, 5, Core.BORDER_CONSTANT);
    return dstMat;
  }

  /**
   * 检测出物体的轮廓.
   * (实际使用过程中发现除了内外有区别,其余那几个参数没有什么卵用,没找到找几个参数的精髓)
   *
   * @param mode   取值一:RETR_EXTERNAL 只检测最外围轮廓,包含在外围轮廓内的内围轮廓被忽略
   *               取值二:RETR_LIST   检测所有的轮廓,包括内围、外围轮廓,但是检测到的轮廓不建立等级关
   *               系,彼此之间独立,没有等级关系,这就意味着这个检索模式下不存在父轮廓或内嵌轮廓,
   *               所以hierarchy向量内所有元素的第3、第4个分量都会被置为-1,具体下文会讲到
   *               取值三:RETR_CCOMP  检测所有的轮廓,但所有轮廓只建立两个等级关系,外围为顶层,若外围
   *               内的内围轮廓还包含了其他的轮廓信息,则内围内的所有轮廓均归属于顶层
   *               取值四:RETR_TREE, 检测所有轮廓,所有轮廓建立一个等级树结构。外层轮廓包含内层轮廓,内
   *               层轮廓还可以继续包含内嵌轮廓。
   * @param method 取值一:CHAIN_APPROX_NONE 保存物体边界上所有连续的轮廓点到contours向量内
   *               取值二:CHAIN_APPROX_SIMPLE 仅保存轮廓的拐点信息,把所有轮廓拐点处的点保存入contours
   *               向量内,拐点与拐点之间直线段上的信息点不予保留
   *               取值三和四:CHAIN_APPROX_TC89_L1,CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近
   *               似算法
   *               其余参数 point
   *               Point偏移量,所有的轮廓信息相对于原始图像对应点的偏移量,相当于在每一个检测出的轮廓点上加
   *                           上该偏移量,并且Point还可以是负值!
   */
  public ArrayList<MatOfPoint> findContours(Mat sourceMat, int mode, int method) {
    ArrayList<MatOfPoint> contours = new ArrayList<>();
    Mat dstMat = new Mat();
    Imgproc.findContours(sourceMat.clone(), contours, dstMat, mode, method);
    return contours;
  }

  /**
   * 绘制轮廓,下面都是重载方法,在其他demo中又被使用,建议用这个,我主要用这个打印带有轮廓的mat信息看看对不对.
   *
   * @param sourceMat            二值图像的mat
   * @param contours             轮廓点集合
   * @param absolutePathFileName 打印的文件绝对路径
   */
  public Mat drawContours(
      Mat sourceMat, ArrayList<MatOfPoint> contours, String absolutePathFileName) {
    if (Constants.isDebugger()) {
      //为了不去改变原图,clone一个,用这个方法的目的主要用来打印.
      Mat tempMat = sourceMat.clone();
      tempMat = cvtColor(tempMat, Imgproc.COLOR_GRAY2BGR);
      drawContours(tempMat, contours, -1, new Scalar(0, 255, 255), 2);
      writePic(tempMat, absolutePathFileName);
    }
    return sourceMat;
  }

  /**
   * 绘制轮廓,(这个方法写的有问题,生成的mat是一个白色调的).
   */
  public Mat drawContours(Mat sourceMat, ArrayList<MatOfPoint> contours) {
    Mat dstMat = new Mat(sourceMat.size(), CV_8U, new Scalar(255));
    Imgproc.drawContours(dstMat, contours, -1, new Scalar(0, 255, 255), 1);
    return dstMat;
  }

  /**
   * 绘制轮廓基础,用于绘制找到的图像轮廓.
   */
  public Mat drawContours(
      Mat sourceMat, ArrayList<MatOfPoint> contours, int contourIdx, Scalar color) {
    Imgproc.drawContours(sourceMat, contours, contourIdx, color);
    return sourceMat;
  }

  /**
   * 绘制轮廓基础,用于绘制找到的图像轮廓.
   *
   * @param thickness 画线的宽度
   */
  public Mat drawContours(
      Mat sourceMat, ArrayList<MatOfPoint> contours, int contourIdx, Scalar color, int thickness) {
    Imgproc.drawContours(sourceMat, contours, contourIdx, color, thickness);
    return sourceMat;
  }

  /**
   * 升序排列,其实这个方法是有问题的,暂时放在这里当做参考吧.
   */
  public MatOfPoint2f sortByXandY(MatOfPoint2f matOfPoint2f) {
    List<Point> points = matOfPoint2f.toList();
    //x升序
    points.sort((o1, o2) -> {
      if (o1.x > o2.x) {
        return 1;
      }
      if (o1.x == o2.x) {
        return 0;
      }
      if (o1.x < o2.x) {
        return -1;
      }
      return -1;
    });
    //y升序
    points.sort((o1, o2) -> {
      if (o1.y > o2.y) {
        return 1;
      }
      if (o1.y == o2.y) {
        return 0;
      }
      if (o1.y < o2.y) {
        return -1;
      }
      return -1;
    });
    Point[] pointsTemp = new Point[points.size()];
    Point[] objects = points.toArray(pointsTemp);
    return new MatOfPoint2f(objects);
  }

  /**
   * MatOfPoint -> MatOfPoint2f 工具类.
   */
  public ArrayList<MatOfPoint2f> pot2pot2f(List<MatOfPoint> contours) {
    ArrayList<MatOfPoint2f> newContours = new ArrayList<>();
    for (MatOfPoint point : contours) {
      MatOfPoint2f newPoint = new MatOfPoint2f(point.toArray());
      newContours.add(newPoint);
    }
    return newContours;
  }

  /**
   * MatOfPoint2f -> MatOfPoint  工具类.
   */
  public ArrayList<MatOfPoint> pot2f2pot(List<MatOfPoint2f> contours) {
    ArrayList<MatOfPoint> newContours = new ArrayList<>();
    for (MatOfPoint2f point : contours) {
      MatOfPoint newPoint = new MatOfPoint(point.toArray());
      newContours.add(newPoint);
    }
    return newContours;
  }


  /**
   * 主要功能是把一个连续光滑曲线折线化,对图像轮廓点进行多边形拟合.
   *
   * @param curve       一般是由图像的轮廓点组成的点集
   * @param approxCurve 表示输出的多边形点集
   * @param epsilon     主要表示输出的精度,就是另个轮廓点之间最大距离数,5,6,7,,8,,,,,
   * @param closed      表示输出的多边形是否封闭
   */
  public void approxPolyDp(
      MatOfPoint2f curve, MatOfPoint2f approxCurve, double epsilon, boolean closed) {
    Imgproc.approxPolyDP(curve, approxCurve, epsilon, closed);
  }

  /**
   * 计算图像轮廓的周长.
   *
   * @param curve  表示图像的轮廓
   * @param closed 表示轮廓是否封闭的
   */
  public double arcLength(MatOfPoint2f curve, boolean closed) {
    return Imgproc.arcLength(curve, closed);
  }

  /**
   * 打印点.
   */
  public void printPoint(
      Mat sourceMat, int lx, int ly, int rx, int ry) {
    Point pointLt = new Point(lx, ly);
    Point pointRb = new Point(rx, ry);
    //黄色
    Scalar color = new Scalar(0, 255, 255);
    Imgproc.rectangle(sourceMat, pointLt, pointRb, color, 4);
  }

  /**
   * 打印图像.
   */
  public void printRect(
      Mat sourceMat, int x, int y, int width, int height) {
    Rect rect = new Rect(x, y, width, height);
    //黄色
    Scalar color = new Scalar(255, 255, 0);
    Imgproc.rectangle(sourceMat, rect, color, 10);
  }

  /**
   * 显示图片.
   */
  public void show(Mat sourceMat) {
    imshow("test", sourceMat);
  }

  /**
   * Mat 打印.
   */
  public void printMatParams(Mat sourceMat) {
    String msg = "\nrows:" + sourceMat.rows();
    msg += "\ncols:" + sourceMat.cols();
    msg += "\nheight:" + sourceMat.height();
    msg += "\nwidth:" + sourceMat.width();
    //每个元素的大小
    msg += "\nelemSide:" + sourceMat.elemSize();
    //每个通道的大小
    msg += "\nelemSide1:" + sourceMat.elemSize1();
    //求step1(i):每一维元素的通道数
    msg += "\nstep1:" + sourceMat.step1();
    msg += "\nstep1(0):" + sourceMat.step1(0);//面
    msg += "\nstep1(1):" + sourceMat.step1(1);//线的大小(第二维)
    msg += "\nstep1(2):" + sourceMat.step1(2);//点的大小(第三维)
    //求size(i):每一维元素的个数
    msg += "\nsize(0):" + sourceMat.size(0);//面
    msg += "\nsize(1):" + sourceMat.size(1);//线
    msg += "\nsize(2):" + sourceMat.size(2);//点
    //msg += "\neye:" + Mat.eye(sourceMat.rows(), sourceMat.cols(), CvType.CV_8UC1);
    //msg += "\ndump:" + sourceMat.dump();
    log.info("info.mat params:" + msg);
  }
}

我做了一个demo可以用来识别答题卡的程序,附上几张图记录一下思考的过程。

SAT答题卡识别流程图

sat-process-2

sat-process-3

sat-process-4

sat-process-5

sat-process-7

sat-process-8

sat-process-9

sat-process-10

sat-process-11

sat-process-12

sat-process-13


标题:搭建支持opencv的java环境
作者:LeqiangBian
地址:https://shopingpro.top/articles/2019/08/27/1566878539739.html

评论