一文带你掌握Java ImageIO类

Java
418
0
0
2023-05-27
目录
  • 一、ImageIO 的基础知识
  • 1.1 ImageIO 的作用
  • 1.2 ImageIO 的特点
  • 1.3 ImageIO 的使用环境
  • 1.4 ImageIO 的基本使用方法
  • 二、ImageIO 的读取操作
  • 2.1 ImageIO 的 read() 方法
  • 2.2 以 Byte 数组形式读取图像
  • 2.3 指定图像格式
  • 三、ImageIO 的写入操作
  • 3.1 ImageIO 的 write() 方法
  • 3.2 指定图像格式
  • 3.3 指定压缩质量
  • 3.4 指定保存位置
  • 四、ImageIO 的格式转换
  • 五、ImageIO 的高级操作
  • 5.1 裁剪图像
  • 5.2 旋转图像
  • 5.3 缩放图像
  • 5.4 反转图像
  • 5.5 镜像图像
  • 5.6 组合操作
  • 六、注意事项

一、ImageIO 的基础知识

1.1 ImageIO 的作用

ImageIO 是 Java 标准库提供的一个类,它提供了读取和写入多种常见图像格式的功能。开发者可以使用 ImageIO 类快速、便捷地读取和写入图像,而无需关心底层的细节。

1.2 ImageIO 的特点

ImageIO 支持的图像格式非常广泛,包括 JPEG、PNG、BMP、GIF 等常见格式,同时也支持 TIFF、ICO、WBMP、JPEG 2000 等更加少见的格式。这使得开发者可以方便地读取和写入多种不同格式的图像。

ImageIO 还支持灵活的、可配置的图片格式转换。例如,可以将一张 JPEG 格式的图像读取到内存中,然后将其保存为 PNG 格式的图像。此外,ImageIO 还支持高级的图像处理操作,如裁剪、旋转、缩放、反转、镜像等。

1.3 ImageIO 的使用环境

ImageIO 是 Java 标准库中的一个类,因此不需要额外的库或安装,可以直接在 Java 应用程序中使用。它支持 Java SE 6 及以上版本,并且可以运行在所有常见的操作系统上。

1.4 ImageIO 的基本使用方法

使用 ImageIO 类读取和写入图像的基本流程如下:

  • 通过 ImageIO 类的静态方法 read() 或者 write() 方法获取图片输入输出流;
  • 创建 ImageInputStream 或者 ImageOutputStream;
  • 读/写图像。

例如,读取一张 JPEG 格式的图片,代码如下:

    String imageFilePath = "image.jpg";
    try {
        InputStream inputStream = new FileInputStream(new File(imageFilePath));
        BufferedImage bufferedImage = ImageIO.read(inputStream);
        // 图像处理逻辑
    } catch (IOException e) {
        e.printStackTrace();
    }

上述代码通过 FileInputStream 打开文件输入流,然后通过 ImageIO.read() 方法读取图像数据,最后对图像进行了处理操作。

下面我们将详细介绍 ImageIO 类的各个方面,包括读取、写入、压缩、格式转换以及高级操作等。

二、ImageIO 的读取操作

2.1 ImageIO 的 read() 方法

ImageIO 的 read() 方法是最常见的读取图像的方式,它有多个重载形式,可以用来读取 File、InputStream、URL 和 ImageInputStream 等多种类型的输入流。此外,ImageIO 还支持从 byte 数组读取或者从任意 SeekableStream 中读取文件。

例如,通过 ImageIO 的 read() 方法从本地文件读取一张图片:

    String imageFilePath = "image.jpg";
    try {
        BufferedImage bufferedImage = ImageIO.read(new File(imageFilePath));
        // 图像处理逻辑
    } catch (IOException e) {
        e.printStackTrace();
    }

read() 方法返回了一个 BufferedImage 对象,开发者可以对这个对象进行各种操作,如裁剪、旋转、缩放等。注意:当读取的图像不是 RGB 格式时,返回的 BufferedImage 对象可能为 null。

2.2 以 Byte 数组形式读取图像

