java:图像(BufferedImage)色彩空间转换(灰度)暨获取图像矩阵数据byte[](sRGB/gray)

时间:2022-06-22
本文章向大家介绍java:图像(BufferedImage)色彩空间转换(灰度)暨获取图像矩阵数据byte[](sRGB/gray),主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

版权声明:本文为博主原创文章,转载请注明源地址。 https://blog.csdn.net/10km/article/details/51866321

ColorConvertOp

java.awt.image包下面有个类java.awt.image.ColorConvertOp,类名直译就是”颜色转换操作”。 顾名思义,它的作用就是将一个色彩空间(color space)的图像转换为另一个色彩空间的图像。有了这个神器我们就能轻易的将一张彩色图你像转换成灰度(gray)或其他色彩空间图像。 代码非常简单,只要一行。

    public BufferedImage toGray(BufferedImage srcImg){      
        return new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null).filter(srcImg, null);
    }

依此类推,你可以参照ColorConvertOp的参数说明将图像转为其他格式。 java.awt.color.ColorSpace中列出了很多支持的色彩空间定义TYPE_RGB,TYPE_CMYK,TYPE_HSV,TYPE_YCbCr….

Raster.getDataElements

有时我们通过ImageIO得到解码后的图像数据对象(BufferedImage)以后,需要获取图像矩阵的裸数据(即一个存储图像数据的byte数组)。 BufferedImage中提供了一个getRGB()方法,它返回的是一个ARGB格式int[]数组(每个int型元素的4个字节分别代表一个像素的Alpha,Red,Green,Blue四个通道) 如果你要从这个方法获取RGB的数组,你还得自己写转换代码:

    /**
     * 返回图像的RGB格式字节数组
     * @param image
     * @return
     */
    public static byte[] getMatrixRGB(BufferedImage image){
        int w = image.getWidth();
        int h = image.getHeight();
        int[] intArray = new int[w * h];
        byte[] matrixRGB = new byte[w * h * 3];
        image.getRGB(0, 0, w, h, intArray, 0, w);
        // ARGB->RGB
        for(int i=0,b=0;i<intArray.length;++i){
            matrixRGB[b++]=(byte) (matrixRGB[i]&0x000000FF);
            matrixRGB[b++]=(byte) ((matrixRGB[i]&0x0000FF00)>>8);
            matrixRGB[b++]=(byte) ((matrixRGB[i]&0x00FF0000)>>16);
        }
        return matrixRGB;
    }

好烦呐,我以前就是这么干的,真的没有提供更好的方法吗? 不是没有更好的方法,而是我学艺不精没找到而已。 在仔细研究了BufferedImage的代码之后,才明白getRGB()只是BufferedImage为默认 RGB 颜色模型 (TYPE_INT_ARGB)提供的一个便利性封装。 通过getRGB()源码可以知道BufferedImage对象中真正的图像数据是由成员对象raster(java.awt.image.WritableRaster)管理。而WritableRasterjava.awt.image.Raster的子类。Raster中getDataElements方法可以我们所需要的字节数组。 还以前面图像转灰度举例,如果要从灰度图像中获取图像矩阵的字节数组,代码示例如下:

    /**
     * 获取灰度图像的字节数组
     * @param image
     * @return
     */
    public static byte[] getMatrixGray(BufferedImage image) {
            // 转灰度图像
            BufferedImage grayImage = new BufferedImage(width, height,  
                        BufferedImage.TYPE_BYTE_GRAY);      
            new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null).filter(image, grayImage);
            // getData方法返回BufferedImage的raster成员对象
            return (byte[]) grayImage.getData().getDataElements(0, 0, image.getWidth(), image.getHeight(), null);       
    }

注意这里return语句使用了(byte[])强制类型转换,因为getDataElements返回的是打开声明 java.lang.Object对象。 也就是说getDataElements返回的未必是byte[]类型,为什么呢?看下面getDataElements方法的说明:

看不懂没关系,我们可以看到这里的返回的类型可能是:TYPE_BYTE,TYPE_USHORT,TYPE_INT,TYPE_SHORT,TYPE_FLOAT,TYPE_DOUBLE。并不一定是byte。 那么问题来了,如何控制返回的数组类型是byte[]呢? 同样,我们可以使用前面的ColorConvertOp对象进行转换。 比如我们需要得到图像的RGB数据:

    /**
     * 获取图像RGB格式数据
     * @param image
     * @return
     */
    public static byte[] getMatrixRGB(BufferedImage image){
        if(image.getType()!=BufferedImage.TYPE_3BYTE_BGR){
            // 转sRGB格式
            BufferedImage rgbImage = new BufferedImage(
                        image.getWidth(), 
                        image.getHeight(),  
                        BufferedImage.TYPE_3BYTE_BGR);
            new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_sRGB), null).filter(image, rgbImage);
            // 从Raster对象中获取字节数组
            return (byte[]) rgbImage.getData().getDataElements(0, 0, rgbImage.getWidth(), rgbImage.getHeight(), null);
        }else{
            return (byte[]) image.getData().getDataElements(0, 0, image.getWidth(), image.getHeight(), null);
        }
    }