《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);
    }
}
相关文章
|
2月前
|
人工智能 Java API
Java也能快速搭建AI应用?一文带你玩转Spring AI可落地性
Java语言凭借其成熟的生态与解决方案,特别是通过 Spring AI 框架,正迅速成为 AI 应用开发的新选择。本文将探讨如何利用 Spring AI Alibaba 构建在线聊天 AI 应用,并实现对其性能的全面可观测性。
725 21
|
1月前
|
前端开发 Cloud Native Java
Java||Springboot读取本地目录的文件和文件结构,读取服务器文档目录数据供前端渲染的API实现
博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
Java||Springboot读取本地目录的文件和文件结构,读取服务器文档目录数据供前端渲染的API实现
|
1月前
|
缓存 安全 Java
《从头开始学java,一天一个知识点》之:字符串处理:String类的核心API
🌱 **《字符串处理:String类的核心API》一分钟速通!** 本文快速介绍Java中String类的3个高频API:`substring`、`indexOf`和`split`,并通过代码示例展示其用法。重点提示:`substring`的结束索引不包含该位置,`split`支持正则表达式。进一步探讨了String不可变性的高效设计原理及企业级编码规范,如避免使用`new String()`、拼接时使用`StringBuilder`等。最后通过互动解密游戏帮助读者巩固知识。 (上一篇:《多维数组与常见操作》 | 下一篇预告:《输入与输出:Scanner与System类》)
72 11
|
1月前
|
人工智能 Java API
Java 也能快速搭建 AI 应用?一文带你玩转 Spring AI 可观测性
Java 也能快速搭建 AI 应用?一文带你玩转 Spring AI 可观测性
115 4
|
2月前
|
数据采集 存储 Java
Java爬虫获取微店店铺所有商品API接口设计与实现
本文介绍如何使用Java设计并实现一个爬虫程序,以获取微店店铺的所有商品信息。通过HttpClient发送HTTP请求,Jsoup解析HTML页面,提取商品名称、价格、图片链接等数据,并将其存储到本地文件或数据库中。文中详细描述了爬虫的设计思路、代码实现及注意事项,包括反爬虫机制、数据合法性和性能优化。此方法可帮助商家了解竞争对手,为消费者提供更全面的商品比较。
|
2月前
|
缓存 Java 应用服务中间件
java语言后台管理若依框架-登录提示404-接口异常-系统接口404异常如何处理-登录验证码不显示prod-api/captchaImage 404 (Not Found) 如何处理-解决方案优雅草卓伊凡
java语言后台管理若依框架-登录提示404-接口异常-系统接口404异常如何处理-登录验证码不显示prod-api/captchaImage 404 (Not Found) 如何处理-解决方案优雅草卓伊凡
434 5
|
2月前
|
缓存 Java 物联网
CRaC技术助力ACS上的Java应用启动加速
容器计算服务借助ACS的柔性算力特性并搭配CRaC技术极致地提升Java类应用的启动速度。
|
2月前
|
人工智能 Java API
Java 也能快速搭建 AI 应用?一文带你玩转 Spring AI 可观测性
Java 也能快速搭建 AI 应用?一文带你玩转 Spring AI 可观测性
|
3月前
|
Java 编译器 开发者
Java中的this关键字详解:深入理解与应用
本文深入解析了Java中`this`关键字的多种用法
528 9
|
3月前
|
Java 应用服务中间件 API
【潜意识Java】javaee中的SpringBoot在Java 开发中的应用与详细分析
本文介绍了 Spring Boot 的核心概念和使用场景,并通过一个实战项目演示了如何构建一个简单的 RESTful API。
80 5
下一篇
oss创建bucket