有些场景下,开发者需要将图片数据保存在内存中,然后再进行处理。ImageIO 的 read() 方法提供了一个重载形式,可以将图像数据读取到 byte 数组中,示例代码如下:

    String imageFilePath = "image.jpg";
    try {
        File file = new File(imageFilePath);
        BufferedImage bufferedImage = ImageIO.read(file);

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ImageIO.write(bufferedImage, "jpg", byteArrayOutputStream);

        byte[] bytes = byteArrayOutputStream.toByteArray();
        // 处理字节数组中的图像
    } catch (IOException e) {
        e.printStackTrace();
    }

在上述代码中,首先使用 ImageIO.read() 方法将图片读取到内存中,并得到了一个 BufferedImage 对象。然后,使用 ImageIO.write() 方法将 BufferedImage 对象写入到 ByteArrayOutputStream 中,最终得到了字节数组形式的图像数据。

2.3 指定图像格式

ImageIO 的 read() 方法可以自动识别常见的图像格式,但有时候需要指定特定的格式。通过 ImageIO 的 getReaderFormatNames() 方法可以查看支持的格式列表,示例代码如下:

    String[] formatNames = ImageIO.getReaderFormatNames();
    for (String format : formatNames) {
        System.out.println(format);
    }

如果要指定读取的格式,可以使用 ImageIO 的 getImageReadersByFormatName() 方法指定,例如:

    File file = new File("image.xx");
    Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName("xxx");
    try {
        ImageReader reader = readers.next();
        ImageInputStream inputStream = ImageIO.createImageInputStream(file);
        reader.setInput(inputStream);
        BufferedImage image = reader.read(0);
        // 处理读取的图像
    } catch (IOException e) {
        e.printStackTrace();
    }

在上述代码中,getImageReadersByFormatName() 方法传入的参数是指定的图像格式名,然后通过 Iterator 获取到 ImageReader 对象,再使用 ImageIO.createImageInputStream() 方法创建 ImageInputStream 对象,并通过 setInput() 方法设置输入源。最后通过 reader.read() 方法读取图像。

三、ImageIO 的写入操作

3.1 ImageIO 的 write() 方法

ImageIO 的 write() 方法是写入图像的主要方法,它可以将 BufferedImage 对象写入到文件或者输出流中。除此之外,ImageIO 还提供了其他的方法来指定写出的格式和压缩质量等参数。

下面是一个将 BufferedImage 写入 PNG 格式的简单示例:

    BufferedImage image = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB);
    try {
        ImageIO.write(image, "png", new File("output.png"));
    } catch (IOException e) {
        e.printStackTrace();
    }

在上述代码中,使用 ImageIO.write() 方法将 BufferedImage 对象写入到本地文件 output.png 中。

3.2 指定图像格式

与读取图像类似,ImageIO 的 write() 方法默认会根据扩展名或者其他信息自动识别输出的图片格式。但是有些场景下需要手动指定输出的图像格式,可通过如下方式进行设置:

    BufferedImage image = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB);
    try {
        File file = new File("output.xx");
        ImageWriter writer = ImageIO.getImageWritersByFormatName("xxx").next();
        ImageOutputStream outputStream = ImageIO.createImageOutputStream(file);
        writer.setOutput(outputStream);
        writer.write(image);
        outputStream.close();
    } catch (Exception e) {
        e.printStackTrace();
    }

在上述代码中,getImageWritersByFormatName() 方法传入指定的图像格式名,然后通过 ImageWriter 对象设置 ImageOutputStream,最后通过 write() 方法写入图像。

3.3 指定压缩质量

在写入图像时,ImageIO 还可以控制输出的压缩质量,从而控制输出图像文件的大小。不过需要注意的是,并非所有的图像格式都支持压缩。目前只有 JPEG 和 PNG 格式支持压缩功能。

