在OpenCV中将`BufferedImage`转换为`Mat`

35
我如何将BufferedImage转换为OpenCV中的Mat
我正在使用OpenCV的JAVA包装器(不是JavaCV)。由于我对OpenCV不太熟悉,因此在理解Mat的工作原理方面存在一些问题。
我想做类似于这样的事情。(基于Ted W.的回答):
BufferedImage image = ImageIO.read(b.getClass().getResource("Lena.png"));

int rows = image.getWidth();
int cols = image.getHeight();
int type = CvType.CV_16UC1;
Mat newMat = new Mat(rows, cols, type);

for (int r = 0; r < rows; r++) {
    for (int c = 0; c < cols; c++) {
        newMat.put(r, c, image.getRGB(r, c));
    }
}

Highgui.imwrite("Lena_copy.png", newMat);

这个不起作用。Lena_copy.png只是一张具有正确尺寸的黑色图片。


这是您自定义的Java包装器吗?对于javacv,只需使用IplImage.createFrom(bufferedImage)即可。请发布一些您卡住的代码? - Nikson Kanti Paul
2
Opencv自2.4.4版本起拥有自己的Java绑定。http://opencv.org/opencv-java-api.html - Jompa234
我看到了,但还没有检查,谢谢。 - Nikson Kanti Paul
9个回答

30

我也曾尝试做同样的事情,因为需要将图像处理与两个库结合起来。我试图将byte[]放入Mat中而不是RGB值。而且它奏效了!所以我所做的是:

1.使用以下代码将BufferedImage转换为字节数组:

byte[] pixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();

2. 如果将类型设置为CV_8UC3,则可以将其直接放入Mat中。

image_final.put(0, 0, pixels);

编辑: 您也可以尝试像这个回答中所述的那样进行反向转换。


7
在viktorich的回答中,有一些他没有提到的内容。BufferedImage对象必须像这样声明:img = new BufferedImage(320, 240, BufferedImage.TYPE_3BYTE_BGR);<br>另外,以上代码才能正常工作。请注意,如果没有这样声明,上述代码将不起作用。 - Kevin Infante
代码也适用于其他情况。两种格式必须匹配,例如BufferedImage.TYPE_BYTE_GRAYCV_8UC(1)同样适用。 - Mene
1
这个库的版本是哪个?它对我不起作用(Mat类没有这个带有这样签名的put方法),我正在使用bytedeco javacv 1.3.2。 - Sergey Karpushin

29

不想处理大型像素数组?只需使用此

BufferedImage转换为Mat

public static Mat BufferedImage2Mat(BufferedImage image) throws IOException {
    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    ImageIO.write(image, "jpg", byteArrayOutputStream);
    byteArrayOutputStream.flush();
    return Imgcodecs.imdecode(new MatOfByte(byteArrayOutputStream.toByteArray()), Imgcodecs.CV_LOAD_IMAGE_UNCHANGED);
}

将Mat转换为BufferedImage

public static BufferedImage Mat2BufferedImage(Mat matrix)throws IOException {
    MatOfByte mob=new MatOfByte();
    Imgcodecs.imencode(".jpg", matrix, mob);
    return ImageIO.read(new ByteArrayInputStream(mob.toArray()));
}

注意,虽然很微小,但通过这种方式,您可以获得可靠的解决方案,但它使用了编码+解码,因此会损失一些性能,通常为10到20毫秒。JPG 编码会失去一些图像质量,而且速度较慢(可能需要10到20毫秒)。BMP 是无损的,速度快(1或2毫秒),但需要更多的内存(微不足道)。 PNG 是无损的,但编码的时间比 BMP 长一些。在我看来,使用BMP 应该适合大多数情况。


4
好的,谢谢。这个解决方案不受 BufferedImage 图像输入格式的影响。它适用于24位JPG和同时也适用于32位PNG。 - asmaier
1
这个方法真是救了我一命,之前我的图像出现了非常奇怪的色彩失真,直到我找到了这个方法。很奇怪的是,如果我保存了图像然后用imread加载它,它就能正常工作,但我没有理由去保存那些图像。 - thekevshow

25

这个方法对我来说运行良好,并且执行时间为0到1毫秒。

public static Mat bufferedImageToMat(BufferedImage bi) {
  Mat mat = new Mat(bi.getHeight(), bi.getWidth(), CvType.CV_8UC3);
  byte[] data = ((DataBufferByte) bi.getRaster().getDataBuffer()).getData();
  mat.put(0, 0, data);
  return mat;
}

