欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 教育 > 高考 > Java使用OpenCV计算两张图片相似度

Java使用OpenCV计算两张图片相似度

2025/5/9 6:07:05 来源:https://blog.csdn.net/m0_64346565/article/details/139445794  浏览:    关键词:Java使用OpenCV计算两张图片相似度

业务:找出两个表的重复的图片。

图片在表里存的是二进制值,存在大量由于一些特殊情况例如扫描有差异,导致图片存的二进制值不同,但图片其实是一样来的。

所以找出两个表重复相同的图片,不可能只是单纯的比较二进制值相等。

方法:针对这种情况,使用OpenCV直方图算法可以比较两张图片的相似度,测试发现完全相同的图片相似度等于1(表里存的二进制值不相等)

实操:Java引入使用opencv步骤详解

1.引入opencv依赖

<!-- https://mvnrepository.com/artifact/org.openimaj/core -->
<dependency><groupId>org.openpnp</groupId><artifactId>opencv</artifactId><version>4.5.5-1</version>
</dependency>

2.代码Demo

opencv提供了均方差算法(MSE)、结构相似性指数算法(SSIM)、峰值信噪比算法(PSNR)、直方图算法(SSIM-WH),其中使用直方图算法来比较图片相似效果最好。

    public static void main(String[] args) {// 加载OpenCV库System.loadLibrary(Core.NATIVE_LIBRARY_NAME);// 读取两张图像。准备比对的图片Mat image1 = Imgcodecs.imread("D:\\work\\testdata\\psc_1716260008343.jpg");Mat image2 = Imgcodecs.imread("D:\\work\\testdata\\psc_1716260008345.jpg");// 将图片处理成一样大Imgproc.resize(image1, image1, image2.size());Imgproc.resize(image2, image2, image1.size());// 计算均方差(MSE)double mse = calculateMSE(image1, image2);System.out.println("均方差(MSE): " + mse);// 计算结构相似性指数(SSIM)double ssim = calculateSSIM(image1, image2);System.out.println("结构相似性指数(SSIM): " + ssim);// 计算峰值信噪比(PSNR)double psnr = calculatePSNR(image1, image2);System.out.println("峰值信噪比(PSNR): " + psnr);// 计算直方图final double similarity = calculateHistogram(image1, image2);System.out.println("图片相似度(直方图): " + similarity);// 计算归一化交叉相关(NCC)
//        double ncc = calculateNCC(image1, image2);
//        System.out.println("归一化交叉相关(NCC): " + ncc);}// 计算均方差(MSE)private static double calculateHistogram(Mat image1, Mat image2) {// 计算直方图Mat hist1 = calculateHistogram(image1);Mat hist2 = calculateHistogram(image2);// 计算相似度final double similarity = Imgproc.compareHist(hist1, hist2, Imgproc.CV_COMP_CORREL);// 手动释放内存
//        if (hist1 != null) {
//            hist1.release();
//        }
//        if (hist2 != null) {
//            hist2.release();
//        }return similarity;}// 计算均方差(MSE)private static double calculateMSE(Mat image1, Mat image2) {Mat diff = new Mat();Core.absdiff(image1, image2, diff);Mat squaredDiff = new Mat();Core.multiply(diff, diff, squaredDiff);Scalar mseScalar = Core.mean(squaredDiff);return mseScalar.val[0];}// 计算结构相似性指数(SSIM)private static double calculateSSIM(Mat image1, Mat image2) {Mat image1Gray = new Mat();Mat image2Gray = new Mat();Imgproc.cvtColor(image1, image1Gray, Imgproc.COLOR_BGR2GRAY);Imgproc.cvtColor(image2, image2Gray, Imgproc.COLOR_BGR2GRAY);MatOfFloat ssimMat = new MatOfFloat();Imgproc.matchTemplate(image1Gray, image2Gray, ssimMat, Imgproc.CV_COMP_CORREL);Scalar ssimScalar = Core.mean(ssimMat);return ssimScalar.val[0];}// 计算峰值信噪比(PSNR)private static double calculatePSNR(Mat image1, Mat image2) {Mat diff = new Mat();Core.absdiff(image1, image2, diff);Mat squaredDiff = new Mat();Core.multiply(diff, diff, squaredDiff);Scalar mseScalar = Core.mean(squaredDiff);double mse = mseScalar.val[0];double psnr = 10.0 * Math.log10(255.0 * 255.0 / mse);return psnr;}// 计算归一化交叉相关(NCC)
//    private static double calculateNCC(Mat image1, Mat image2) {
//        Mat image1Gray = new Mat();
//        Mat image2Gray = new Mat();
//        Imgproc.cvtColor(image1, image1Gray, Imgproc.COLOR_BGR2GRAY);
//        Imgproc.cvtColor(image2, image2Gray, Imgproc.COLOR_BGR2GRAY);
//        MatOfInt histSize = new MatOfInt(256);
//        MatOfFloat ranges = new MatOfFloat(0, 256);
//        Mat hist1 = new Mat();
//        Mat hist2 = new Mat();
//
//        Core.normalize(hist1, hist1, 0, 1, Core.NORM_MINMAX);
//        Core.normalize(hist2, hist2, 0, 1, Core.NORM_MINMAX);
//        double ncc = Core.compareHist(hist1, hist2, Imgproc.CV_COMP_CORREL);
//        return ncc;
//    }private static Mat calculateHistogram(Mat image) {Mat hist = new Mat();// 设置直方图参数MatOfInt histSize = new MatOfInt(256);MatOfFloat ranges = new MatOfFloat(0, 256);MatOfInt channels = new MatOfInt(0);List<Mat> images = new ArrayList<>();images.add(image);// 计算直方图Imgproc.calcHist(images, channels, new Mat(), hist, histSize, ranges);return hist;}

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)

byte[] ecf2Image = bsImage.getImage();
byte[] upsImage = upsPage.getScanPage();
// 1.先直接对比ecf2和ups图片的二进制值
if (Arrays.equals(ecf2Image, upsImage)) {// 二进制值相等则给ecf2图片状态更新为重复的updateAndRecord(shipmentNo, filename, upsPage, 1);// 然后跳出scanPageList的循环,已经确认为重复就不用再去匹配break;
}// 2.byte值不等,再用OpenCV来比较
// 将图片二进制数据转换为Mat对象
Mat image1 = Imgcodecs.imdecode(new MatOfByte(ecf2Image), Imgcodecs.IMREAD_COLOR);
Mat image2 = Imgcodecs.imdecode(new MatOfByte(upsImage), Imgcodecs.IMREAD_COLOR);
try {// 将图片处理成一样大Imgproc.resize(image1, image1, image2.size());Imgproc.resize(image2, image2, image1.size());// 计算直方图final double similarity = calculateHistogram(image1, image2);if (similarity == 1) {// 更新状态为重复的updateAndRecord(shipmentNo, filename, upsPage, 2);break;}
} catch (Exception e) {e.printStackTrace();
} finally {// 手动释放内存if (image1 != null) {image1.release();}if (image2 != null) {image2.release();}
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词