【问题标题】:How to match the color models of BufferedImage and Mat?BufferedImage和Mat的颜色模型如何匹配?
【发布时间】:2016-01-28 23:06:30
【问题描述】:

我正在尝试将 BufferedImage 转换为 Mat 以获取从 Internet 下载的具有不同文件类型的大量图像。因为我是从网站上抓取图像,所以我无法控制文件格式。我可以在不知道格式的情况下轻松地将它们加载到BufferedImage,但是要将它们转换为Mat,我需要知道图像类型。不幸的是,CvTypeBufferedImage 类型之间似乎没有很好的对应关系。

CvType 表示格式为CV_<bit-depth>{U|S|F}C<number_of_channels> 的图像类型,其中U 是无符号字符,S 是有符号字符,F 是浮点数。

BufferedImage 类型的表示形式更加多样,包括对称通道 (TYPE_4BYTE_ABGR)、不同数量的位 (TYPE_BYTE_BINARY) 以及任何索引字节图像 (TYPE_BYTE_INDEXED)。

根据文档,我尝试完成自己的通信。

BufferedImage imgBuffer = ImageIO.read(new File("example.gif"));

//Save file as reference
File outputfile = new File("temp/image.png");
ImageIO.write(imgBuffer, "png", outputfile);

//My correspondance
int curCVtype = -1;
switch (imgBuffer.getType()) {
case BufferedImage.TYPE_3BYTE_BGR:
    curCVtype = CvType.CV_8UC3;
    break;
case BufferedImage.TYPE_BYTE_GRAY:
    curCVtype = CvType.CV_8UC1;
    break;
case BufferedImage.TYPE_INT_BGR:
case BufferedImage.TYPE_INT_RGB:
    curCVtype = CvType.CV_8SC3;
    break;
case BufferedImage.TYPE_INT_ARGB:
case BufferedImage.TYPE_INT_ARGB_PRE:
    curCVtype = CvType.CV_8SC4;
    break;
default:
//  The types not handled by my correspondence
//                  BufferedImage.TYPE_BYTE_BINARY;
//                  BufferedImage.TYPE_USHORT_GRAY;
//                  BufferedImage.TYPE_4BYTE_ABGR;
//                  BufferedImage.TYPE_4BYTE_ABGR_PRE;
//                  BufferedImage.TYPE_BYTE_INDEXED;
//                  BufferedImage.TYPE_CUSTOM;

    System.out.println("Unsupported format:" + imgBuffer.getType());
    //Here I choose a default type
    curCVtype = CvType.CV_8SC3;
}

//Convert to Mat
byte[] pixels = ((DataBufferByte) imgBuffer.getRaster().getDataBuffer()).getData();
Mat img = new Mat(imgBuffer.getHeight(), imgBuffer.getWidth(), curCVtype);
img.put(0, 0, pixels);

//Write the output to compare
Imgcodecs.imwrite("temp/image_mat.png", img);

问题

  1. 我这样做正确吗?还是有更好的方法?
  2. 默认情况下的类型的正确对应关系是什么?

示例

输入

BufferedImage 转换为PNG

转换为 Mat 后输出 PNG。 BufferedImage 类型被TYPE_BYTE_INDEXED 转换为CvType.CV_8SC3

转换为 Mat 后输出 PNG。 BufferedImage 类型被TYPE_BYTE_INDEXED 转换为CvType.CV_8UC3

资源:

我的起始代码来自 Converting BufferedImage to Mat in opencv

我对 CvTypes 的了解来自 What's the difference between cvtype values in OPENCV?

【问题讨论】:

  • TYPE_INT 应该映射到 CV_32S,而不是 CV_8S。然后 TYPE_BYTE_BINARY 到 CV_8UC1,TYPE_USHORT_GRAY 到 CV_16UC1,TYPE_4BYTE_ABGR 到 CV_8UC4。并默认使用 CV_8UC3
  • @Miki 我最初对 int 类型也有同样的想法,但后来被文档弄糊涂了,上面写着“表示具有 8 位 RGB 颜色分量打包成整数像素的图像”。澄清一下,颜色只需要 8 位,但它们存储为 32 位?
  • 打包成整数像素,所以你需要CV_32S。我不知道 BufferedImage,但我认为您可以将所有缓冲图像转换为 RGB。然后你可以转换为 opencv 就好像类型是 TYPE_3BYTE_BGR
  • @Miki 谢谢,我认为你的 cmets 会是一个很好的答案。
  • 请注意,TYPE_CUSTOM 不是单一类型,它用于表示不是其他类型之一的所有内容。您可能可以为所有已知类型创建一个直接转换,然后使用getRGBCV_8UC4CV_32U 的回退(我不认为任何Java TYPE_INT_xxx 应该被视为已签名)这将是速度较慢,但​​仍完全兼容。

