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

2023年 9月 25日 60.9k 0

引言

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

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

准备工具

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

编译

  • 打开cmake选择opencv的源代码以及编译后存放的文件夹
  • image.png

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

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

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

    • 若选择其他版本的VS,可能出现OpenCV does not recognize MSVC_VERSION “1933“等错误Visual Studio版本号、MSVC版本、工具集版本号_查看msvc版本-CSDN博客
      查阅VS对应的MSVC版本,在opencv/cmake/OpenCVDetectCXXCompiler.cmake文件中添加如图所示语句即可解决。

    image.png

  • 勾选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

    image.png

  • 等待编译完成后,点击Open Project
  • 打开VS2019,重新生成解决方案

    image.png

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

    image.png

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

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

    SIFT算法的使用

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

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

    
     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 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 goodMatchesList = new LinkedList();
            //对匹配结果进行筛选,依据distance进行筛选
            matches.forEach(match -> {
                DMatch[] dmatcharray = match.toArray();
                DMatch m1 = dmatcharray[0];
                DMatch m2 = dmatcharray[1];
    
                if (m1.distance = 4) {
                System.out.println("模板图在原图匹配成功!");
    
                List templateKeyPointList = templateKeyPoints.toList();
                List originalKeyPointList = originalKeyPoints.toList();
                LinkedList objectPoints = new LinkedList();
                LinkedList 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博客

    结语

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

    相关文章

    JavaScript2024新功能:Object.groupBy、正则表达式v标志
    PHP trim 函数对多字节字符的使用和限制
    新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
    使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
    为React 19做准备:WordPress 6.6用户指南
    如何删除WordPress中的所有评论

    发布评论