Bootstrap

根据轮廓点坐标生成mask(java + opencv实现)

1. 背景

实验室的项目中有一个需求是:用户在前端界面的一张图片上勾勒出自己感兴趣的区域,后台根据该区域的顶点坐标生成一个mask。实际场景如下图所示。
在这里插入图片描述

2. 方法

目前已找到两种方式实现上述需求:

  • 使用OpenCV提供的API;
  • 利用OpenCV填充的思想(漫水填充法)自己写代码实现。

2.1 使用OpenCV提供的API实现

2.1.1 依赖文件下载

在使用OpenCV提供的API之前,需要事先配置好环境。先从opencv官网上下载所需的版本,我是在windows平台下进行开发的,所以直接下载的.exe文件(下载速度很慢且不稳地,可能需要科学上网),如果是在Linux上进行开发,可能需要下载源码进行编译。下载完成后运行,将文件解压到合适的目录下即可。对于Java开发,只需要找到解压目录下的这些文件即可。
opencv解压文件

2.1.1 IDE环境配置

我本人使用的是eclipse进行的开发,以下是eclipse中的配置方式。
首先在工程目录下新建一个“lib”文件夹,将之前解压的“opencv-412.jar”复制到该文件夹下。添加完成后,选中该jar包并点击右键,选择“Build path” —> “Add to Build Path”,将该jar包添加到项目中。在这里插入图片描述
然后右键刚添加的jar包,选择"Build Path" --> “Cofigure Build Path”。
在这里插入图片描述
在弹出的对话框中双击“Native library location”,在“location path”输入之前解压的目录下的x64文件的路径,因为该路径下含有opencv的动态库文件opencv-xxx.dll,Linux下编译完成后是opencv-xxx.so文件,java程序将通过JNI去调用opencv C++的动态库中的方法去实现相关功能。
在这里插入图片描述
点击"ok"和apply即完成了以上配置的过程。

2.1.2 代码

opencv中完成区域填充的关键函数是fillpoly函数,其实现填充的原理是“漫水填充法”。废话不多说,直接上代码。

/**
 * 通过 OpenCV 创建一张图的mask
 * @author tong
 */
public class CreateMaskByOpenCV {
   
	/**
	 * 创建一个掩膜
	 * @param width: 图片的宽度
	 * @param height: 图片的高度
	 * @param filePath: 文件保存的路径
	 * @param type: 文件的类型
	 * @param points: 轮廓的顶点
	 * @throws IOException 
	 */
	public static void create(int width, int height, String filePath, String type, List<Point> points) throws IOException {
   
		// 对输入的点进行预处理
		List<org.opencv.core.Point> list = new ArrayList<>();
		for (Point p : points) {
   
			list.add(new org.opencv.core.Point(p.getxAxis(), p.getyAxis()));
		}
		
		// 创建掩膜区域
		List<MatOfPoint> maskArea = new ArrayList<>();
		MatOfPoint maskPoints = new MatOfPoint();
		maskPoints.fromList(list);
		maskArea.add(maskPoints);
		
		// 构建掩膜
		Mat mask = new Mat(new Size(width, height), CvType.CV_8UC3, new Scalar(0, 0, 0));
		Imgproc.fillPoly(mask, maskArea, new Scalar(255, 255, 255));
		
		// 保存图片
		imWrite(filePath, mask, type);
	}
	
	
	/**
	 * 保存图片
	 * @param filePath
	 * @param mat
	 * @return
	 */
	private static boolean imWrite(String filePath, Mat mat, String type) {
   
		FileOutputStream fos = null;
		try {
   
			// 预处理
			File file = new File(filePath);
			if (file.exists()) file.delete();
			file.createNewFile();
			
			
			MatOfByte matOfByte = new MatOfByte();
			Imgcodecs.imencode("." + type, mat, matOfByte);
			matOfByte.toArray();
			
			// 将字节列表转为字节数组
			byte[] bytes = matOfByte.toArray();
		
			// 写文件
			fos = new FileOutputStream(new File(filePath));
			fos.write(bytes);
			
			// 返回结果
			return true;
		} catch (IOException e) {
   
			System.out.println("图片保存失败: " + e.getMessage());
		} finally {
   
			try {
   
				fos.close();
			} catch (IOException e) {
   
				System.out.println("图片保存失败: " + e.getMessage());
			}
		}
		return false;
	}
	
	
	public static void main(String[] args) {
   
		long start = System.currentTimeMillis();
		
		System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
		
		String filePath = "E:/projects/Java/json/智能配置文件/海康设备1_IPCamera1/mask1.bmp";
		List<Point> points = new ArrayList<>();
		points.add(new Point(334, 362));
		points.add(new Point(906, 300));
		points.add(new Point(934, 309));
		points.add(new Point(875, 1075));
		points.add(new Point(696, 1075));
		points.add(new Point(686, 921)
;