Ask Your Question

Revision history [back]

click to hide/show revision 1
initial version

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
 */
public static BufferedImage matToBufferedImage(Mat bgr) {
    int width = bgr.width();
    int height = bgr.height();
    BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
    WritableRaster raster = image.getRaster();

    if (bgr.channels()==1) {
        byte[] px = new byte[1];
        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[0];
                rgb[1] = px[0];
                rgb[2] = px[0];
                raster.setPixel(x,y,rgb);
            }
        }
    } else {
        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;
}
click to hide/show revision 2
better solution/more efficient for grayscale images

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
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_INT_RGB);
    WritableRaster BufferedImage.TYPE_BYTE_GRAY);
        raster = image.getRaster();

    if (bgr.channels()==1) {
        byte[] px = new byte[1];
        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[0];
                rgb[1] = px[0];
                rgb[2] = px[0];
                raster.setPixel(x,y,rgb);
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;
}
click to hide/show revision 3
much cleaner and faster solution

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 - and also way cleaner. Observe:

/**
 * Converts/writes a Mat into a BufferedImage.
 * 
 * @param matrix
 * @return BufferedImage
 */
public static BufferedImage matToBufferedImage(Mat matrix) {
    BufferedImage image = null;
    int cols = matrix.cols();
    int rows = matrix.rows();
    int elemSize = (int)matrix.elemSize();
    byte[] data = new byte[cols * rows * elemSize];

    matrix.get(0, 0, data);

    try {
        image = ImageIO.read(new ByteArrayInputStream(data));
    } catch (IOException e) {
        System.out.println("IOException: " + e.getMessage());
    }

    return image;
}

A short benchmark (converting a 640x480 Mat (8-bit, 3 channels) to BufferedImage, 10 times, then taking the average) told me that this second method is about 7.15 times faster. And converting a gray scale image (only 1-channel) was about 6.2 times faster.

Note how I don't even have to manually convert bgr to rgb or anything, as I originally assumed I would have to. But nope; it just works and the colors come out fine (anybody knows why?).

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 - and also way cleaner. Observe:

/**
 * Converts/writes a Mat into a BufferedImage.
 * 
 * @param matrix
 * @return BufferedImage
 */
public static BufferedImage matToBufferedImage(Mat matrix) {
    BufferedImage image = null;
    int cols = matrix.cols();
    int rows = matrix.rows();
    int elemSize = (int)matrix.elemSize();
    byte[] data = new byte[cols * rows * elemSize];

    matrix.get(0, 0, data);

    try {
        image = ImageIO.read(new ByteArrayInputStream(data));
    } catch (IOException e) {
        System.out.println("IOException: " + e.getMessage());
    }

    return image;
}

A short benchmark (converting a 640x480 Mat (8-bit, 3 channels) to BufferedImage, 10 times, then taking the average) told me that this second method is about 7.15 times faster. And converting a gray scale image (only 1-channel) was about 6.2 times faster.

Note how I don't even have to manually convert bgr to rgb or anything, as I originally assumed I would have to. But nope; it just works and the colors come out fine (anybody knows why?).

click to hide/show revision 5
faster solution, 2nd attempt :)

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 anyways. 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) {
    BufferedImage image;
    int channels = matrix.channels();
    int cols = matrix.cols();
    int rows = matrix.rows();
    int elemSize = (int)matrix.elemSize();
    byte[] data = new byte[cols * rows * elemSize];

    matrix.get(0, 0, data);

    switch (channels) {
        case 1:
            image = new BufferedImage(cols, rows, BufferedImage.TYPE_BYTE_GRAY);
            break;

        case 3: 
            byte b;
            // bgr to rgb
            for(int i=0; i<data.length; i=i+3) {
                b = data[i];
                data[i] = data[i+2];
                data[i+2] = b;
            }
            image = new BufferedImage(cols, rows, BufferedImage.TYPE_3BYTE_BGR);
            break;

        default:
            return null;
    }

    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 and B bytes; instead we could try to define an appropriate SampleModel (as far as I can tell, that should be possible...).

So watch out for yet another edit. :)

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 anyways. 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) {
    BufferedImage image;
    int channels = matrix.channels();
    int cols = matrix.cols();
    int rows = matrix.rows();
    int elemSize = (int)matrix.elemSize();
    byte[] data = new byte[cols * rows * elemSize];

    matrix.get(0, 0, data);

    switch (channels) {
        case 1:
            image = new BufferedImage(cols, rows, BufferedImage.TYPE_BYTE_GRAY);
            break;

        case 3: 
            byte b;
            // bgr to rgb
            for(int i=0; i<data.length; i=i+3) {
                b = data[i];
                data[i] = data[i+2];
                data[i+2] = b;
            }
            image = new BufferedImage(cols, rows, BufferedImage.TYPE_3BYTE_BGR);
            break;

        default:
            return null;
    }

    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 and B bytes; instead we could try to define an appropriate SampleModel (as far as I can tell, that should be possible...).

So watch out for yet another edit. :)

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) {
    BufferedImage image;
    int channels = matrix.channels();
    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 (channels) (matrix.channels()) {
        case 1:
            image = new BufferedImage(cols, rows, BufferedImage.TYPE_BYTE_GRAY);
type = BufferedImage.TYPE_BYTE_GRAY;
            break;

        case 3: 
            byte b;
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, BufferedImage.TYPE_3BYTE_BGR);
            break;

        default:
            return null;
    }

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 and B bytes; instead we could try to define an appropriate SampleModel (as far as I can tell, that should be possible...).

So watch out for yet another edit. :)