3.0 Core Data Structures and Image I/O
Before any image processing can occur, we must first understand how OpenCV stores and manipulates image data. The fundamental data structure for this purpose is the Mat class. This module will cover the properties of this essential class and then demonstrate the core operations of reading image files from disk, writing them back, and displaying them in a graphical user interface.
A Note on File Paths: Throughout the following code examples, you will see file paths represented as “path/to/your/image.jpg”. This is a placeholder. To run these examples, you must replace this string with the actual, valid path to an image file on your system.
3.1 Digital Image Representation: The Mat Class
The Mat class, found in the org.opencv.core package, is an n-dimensional array designed to store not only 2D image data but also other numerical information like voxel volumes, point clouds, and histograms.
Conceptually, a Mat object consists of two distinct parts:
- The header: This is a small, constant-size part that contains metadata about the matrix, such as its size (rows and columns), the data type of its elements, and the memory address where the data is stored.
- The pointer: This points to the actual matrix of pixel data. The size of this data block can vary greatly depending on the image dimensions.
This two-part structure is a key feature, as it allows multiple Mat objects to point to the same data block, making operations like creating a region of interest (ROI) highly efficient.
Mat Class Constructors
The Mat class provides several constructors for creating matrices. The table below outlines some of the most common ones.
| # | Constructor | Description |
| 1 | Mat() | The default constructor, creating an empty matrix that can be passed to other OpenCV methods. |
| 2 | Mat(int rows, int cols, int type) | Creates a 2D matrix with the specified number of rows, columns, and a specific data type. |
| 3 | Mat(Size size, int type) | Creates a 2D matrix using a Size object and a specific data type. |
| 4 | Mat(int rows, int cols, int type, Scalar s) | Similar to the above, but also initializes every element of the matrix with the values from the provided Scalar object. |
| 5 | Mat(Size size, int type, Scalar s) | Creates a 2D matrix using a Size object and initializes every element with the values from the provided Scalar object. |
| 6 | Mat(Mat m, Range rowRange) | Creates a new matrix header for a specific range of rows from an existing matrix m. |
| 7 | Mat(Mat m, Range rowRange, Range colRange) | Creates a new matrix header that points to a specific sub-rectangle of an existing matrix m. |
| 8 | Mat(Mat m, Rect roi) | Creates a new matrix header for a specific Region Of Interest (ROI) from an existing matrix m. |
Note: The type parameter uses constants from the CvType class, such as CV_8UC1 for an 8-bit single-channel image (grayscale) or CV_8UC3 for an 8-bit three-channel image (color).
Key Mat Class Methods
The Mat class also includes a rich set of methods for accessing and manipulating its data.
| # | Method | Description |
| 1 | col(int x) | Returns a Mat header for the specified column. |
| 2 | row(int y) | Returns a Mat header for the specified row. |
| 3 | cols() | Returns the number of columns in the matrix. |
| 4 | rows() | Returns the number of rows in the matrix. |
| 5 | setTo(Scalar s) | Sets every element of the array to the specified Scalar value. |
Practical Example: Creating and Displaying a Matrix
Let’s see a practical example of creating a matrix. The process involves three main steps:
- Load the native library: Every OpenCV Java program must start by loading the native library.
- Instantiate the Mat class: Use a constructor to create a new matrix.
- Fill the matrix: Use methods like row() and setTo() to populate the matrix with data.
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.Scalar;
public class DisplayingMatrix {
public static void main(String[] args) {
//Loading the core library
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
//Creating a matrix
Mat matrix = new Mat(5, 5, CvType.CV_8UC1, new Scalar(0));
//Retrieving the row with index 0
Mat row0 = matrix.row(0);
//setting values of all elements in the row with index 0
row0.setTo(new Scalar(1));
//Retrieving the column with index 3
Mat col3 = matrix.col(3);
//setting values of all elements in the column with index 3
col3.setTo(new Scalar(3));
//Printing the matrix
System.out.println(“OpenCV Mat data:\n” + matrix.dump());
}
}
Program Output:
OpenCV Mat data:
[ 1, 1, 1, 3, 1;
0, 0, 0, 3, 0;
0, 0, 0, 3, 0;
0, 0, 0, 3, 0;
0, 0, 0, 3, 0]
3.2 Fundamental Image Input/Output Operations
The Imgcodecs class, located in the org.opencv.imgcodecs package, provides the core methods for reading images from files and writing them back to disk.
Reading an Image
The primary method for reading an image is imread(filename). It accepts a string representing the path to the image file and returns a Mat object containing the image data.
The process involves three steps:
- Load the OpenCV native library.
- Instantiate the Imgcodecs class.
- Call the imread() method with the file path.
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.imgcodecs.Imgcodecs;
public class ReadingImages {
public static void main(String args[]) {
//Loading the OpenCV core library
System.loadLibrary( Core.NATIVE_LIBRARY_NAME );
//Reading the Image from the file
String file = “path/to/your/image.jpg”;
Mat matrix = Imgcodecs.imread(file);
System.out.println(“Image Loaded: ” + (matrix.empty() ? “failure” : “success”));
}
}
Writing an Image
To save a Mat object to an image file, we use the imwrite(filename, mat) method. It takes two parameters:
- filename: A string representing the path and filename where the image should be saved. The file extension (e.g., .jpg, .png) determines the format.
- mat: The Mat object containing the image data to be written.
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.imgcodecs.Imgcodecs;
public class WritingImages {
public static void main(String args[]) {
//Loading the OpenCV core library
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
//Reading the Image from the file and storing it in to a Matrix object
String inputFile = “path/to/your/image.jpg”;
Mat matrix = Imgcodecs.imread(inputFile);
System.out.println(“Image Loaded.”);
String outputFile = “path/to/your/image_resaved.jpg”;
//Writing the image
Imgcodecs.imwrite(outputFile, matrix);
System.out.println(“Image Saved.”);
}
}
Executing this code will read the input image and save a new copy to the specified output path.
3.3 Displaying Images in a Graphical User Interface (GUI)
Simply reading and writing images is not enough; we often need to display them. Since OpenCV’s Mat object is not directly compatible with Java’s native GUI libraries (like AWT/Swings or JavaFX), a conversion process is required.
Converting Mat to BufferedImage
To display a Mat object in a Java GUI, we must first convert it to a java.awt.image.BufferedImage. This is a multi-step process:
- Encode the Mat to MatOfByte: Use the Imgcodecs.imencode() method to convert the raw pixel data in the Mat object into a memory buffer formatted as a standard image type (e.g., .jpg). This places the result in a MatOfByte object.
- Convert MatOfByte to a byte array: Use the toArray() method on the MatOfByte object to get a standard Java byte[].
- Create an InputStream: Create a java.io.ByteArrayInputStream from the byte array.
- Read the InputStream: Use the static method javax.imageio.ImageIO.read() to read the input stream and produce the final BufferedImage object.
Displaying with AWT/Swings
Once you have a BufferedImage, displaying it with Swings is straightforward. You create a JFrame, wrap the BufferedImage in an ImageIcon and then a JLabel, and add the label to the frame’s content pane.
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.MatOfByte;
import org.opencv.imgcodecs.Imgcodecs;
public class DisplayingImagesUsingSwings {
public static void main(String args[]) throws Exception {
//Loading the OpenCV core library
System.loadLibrary( Core.NATIVE_LIBRARY_NAME );
String file = “path/to/your/image.jpg”;
Mat image = Imgcodecs.imread(file);
//Encoding the image
MatOfByte matOfByte = new MatOfByte();
Imgcodecs.imencode(“.jpg”, image, matOfByte);
byte[] byteArray = matOfByte.toArray();
//Preparing the Buffered Image
InputStream in = new ByteArrayInputStream(byteArray);
BufferedImage bufImage = ImageIO.read(in);
//Instantiate JFrame
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new JLabel(new ImageIcon(bufImage)));
frame.pack();
frame.setVisible(true);
System.out.println(“Image Loaded”);
}
}
When run, this program will open a new GUI window displaying the loaded image.
Displaying with JavaFX
The process for JavaFX is similar, with an extra conversion step. After obtaining the BufferedImage, you convert it into a JavaFX WritableImage using the SwingFXUtils.toFXImage() method. This WritableImage can then be used to create an ImageView, which is the JavaFX component for displaying images.
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
import javafx.stage.Stage;
import javax.imageio.ImageIO;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.MatOfByte;
import org.opencv.imgcodecs.Imgcodecs;
public class DisplayingImagesJavaFX extends Application {
@Override
public void start(Stage stage) throws IOException {
WritableImage writableImage = loadImage();
ImageView imageView = new ImageView(writableImage);
imageView.setFitHeight(400);
imageView.setFitWidth(600);
imageView.setPreserveRatio(true);
Group root = new Group(imageView);
Scene scene = new Scene(root, 600, 400);
stage.setTitle(“Displaying Image with JavaFX”);
stage.setScene(scene);
stage.show();
}
public WritableImage loadImage() throws IOException {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
String file = “path/to/your/image.jpg”;
Mat image = Imgcodecs.imread(file);
MatOfByte matOfByte = new MatOfByte();
Imgcodecs.imencode(“.jpg”, image, matOfByte);
byte[] byteArray = matOfByte.toArray();
InputStream in = new ByteArrayInputStream(byteArray);
BufferedImage bufImage = ImageIO.read(in);
System.out.println(“Image Loaded”);
return SwingFXUtils.toFXImage(bufImage, null);
}
public static void main(String args[]) {
launch(args);
}
}
This JavaFX application will also create a window to display the specified image.
Having mastered the fundamentals of image representation and I/O, we are now ready to explore more advanced image manipulation techniques, such as converting between different color spaces.