下面是一个将 BufferedImage 写入 JPG 格式并指定压缩质量的示例:

    BufferedImage image = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB);
    try {
        File file = new File("output.jpg");

        // 获取 JPEG 格式的 ImageWriter
        ImageWriter writer = ImageIO.getImageWritersByFormatName("jpeg").next();

        // 设置输出流
        ImageOutputStream outputStream = ImageIO.createImageOutputStream(file);
        writer.setOutput(outputStream);

        // 设置压缩参数
        JPEGImageWriteParam param = (JPEGImageWriteParam) writer.getDefaultWriteParam();
        param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
        param.setCompressionQuality(0.8f);

        // 写入图像
        writer.write(null, new IIOImage(image, null, null), param);
        outputStream.close();
    } catch (Exception e) {
        e.printStackTrace();
    }

在上述代码中,首先从 ImageIO 中获取 JPEG 格式的 ImageWriter 对象,然后设置输出流和压缩参数。其中,setCompressionQuality() 方法用于设置压缩质量,值范围为 0.0~1.0,其中 1.0 表示不压缩,0.0 表示最大压缩。

3.4 指定保存位置

如果需要将 BufferedImage 对象写入到指定的文件目录下,可以使用 ImageIO 的 write() 方法进行操作。下面是一个将 BufferedImage 写入本地文件的示例:

    BufferedImage image = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB);
    try {
        File file = new File("output.png");
        ImageIO.write(image, "png", file);
    } catch (Exception e) {
        e.printStackTrace();
    }

需要注意的是,write() 方法不支持压缩参数设置,如果需要控制输出文件大小,需要使用上述的 ImageWriter 方式。

四、ImageIO 的格式转换

在实际开发中,经常需要将图像从一种格式转换成另一种格式。ImageIO 提供的格式转换功能非常灵活,可以通过如下代码片段实现:

    BufferedImage srcImage = ImageIO.read(new File("input.jpg"));
    File outputFile = new File("output.png");

    // 获取 png 格式的 ImageWriter
    ImageWriter writer = ImageIO.getImageWritersByFormatName("png").next();

    // 设置输出流
    ImageOutputStream outputStream = ImageIO.createImageOutputStream(outputFile);
    writer.setOutput(outputStream);

    // 写入图像
    writer.write(null, new IIOImage(srcImage, null, null), null);
    outputStream.close();

在上述代码中,首先使用 ImageIO.read() 方法读取 JPEG 格式的图像,然后通过 getImageWritersByFormatName() 方法获取 PNG 格式的 ImageWriter 对象。接着创建 ImageOutputStream 对象,并写入转换后的 PNG 格式图像。

需要注意的是,有些格式转换可能会导致图像信息的丢失,例如将 24-bit 的 RGB 图像转换成 8-bit 的 GIF 图像,会导致颜色数量减少,从而造成图像质量下降。

五、ImageIO 的高级操作

除了基本的格式读取、写入和转换外,ImageIO 还提供了一些高级的图像处理操作,如裁剪、旋转、缩放、反转、镜像等。下面简单介绍一下这些操作。

5.1 裁剪图像

通过 ImageIO.read() 方法读取的 BufferedImage 对象支持裁剪功能。可以通过调用 getSubimage() 方法获取指定区域的子图像。例如:

    BufferedImage srcImage = ImageIO.read(new File("input.jpg"));
    BufferedImage destImage = srcImage.getSubimage(10, 10, 100, 100);

5.2 旋转图像

ImageIO 也支持对图像进行旋转操作。可以通过 AffineTransform 类来实现图像的旋转变换。

    BufferedImage srcImage = ImageIO.read(new File("input.jpg"));
    double radian = Math.toRadians(30); // 旋转角度为 30 度
    AffineTransform transform = new AffineTransform();
    transform.rotate(radian, srcImage.getWidth() / 2, srcImage.getHeight() / 2);

    AffineTransformOp op = new AffineTransformOp(transform, AffineTransformOp.TYPE_BILINEAR);
    BufferedImage destImage = op.filter(srcImage, null);

在上述代码中,radian 变量表示旋转的角度,AffineTransform 类用于创建旋转变换,AffineTransformOp 类用于将变换应用到图像上。

