杰瑞科技汇

如何在Java中使用OpenCV的Mat类?

Of course! Here's a comprehensive guide to using the Mat class in OpenCV with Java, covering everything from basic creation to common operations.

如何在Java中使用OpenCV的Mat类?-图1
(图片来源网络,侵删)

What is a Mat?

In OpenCV, the Mat (Matrix) class is the primary container for images or any 2D or multi-dimensional array. It's a C++ class, but OpenCV provides a complete and well-designed Java wrapper for it.

Key advantages of Mat over older OpenCV structures (like IplImage):

  • Automatic Memory Management: You don't have to manually allocate and deallocate memory. Java's garbage collector handles it.
  • Efficiency: It avoids unnecessary data copying. Operations are performed in-place or on the underlying data buffer when possible.
  • Flexibility: It can store not just 8-bit 3-channel (BGR) images, but also various data types (e.g., 32-bit floating point, 16-bit unsigned) and numbers of channels.

Creating a Mat

There are several ways to create a Mat object.

a) Creating an Empty Mat

You can create a Mat with a specified size and data type.

如何在Java中使用OpenCV的Mat类?-图2
(图片来源网络,侵删)
import org.opencv.core.Mat;
import org.opencv.core.CvType; // Contains constants for data types
public class MatCreation {
    public static void main(String[] args) {
        // Load the OpenCV native library
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
        // Create a 100x100 matrix of 8-bit unsigned integers (CV_8U)
        // with 3 channels (BGR image)
        Mat emptyMat = new Mat(100, 100, CvType.CV_8UC3);
        System.out.println("Created an empty Mat: " + emptyMat);
        // Output: Created an empty Mat: Mat [ 100*100*CV_8UC3, isCont=true, isSubmat=false, nativeObj=0x... ]
    }
}

Understanding CvType: The CvType constant encodes three pieces of information:

  • Depth (Data Type): The number of bits per element.
    • CV_8U: 8-bit unsigned integer (0-255). Most common for images.
    • CV_8S: 8-bit signed integer.
    • CV_16U: 16-bit unsigned integer.
    • CV_32F: 32-bit floating-point number. Common for processing.
    • CV_64F: 64-bit floating-point number.
  • Channels: The number of data channels per pixel.
    • C: 1 channel (e.g., Grayscale)
    • UC3: 3 channels (e.g., BGR or RGB)
    • UC4: 4 channels (e.g., BGRA)

So, CvType.CV_8UC3 means: 8-bit unsigned integers, 3 channels.

b) Creating a Mat with a Scalar Value

You can initialize all elements of the Mat with a specific value. A Scalar is a 4-element vector used to initialize or set values.

import org.opencv.core.Mat;
import org.opencv.core.Scalar;
import org.opencv.core.CvType;
public class MatWithScalar {
    public static void main(String[] args) {
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
        // Create a 3x3 Mat of 64-bit floats, initialized with all zeros
        Mat zeroMat = new Mat(3, 3, CvType.CV_64FC(new Scalar(0.0)));
        // Create a 100x100 BGR image, filled with blue color
        // Scalar(B, G, R)
        Mat blueMat = new Mat(100, 100, CvType.CV_8UC3, new Scalar(255, 0, 0));
        System.out.println("Created a blue Mat: " + blueMat.dump());
        // .dump() prints the matrix data in a readable format
    }
}

c) Creating from a File (Loading an Image)

This is the most common way to get a Mat.

如何在Java中使用OpenCV的Mat类?-图3
(图片来源网络,侵删)
import org.opencv.core.Mat;
import org.opencv.imgcodecs.Imgcodecs; // For image reading/writing
public class LoadImage {
    public static void main(String[] args) {
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
        // Path to your image file
        String imagePath = "path/to/your/image.jpg";
        // imread loads the image. The second flag can be:
        // Imgcodecs.IMREAD_COLOR: Loads a 3-channel BGR image. (Default)
        // Imgcodecs.IMREAD_GRAYSCALE: Loads a 1-channel grayscale image.
        // Imgcodecs.IMREAD_UNCHANGED: Loads the image as-is (including alpha channel).
        Mat image = Imgcodecs.imread(imagePath, Imgcodecs.IMREAD_COLOR);
        if (image.empty()) {
            System.out.println("Error: Could not load image.");
            return;
        }
        System.out.println("Successfully loaded image with size: " + image.size());
    }
}

Accessing and Modifying Pixel Data

You should almost never access pixels using raw array indices. It's slow, error-prone, and ignores the internal structure of the Mat. Always use OpenCV's provided methods.

a) get() and put() Methods

These are simple, safe, but slower methods for getting and setting pixel values. They work best for single pixels or small regions.

import org.opencv.core.Mat;
import org.opencv.core.Scalar;
import org.opencv.imgcodecs.Imgcodecs;
public class GetPutPixels {
    public static void main(String[] args) {
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
        Mat image = Imgcodecs.imread("path/to/your/image.jpg");
        if (image.empty()) return;
        // --- GET a pixel value ---
        // get(row, col) returns a double[] array
        // The array length corresponds to the number of channels
        double[] pixelData = image.get(50, 50); // Get pixel at row 50, col 50
        if (pixelData != null) {
            System.out.println("Pixel at (50,50) - B: " + pixelData[0] + ", G: " + pixelData[1] + ", R: " + pixelData[2]);
        }
        // --- SET a pixel value ---
        // put(row, col, double[]) sets the pixel value
        // Set the pixel at (100, 100) to pure green
        image.put(100, 100, new double[]{0, 255, 0});
        // Save the modified image
        Imgcodecs.imwrite("modified_image.jpg", image);
    }
}

