龙哥网

龙哥网

opencv java windows 环境搭建
2022-03-01

博文目录

文章目录

  • OpenCV
  • 下载安装
  • Java 加载 dll 库
  • Maven 工程配置
    • 测试 OpenCV 加载是否正常
    • 工具类 MatKit 将 Mat 转换为 BufferedImage 并弹窗展示
    • 第一个示例程序 模板匹配(大图中找小图, 不支持旋转缩放找图)

OpenCV

OpenCV 官网

OpenCV 是使用C/C++开发的, 然后开放了其他多种语言的调用入口, java利用JNI的方式调用OpenCV函数

下载安装

OpenCV GitHub Releases

到OpenCV的GitHub的Releases页面找到当前最新版本, 下载其中的 opencv-4.5.5-vc14_vc15.exe 安装包

安装好后文件夹内容大概如下

我们需要的是 opencv > build > java 下的内容

其中, opencv-454.jar 是我们需要引入的依赖 jar, 其内部方法全是本地(native)调用 x64 下的 opencv_java454.dll 中的实现, 这两个文件都需要引入到我们的工程中

Java 加载 dll 库

Java加载dll或so库文件的路径 java.library.path

Java的System.load 和 System.loadLibrary都可以用来加载库文件

例如你可以这样载入一个windows平台下JNI库文件:
System.load("C://Documents and Settings//TestJNI.dll"); // 绝对路径
System.loadLibrary ("TestJNI"); // 参数为库文件名
这里TestJNI必须在 java.library.path这一jvm变量所指向的路径中,可以查看 System.getProperty("java.library.path"); 默认情况下,Windows平台下包含下面的路径:

  • 和jre相关的目录
  • 程序当前目录
  • Windows目录
  • 系统目录(system32)
  • 系统环境变量path指定的目录

在linux下添加一个java.library.path的方法如下:在/etc/profile 后面加上一行 export LB_LIBRARY_PATH=路径

也可以在执行程序的时候可以显式指定,使用jvm参数 -Djava.library.path=路径,这种会清除掉预设置的java.library.path的值 。实例如下:
java -jar -Djava.library.path=/home/fly/Desktop/sound_dream sound.war

Maven 工程配置

使用Java开发OpenCV3程序-1.开发环境安装与配置
使用Java开发OpenCV3程序-2.读取并显示一幅图片

在工程根目录下新建 lib 目录, 将依赖的 jar 和 dll 都复制过来, 不和本地文件目录产生强关联

在 pom.xml 里添加 jar 的本地引用

<dependency>
    <groupId>opencv</groupId>
    <artifactId>opencv</artifactId>
    <version>454</version>
    <scope>system</scope>
    <systemPath>${project.basedir}/lib/opencv-454.jar</systemPath>
</dependency>

需确保程序运行时, 系统已载入 dll, System.loadLibrary 需要提前配置环境变量或添加JVM参数, 且JVM参数需要用到绝对路径? 会本地文件目录产生强关联, 所以我们推荐使用 System.load 的方式, 这样可以通过代码获取到工程路径
System.load(System.getProperty("user.dir") + "\\lib\\opencv_java454.dll");

测试 OpenCV 加载是否正常

package com.mrathena.opencv.starter;

import lombok.extern.slf4j.Slf4j;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.Scalar;
import org.opencv.core.Size;

import java.lang.reflect.Field;

@Slf4j
public class Starter { 

	/* 使用Java开发OpenCV3程序-1.开发环境安装与配置 https://www.cnblogs.com/superbool/p/5323211.html 用Java开发OpenCV3程序-2.读取并显示一幅图片 https://www.cnblogs.com/superbool/p/5331196.html 使用Java开发OpenCV3程序-3.OpenCV的组件结构以及java版本的数据结构 https://www.cnblogs.com/superbool/p/5426890.html */
	public static void main(String[] args) { 
		try { 
			// 方式1
			System.load(System.getProperty("user.dir") + "\\lib\\opencv_java454.dll");
			// 方式2
			// 需要 jvm 参数 -Djava.library.path=D:\\resource\\develop\\opencv\\build\\java\\x64
			// 也可以将该 dll 复制到工程目录下, 通过代码修改添加依赖目录
// Starter.addLibraryDir(System.getProperty("user.dir") + "\\lib");
// System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
			// 测试代码
			Mat mat = Mat.eye(3, 3, CvType.CV_8UC1);
			log.info("{}", mat.dump());
		} catch (Throwable cause) { 
			cause.printStackTrace();
		}
	}

	public static void addLibraryDir(String path) { 
		try { 
			Field field = ClassLoader.class.getDeclaredField("usr_paths");
			field.setAccessible(true);
			String[] paths = (String[]) field.get(null);
			String[] tmp = new String[paths.length + 1];
			System.arraycopy(paths, 0, tmp, 0, paths.length);
			tmp[paths.length] = path;
			field.set(null, tmp);
		} catch (Throwable cause) { 
			cause.printStackTrace();
		}
	}

}

工具类 MatKit 将 Mat 转换为 BufferedImage 并弹窗展示