5.3 缩放图像

缩放图像同样是 ImageIO 的高级操作之一。可以通过 AffineTransform 类来实现图像的缩放变换。

    BufferedImage srcImage = ImageIO.read(new File("input.jpg"));
    AffineTransform transform = new AffineTransform();
    double scale = 0.5; // 图像缩小一倍
    transform.scale(scale, scale);

    AffineTransformOp op = new AffineTransformOp(transform, AffineTransformOp.TYPE_BILINEAR);
    BufferedImage destImage = op.filter(srcImage, null);

在上述代码中,通过 AffineTransform 的 scale() 方法设置缩放比例,AffineTransformOp 类用于将变换应用到图像上。

5.4 反转图像

对于需要水平或垂直翻转的图像,ImageIO 提供了如下方式实现:

    ```
// 读取输入图像(示例中为名为 input.png 的图像)
BufferedImage srcImage = ImageIO.read(new File("input.png"));

// 水平镜像
AffineTransform transform = new AffineTransform(-1, 0, 0, 1, srcImage.getWidth(), 0);
AffineTransformOp op = new AffineTransformOp(transform, AffineTransformOp.TYPE_BILINEAR);
BufferedImage horizontalFlipped = op.filter(srcImage, null);

// 垂直镜像
transform = new AffineTransform(1, 0, 0, -1, 0, srcImage.getHeight());
op = new AffineTransformOp(transform, AffineTransformOp.TYPE_BILINEAR);
BufferedImage verticalFlipped = op.filter(srcImage, null);

// 上下颠倒
transform = new AffineTransform(-1, 0, 0, -1, srcImage.getWidth(), srcImage.getHeight());
op = new AffineTransformOp(transform, AffineTransformOp.TYPE_BILINEAR);
BufferedImage upsideDown = op.filter(srcImage, null);

// 保存结果图像(保存为 PNG 格式的文件)
ImageIO.write(horizontalFlipped, "png", new File("horizontal.png"));
ImageIO.write(verticalFlipped, "png", new File("vertical.png"));
ImageIO.write(upsideDown, "png", new File("upside_down.png"));

5.5 镜像图像

对于需要镜像的图像,ImageIO 提供了如下方式实现:

    BufferedImage srcImage = ImageIO.read(new File("input.jpg"));

    // 水平镜像
    AffineTransform transform = new AffineTransform(-1, 0, 0, 1, srcImage.getWidth(), 0);

    // 垂直镜像
    AffineTransform transform = new AffineTransform(1, 0, 0, -1, 0, srcImage.getHeight());

在上述代码中,水平镜像通过将 x 轴翻转并沿 y 轴平移一段距离实现,垂直镜像通过将 y 轴翻转并沿 x 轴平移一段距离实现。

5.6 组合操作

同时进行多个图像变换时,可以通过 AffineTransform 中的 concatenate() 方法,将多个变换组合到一起进行处理。例如:

    BufferedImage srcImage = ImageIO.read(new File("input.jpg"));
    AffineTransform transform1 = new AffineTransform();
    AffineTransform transform2 = new AffineTransform();
    double scale = 0.5;
    transform1.scale(scale, scale);
    transform2.translate(50, 50);

    transform1.concatenate(transform2); // 将两个变换组合在一起

    AffineTransformOp op = new AffineTransformOp(transform1, AffineTransformOp.TYPE_BILINEAR);
    BufferedImage destImage = op.filter(srcImage, null);

在上述代码中,首先创建两个不同的 AffineTransform 对象,一个用于缩放,一个用于平移,然后将其组合在一起。注意,组合时要先进行平移变换,再进行缩放变换,否则会产生错误的结果。

六、注意事项

在使用 ImageIO 时,需要注意以下几点:

  • 避免频繁的读写文件,选择合适的内存缓存方式进行操作;
  • 在处理不同格式的图像时,应该注意色彩模式、透明度等方面的差异,以避免图像信息的丢失;
  • 在进行高级操作时,应该考虑到图像质量的问题,避免操作过度造成图像质量的下降。