IT评测·应用市场-qidao123.com

标题: Java利用OpenCV盘算两张图片相似度 [打印本页]

作者: 傲渊山岳    时间: 2024-6-21 13:37
标题: Java利用OpenCV盘算两张图片相似度
业务:找出两个表的重复的图片。
图片在表里存的是二进制值,存在大量由于一些特殊情况例如扫描有差异,导致图片存的二进制值不同,但图片其实是一样来的。
所以找出两个表重复相同的图片,不可能只是单纯的比较二进制值相等。
方法:针对这种情况,利用OpenCV直方图算法可以比较两张图片的相似度,测试发现完全相同的图片相似度等于1(表里存的二进制值不相等)

实操:Java引入利用opencv步骤详解
1.引入opencv依赖

  1. <!-- https://mvnrepository.com/artifact/org.openimaj/core -->
  2. <dependency>
  3.         <groupId>org.openpnp</groupId>
  4.         <artifactId>opencv</artifactId>
  5.         <version>4.5.5-1</version>
  6. </dependency>
复制代码

2.代码Demo

opencv提供了均方差算法(MSE)、结构相似性指数算法(SSIM)、峰值信噪比算法(PSNR)、直方图算法(SSIM-WH),此中利用直方图算法来比较图片相似结果最好。
  1.     public static void main(String[] args) {
  2.         // 加载OpenCV库
  3.         System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
  4.         // 读取两张图像。准备比对的图片
  5.         Mat image1 = Imgcodecs.imread("D:\\work\\testdata\\psc_1716260008343.jpg");
  6.         Mat image2 = Imgcodecs.imread("D:\\work\\testdata\\psc_1716260008345.jpg");
  7.         // 将图片处理成一样大
  8.         Imgproc.resize(image1, image1, image2.size());
  9.         Imgproc.resize(image2, image2, image1.size());
  10.         // 计算均方差(MSE)
  11.         double mse = calculateMSE(image1, image2);
  12.         System.out.println("均方差(MSE): " + mse);
  13.         // 计算结构相似性指数(SSIM)
  14.         double ssim = calculateSSIM(image1, image2);
  15.         System.out.println("结构相似性指数(SSIM): " + ssim);
  16.         // 计算峰值信噪比(PSNR)
  17.         double psnr = calculatePSNR(image1, image2);
  18.         System.out.println("峰值信噪比(PSNR): " + psnr);
  19.         // 计算直方图
  20.         final double similarity = calculateHistogram(image1, image2);
  21.         System.out.println("图片相似度(直方图): " + similarity);
  22.         // 计算归一化交叉相关(NCC)
  23. //        double ncc = calculateNCC(image1, image2);
  24. //        System.out.println("归一化交叉相关(NCC): " + ncc);
  25.     }
  26.     // 计算均方差(MSE)
  27.     private static double calculateHistogram(Mat image1, Mat image2) {
  28.         // 计算直方图
  29.         Mat hist1 = calculateHistogram(image1);
  30.         Mat hist2 = calculateHistogram(image2);
  31.         // 计算相似度
  32.         final double similarity = Imgproc.compareHist(hist1, hist2, Imgproc.CV_COMP_CORREL);
  33.         // 手动释放内存
  34. //        if (hist1 != null) {
  35. //            hist1.release();
  36. //        }
  37. //        if (hist2 != null) {
  38. //            hist2.release();
  39. //        }
  40.         return similarity;
  41.     }
  42.     // 计算均方差(MSE)
  43.     private static double calculateMSE(Mat image1, Mat image2) {
  44.         Mat diff = new Mat();
  45.         Core.absdiff(image1, image2, diff);
  46.         Mat squaredDiff = new Mat();
  47.         Core.multiply(diff, diff, squaredDiff);
  48.         Scalar mseScalar = Core.mean(squaredDiff);
  49.         return mseScalar.val[0];
  50.     }
  51.     // 计算结构相似性指数(SSIM)
  52.     private static double calculateSSIM(Mat image1, Mat image2) {
  53.         Mat image1Gray = new Mat();
  54.         Mat image2Gray = new Mat();
  55.         Imgproc.cvtColor(image1, image1Gray, Imgproc.COLOR_BGR2GRAY);
  56.         Imgproc.cvtColor(image2, image2Gray, Imgproc.COLOR_BGR2GRAY);
  57.         MatOfFloat ssimMat = new MatOfFloat();
  58.         Imgproc.matchTemplate(image1Gray, image2Gray, ssimMat, Imgproc.CV_COMP_CORREL);
  59.         Scalar ssimScalar = Core.mean(ssimMat);
  60.         return ssimScalar.val[0];
  61.     }
  62.     // 计算峰值信噪比(PSNR)
  63.     private static double calculatePSNR(Mat image1, Mat image2) {
  64.         Mat diff = new Mat();
  65.         Core.absdiff(image1, image2, diff);
  66.         Mat squaredDiff = new Mat();
  67.         Core.multiply(diff, diff, squaredDiff);
  68.         Scalar mseScalar = Core.mean(squaredDiff);
  69.         double mse = mseScalar.val[0];
  70.         double psnr = 10.0 * Math.log10(255.0 * 255.0 / mse);
  71.         return psnr;
  72.     }
  73.     // 计算归一化交叉相关(NCC)
  74. //    private static double calculateNCC(Mat image1, Mat image2) {
  75. //        Mat image1Gray = new Mat();
  76. //        Mat image2Gray = new Mat();
  77. //        Imgproc.cvtColor(image1, image1Gray, Imgproc.COLOR_BGR2GRAY);
  78. //        Imgproc.cvtColor(image2, image2Gray, Imgproc.COLOR_BGR2GRAY);
  79. //        MatOfInt histSize = new MatOfInt(256);
  80. //        MatOfFloat ranges = new MatOfFloat(0, 256);
  81. //        Mat hist1 = new Mat();
  82. //        Mat hist2 = new Mat();
  83. //
  84. //        Core.normalize(hist1, hist1, 0, 1, Core.NORM_MINMAX);
  85. //        Core.normalize(hist2, hist2, 0, 1, Core.NORM_MINMAX);
  86. //        double ncc = Core.compareHist(hist1, hist2, Imgproc.CV_COMP_CORREL);
  87. //        return ncc;
  88. //    }
  89.     private static Mat calculateHistogram(Mat image) {
  90.         Mat hist = new Mat();
  91.         // 设置直方图参数
  92.         MatOfInt histSize = new MatOfInt(256);
  93.         MatOfFloat ranges = new MatOfFloat(0, 256);
  94.         MatOfInt channels = new MatOfInt(0);
  95.         List<Mat> images = new ArrayList<>();
  96.         images.add(image);
  97.         // 计算直方图
  98.         Imgproc.calcHist(images, channels, new Mat(), hist, histSize, ranges);
  99.         return hist;
  100.     }