标签: java image opencv


【解决方案1】:

感谢 Miki 和 haraldK 提供帮助的 cmets。

我的未知图像类型解决方案检索 RGB 格式的像素,并将它们放入 CvType.CV_8UC4Mat 中。最后,使用Core.mixChannels 将通道重新排序为 OpenCV 首选顺序:BGR(A)。

此示例仅对未知图像类型的通道重新排序,但所有非 BGR 图像类型都需要重新排序。

BufferedImage imgBuffer = ImageIO.read(new File("example.gif"));

//Save image as reference
File outputfile = new File("temp/image.png");
ImageIO.write(imgBuffer, "png", outputfile);

//My correspondance

int curCVtype = CvType.CV_8UC4; //Default type
boolean supportedType = true;

switch (imgBuffer.getType()) {
case BufferedImage.TYPE_3BYTE_BGR:
    curCVtype = CvType.CV_8UC3;
    break;
case BufferedImage.TYPE_BYTE_GRAY:
case BufferedImage.TYPE_BYTE_BINARY:
    curCVtype = CvType.CV_8UC1;
    break;
case BufferedImage.TYPE_INT_BGR:
case BufferedImage.TYPE_INT_RGB:
    curCVtype = CvType.CV_32SC3;
    break;
case BufferedImage.TYPE_INT_ARGB:
case BufferedImage.TYPE_INT_ARGB_PRE:
    curCVtype = CvType.CV_32SC4;
    break;
case BufferedImage.TYPE_USHORT_GRAY:
    curCVtype = CvType.CV_16UC1;
    break;
case BufferedImage.TYPE_4BYTE_ABGR:
case BufferedImage.TYPE_4BYTE_ABGR_PRE:
    curCVtype = CvType.CV_8UC4;
    break;
default:
    // BufferedImage.TYPE_BYTE_INDEXED;
    // BufferedImage.TYPE_CUSTOM;
    System.out.println("Unsupported format:" + imgBuffer.getType());
    supportedType = false;
}

//Convert to Mat
Mat img = new Mat(imgBuffer.getHeight(), imgBuffer.getWidth(), curCVtype);
if (supportedType) {
    // Insert pixel buffer directly
    byte[] pixels = ((DataBufferByte) imgBuffer.getRaster().getDataBuffer()).getData();
    img.put(0, 0, pixels);
} else {
    // Convert to RGB first
    int height = imgBuffer.getHeight();
    int width = imgBuffer.getWidth();
    int[] pixels = imgBuffer.getRGB(0, 0, width - 1, height - 1, null, 0, width);

    // Convert ints to bytes
    ByteBuffer byteBuffer = ByteBuffer.allocate(pixels.length * 4);
    IntBuffer intBuffer = byteBuffer.asIntBuffer();
    intBuffer.put(pixels);

    byte[] pixelBytes = byteBuffer.array();

    img.put(0, 0, pixelBytes);

    // Reorder the channels for Opencv BGRA format from
    // BufferedImage ARGB format
    Mat imgMix = img.clone();
    ArrayList<Mat> imgSrc = new ArrayList<Mat>();
    imgSrc.add(imgMix);

    ArrayList<Mat> imgDest = new ArrayList<Mat>();
    imgDest.add(img);

    int[] fromTo = { 0, 3, 1, 2, 2, 1, 3, 0 }; //Each pair is a channel swap
    Core.mixChannels(imgSrc, imgDest, new MatOfInt(fromTo));
}

//Save output image
Imgcodecs.imwrite("temp/image_mat.png", img);

新的输出图片

【讨论】:

  • 我使用的是 Javacv,里面没有 Matofint(int...a) 函数。
  • @AqeelHaider 包含它的标准 Java 绑定 (docs.opencv.org/java/2.4.9/org/opencv/core/MatOfInt.html)
  • 我使用 IplImage 加载图像。使用 cvResize() 方法来调整它的大小。将图像转换为缓冲图像以使图像角变圆。并使用 TYPE_INT_ARGB 然后将其转换回 Mat 进行透视变换。将其转换回缓冲图像以与另一个图像合并。然后保存它。当我将图像从缓冲区转换为 Mat 它的颜色变化时,问题就来了。
  • 如果我在缓冲图像中使用 TYPE_3BYTE_BGR 然后将其转换为 Mat 那么颜色不会改变,但它会将图像的背景透明度移到后面。
猜你喜欢
  • 1970-01-01
  • 2016-09-25
  • 1970-01-01
  • 2018-03-11
  • 1970-01-01
  • 1970-01-01
  • 2012-11-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多