《Java数字图像处理:编程技巧与应用实践》——第2章 Java BufferedImage对象及其支持的API操作 2.1 BufferedImage对象的构成

简介:

本节书摘来自华章计算机《Java数字图像处理:编程技巧与应用实践》一书中的第2章,第2.1节,作者 贾志刚,更多章节内容可以访问云栖社区“华章计算机”公众号查看。

第2章

Java BufferedImage对象及其支持的API操作

第1章我们一起学习了Java中的Graphics图形包基本概念与知识,本章将介绍Java中关于图像文件操作的基本知识。首先是Java 2D图像对象BufferedImage的组件构成、与图像文件之间的关系、格式支持,以及如何利用BufferedImage对象在Java语言中实现像素读写操作。然后通过BufferedImageOp接口介绍Java中几种非常有用的对像素操作的Buffered-ImageOp的实现类。最后将集合上述知识点,实现一个简单Java Swing的滤镜程序,帮助读者实现学以致用,加深理解。

在介绍本章内容之前,笔者假设你已经掌握了基本Java语言编程知识,学习过简单的Swing程序,同时对图像文件的格式及其特点有一些简单的了解。这些知识点可以帮助你更好地学习本章内容。

2.1 BufferedImage对象的构成

BufferedImage是一个内存对象,当通过ImageIO.read()方法读取一个图像文件时,读取到的关于图像文件的所有信息都会被存储在该API返回的BufferedImage内存对象中。此外还可以通过BufferedImage类的构造函数来创建BufferedImage内存对象。BufferedImage对象中最重要的两个组件为Raster与ColorModel,分别用于存储图像的像素数据与颜色数据,BufferedImage中的其他属性还包括宽、高、图像类型等。当需要对BufferedImage对象实现一些像素级别的操作时,调用Raster对象总是有点道理,如果做个形象的比喻,Raster就好像一个像素操作的场地,任何像素读写操作都可以通过调用Raster相关接口来完成。一个完整的BufferedImage构成类关系图如图2-1所示。

screenshot

2.1.1 Raster对象的作用与像素存储

由于Raster对象是BufferedImage对象中的像素数据存储对象,因此,BufferedImage支持从Raster对象中获取任意位置(x,y)点的像素值p(x,y)。对于任意的BufferedImage对象来说,拥有越多的像素,Raster对象需要的内存空间也就越大,同时Raster对象需要的内存空间的大小还跟每个像素需要存储的字节数有一定的关系。首先来探讨一下如何从Raster对象获取像素数据,从Raster对象中读取BufferedImage全部像素数据的代码如下:

public int[] getRGB(BufferedImage image, int x, int y, 
        int width, int height, int[] pixels) {
    int type = image.getType();
    if (type == BufferedImage.TYPE_INT_ARGB
        || type == BufferedImage.TYPE_INT_RGB) {
        return (int[]) image.getRaster().
        getDataElements(x, y, width,height, pixels);
    } else {
        return image.getRGB(x, y, width, 
            height, pixels, 0, width);
    }
}

上述方法实现了从Raster中读取像素数据,其中x, y表示开始的像素点,width与height表示像素数据的宽度与高度,pixels数组用来存放获取到的像素数据,image是一个BufferedImage的实例化引用。向BufferedImage对象实例中写入像素数据需要通过Raster来完成,其代码如下:

public void setRGB(BufferedImage image, int x, int y, 
        int width, int height, int[] pixels) {
        int type = image.getType();
    if (type == BufferedImage.TYPE_INT_ARGB
    || type == BufferedImage.TYPE_INT_RGB) {
        image.getRaster().
        setDataElements(x, y, width, height, pixels);
    } else {
        image.setRGB(x, y, width, height, 
                                    pixels, 0, width);
    }
}

上面的代码演示了如何从Raster对象中读取与写入像素数据,也许有读者会问如何自己创建一个Raster对象呢?其实,Java图像的API操作中已经提供了这样的功能,实现代码如下:

Raster.createWritableRaster(sm, db, null);

其中sm指的是SampleModel对象实例,db表示DataBuffer对象实例,最后一个参数Point参数默认为null。如何创建SampleModel将在下一小节中详细解释。

2.1.2 图像类型与ColorModel

