OpenCV Java入门三 Mat的基本操作

Posted TGITCIC

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OpenCV Java入门三 Mat的基本操作相关的知识,希望对你有一定的参考价值。

环境好了,我们就可以进入正文了。

在之前入门一、二中分别已经有画图的两个例子了。但没有细节展开我们的代码和OpenCV到底在干什么。

使用OpenCV时你需要补充的知识

你需要熟练使用Java Swing,或者是其它任何一门语言中关于GUI方面的编程。

我们这用的是OpenCV Java,因此对于Java Swing必须熟练。你可以安装eclipse 中的windowbuilder来帮助你做Swing的编程。

至于Java Swing中的界面、Frame、Panel、Button以及Layout,这块在“JDK核心技术卷1、卷2”中已有详细描述,我就不多此一举了。

Mat对象

OpenCV用来存储图像,很多时候都会用到这个Mat方法。数字图像可看做一个数值矩阵, 其中的每一个元素表明一个像素点。Mat在 OpenCV 中表示的是 N 维稠密矩阵,与稠密矩阵相对的是稀疏矩阵(只存储非零的像素值)。

Mat 类包含两部分,一是 矩阵头 (matrix header),二是 矩阵指针 (pointer to matrix),部分矩阵头以下:blog

int  flags;  // signaling the contents of the matrix
int  dims;   // dimensions
int  rows, cols;  // rows and columns
MatSize  size;  //
MatStep  step;  //

具体不作进一步展开,但我们要会使用这个Mat。

因此今天以Mat来做几个小练习。

Mat划线

package org.mk.opencv;

import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.highgui.HighGui;

public class DrawLine 

	public static void main(String[] args) 
		// 载入dll(必须先加载)
		System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
		Mat truth = new Mat(500, 500, CvType.CV_8UC3);

		byte[] line = new byte[truth.channels() * truth.width()];

		for (int i = 0; i < line.length; i++) 
			line[i] = 125;
		
		truth.put(250, 0, line);

		HighGui.imshow("原图", truth);
		HighGui.waitKey(0);

	


记得OpenCV上手都有一句“System.loadLibrary(Core.NATIVE_LIBRARY_NAME);”,是因为OpenCV Java虽然使用的是“opencv-343.jar”,实际它会去调用“opencv_java343.dll”,并且opencv_java343.dll有依赖,它会去找它自己在Windows的控制面板->系统变量->path中的依赖的那些opencv编译出来的包。

我不喜欢把opencv_java343.dll所依赖的这些DLL放到windows的安装目录的System32目录下。

因为你把这些dll放在system32目录下,和你直接在System的path下加入这些dll效果是一样的。

 

HighGui是一个OpenCV自带的“内嵌面板”。

有时我也会自己写JFrame来做“展示”。如下面这个例子。

Mat在己有图片上加圆圈

ImageShowAddCircle.java

package org.mk.opencv;

import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.imgcodecs.Imgcodecs;

public class ImageShowAddCircle 
	public static void main(String[] args) 
		// 载入dll(必须先加载)
		System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
		// 将文件读入为OpenCV的Mat格式。注意测试时,路径不要包括中文
		Mat src = Imgcodecs.imread("D:\\\\opencv-demo\\\\1.jpg");
		if (src.dataAddr() == 0) 
			System.out.println("打开文件出错");
		
		ImageViewerAddCircle imageViewer = new ImageViewerAddCircle(src, "图片上加圆圈");
		imageViewer.imshow();
	

 ImageViewer.java

package org.mk.opencv;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Image;

import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.WindowConstants;

import org.mk.opencv.util.OpenCVUtil;
import org.opencv.core.Mat;
import org.opencv.core.Point;
import org.opencv.core.Scalar;
import org.opencv.imgproc.Imgproc;

