Java OpenCV学习之Mat的基本操作详解
目录
- 使用OpenCV时你需要补充的知识
- Mat对象
- Mat划线
- Mat在己有图片上加圆圈
- ImageShowAddCircle.java
- ImageViewer.java
- Mat与Image互转
- OpenCVUtil.java
- Mat使用blur图片
环境好了,我们就可以进入正文了。
在之前入门一、二中分别已经有画图的两个例子了。但没有细节展开我们的代码和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%的教程只能到达认脸这一步,即这是一个脸。但不代表这是谁?这是谁就叫“识脸”。
以上就是Java OpenCV学习之Mat的基本操作详解的详细内容,更多关于Java OpenCV Mat的资料请关注我们其它相关文章!