从前面的内容可以知道,BufferedImage对象中最重要的一个组件是ColorModel对象,最常用的实现类是IndexColorModel,下面就以此为例来演示如何创建与使用ColorModel对象。首先来看如何创建一个IndexColorModel对象,IndexColorModel的构造函数有五个参数,分别为:

  • Bits:表示每个像素的所占的位数,对RGB单色来说是8位。
  • Size:表示颜色组件数组长度,对于RGB取值范围0~255而言,值为256。
  • r[]:字节数组r表示颜色组件的RED值数组。
  • g[]:字节数组r表示颜色组件的GREEN值数组。
  • b[]:字节数组r表示颜色组件的BLUE值数组。

通常而言,每个单色所占的位数都在1~16之间,size值必须大等于1。正确创建Index-ColorModel的代码如下:

public IndexColorModel getColorModel() {
    byte[] r = new byte[256];
    byte[] g = new byte[256];
    byte[] b = new byte[256];
    for (int i = 0; i < 256; i++) 
    {
        r[i] = (byte) i;
        g[i] = (byte) i;
        b[i] = (byte) i;
        }
    return new IndexColorModel(8, 256, r, g, b);
}

BufferedImage对象中最重要的两个组件如何创建我们都知道了,下面一小节就一起来看看BufferedImage对象本身是如何创建的。

2.1.3 BufferedImage对象的创建与保存

根据不同的使用场景创建一个BufferedImage对象有三种常见方法,第一种方法是创建一个全新的BufferedImage对象,直接调用BufferedImage的构造函数。这在图形绘制的程序中比较常见,其代码如下:

BufferedImage bi = new BufferedImage(width, height,
                       BufferedImage.TYPE_BYTE_GRAY);

其中width表示图像的宽度,height表示高度,最后一个参数声明图像字节灰度图像。

第二种方法是根据已经存在的BufferedImage对象来创建一个相同的copy体。这种方法在图像处理的程序中最常见,其代码如下:

public BufferedImage createBufferedImage(BufferedImage src) {
    ColorModel cm = src.getColorModel();
    BufferedImage image = new BufferedImage(cm, 
        cm.createCompatibleWritableRaster(
        src.getWidth(), 
        src.getHeight()), 
        cm.isAlphaPremultiplied(), null);
    return image;
}

第三种方法是通过创建ColorModel与Raster对象实现BufferedImage对象的实例化,其代码如下:

public BufferedImage createBufferedImage(int width, 
            int height,
            byte[] pixels) {
    ColorModel cm = getColorModel();
    SampleModel sm = 
            getIndexSampleModel((IndexColorModel) cm, 
            width, height);
    DataBuffer db = new DataBufferByte(pixels, 
            width * height, 0);
    WritableRaster raster = 
            Raster.createWritableRaster(sm, db, null);
    BufferedImage image = new BufferedImage(cm, 
                                    raster, false, null);
    return image;
}

上述几种方法都是关于如何创建一个BufferedImage对象的,下面来看一下如何保存BufferedImage对象为本地图像文件。Java中提供了ImageIO工具类来实现图像文件与BufferedImage对象之间的转换,读取一个图像文件时使用如下代码即可:

public BufferedImage readImageFile(File file)
{
    try {
        BufferedImage image = ImageIO.read(file);
        return image;
    } catch (IOException e) {
        e.printStackTrace();
    }
    return null;
}

保存BufferedImage对象为图像文件的代码如下:

public void writeImageFile(BufferedImage bi) 
                           throws IOException
{
    File outputfile = new File("saved.png");  
    ImageIO.write(bi, "png",outputfile); 
}

2.1.4 一个完整的ImageBuffered读取例子

本例将会演示前面所讲到的关于BufferedImage对象的所有知识点,包括像素的读取、Raster对象的创建、ColorModel的使用等。下面的代码演示了通过获取鼠标位置改变图像ColorModel对象索引,从而实现图像像素自动变化的方法。