public class ImageViewerAddCircle 
	private JLabel imageView;
	private Mat image;
	private String windowName;


	public ImageViewerAddCircle(Mat image) 
		this.image = image;
	

	/**
	 * @param image      要显示的mat
	 * @param windowName 窗口标题
	 */
	public ImageViewerAddCircle(Mat image, String windowName) 
		this.image = image;
		this.windowName = windowName;
	

	/**
	 * 图片显示,并使用opencv的ImgProc.circle在图片上加两个圆圈
	 */
	public void imshow() 
		setSystemLookAndFeel();
		//在图上画圆
		Imgproc.circle(image, new Point(50, 50), 40, new Scalar(255, 0, 0), 2);
		//在图上画另一个圆
		Imgproc.circle(image, new Point(50, 100), 80, new Scalar(0, 255, 0), 5);
		//展示画了圆的图像
		Image loadedImage = OpenCVUtil.matToImage(image);
		JFrame frame = createJFrame(windowName, image.width(), image.height());
		imageView.setIcon(new ImageIcon(loadedImage));
		frame.pack();
		frame.setLocationRelativeTo(null);
		frame.setVisible(true);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);// 用户点击窗口关闭
	

	private void setSystemLookAndFeel() 
		try 
			UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
		 catch (ClassNotFoundException e) 
			e.printStackTrace();
		 catch (InstantiationException e) 
			e.printStackTrace();
		 catch (IllegalAccessException e) 
			e.printStackTrace();
		 catch (UnsupportedLookAndFeelException e) 
			e.printStackTrace();
		
	

	private JFrame createJFrame(String windowName, int width, int height) 
		JFrame frame = new JFrame(windowName);
		imageView = new JLabel();
		final JScrollPane imageScrollPane = new JScrollPane(imageView);
		imageScrollPane.setPreferredSize(new Dimension(width, height));
		frame.add(imageScrollPane, BorderLayout.CENTER);
		frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
		return frame;
	

它显示的效果如下:

它会在一个图片上(未加圆圈前)

 显示带两个圆圈的画(加了圆圈后)

 Mat与Image互转

由于我们经常使用Swing组件,Swing中有一个imageView.setIcon方法或者是setImage方法,它要求的是输入一个java.awt.Image对象。

那么Mat和Image经常会互转,因此我们有一套互转的小工具类如下:

OpenCVUtil.java

package org.mk.opencv.util;

import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.io.File;

import org.apache.log4j.Logger;
import org.opencv.core.CvType;
import org.opencv.core.Mat;


public class OpenCVUtil 
	private static Logger logger = Logger.getLogger(OpenCVUtil.class);

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


	public static BufferedImage matToBufferedImage(Mat matrix) 
		int cols = matrix.cols();
		int rows = matrix.rows();
		int elemSize = (int) matrix.elemSize();
		byte[] data = new byte[cols * rows * elemSize];
		int type;
		matrix.get(0, 0, data);
		switch (matrix.channels()) 
		case 1:
			type = BufferedImage.TYPE_BYTE_GRAY;
			break;
		case 3:
			type = BufferedImage.TYPE_3BYTE_BGR;
			// bgr to rgb
			byte b;
			for (int i = 0; i < data.length; i = i + 3) 
				b = data[i];
				data[i] = data[i + 2];
				data[i + 2] = b;
			
			break;
		default:
			return null;
		
		BufferedImage image2 = new BufferedImage(cols, rows, type);
		image2.getRaster().setDataElements(0, 0, cols, rows, data);
		return image2;
	

	public static Mat bufferedImageToMat(BufferedImage bi) 
		Mat mat = new Mat(bi.getHeight(), bi.getWidth(), CvType.CV_8UC3);
		byte[] data = ((DataBufferByte) bi.getRaster().getDataBuffer()).getData();
		mat.put(0, 0, data);
		return mat;
	

Mat使用blur图片

package org.mk.opencv;

import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.Size;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;

public class Blur 
	public static void main(String[] args) 
		try 
			System.loadLibrary(Core.NATIVE_LIBRARY_NAME);

			Mat src = Imgcodecs.imread("D:/opencv-demo/1.jpg");
			if (src.empty()) 
				throw new Exception("no file");
			

			Mat dst = src.clone();

			Imgproc.blur(src, dst, new Size(800, 600));
			Imgcodecs.imwrite("D:/opencv-demo/blur.jpg", dst);
		 catch (Exception e) 
			System.out.println("出错啦:" + e);
		
	


它把一张原来的未blur处理的图片,变成了如下这样

 结束今天的博客,下一篇会讲“认脸”。认脸和识脸是两个课题,我们一步步来。目前网上99%的教程只能到达认脸这一步,即这是一个脸。但不代表这是谁?这是谁就叫“识脸”。

以上是关于OpenCV Java入门三 Mat的基本操作的主要内容,如果未能解决你的问题,请参考以下文章

6opencv入门Mat类介绍,基本的绘图函数

OpenCV入门教程-Mat类之选取图像局部区域

OpenCV入门——基础

OpenCVQt + OpenCV 开发配置 + 入门知识(代码示例)

OpenCV Java入门五 结合摄像头识脸和拍照

opencv入门 二