While I'm not sure the following is the most efficient (or even elegant) solution, it works and is definitely better than this "writing to disk..., read from the disk again"-approach:
/**
* Converts/writes a Mat into a BufferedImage.
*
* @param bgr Mat of type CV_8UC3 or CV_8UC1
* @return BufferedImage of type TYPE_INT_RGB or TYPE_BYTE_GRAY
*/
public static BufferedImage matToBufferedImage(Mat bgr) {
int width = bgr.width();
int height = bgr.height();
BufferedImage image;
WritableRaster raster;
if (bgr.channels()==1) {
image = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
raster = image.getRaster();
byte[] px = new byte[1];
for (int y=0; y<height; y++) {
for (int x=0; x<width; x++) {
bgr.get(y,x,px);
raster.setSample(x, y, 0, px[0]);
}
}
} else {
image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
raster = image.getRaster();
byte[] px = new byte[3];
int[] rgb = new int[3];
for (int y=0; y<height; y++) {
for (int x=0; x<width; x++) {
bgr.get(y,x,px);
rgb[0] = px[2];
rgb[1] = px[1];
rgb[2] = px[0];
raster.setPixel(x,y,rgb);
}
}
}
return image;
}
EDIT
Ahh, thanks to an earlier post by Alexander Smorkalov (see http://answers.opencv.org/question/6281/is-there-a-way-to-grab-all-the-pixels-of-a-matrix/?answer=6286#post-id-6286 ) I've figured out to do this much faster by retrieving all image data in one junk.
My first attempt with ImageIO.read given an ByteArrayInputStream of said data yielded promising results at first (6 to 7 times faster), but right after posting the method, that piece of code didn't even work any longer (no idea why it did work at some point earlier). Think this has something to do with unavailable/uninitialized ImageReader or (sorry, I'm not too confident with Java and get easily lost...).
Anyways, a second attempt (without ImageIO and ByteArrayInputStreams and what not) yielded even better results. Observe:
/**
* Converts/writes a Mat into a BufferedImage.
*
* @param matrix Mat of type CV_8UC3 or CV_8UC1
* @return BufferedImage of type TYPE_3BYTE_BGR or TYPE_BYTE_GRAY
*/
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 image = new BufferedImage(cols, rows, type);
image.getRaster().setDataElements(0, 0, cols, rows, data);
return image;
}
Quick benchmark results, comparing this new method to the initially posted one (converting a 640x480 Mat to BufferedImage, 10 times, then taking the average):
- with a grayscale (8-bit, 1 channels) mat: 127 times faster
- with a bgr (8-bit, 3 channels) mat: 15 times faster
Not surprisingly this is much faster than the first version (and also faster than my ImageIO experiments). But I think we might even do better, by getting rid of that i=i+3 loop which swaps the R ... (more)