package com.mrathena.opencv.starter;

import org.opencv.core.Mat;

import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;

/** * 参考自 * 用Java开发OpenCV3程序-2.读取并显示一幅图片 * https://www.cnblogs.com/superbool/p/5331196.html */
public class MatKit { 

	public static void showImage(Mat mat) { 
		showImage(mat, null);
	}

	public static void showImage(Mat mat, String windowName) { 
		try { 
			// 本来是组件的原始皮肤, 这样写会加载适合本系统的好看的皮肤
			UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
		} catch (Throwable cause) { 
			cause.printStackTrace();
		}

		Image image = toBufferedImage(mat);

		JLabel label = new JLabel(new ImageIcon(image));
		JScrollPane scrollPane = new JScrollPane(label);
		// 不设置大小而设置必要时显示滚动条, 则窗口达到屏幕最大时才展示滚动条
// scrollPane.setPreferredSize(new Dimension(width, height));
		scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
		scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);

		JFrame frame = new JFrame(windowName);
		frame.add(scrollPane, BorderLayout.CENTER);
		frame.pack();
		frame.setLocationRelativeTo(null);
		frame.setVisible(true);
		// 用户点击窗口关闭
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}

	private static Image toBufferedImage(Mat mat) { 
		int type = BufferedImage.TYPE_BYTE_GRAY;
		if (mat.channels() > 1) { 
			type = BufferedImage.TYPE_3BYTE_BGR;
		}
		int bufferSize = mat.channels() * mat.cols() * mat.rows();
		byte[] buffer = new byte[bufferSize];
		// 获取所有的像素点
		mat.get(0, 0, buffer);
		BufferedImage image = new BufferedImage(mat.cols(), mat.rows(), type);
		final byte[] targetPixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
		System.arraycopy(buffer, 0, targetPixels, 0, buffer.length);
		return image;
	}

}

第一个示例程序 模板匹配(大图中找小图, 不支持旋转缩放找图)

package com.mrathena.opencv.starter;

import com.mrathena.exception.ServiceException;
import lombok.extern.slf4j.Slf4j;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.Point;
import org.opencv.core.Rect;
import org.opencv.core.Scalar;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;

@Slf4j
public class 模板匹配 { 

	public static void main(String[] args) { 
		System.load(System.getProperty("user.dir") + "\\lib\\opencv_java454.dll");

		String big = System.getProperty("user.dir") + "\\image\\big.bmp";
		String small = System.getProperty("user.dir") + "\\image\\small.png";
		System.out.println(find(big, small));
		findAndShow(big, small);
	}

	public static Point find(String big, String small) { 
		try { 
			Mat bigMat = Imgcodecs.imread(big);
			Mat templateMat = Imgcodecs.imread(small);

			int method = Imgproc.TM_CCORR_NORMED;
			int width = bigMat.cols() - templateMat.cols() + 1;
			int height = bigMat.rows() - templateMat.rows() + 1;
			Mat result = new Mat(width, height, CvType.CV_32FC1);

			Imgproc.matchTemplate(bigMat, templateMat, result, method);
			Core.normalize(result, result, 0, 1, Core.NORM_MINMAX, -1, new Mat());
			Core.MinMaxLocResult mmr = Core.minMaxLoc(result);

			return (method == Imgproc.TM_SQDIFF_NORMED || method == Imgproc.TM_SQDIFF) ? mmr.minLoc : mmr.maxLoc;
		} catch (Throwable cause) { 
			throw new ServiceException(cause);
		}
	}

	public static void findAndShow(String big, String small) { 
		try { 
			Mat bigMat = Imgcodecs.imread(big);
			Mat templateMat = Imgcodecs.imread(small);

			int method = Imgproc.TM_CCORR_NORMED;
			int width = bigMat.cols() - templateMat.cols() + 1;
			int height = bigMat.rows() - templateMat.rows() + 1;
			Mat result = new Mat(width, height, CvType.CV_32FC1);

			Imgproc.matchTemplate(bigMat, templateMat, result, method);
			Core.normalize(result, result, 0, 1, Core.NORM_MINMAX, -1, new Mat());
			Core.MinMaxLocResult mmr = Core.minMaxLoc(result);

			Point point = (method == Imgproc.TM_SQDIFF_NORMED || method == Imgproc.TM_SQDIFF) ? mmr.minLoc : mmr.maxLoc;

			// 在大图里标记找到的小图(画矩形)
			Imgproc.rectangle(bigMat, new Rect((int) point.x, (int) point.y, templateMat.cols(), templateMat.rows()), new Scalar(0, 255, 0));

			MatKit.showImage(bigMat);

		} catch (Throwable cause) { 
			throw new ServiceException(cause);
		}
	}

}



执行结果如下, 大图的左上角位置是(0,0), 数字是小图左上角在大图中的相对位置, 第一个是横向, 第二个是纵向

免责声明
本站部分资源来源于互联网 如有侵权 请联系站长删除
龙哥网是优质的互联网科技创业资源_行业项目分享_网络知识引流变现方法的平台为广大网友提供学习互联网相关知识_内容变现的方法。