Contents | Previous | Next | Programmer's Guide to the JavaTM 2D API |
Chapter 5
Imaging
The Java 2D™ API 支持3种成像模型
下面的表格列举了这些成像模型的特征之间的对比。
这一章着眼于对象和直接方式的成像模型。Java 2D API的直接方式的成像类和接口提供了处理加载到内存中的像素图像的处理技术。这个API支持存取以多种格式存储的图像数据,也支持通过几种类型的过滤来操作图像数据。
5.1 Interfaces and Classes
Java 2D™ API中直接方式成像的APIs可以分为6大类:接口、图像数据类、图像操作类、取样模型类、颜色模型类和异常。
5.1.1 Imaging Interfaces
5.1.2 Image Data Classes
5.1.3 Image Operation Classes
5.1.4 Sample Model Classes
5.1.5 Color Model Classes
5.1.6 Exception Classes
Class
|
Description
|
---|---|
ImagingOpException
|
Extends:
RuntimeException
当一个BufferedImageOp或者RasterOp的过滤方法不能处理图像时被抛出。
|
RasterFormatException
|
Extends:
RuntimeException
当Raster中有无效的布局信息时被抛出。
|
5.2 Immediate Mode Imaging Concepts
5.2 直接方式成像的概念
直接方式的成像模型支持载入内存的、分辨率确定的图像。这种模型也支持对图像数据执行过滤。有许多类和接口可供使用。
如Figure 5-1所示,BufferedImage
提供全面的图像处理。BufferedImage
可以在内存中直接创建,用来持有和操作从文件或者URL中取得的图像数据。BufferedImage
可以通过任何以屏幕设备为目标的Graphics2D
对象显示,或者也可以通过合适的Graphics2D
配置场景呈现到其他目的地。 BufferedImage
对象由另外两个对象组成:Raster(光栅)
对象和ColorModel(颜色模型)
对象。
Raster
类提供图像数据处理。它阐明图像的直角坐标,维护内存里的图像数据,并且提供可以从单一图像数据缓冲创建多个子图像的机制。也提供了存取图像中指定像素的方法。一个Raster对象由另外两个对象组成,DataBuffer
和SampleModel(取样模型)
。
DataBuffer
类掌管内存中的像素数据。
SampleModel
类解释缓冲中的数据,把数据以单独的像素或者某一矩形区域中的像素等形式对外提供。
对于取样模型所提供的像素,ColorModel
类提供对该像素颜色的解释。
图像包还提供了另外一些类,定义了对BufferedImage
对象和Raster
对象的过滤操作。每一个图像的处理操作在实现BufferedImageOp
接口、RasterOp
接口或者同时两个接口的类中得到具体化。其中,由在操作类中定义的filter
方法来执行实际图像操作。
Figure 5-2举例说明了Java 2D™ API处理图像的基本模型:
其支持的操作有:
注意,如果只对显示和操作图像感兴趣,了解BufferedImage
类和执行过滤的类就可以了。反之,如果计划编写过滤器或者以直接存取图像数据的其他方法,那么就需要了解BufferedImage
的相关类。
5.2.1 Terminology
5.2.1 术语
这里是下面讨论中常常出现的一些术语:
数据元素(或元素):一个基本类型的数据用来作为储存图像数据的单元。数据元素是DataBuffer
数组中的独立成员。元素在数据缓冲中的布局与该图像的SampleModel
对像素数据的解释无关。
取样(Samples):像素中的不同成员。SampleModel
提供了把DataBuffer
中的元素转换成像素及其取样的机制。 像素中的取样可以表现为特定颜色模型中的基值。比如,在RGB颜色模型中的像素由3个取样,红、绿、蓝组成。
成分(Components):像素的值,其实际值的得出依赖于颜色解析。对照IndexColorModel
可以帮助理解成分和取样之间的区别,这种模型中像素的成分是LookupTable
的索引值。
波段(Band):图像的所有相同类型采样的集合,例如所有红色的采样或者所有绿色的采样分别集合于各自的波段。像素可以用多种方式存储,Java 2D API支持两种方式是波段化和像素交叉存取。波段化存储是以波段来组织图像的数据,其中的像素由每个波段中相同位置的取样构成。像素交叉存取方式以像素为单位组织图像数据,使用一个单一数组来容纳所有像素,其中的波段由处于每个像素中的相同下标的采样集合组成。
原色(Primaries): 在指定颜色模型的颜色值中的不同成员;例如,RGB模型中的颜色值由红、绿和蓝三种原色构成。
5.3 使用缓冲图像(BufferedImage)
BufferedImage
类是支撑直接成像模型的主要类。它管理内存中的图像,提供了存储像素、解释像素,以及在Graphics
或者Graphics2D
的场景中呈现像素的方法。
5.3.1 创建一个缓冲图像(BufferedImage)
创建BufferedImage
,可以调用Component.createImage
方法;该方法返回一个BufferedImage
对象,其绘图特征与创建它的部件相匹配;由此创建的图像是不透明的,前景和背景颜色与Component
相同,并且你不能改变图像的透明度。在使用双缓冲在部件中显示动画的时候,可以使用这种技术;在“Drawing in an Offscreen Buffer” on page 79中的给出了更详细的说明。
public Graphics2D createDemoGraphics2D(Graphics g) { Graphics2D g2 = null; int width = getSize().width; int height = getSize().height; if (offImg == null || offImg.getWidth() != width || offImg.getHeight() != height) { offImg = (BufferedImage) createImage(width, height); } if (offImg != null) { g2 = offImg.createGraphics(); g2.setBackground(getBackground()); } // .. clear canvas .. g2.clearRect(0, 0, width, height); return g2; }
你也可以使用BufferedImage
提供的几个构造方法,在内存中创建一个空白BufferedImage
。
5.3.2 在屏外缓冲器中绘图
BufferedImage
类可以用来预先在屏外绘制图形元素,然后再拷贝到屏幕上。当图形复杂或者频繁使用时,这个技术尤其有用。例如,如果你要多次显示一个复杂的形状,你可以在屏外缓存中一次绘制成形,然后再拷贝到窗体中的不同位置。通过这种一次成形、多次复制的方法,你可以更快的显示图形。
java.awt
包使屏外缓冲的使用变得容易,它允许你象在窗体上绘图一样在Image
对象上画图。在绘制屏外图像时可以使用所有Java 2D™ API所能提供的绘画特征。
屏外缓冲经常用来显示动画。例如,可以使用一个屏外缓存一次性地绘制一个对象,然后在窗体中到处移动。同样地,可以使用屏外缓冲来提供一个应对用户使用鼠标拖曳图形的反馈。在屏外缓存中完成图形,然后当用户拖动鼠标时把图像拷贝到鼠标当前位置,这样就不需要在每次鼠标位置变化时重画图形。1
Figure 5-3 示范了程序怎样在屏外图像中绘图,然后把绘制好的图像一次次地拷贝到窗体。这个例子中,最后一次拷贝的图像是经过转换的。注意,直接转换这里的屏外图像而不是在窗体执行redraw()操作时进行图像转换,将得到令人失望的结果。
5.3.2.1 创建一个屏外缓冲
最简单的创建图像屏外缓存方法是使用Component
.createImage
方法。
通过创建一个图像,并且该图像的颜色空间、深度和像素布局与窗体完全匹配,那么这个图像可以高效地blit(大量、成批地复制)到图形设备中。这就使drawImage
能更快地工作。
也可以直接使用 BufferedImage
的构造方法创建屏外缓存。这种方法在需要控制屏外图像的类型和透明度时很有用。
BufferedImage
supports several predefined image types:
BufferedImage
支持几个预定义的图像类型:
TYPE_3BYTE_BGR
TYPE_4BYTE_ABGR
TYPE_4BYTE_ABGR_PRE
TYPE_BYTE_BINARY
TYPE_BYTE_GRAY
TYPE_BYTE_INDEXED
TYPE_CUSTOM
TYPE_INT_ARGB_PRE
TYPE_INT_ARGB
TYPE_INT_BGR
TYPE_INT_RGB
TYPE_USHORT_555_RGB
TYPE_USHORT_565_RGB
TYPE_INT_GRAY
BufferedImage
对象可以包含一个alpha(主成分)信道。在Figure 5-3中,alpha信道的用来区别绘画区和非绘画区,这样就允许一个不规则的形状在画好的图像上显示(例子中的虚线框)。在其他情况下,你可以使用alpha信道把新图像的颜色混合到先前的图像。
注意:除非你需要alpha数据实现透明,就象在Figure5-2显示不规则形状的图像那样,否则你应该避免去创建带有alpha的屏外缓存。在不必要的地方使用alpha会降低呈现的执行性能。
GraphicsConfiguration
提供了便利的方法,能自动创建图像格式与你的配置相兼容的缓冲图像。你也可以查询关联于窗体所在图形设备的图形配置(GraphicsConfiguration),以获得创建兼容BufferedImage
对象所需要的信息。
5.3.2.2 在屏外缓冲中绘图
在BufferedImage中绘图,要先调用BufferedImage.createGraphics
方法,得到其返回的Graphics2D
对象。使用这个对象,你可以使用Graphics2D
的所有方法在BufferedImage中绘制基本图形、安置文本以及在其中呈现其他图像。这种绘图技术支持抖动以及其他2D成像包提供的增强性能。下面的代码举例说明了屏外缓冲的使用:
public void update(Graphics g){
Graphics2D g2 = (Graphics2D)g;
if(firstTime){
Dimension dim = getSize();
int w = dim.width;
int h = dim.height;
area = new Rectangle(dim);
bi = (BufferedImage)createImage(w, h); big = bi.createGraphics(); rect.setLocation(w/2-50, h/2-25); big.setStroke(new BasicStroke(8.0f)); firstTime = false; } // Clears the rectangle that was previously drawn. big.setColor(Color.white); big.clearRect(0, 0, area.width, area.height); // Draws and fills the newly positioned rectangle to the buffer. big.setPaint(strokePolka); big.draw(rect); big.setPaint(fillPolka); big.fill(rect); // Draws the buffered image to the screen. g2.drawImage(bi, 0, 0, this);
}
5.3.3 直接操作缓冲图像(BufferedImage)中的数据
除了能在BufferedImage
中直接绘图之外,还可以通过两种方法直接存取和操作像素。实现了BufferedImageOp
过滤接口的类会帮助你实现这类功能,就象84页,“Image Processing and Enhancement” 中讲述的那样。
使用BufferedImage
.setRGB
方法可以直接把一个指定的RGB值设置给一个像素或者一个像素数组。注意,如果直接修改像素将不会有抖动效果。还可以通过操作与BufferedImage
相关联的WritableRaster
来操作像素。(参考“Managing and Manipulating Rasters” on page 80)。
5.3.4 对缓冲图像(BufferedImage)进行过滤操作
你可以使用实现了BufferedImageOp
接口的对象对BufferedImage
进行过滤。过滤操作以及实现过滤操作的类在“Image Processing and Enhancement” on page 84中介绍。
5.3.5 呈现缓冲图像(BufferedImage)
把一个缓冲的图像呈现到特定的场景中,需要调用Graphics
场景对象的drawImage
方法。例如,在Component
.paint
方法中,使用该方法传递进来的Graphics对象的drawImage
方法就可以呈现图像。
public void paint(Graphics g) { if (getSize().width <= 0 || getSize().height <= 0) return; Graphics2D g2 = (Graphics2D) g; if (offImg != null && isShowing()) { g2.drawImage(offImg, 0, 0, this); } }
5.4 管理和操作Raster
BufferedImage
对象使用Raster
来管理像素的矩形数组。Raster
类定义了几个域变量来表示图像的坐标系:宽度、长度和原点。Raster
对象自己使用两个对象来管理像素,一个是DataBuffer
,另一个是SampleModel
。DataBuffer
为Raster存储像素(详述于page 82),SampleModel
则用来解释来自于DataBuffer
的像素(详述于page 82)。
5.4.1 创建一个Raster
大多数情况下,你不需要直接创建Raster
对象,因为你创建的BufferedImage
对象会提供这个对象。不过,有一个BufferedImage
的构造方法允许你以WritableRaster
对象为参数创建Raster
。
Raster
类也提供了许多静态的使用DataBuffers
和SampleModels
创建Raster
的工厂方法。当使用实现RasterOp
接口的类执行过滤操作时,你会用到这些工厂方法。
5.4.2 父子Raster
Raster
类混合了父、子概念,允许从同一父raster构建任意数量的缓冲图像(子raster),这样就可以提高数据存储的效率。父与子raster引用同一个数据缓冲,每个子raster都有一个指定的偏移量和范围,用以标识所代表的图像在主缓冲中的位置。子对象通过getParent
方法来标识自身。
要创建一个子raster,可以使用Raster
.createSubRaster
方法。当被创建了一个子raster,你就可以用它的大小以及相对于父原点的偏移量来标识它在父raster中的所覆盖的区域。
5.4.3 对Raster的操作
Raster
类定义了许多访问像素以及像素数据的方法。当你实现RasterOp
的接口(该接口提供了raster-level的过滤和图像数据操作)时,或者当你实现任何需要执行低层次的像素操作方法时,这些方法会给你提供帮助。
Raster.getPixel
方法可以使你得到独立的像素,它是该方法返回的一个包含每个样本的数组。Raster
.getDataElements
方法返回指定的一连串来自DataBuffer
的未经解释的图像数据。Raster
.getSample
方法返回一个单独像素的全部取样。getSamples
方法返回图像在指定区域内的指定波段的全部取样。
除了这些方法之外,你也可以通过Raster
类的实例变量访问数据缓冲和取样模型。这些对象提供了其他存取和解释Raster
中像素的方法。
5.4.4 WritableRaster子类
WritableRaster
子类提供了设置像素、取样的方法。与BufferedImage
相关联的Raster
实际上就是WritableRaster
,因此为操作其中的像素提供了完全的存取方法。
5.5 图像数据和DataBuffers
附属于Raster
的DataBuffer
中提供了一个图像数据的数组。当你直接或者通过BufferedImage
的构造方法创建Raster
时,你指定了以像素为单位的宽和高,还有该图像的SampleModel
。这些信息就可用来创建适当的数据类型及大小的DataBuffer
。
DataBuffer
有3个子类,分别提供不同类型的数据元素:
DataBufferByte
(represents 8-bit values)
DataBufferInt
(represents 32-bit values)
DataBufferShort
(represents 16-bit values)
DataBufferUShort
(represents unsigned short values)
象先前定义的,元素是数据缓冲中的数组的离散成员,并且成分或者取样是组成像素的离散数值。DataBuffer
中元素的特定类型与SampleModel
所提供像素的特定类型之间可以有多种映射方式。实现映射并且提供从指定DataBuffer
中获得指定像素的方法,就成了各个SampleModel
的子类要承担的责任。
DataBuffer
的构造器提供了创建指定大小并且包含指定个数波段的缓冲的方法。
不过,虽然你可以在DataBuffer
直接访问图像数据,一般情况下,更简单和更方便的是通过Raster
和WritableRaster
中的方法来完成这些任务。
5.6 从SampleModel中提取像素数据
SampleModel
抽象类定义了在不需要知道其底层数据如何存储的情况下提取图像采样的方法。该类提供了域成员来跟踪与其相关联的DataBuffer
中的图像高度和宽度,以及描述该DataBuffer
的数据类型以及所含波段的个数。由SampleModel
中的方法提供的图像数据是一个像素的集,其中每个像素由多个取样或成分组成。
java.awt.image
包提供了5种类型的取样模型:
ComponentSampleModel
—用来从特定格式的图像中提取像素。图像的存储格式为:采样储存在
DataBuffer
的某个储存区的分离的数据组元素中。
BandedSampleModel
—用来从特定格式的图像中提取像素。该图像的存储格式为:顺着在连续的数据元素里存储波段的方向,每个采样存储在分离的数据元素里。
PixelInterleavedSampleModel
—用来从特定格式的图像中提取像素。该图像的存储格式为:顺着在连续的元素里存储像素的方向,每个采样到分开的数据元素中。
MultiPixelPackedSampleModel
—用来从特定格式的图像中提取像素。该图像的存储格式为:在一个数据元素力存储多个单采样的像素。
SinglePixelPackedSampleModel
—用来从特定格式的图像中提取像素。该图像的存储格式为:单个像素的采样数据存储在
DataBuffer
的第一个波段中的某一数据组元素里。
由SampleModel
表示的像素数据可能直接与特定颜色模型对颜色数据的表示相关或者不相关,取决于数据源。例如,在照片数据里,采样可能表示为RGB数据。而对于医学成像设备产生的图像,其采样可以表示为多种类型的数据,比如温度和骨密度。
有三类存取图像数据的方法。getPixel
方法通过使用一个适合于每个取样的入口,返回一个数组形式的完整像素。getDataElement
方法提供了对存储于DataBuffer
的未加工、未解释数据的存取。getSample
方法提供了对指定波段的像素成分的存取。
5.7 ColorModels与颜色
除了使用Raster
管理图像数据,BufferedImage
还包含了ColorModel
用来解释像素的颜色值。ColorModel
抽象类定义了在与其相关联的ColorSpace
中转化像素数据到的颜色值的方法。
java.awt.image
包提供了4种颜色模型:
PackedColorModel
—一个抽象的
ColorModel
,表现其颜色成分直接嵌入成为该整数像素的数据位的像素值。
DirectColorModel
是
PackedColorModel
的子类。
DirectColorModel
— 一个
ColorModel
可以表现含有RGB颜色成分,并且其颜色成分直接嵌入作为该像素自身数据位的像素。
DirectColorModel
模型的观感与X11 TrueColor相似。
ComponentColorModel
—
ColorModel
可以处理任意的
ColorSpace
以及由颜色成分构成的、与
ColorSpace
相匹配的数组。
IndexColorModel
—一个
ColorModel
,所表现的像素值是一个sRGB颜色空间的固定颜色表的索引。
ComponentColorModel
和PackedColorModel
是刚出现在Java™ 2 SDK发行版本中的。
基于DataBuffer
中的数据,SampleModel
把一个像素提供给ColorModel
,ColorModel
进而把该像素解释成颜色。
5.7.1 查找表(Lookup Table)
查找表包含了用于存有一个或多个信道或者图像成分的数据;例如,分别用来表现红、绿、蓝的分离的数组。java.awt.image
包定义了两种扩展了LookupTable
抽象类的查找表,一个容纳byte数据,一个容纳short数据(ByteLookupTable
和ShortLookupData
)。
5.8 图像处理及图像增强
图像包提供了一对定义了对BufferedImage
和Raster
进行操作的接口:BufferedImageOp
和RasterOp
。
实现这些接口的类包括AffineTransformOp
,BandCombineOpColorConvertOp, ConvolveOp
, LookupOp, RescaleOp
。这些类可以用于对图像进行几何变形、模糊、锐化、增强对比度、阈值以及颜色修正等处理。
Figure 5-4 举例说明了边缘探测及增强,一个突出图像中亮度急剧变化的操作。边缘探测经常在医学成像和地图软件中使用。边缘探测用来增强图像中相邻结构的对比度,使阅图人能辨别更多的细节。
下面的代码举例说明边缘探测:
float[] elements = { 0.0f, -1.0f, 0.0f, -1.0f, 4.f, -1.0f, 0.0f, -1.0f, 0.0f}; ... BufferedImage bimg = new BufferedImage(bw,bh,BufferedImage.TYPE_INT_RGB); Kernel kernel = new Kernel(3, 3, elements); ConvolveOp cop = new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null); cop.filter(bi,bimg);
Figure 5-5 示范了查找表的操作。一个查找操作可用来改变一个像素中单独的成分。
下面的代码示范了查找表操作:
byte reverse[] = new byte[256]; for (int j=0; j<200; j++){ reverse[j]=(byte)(256-j); } ByteLookupTable blut=new ByteLookupTable(0, reverse); LookupOp lop = new LookupOp(blut, null); lop.filter(bi,bimg);
Figure 5-6 举例说明了比例重调。比例重调可以增加或减少所有像素的亮度。对于一个灰度不明显的图像,使用比例重调可以增加灰度的变化范围,从而把原先看上去不明显或者单调区域里的细节显示出来。
下面的代码片断举例说明了比例重调:
5.8.1 应用图形处理操作
卷积是大多数空间过滤算法的基本处理过程。卷积是对图像中的每个像素的值及其相邻像素的值进行加权或者取平均值的处理过程。这就使得每个输出像素受到其直接相邻像素,以一种通过使用kernal的由数学确定的方法,的影响。Figure 5-7 illustrates Convolution.
下面的代码片断举例说明了怎样使用实现ConvolveOp
操作的类。在这个例子中,源图像中的每个像素是其周围的8个像素平均值。
float weight = 1.0f/9.0f; float[] elements = new float[9]; // create 2D array // fill the array with nine equal elements for (i = 0; i < 9; i++) { elements[i] = weight;}// use the array of elements as argument to create a Kernelprivate Kernel myKernel = new Kernel(3, 3, elements); public ConvolveOp simpleBlur = new ConvolveOp(myKernel); // sourceImage and destImage are instances of BufferedImagesimpleBlur.filter(sourceImage, destImage) // blur the image
变量simpleBlur包含一个新的ConvolveOp
实例,在BufferedImage
或Raster
上实现模糊处理。假设sourceImage和destImage是两个BufferedImage
的实例,当你调用ConvolveOp
类的核心方法filter
时,它赋给目标图像中的每个像素以源图像中对应像素及其周围8个像素的平均值。
在这个例子中卷积的kernel可以由下面的矩阵,连同指定的4个重要的数字提供:
当图像经过卷积,目标图像中的每个像素的值是通过使用kernel作为权的集合去求得源像素及其环绕像素的平均值,从而计算获得。这个操作执行于图像的每个通道。
下面的公式显示了 当卷积执行后,kennel中的权是怎样与源图像中的像素关联的。每个kernel中的值与图像中的空间位置连结在一起。
目标像素的值是kernel与相应源像素的乘积之和。在很多简单的操作中,kernel是一个正方形的对称矩阵,并且权的和合计为1。2
这例子中卷积的kernel相对简单。它平均地给源图像中的每个像素加权。通过选择kernel对源图像加权级别的高低,程序可以使目标图像的亮度增强或者降低。Kernel
对象,这个设置给ConvolveOp
构造方法中的对象,决定着要执行的过滤的类型。通过设置别的值,你可以执行其他类型的卷积,包括模糊(比如高斯模糊、光线模糊和运动模糊),锐化和滤波操作。Figure 5-8 illustrates sharpening using Convolution.
下面的代码片断举例说明了使用卷积实现的锐化:
float[] elements = { 0.0f, -1.0f, 0.0f, -1.0f, 5.f, -1.0f, 0.0f, -1.0f, 0.0f}; ... Kernel kernel = new Kernel(3,3,elements); ConvolveOp cop = new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null); cop.filter(bi,bimg);
Contents | Previous | Next |
Programmer's Guide to the JavaTM 2D API JavaTM 2 SDK, Standard Edition, 1.4 version |
Copyright © 2003 Sun Microsystems, Inc. All rights reserved.