Windows平台编译opencv_contrib for java以及对SIFT(SURF)特征检测算法的使用(附编译好的dll jar包)

引言

近几个月来一直在使用opencv,但无奈于网络上用java语言做图像处理的少之又少,很多时候找不到资料。

最近需要使用opencv的超分和SIFT等特征检测算法,查阅了几天资料踩了一些坑,最终得到了可以使用的opencv_contrib的dll以及jar包,随后会附赠到文章中(某隔壁网站拿一些没法用的资源收费简直是损阴德。)

准备工具

  • VS2019(请务必选择2019,使用2022编译时可能出现未知错误)
  • opencv和opencv_contrib的包(OpenCV (github.com)
  • cmake安装并添加环境变量
  • ant安装并添加环境变量
  • python环境安装并添加环境变量

编译

  1. 打开cmake选择opencv的源代码以及编译后存放的文件夹
  1. 点击Configure选择VS2019,确认后开始编译
  • 由于网络问题,opencv源文件中的.cache文件夹中需要下载的文件下载失败。

可以直接查看cmake的错误日志,前往github下载所需下载的文件放入.cache文件夹中即可正常编译

附上4.1.2所需的.cache链接 链接:pan.baidu.com/s/1V-yUfMW_... 提取码:hgqd

  1. 勾选cmake中的以下选项,点击generate
  • 搜索java,勾选BUILD_JAVA、BUILD_opencv_java、BUILD_opencv_java_bindings_generator
  • 搜索nonf,勾选OPENCV_ENABLE_NONFREE(对SIFT等算法的支持)
  • 搜索share,取消勾选BUILD_SHARED_LIBS(让生成的dll都在一个文件中)
  • 搜索ant,在ANT_EXECUTABLE行添加ant.bat批处理文件的路径
  • 搜索module,在OPENCV_EXTRA_MODUELES_PATH行添加下载的opencv_contrib源代码中的module文件夹路径

可能遇到的问题:下载超时

在opencv_contrib/modules/face/CMakeList.txt中将下载链接改成如下图所示raw.sevencdn.com

  1. 等待编译完成后,点击Open Project

打开VS2019,重新生成解决方案

待编译完成后,在CMakeTargets中的INSTALL-仅用于项目-仅重新生成。等待编译结束即可在Cmake中选择的目标文件夹下的install目录中找到java所需相关包

附赠链接,opencv4.1.2包含conrtib模块的jar包和dll拓展文件

链接:pan.baidu.com/s/16fZfbpah... 提取码:s4y4 --来自百度网盘超级会员V6的分享

SIFT算法的使用

不得不说用JAVA写这个的资料是更少......在这里我只放核心模块,具体可以直接执行的代码随后会放参考链接,按照链接博客里的代码稍微改动就可以直接使用

其中nndrRatio博主填写的是0.7f,算是一个效果比较好的值,各位可以自行尝试调整。

java 复制代码
 public void matchImage(BufferedImage templateImageB, BufferedImage originalImageB) {

        Mat resT = new Mat();
        Mat resO = new Mat();

        //即当detector 又当Detector
        SIFT sift = SIFT.create();

        Mat templateImage = getMatify(templateImageB);
        Mat originalImage = getMatify(originalImageB);

        MatOfKeyPoint templateKeyPoints = new MatOfKeyPoint();
        MatOfKeyPoint originalKeyPoints = new MatOfKeyPoint();

        //获取模板图的特征点
        sift.detect(templateImage, templateKeyPoints);
        sift.detect(originalImage, originalKeyPoints);

        
        sift.compute(templateImage, templateKeyPoints, resT);
        sift.compute(originalImage, originalKeyPoints, resO);

        List<MatOfDMatch> matches = new LinkedList();
        DescriptorMatcher descriptorMatcher = DescriptorMatcher.create(DescriptorMatcher.FLANNBASED);
        System.out.println("寻找最佳匹配");

        printPic("ptest", templateImage);
        printPic("ptesO", originalImage);

        printPic("test", resT);
        printPic("tesO", resO);

        /**
         * knnMatch方法的作用就是在给定特征描述集合中寻找最佳匹配
         * 使用KNN-matching算法,令K=2,则每个match得到两个最接近的descriptor,然后计算最接近距离和次接近距离之间的比值,当比值大于既定值时,才作为最终match。
         */
        descriptorMatcher.knnMatch(resT, resO, matches, 2);
        System.out.println("计算匹配结果");
        LinkedList<DMatch> goodMatchesList = new LinkedList();
        //对匹配结果进行筛选,依据distance进行筛选
        matches.forEach(match -> {
            DMatch[] dmatcharray = match.toArray();
            DMatch m1 = dmatcharray[0];
            DMatch m2 = dmatcharray[1];

            if (m1.distance <= m2.distance * nndrRatio) {
                goodMatchesList.addLast(m1);
            }
        });

        matchesPointCount = goodMatchesList.size();
        //当匹配后的特征点大于等于 4 个,则认为模板图在原图中,该值可以自行调整
        if (matchesPointCount >= 4) {
            System.out.println("模板图在原图匹配成功!");

            List<KeyPoint> templateKeyPointList = templateKeyPoints.toList();
            List<KeyPoint> originalKeyPointList = originalKeyPoints.toList();
            LinkedList<Point> objectPoints = new LinkedList();
            LinkedList<Point> scenePoints = new LinkedList();
            goodMatchesList.forEach(goodMatch -> {
                objectPoints.addLast(templateKeyPointList.get(goodMatch.queryIdx).pt);
                scenePoints.addLast(originalKeyPointList.get(goodMatch.trainIdx).pt);
            });
            MatOfPoint2f objMatOfPoint2f = new MatOfPoint2f();
            objMatOfPoint2f.fromList(objectPoints);
            MatOfPoint2f scnMatOfPoint2f = new MatOfPoint2f();
            scnMatOfPoint2f.fromList(scenePoints);
            //使用 findHomography 寻找匹配上的关键点的变换
            Mat homography = Calib3d.findHomography(objMatOfPoint2f, scnMatOfPoint2f, Calib3d.RANSAC, 3);

            /**
             * 透视变换(Perspective Transformation)是将图片投影到一个新的视平面(Viewing Plane),也称作投影映射(Projective Mapping)。
             */
            Mat templateCorners = new Mat(4, 1, CvType.CV_32FC2);
            Mat templateTransformResult = new Mat(4, 1, CvType.CV_32FC2);
            templateCorners.put(0, 0, new double[]{0, 0});
            templateCorners.put(1, 0, new double[]{templateImage.cols(), 0});
            templateCorners.put(2, 0, new double[]{templateImage.cols(), templateImage.rows()});
            templateCorners.put(3, 0, new double[]{0, templateImage.rows()});
            //使用 perspectiveTransform 将模板图进行透视变以矫正图象得到标准图片
            Core.perspectiveTransform(templateCorners, templateTransformResult, homography);

            //矩形四个顶点  匹配的图片经过旋转之后就这个矩形的四个点的位置就不是正常的abcd了
            double[] pointA = templateTransformResult.get(0, 0);
            double[] pointB = templateTransformResult.get(1, 0);
            double[] pointC = templateTransformResult.get(2, 0);
            double[] pointD = templateTransformResult.get(3, 0);

            //指定取得数组子集的范围
//            int rowStart = (int) pointA[1];
//            int rowEnd = (int) pointC[1];
//            int colStart = (int) pointD[0];
//            int colEnd = (int) pointB[0];
            //rowStart, rowEnd, colStart, colEnd 好像必须左上右下  没必要从原图扣下来模板图了
//            Mat subMat = originalImage.submat(rowStart, rowEnd, colStart, colEnd);
//            printPic("yppt", subMat);

            //将匹配的图像用用四条线框出来
            Imgproc.rectangle(originalImage, new Point(pointA), new Point(pointC), new Scalar(0, 255, 0));
           /* Core.line(originalImage, new Point(pointA), new Point(pointB), new Scalar(0, 255, 0), 4);//上 A->B
            Core.line(originalImage, new Point(pointB), new Point(pointC), new Scalar(0, 255, 0), 4);//右 B->C
            Core.line(originalImage, new Point(pointC), new Point(pointD), new Scalar(0, 255, 0), 4);//下 C->D
            Core.line(originalImage, new Point(pointD), new Point(pointA), new Scalar(0, 255, 0), 4);//左 D->A*/

            MatOfDMatch goodMatches = new MatOfDMatch();
            goodMatches.fromList(goodMatchesList);
            Mat matchOutput = new Mat(originalImage.rows() * 2, originalImage.cols() * 2, Imgcodecs.IMREAD_COLOR);
            Features2d.drawMatches(templateImage, templateKeyPoints, originalImage, originalKeyPoints, goodMatches, matchOutput, new Scalar(0, 255, 0), new Scalar(255, 0, 0), new MatOfByte(), 2);
            Features2d.drawMatches(templateImage, templateKeyPoints, originalImage, originalKeyPoints, goodMatches, matchOutput, new Scalar(0, 255, 0), new Scalar(255, 0, 0), new MatOfByte(), 2);

            printPic("ppgc", matchOutput);
            printPic("ytwz", originalImage);
        } else {
            System.out.println("模板图不在原图中!");
        }
        printPic("模板特征点", resT);
    }


    public void printPic(String name, Mat pre) {
        Imgcodecs.imwrite(name + ".jpg", pre);
    }

    /**
     * 尝试把BufferedImage转换为Mat
     *
     * @param im
     * @return
     */
    public Mat getMatify(BufferedImage im) {
        BufferedImage bufferedImage = toBufferedImageOfType(im, BufferedImage.TYPE_3BYTE_BGR);
        //将bufferedimage转换为字节数组
        byte[] pixels = ((DataBufferByte) bufferedImage.getRaster().getDataBuffer()).getData();
//        byte[] pixels = ((DataBufferByte) im.getRaster().getDataBuffer()).getData();
        Mat image = new Mat(bufferedImage.getHeight(), bufferedImage.getWidth(), CvType.CV_8UC3);

        image.put(0, 0, pixels);

        return image;
    }

参考博客

【一】win10下编译opencv4.1.2,opencv_contrib for java_win10编译opencv cuda cudnn支持java_东街猫儿的博客-CSDN博客

Visual Studio版本号、MSVC版本、工具集版本号_查看msvc版本-CSDN博客

openCV4.4.0 基于SIFT特征值的图像匹配【java】。。。。。搞到吐_public picpoint_Sakura小败狗的博客-CSDN博客

结语

整理不易,觉得有用的话麻烦点点赞吧。 再次吐槽某些拿国外资源以及无法使用的资源进行付费的行为。

相关推荐
红米煮粥9 小时前
OpenCV-图像拼接
人工智能·opencv·计算机视觉
jndingxin10 小时前
OpenCV视频I/O(8)视频采集类VideoCapture之从视频源中读取一帧图像函数read()的使用
人工智能·opencv·音视频
Bill6610 小时前
OpenCV 形态学相关函数详解及用法示例
opencv·opencv形态学函数·腐蚀与膨胀·开运算与闭运算·顶帽与黑帽
翁乐安10 小时前
opencv-如何获取图像区域特定像素区域大小
人工智能·python·opencv
菜就多练_082810 小时前
《深度学习》OpenCV 背景建模 原理及案例解析
人工智能·深度学习·opencv
weixin_543662861 天前
一个简单的摄像头应用程序3
人工智能·opencv·计算机视觉
FL16238631292 天前
[C++]使用纯opencv部署yolov11目标检测onnx模型
c++·opencv·yolo
昱禹2 天前
记一次因视频编码无法在浏览器播放、编码视频报错问题
linux·python·opencv·ffmpeg·音视频
Death2002 天前
深入掌握 Qt 中的数据库操作:从基础到高级技巧
开发语言·数据库·c++·qt·opencv
Bill662 天前
OpenCV Canny()函数
人工智能·opencv·计算机视觉