谢谢。上述功能对我有所帮助,只需稍作修改即可。我正在处理灰度单通道图像。我使用了 Mat mat = new Mat(bi.getHeight(), bi.getWidth(), CvType.CV_8UC1);,并且它对我起作用了。 - Karthik N G
3
针对 RGBA 图像,您会如何处理? - arqam

4
我在我的程序中使用以下代码。
protected Mat img2Mat(BufferedImage in) {
        Mat out;
        byte[] data;
        int r, g, b;

        if (in.getType() == BufferedImage.TYPE_INT_RGB) {
            out = new Mat(in.getHeight(), in.getWidth(), CvType.CV_8UC3);
            data = new byte[in.getWidth() * in.getHeight() * (int) out.elemSize()];
            int[] dataBuff = in.getRGB(0, 0, in.getWidth(), in.getHeight(), null, 0, in.getWidth());
            for (int i = 0; i < dataBuff.length; i++) {
                data[i * 3] = (byte) ((dataBuff[i] >> 0) & 0xFF);
                data[i * 3 + 1] = (byte) ((dataBuff[i] >> 8) & 0xFF);
                data[i * 3 + 2] = (byte) ((dataBuff[i] >> 16) & 0xFF);
            }
        } else {
            out = new Mat(in.getHeight(), in.getWidth(), CvType.CV_8UC1);
            data = new byte[in.getWidth() * in.getHeight() * (int) out.elemSize()];
            int[] dataBuff = in.getRGB(0, 0, in.getWidth(), in.getHeight(), null, 0, in.getWidth());
            for (int i = 0; i < dataBuff.length; i++) {
                r = (byte) ((dataBuff[i] >> 0) & 0xFF);
                g = (byte) ((dataBuff[i] >> 8) & 0xFF);
                b = (byte) ((dataBuff[i] >> 16) & 0xFF);
                data[i] = (byte) ((0.21 * r) + (0.71 * g) + (0.07 * b));
            }
        }
        out.put(0, 0, data);
        return out;
    }

参考文献:这里


3
我在这里找到了一个解决方案,与Andriys的解决方案相似。
Camera c;
c.Connect();
c.StartCapture();
Image f2Img, cf2Img;
c.RetrieveBuffer(&f2Img);
f2Img.Convert( FlyCapture2::PIXEL_FORMAT_BGR, &cf2Img );
unsigned int rowBytes = (double)cf2Img.GetReceivedDataSize()/(double)cf2Img.GetRows();

cv::Mat opencvImg = cv::Mat( cf2Img.GetRows(), cf2Img.GetCols(), CV_8UC3, cf2Img.GetData(),rowBytes );

那需要使用JNI。 - AMDG

3
为了将BufferedImage转换为Mat,我使用下面的方法:
    public static Mat img2Mat(BufferedImage image) {
        image = convertTo3ByteBGRType(image);
        byte[] data = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
        Mat mat = new Mat(image.getHeight(), image.getWidth(), CvType.CV_8UC3);
        mat.put(0, 0, data);
        return mat;
    }

在将图像转换为Mat之前,我会更改bufferedImage的类型为TYPE_3BYTE_BGR,因为对于某些类型的BufferedImages,方法((DataBufferByte) image.getRaster().getDataBuffer()).getData();可能会返回int[],这将导致代码错误。

下面是将其转换为TYPE_3BYTE_BGR的方法。

    private static BufferedImage convertTo3ByteBGRType(BufferedImage image) {
        BufferedImage convertedImage = new BufferedImage(image.getWidth(), image.getHeight(),
                BufferedImage.TYPE_3BYTE_BGR);
        convertedImage.getGraphics().drawImage(image, 0, 0, null);
        return convertedImage;
    }

1
这是一种方法,每次都能正常工作,并且速度相当快。其他答案缺乏细节或根据输入图像而中断。如果使用dispose()释放中间的Graphics2D对象来绘制图像,甚至会更好一点。 - Christoph Henkelmann

2
当您使用JavaCP包装器bytedeco库(版本1.5.3)时,您可以使用Java2DFrameUtils
简单的用法是:
import org.bytedeco.javacv.Java2DFrameUtils;
...
BufferedImage img = ImageIO.read(new File("some/image.jpg");
Mat mat = Java2DFrameUtils.toMat(img);

注意:不要混合使用不同的包装器,bytedeco Mat 与 opencv Mat 是不同的。

1
一种简单的方法是使用

创建一个新的段落。
Mat newMat = Mat(rows, cols, type);

然后从您的BufferedImage获取像素值,并使用它们放入新的Mat中。
newMat.put(row, col, pixel);

-6
您可以在OpenCV中按以下方式完成它:
File f4 = new File("aa.png");
Mat mat = Highgui.imread(f4.getAbsolutePath());

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接