复制代码

3.运行遇到的报错问题以及解决方法

Exception in thread "main" java.lang.UnsatisfiedLinkError: no opencv_java455 in java.library.path

报错缘故原由:
在JDK bin 目录下找不到 opencv_java455.dll 文件
解决方法:
官网下载地址:Releases - OpenCV
找到对应的版本下载opencv(如果下载不起很慢,可以复制链接用迅雷下载)


双击打开安装包选择安装提取目录


等候解压


在目录找到dll文件


然后复制到jdk bin目录中


再重新运行程序即可解决

4.运行


均方差算法(MSE):
盘算两幅图片每个像素之间的差异,并盘算它们的均匀值。MSE值越小,表现两幅图片越相似。
结构相似性指数(SSIM):
通过比较两幅图片的亮度、对比度和结构信息来评估它们的相似性。值越大,越相似。
峰值信噪比(PSNR):
通过盘算两幅图片的MSE值,并将其转换为对数标准,来评估它们的相似性。PSNR值越大,表现两幅图片越相似。
图片相似度(直方图):
通过将SSIM指数和直方图相似性组合起来,来评估两幅图片的相似性。返回的相似性度量值越靠近1,表现两幅图像越相似。

5.结合业务实当代码片断

注:务必手动释放Mat内存,亲测不写手动释放内存,随着循环量越多,创建Mat越多,就会导致内存瓦解泄露(按理说Java有采取机制,但我经过测试发现并没有触发采取内存,纵然是没用的Mat)
  1. byte[] ecf2Image = bsImage.getImage();
  2. byte[] upsImage = upsPage.getScanPage();
  3. // 1.先直接对比ecf2和ups图片的二进制值
  4. if (Arrays.equals(ecf2Image, upsImage)) {
  5.     // 二进制值相等则给ecf2图片状态更新为重复的
  6.     updateAndRecord(shipmentNo, filename, upsPage, 1);
  7.     // 然后跳出scanPageList的循环,已经确认为重复就不用再去匹配
  8.     break;
  9. }
  10. // 2.byte值不等,再用OpenCV来比较
  11. // 将图片二进制数据转换为Mat对象
  12. Mat image1 = Imgcodecs.imdecode(new MatOfByte(ecf2Image), Imgcodecs.IMREAD_COLOR);
  13. Mat image2 = Imgcodecs.imdecode(new MatOfByte(upsImage), Imgcodecs.IMREAD_COLOR);
  14. try {
  15.     // 将图片处理成一样大
  16.     Imgproc.resize(image1, image1, image2.size());
  17.     Imgproc.resize(image2, image2, image1.size());
  18.     // 计算直方图
  19.     final double similarity = calculateHistogram(image1, image2);
  20.     if (similarity == 1) {
  21.         // 更新状态为重复的
  22.         updateAndRecord(shipmentNo, filename, upsPage, 2);
  23.         break;
  24.     }
  25. } catch (Exception e) {
  26.     e.printStackTrace();
  27. } finally {
  28.     // 手动释放内存
  29.     if (image1 != null) {
  30.         image1.release();
  31.     }
  32.     if (image2 != null) {
  33.         image2.release();
  34.     }
  35. }
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 IT评测·应用市场-qidao123.com (https://dis.qidao123.com/) Powered by Discuz! X3.4