b) Mat.get() (Bulk Data Access)

To get the entire image data as a 1D Java array, use the get() method with no arguments. This is very useful for passing data to other libraries or for manual processing (though often slower than native OpenCV functions).

// Get the entire image data as a 1D array of doubles
double[] imageData = image.get();
// imageData now contains all pixel values sequentially.
// For a BGR image, the order is B0, G0, R0, B1, G1, R1, ...

Common Mat Operations

a) Copying a Mat

Be careful! mat1 = mat2 only copies the reference, not the data. Both variables will point to the same underlying matrix.

Mat original = Imgcodecs.imread("image.jpg");
// This is a SHALLOW COPY. 'copy' and 'original' refer to the same data.
Mat copy = original;
// Modifying 'copy' will also modify 'original'
copy.put(0, 0, new double[]{255, 0, 0});
System.out.println(original.get(0, 0)[0]); // Will print 255.0

To create a true, independent copy:

// This is a DEEP COPY. 'deepCopy' has its own data buffer.
Mat deepCopy = original.clone();

b) Converting Between Color Spaces

Use the cvtColor function.

import org.opencv.imgproc.Imgproc; // For image processing functions
public class ColorConvert {
    public static void main(String[] args) {
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
        Mat bgrImage = Imgcodecs.imread("image.jpg");
        Mat grayImage = new Mat();
        // Convert BGR to Grayscale
        Imgproc.cvtColor(bgrImage, grayImage, Imgproc.COLOR_BGR2GRAY);
        // Save the grayscale image
        Imgcodecs.imwrite("grayscale_image.jpg", grayImage);
    }
}

Common conversion codes:

  • COLOR_BGR2GRAY
  • COLOR_BGR2RGB
  • COLOR_GRAY2BGR
  • COLOR_BGR2HSV

c) Resizing an Image

Use the resize function.

import org.opencv.imgproc.Imgproc;
public class ResizeImage {
    public static void main(String[] args) {
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
        Mat original = Imgcodecs.imread("image.jpg");
        Mat resized = new Mat();
        // Size(new_cols, new_rows)
        Imgproc.resize(original, resized, new Size(300, 200), 0, 0, Imgproc.INTER_AREA);
        Imgcodecs.imwrite("resized_image.jpg", resized);
    }
}

Releasing Memory (Best Practice)

Although Java's Garbage Collector (GC) will eventually clean up Mat objects, it's good practice to explicitly release them when you are done with them, especially in long-running applications. This frees up the native memory immediately and can prevent memory leaks.

public class MatRelease {
    public static void main(String[] args) {
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
        Mat image = Imgcodecs.imread("image.jpg");
        // ... do some processing ...
        // Explicitly release the Mat's native memory
        image.release();
        // After release, the Mat object becomes invalid.
        // Any operation on it will throw an exception.
        // image.dump(); // This will cause an error!
    }
}

Complete Example: Grayscale and Save

Here is a full, runnable example that loads an image, converts it to grayscale, and saves it.

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 SimpleOpenCVExample {
    public static void main(String[] args) {
        // 1. Load the OpenCV native library
        // This path might need to be adjusted based on your setup
        // System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
        // A more robust way for some setups:
        nu.pattern.OpenCV.loadShared();
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
        // 2. Define input and output file paths
        String inputImagePath = "resources/lenna.png"; // Assuming 'resources' is in your classpath
        String outputImagePath = "resources/lenna_grayscale.jpg";
        // 3. Load the image
        Mat sourceImage = Imgcodecs.imread(inputImagePath);
        if (sourceImage.empty()) {
            System.err.println("Error: Could not load image at " + inputImagePath);
            return;
        }
        System.out.println("Image loaded successfully. Size: " + sourceImage.size());
        // 4. Create a new Mat for the grayscale image
        Mat grayImage = new Mat();
        // 5. Convert the color image to grayscale
        Imgproc.cvtColor(sourceImage, grayImage, Imgproc.COLOR_BGR2GRAY);
        // 6. Save the grayscale image
        boolean success = Imgcodecs.imwrite(outputImagePath, grayImage);
        if (success) {
            System.out.println("Grayscale image saved successfully to " + outputImagePath);
        } else {
            System.err.println("Error: Failed to save the image.");
        }
        // 7. Release Mats (good practice)
        sourceImage.release();
        grayImage.release();
    }
}

How to Set Up Your Project (Maven)

If you're using Maven, add the OpenCV dependency to your pom.xml. The version should match your OpenCV installation.

<dependencies>
    <dependency>
        <groupId>org.openpnp</groupId>
        <artifactId>opencv</artifactId>
        <version>4.9.0-0</version> <!-- Check for the latest version -->
    </dependency>
</dependencies>

Remember to also place the native OpenCV library file (e.g., opencv_java490.dll on Windows, libopencv_java490.so on Linux, libopencv_java490.jnilib on macOS) in a location where the JVM can find it, or specify the path using -Djava.library.path. The nu.pattern.OpenCV.loadShared(); line can simplify this.

分享:
扫描分享到社交APP
上一篇
下一篇