package com.book.chapter.two;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.IndexColorModel;
import java.awt.image.Raster;
import java.awt.image.SampleModel;
import java.awt.image.SinglePixelPackedSampleModel;
import java.awt.image.WritableRaster;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class BufferedImageDemo extends JPanel 
            implements MouseMotionListener {

    private BufferedImage image = null;
    private int width = 350;
    private int height = 350;
    
    public BufferedImageDemo() {
        image = createImage();
        addMouseMotionListener(this);
    }

    @Override
    public void mouseMoved(MouseEvent e) {
        // 创建新的图片,基于新的颜色模型索引
        image = new BufferedImage(createColorModel(e.getX()),
                image.getRaster(), false, null);
        repaint();
    }

    @Override
    public void mouseDragged(MouseEvent e) {
    }
    
    public void paintComponent(Graphics g) {
        Graphics2D g2d = (Graphics2D) g;
        if(image  != null) {
            g2d.drawImage(image, 2, 2, 
                    width, height, null);
        }
    }

    private BufferedImage createImage() {
        byte[] pixels = new byte[width * height];
        DataBuffer dataBuffer = new DataBufferByte(pixels,
                         width*height, 0);
        SampleModel sampleModel = new
                 SinglePixelPackedSampleModel(
                    DataBuffer.TYPE_BYTE, 
                width, height, new int[] {(byte)0xf});
        WritableRaster raster = Raster.createWritableRaster(
            sampleModel, dataBuffer, null);
        return new BufferedImage(createColorModel(0), 
                        raster, false, null);
    }

    private static ColorModel createColorModel(int n) {
        byte[] r = new byte[16];
        byte[] g = new byte[16];
        byte[] b = new byte[16];

        for (int i = 0; i < r.length; i++) {
            r[i] = (byte) n;
            g[i] = (byte) n;
            b[i] = (byte) n;
        }
        return new IndexColorModel(4, 16, r, g, b);
    }

    public static void main(String[] args) {
        
        JFrame ui = new JFrame("BufferedImage Demo");
        ui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        ui.getContentPane().setLayout(new BorderLayout());
        ui.getContentPane().add(new BufferedImageDemo(), 
                        BorderLayout.CENTER);
        ui.setPreferredSize(new Dimension(380, 380));
        ui.pack();
        ui.setVisible(true);
    }
}
相关文章
|
27天前
|
Java API Maven
HDFS的API操作
HDFS的API操作
25 0
C4.
|
1月前
|
缓存 Java
Java的Integer对象
Java的Integer对象
C4.
11 0
|
20天前
|
Java
java8中List对象转另一个List对象
java8中List对象转另一个List对象
36 0
|
26天前
|
人工智能 关系型数据库 Serverless
Serverless 应用引擎常见问题之API生成的函数镜像改为自定义的镜像如何解决
Serverless 应用引擎(Serverless Application Engine, SAE)是一种完全托管的应用平台,它允许开发者无需管理服务器即可构建和部署应用。以下是Serverless 应用引擎使用过程中的一些常见问题及其答案的汇总:
37 3
|
1天前
|
Java 测试技术 索引
滚雪球学Java(14):快速入门JavaSE-for循环语句,轻松掌握编程技巧
【4月更文挑战第3天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
12 1
滚雪球学Java(14):快速入门JavaSE-for循环语句,轻松掌握编程技巧
|
6天前
|
存储 Java 编译器
对象的交响曲:深入理解Java面向对象的绝妙之处
对象的交响曲:深入理解Java面向对象的绝妙之处
35 0
对象的交响曲:深入理解Java面向对象的绝妙之处
|
11天前
|
Java
在Java中,多态性允许不同类的对象对同一消息做出响应
【4月更文挑战第7天】在Java中,多态性允许不同类的对象对同一消息做出响应
16 2
|
19天前
|
XML JSON 安全
谈谈你对RESTful API设计的理解和实践。
RESTful API是基于HTTP协议的接口设计,通过URI标识资源,利用GET、POST、PUT、DELETE等方法操作资源。设计注重无状态、一致性、分层、错误处理、版本控制、文档、安全和测试,确保易用、可扩展和安全。例如,`/users/{id}`用于用户管理,使用JSON或XML交换数据,提升系统互操作性和可维护性。
14 4
|
20天前
|
Java
Java常用封装Base对象
Java常用封装Base对象
8 0
|
25天前
|
消息中间件 缓存 API
微服务架构下的API网关性能优化实践
在现代的软件开发中,微服务架构因其灵活性和可扩展性被广泛采用。随着服务的细分与增多,API网关作为微服务架构中的关键组件,承担着请求路由、负载均衡、权限校验等重要职责。然而,随着流量的增长和业务复杂度的提升,API网关很容易成为性能瓶颈。本文将深入探讨API网关在微服务环境中的性能优化策略,包括缓存机制、连接池管理、异步处理等方面的具体实现,旨在为开发者提供实用的性能提升指导。