I have posted a question on which of two approaches is more efficient for converting an OpenCV mat object to a JavaFXML Image so it can be displayed later in the application.
Most of the comments suggested to use PixelBuffer to construct a WritableImage.
For this purpose I have chosen @James_D proposition which is:
Mat -> MatOfByte -> byte[] -> ByteBuffer -> PixelBuffer -> WritableImage
This what the code looks like:
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.awt.image.WritableRaster;
import java.nio.ByteBuffer;
import javafx.concurrent.Task;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.image.Image;
import javafx.scene.image.PixelBuffer;
import javafx.scene.image.PixelFormat;
import javafx.scene.image.WritableImage;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
public class MatToFXImage extends Task<Image> {
private final Mat mat;
public MatToFXImage(Mat mat) {
if (mat == null)
throw new IllegalArgumentException("Mat object can't be null");
this.mat = mat;
}
private int determineImageType(int matType) {
if (matType == CvType.CV_8UC1)
return BufferedImage.TYPE_BYTE_GRAY;
else if (matType == CvType.CV_8UC3)
return BufferedImage.TYPE_3BYTE_BGR;
else
throw new IllegalArgumentException("Unsupported Mat type: " + matType);
}
@Override
protected Image call() {
// MatOfByte byteMat = new MatOfByte();
// Imgcodecs.imencode(".bmp", mat, byteMat);
// return new Image(new ByteArrayInputStream(byteMat.toArray()));
int size = (int) (mat.total() * mat.channels());
byte[] byteArray = new byte[size];
mat.get(0, 0, byteArray);
ByteBuffer buffer = ByteBuffer.wrap(byteArray);
PixelFormat<ByteBuffer> pixelFormat = PixelFormat.getByteBgraPreInstance();
PixelBuffer<ByteBuffer> pixelBuffer = new PixelBuffer<>(mat.width(), mat.height(), buffer, pixelFormat);
return new WritableImage(pixelBuffer);
// int type;
// type = determineImageType(mat.type());
// BufferedImage image = new BufferedImage(mat.width(), mat.height(), type);
// WritableRaster raster = image.getRaster();
// DataBufferByte dataBuffer = (DataBufferByte) raster.getDataBuffer();
// // byte[] data = dataBuffer.getData();
// mat.get(0, 0, dataBuffer.getData());
// return SwingFXUtils.toFXImage(image, null);
}
}
I would like to know why it is not working as supposed to do
call()at which it's hanging? Can you create a minimal reproducible example that uses the class you posted here that demonstrates the issue?call()method is throwing an exception, and you're not seeing it because it's thrown asynchronously (i.e. in the thread that's running the task) and being handled internally (and silently) by the usualTaskmachinery. Have you tried something likeTask<Image> task = new MatToFxImage();and thentask.exceptionProperty().subscribe(Throwable::printStackTrace);(or otherwise explicitly handling an exception to get its stack trace) before submitting the task to an executor?Matto be in a format that supports an alpha channel, that would be ideal. Otherwise you may need to code some manual conversion. There are some nice examples of various kinds of integration of JavaFX with other graphics libraries usingPixelBuffers here. Those are probably worth your while to study.