Java?OpenCV學(xué)習(xí)之Mat的基本操作詳解
環(huán)境好了,我們就可以進(jìn)入正文了。
在之前入門一、二中分別已經(jīng)有畫圖的兩個例子了。但沒有細(xì)節(jié)展開我們的代碼和OpenCV到底在干什么。
使用OpenCV時你需要補(bǔ)充的知識
你需要熟練使用Java Swing,或者是其它任何一門語言中關(guān)于GUI方面的編程。
我們這用的是OpenCV Java,因此對于Java Swing必須熟練。你可以安裝eclipse 中的windowbuilder來幫助你做Swing的編程。
至于Java Swing中的界面、Frame、Panel、Button以及Layout,這塊在“JDK核心技術(shù)卷1、卷2”中已有詳細(xì)描述,我就不多此一舉了。
Mat對象
OpenCV用來存儲圖像,很多時候都會用到這個Mat方法。數(shù)字圖像可看做一個數(shù)值矩陣, 其中的每一個元素表明一個像素點。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;? //
具體不作進(jìn)一步展開,但我們要會使用這個Mat。
因此今天以Mat來做幾個小練習(xí)。
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”,實際它會去調(diào)用“opencv_java343.dll”,并且opencv_java343.dll有依賴,它會去找它自己在Windows的控制面板->系統(tǒng)變量->path中的依賴的那些opencv編譯出來的包。
我不喜歡把opencv_java343.dll所依賴的這些DLL放到windows的安裝目錄的System32目錄下。
因為你把這些dll放在system32目錄下,和你直接在System的path下加入這些dll效果是一樣的。
HighGui是一個OpenCV自帶的“內(nèi)嵌面板”。
有時我也會自己寫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 窗口標(biāo)題 */ 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);// 用戶點擊窗口關(guān)閉 } 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互轉(zhuǎn)
由于我們經(jīng)常使用Swing組件,Swing中有一個imageView.setIcon方法或者是setImage方法,它要求的是輸入一個java.awt.Image對象。
那么Mat和Image經(jīng)常會互轉(zhuǎn),因此我們有一套互轉(zhuǎn)的小工具類如下:
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處理的圖片,變成了如下這樣
結(jié)束今天的博客,下一篇會講“認(rèn)臉”。認(rèn)臉和識臉是兩個課題,我們一步步來。目前網(wǎng)上99%的教程只能到達(dá)認(rèn)臉這一步,即這是一個臉。但不代表這是誰?這是誰就叫“識臉”。
以上就是Java OpenCV學(xué)習(xí)之Mat的基本操作詳解的詳細(xì)內(nèi)容,更多關(guān)于Java OpenCV Mat的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java StringBuilder類相關(guān)知識總結(jié)
這篇文章主要介紹了Java StringBuilder類相關(guān)知識總結(jié),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-02-02以Json形式的數(shù)據(jù)格式實現(xiàn)JMeter參數(shù)化
本文以小項目學(xué)院管理系統(tǒng)為例,給大家分享以Json形式的數(shù)據(jù)格式實現(xiàn)JMeter參數(shù)化的相關(guān)知識,包括添加元件操作步驟及使用用戶參數(shù)組件實現(xiàn)參數(shù)化的方法,感興趣的朋友跟隨小編一起看看吧2021-05-05利用Netty+SpringBoot實現(xiàn)定時后端向前端推送數(shù)據(jù)
這篇文章主要介紹了BIO、NIO、AIO三種Java?IO模型,并探討了如何使用Spring?Boot集成Netty實現(xiàn)后臺向前端推送信息的功能,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2025-01-01Spring Security如何使用URL地址進(jìn)行權(quán)限控制
這篇文章主要介紹了Spring Security如何使用URL地址進(jìn)行權(quán)限控制,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2019-12-12SpringCloud如何使用Eureka實現(xiàn)服務(wù)之間的傳遞數(shù)據(jù)
這篇文章主要介紹了SpringCloud使用Eureka實現(xiàn)服務(wù)之間的傳遞數(shù)據(jù)操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06jvm中指定時區(qū)信息user.timezone問題及解決方式
同一份程序使用時間LocalDateTime類型,在國內(nèi)和國外部署后,返回的時間信息前端使用出問題,這篇文章主要介紹了jvm中指定時區(qū)信息user.timezone問題及解決方法,需要的朋友可以參考下2023-02-02