Java SWT图像处理入门
Standard Widget Toolkit ( SWT ,标准窗口小部件工具箱),是在 Eclipse 平台上使用的窗口小部件工具箱,它能向开发者提供和本机平台一致的用户界面和比较稳定的性能,也提供了强大的图像处理功能。本文首先介绍 SWT 封装的 Image , ImageData 等类,接着根据作者实际工作经验给出了一些常见图像处理的解决方法。
Standard Widget Toolkit ( SWT,标准窗口小部件工具箱),是在 Eclipse 平台上使用的窗口小部件工具箱,它能向开发者提供和本机平台一致的用户界面和比较稳定的性能,也提供了强大的图像处理功能。本文首先介绍 SWT 封装的 Image, ImageData 等类,接着根据作者实际工作经验给出了一些常见图像处理的解决方法。
|
软件开发人员可以利用 SWT 封装的 Image, ImageData 等类来创建图像、存储图像数据,也可以对存储的图像数据进行各种图像变换。本文将演示如何正确的使用 Image, ImageData 等类,以及如何使图像变灰、变亮/黑、图像旋转、图像拉伸、图片透明叠加、图片反色等相关问题。
下面就来介绍一下 Image 和 ImageData 这两个在 SWT Images 中最重要的类。
类 org.eclipse.swt.graphics.Image 被用来表示可以在设备上显示的图片,可以用方法如GC.drawImage() 或者 Button.setImage() 等来将它显示出来。Image 类提供了几个构造函数,可以完成以下功能:
- 装载一个现有的图象。可以通过传入文件名或者 InputStream 作为参数,但是图象的格式必须是它所支持的格式之(目前 SWT Image 支持 BMP、GIF、JPG、PNG、Windows ICO 等格式)一,否则会抛出 SWTException 异常。
- 构造一个用已经存在的 ImageData 进行初始化的图像。
- 构造一个空图像。可以通过修改其像素值或者向它拷贝一个 SWT 图形上下文的内容 (GC) 来绘制该图像,并且可以指定空图像的大小。
类 org.eclipse.swt.graphics.ImageData 中存储了图像的像素数据信息。 ImageData 是一个包含有关图像大小、调色板、颜色值和透明度等信息的类。我们可以对这些图像像素数据可以直接读或者写操作,这意味着可以通过直接读取或者修改图像的 数据,来设置或者取得图像中任何像素或者任何一组像素的颜色值。关于 ImageData ,我们还应当了解以下一些字段:
- width 和 height 指定图像的宽和高。Depth 指定图像的颜色深度。可能的值为 1、2、4、8、16、24 或者 32,指定编码每一个像素的值所使用的比特数。
- alpha 与 alphaData 定义图像的透明度。alpha 定义了图象的全局透明度值,默认值为 -1,否则 alphaData 域将被忽略。当 alpha 不等于 -1 时, alphaData 存储了图象的透明度缓冲区,每个像素可以有一个在 0~255 之间的透明度值,数值越大,表示越不透明。值得注意的是,只有部分图象格式具有透明度,例如 GIF 和 PNG。
- palette 包含一个 PaletteData 对象,它存储有关图像的颜色模型的信息。SWT 的颜色模型可以是索引或者直接的,由其域 isDirect 来指定。如果颜色模型是索引的,那么 PaletteData 包含颜色索引,可以通过方法 getRGBs() 来获取 RGB 信息。如果它是直接的,那么它包含转换信息,表明应当如何从像素的整数值中提取出颜色 RGB 成分。
- data 包含像素值的字节缓冲区。字节编码的方法取决于所使用的颜色深度。对于一个 8 位的图像,数组中的一个字节正好表示图像中一个像素的值。对于 16 位图像,每一个像素值编码为缓冲区中的两个字节。这两个字节以最低有效字节顺序存储。对于 24 或者 32 位图像,每一个像素值以最高有效位字节顺序编码为缓冲区中的三个或者四个字节。
- bytesPerLine 表明缓冲区中有多少字节用于表示图像中一行像素的所有像素值。由于一个像素可能有多个字节表示,所以 bytesPerLine 可能是字段 width 值的若干倍。
|
常见的图象处理包含图象的读/写、图像变灰、变亮/黑、图像旋转、图像拉伸、图片透明叠加、图片反色等。下面将就这些问题逐个介绍,表 1 列出了各个清单所对应的图像处理。
代码清单 | 图像处理类型 |
清单 1 | 图象的读写 |
清单 2 | 图像变灰 |
清单 3 | 图像变亮/变黑 |
清单 4 | 图像旋转 |
清单 5 | 图像反色 |
清单 6 | 图像拉伸 |
清单 7 | 图片透明叠加 |
我们可以使用类 org.eclipse.swt.graphics.ImageLoader 来加载或者保存图像。 ImageLoader 具有一个全局的成员变量 ImageData[],它用于存储图片数据。
String fileName = "source.jpg"; String saveName = "saved.jpg"; ImageLoader loader = new ImageLoader(); ImageData[] imageData = loader.load(fileName); if(imageData.length > 0){ Image newImage = new Image(null, imageData[0]); //对newImage进行操作 ... loader.data[0] = newImage.getImageData(); loader.save(saveName, SWT.IMAGE_BMP); } |
图像变灰在桌面应用程序中有着广泛的应用。例如,一个图标被作为一个按钮的背景,我们需要一个灰色效果的图标作为按钮的背景来表示这个按钮处于禁用 状态。在SWT中,基于已经存在的图像来创建一个具有灰色效果的图像,我们可以使用构造函数 Image(Display display, Image image, int flag) 来创建,其中参数 flag 使用 SWT.IMAGE_GRAY。
Image newImage = new Image(null, image, SWT.IMAGE_GRAY); |
下面讨论图像变亮/变黑。 RGB 和 HSL (也叫 HSB/HSV )是两种色彩空间,即:红、绿、蓝( Red, Green, Blue) 和色调、饱和度、亮度( Hue, Saturation, Lightness 或 Brightness 或 Value),前者适用于机器采样,目前的显示器颜色即由这三种基色构成,而后者更符合人类的直观感觉。在 Windows 的标准颜色对话框中均包含这两种表示方法。 RGB 的取值范围在 0~255 之间, HSL 的取值在 0~1 之间。因此我们只需要将 HSL 空间数据的 L 分量进行调整即可调整此图像的亮度。要实现图像变亮/变黑的功能,只需要调整清单 3 中函数 lightImage 行(*)中等号右边的值( 0 到 1 之间)。
private static ImageData lightImage(ImageData srcData) { double[] data = rgbTohsl(srcData.data); byte[] newData = new byte[srcData.data.length]; int bytesPerPixel = srcData.bytesPerLine / srcData.width; int destBytesPerLine = srcData.width * bytesPerPixel; for(int i = 0; i < data.length; i += 3) { data[i + 2] = 0.75; ----------------- (*) } data = hslTorgb(data); for(int i = 0; i < srcData.data.length; i ++) { newData[i] = (byte)data[i]; } ImageData newImageData = new ImageData(srcData.width, srcData.height, srcData.depth,srcData.palette, destBytesPerLine, newData); return newImageData; } |
其中,方法 double[] rgbTohsl(byte[] data) 是把 RGB 空间数据转换到 HSL 空间;相反的,方法 double[] hslTorgb(double[] data) 是把数据从 HSL 空间转换到 RGB 空间。
清单4中方法 rotate 实现了将图像相左旋转 90 度。如图 1 ,对于像素点 (x, y) ,向左旋转90 度以后,它在图象中的位置变成了 (y, width - x - 1) 。因此,相左旋转 90 度即将所有的像素点按照规则换一下位置。其他的旋转可用同样的方法。
private static ImageData rotate(ImageData srcData) { int bytesPerPixel = srcData.bytesPerLine / srcData.width; int destBytesPerLine = srcData.height * bytesPerPixel; byte[] newData = new byte[srcData.data.length]; int width = 0, height = 0; for (int srcY = 0; srcY < srcData.height; srcY++) { for (int srcX = 0; srcX < srcData.width; srcX++) { int destX = 0, destY = 0, destIndex = 0, srcIndex = 0; destX = srcY; destY = srcData.width - srcX - 1; width = srcData.height; height = srcData.width; destIndex = (destY * destBytesPerLine) + (destX * bytesPerPixel); srcIndex = (srcY * srcData.bytesPerLine) + (srcX * bytesPerPixel); System.arraycopy(srcData.data, srcIndex, newData, destIndex, bytesPerPixel); } } return new ImageData(width, height, srcData.depth, srcData.palette, destBytesPerLine, newData); } |
对于彩色图像的 R、G、B 各彩色分量取反的技术就是图像的反色处理,这在处理二值化图像的连通区域选取的时候非常重要。如物体连通域用黑色表示,而二值化后的物体连通域图像可那是 白色的,而背景是黑色的,这时应手动选取图像的反色处理或有程序根据背景和物体连通域两种颜色的数量所占比例而自动选择是否选择选取图像的反色处理,其算 法很简单,假设源图像一像素的红,绿,蓝分量为 (R,G,B),则目标图像该像素的红绿蓝分量应变为 (255 - R,255 - G, 255 - B)。
private static ImageData reverseImage(ImageData srcData) { int bytesPerPixel = srcData.bytesPerLine / srcData.width; int destBytesPerLine = srcData.width * bytesPerPixel; byte[] newData = srcData.data; for (int i = 0; i < newData.length; i ++) newData[i] = (byte) (255 - newData[i]); ImageData newImageData = new ImageData(srcData.width, srcData.height, srcData.depth, srcData.palette, destBytesPerLine, newData); newImageData.transparentPixel = srcData.transparentPixel; return newImageData; } |
图像的缩小/放大一般分为按比例缩小和不按比例缩小两种。图像的缩小操作中,是在现有的信息里如何挑选所需要的有用信息。图像的放大操作中,则需对 尺寸放大后所多出来的空格填入适当的值,这是信息的估计问题,所以较图像的缩小要难一些,而且图像大比例放大时经常会出现马赛克效应。庆幸的是,SWT 工具箱对图像的拉伸进行了封装,开发者只需要调用方法 ImageData.scaledTo(int width, int height) 来获得一个拉伸后的 ImageData。
Image newImage = new Image(null, imageData[0].scaledTo(imageData[0].width / 2, imageData[0].height / 2)); |
透明叠加方式是图象处理中常用的一种处理方式,在这种处理方式中,一幅图片叠加到另一幅图片上,但是这幅图象不是完全将原来的图象覆盖,而是能够部 分的透过叠加的图象显示出来,透明的程度由透明度参数指定(假定为 a,其值在 0 与 1 之间,数值越小表明被叠加的图片越透明),其原理是目标图片的 R、G、B 以及 alpha 分别为待叠加图片 1 的 R、G、B 以及 alpha 分量乘以透明度参数 a 加上待叠加图片 2 的 R、G、B 以及 alpha 分量乘以 1-a 的值。我们可以使用图片的透明叠加作出水印的效果。
private static ImageData watermark(ImageData srcData1, ImageData srcData2, double alpha) { if(srcData1.width != srcData2.width || srcData1.height != srcData2.height || rcData1.bytesPerLine != srcData2.bytesPerLine) //未考虑不同大小图片的叠加 return null; int bytesPerPixe = srcData1.bytesPerLine / srcData1.width; int destBytesPerLine = srcData1.width * bytesPerPixe; byte[] newData = new byte[srcData1.data.length]; ImageData newImageData = new ImageData(srcData1.width, srcData1.height, srcData1.depth, srcData1.palette, destBytesPerLine, newData); for (int srcY = 0; srcY < srcData1.height; srcY++) { for (int srcX = 0; srcX < srcData1.bytesPerLine; srcX++) { int idx = srcY * srcData1.bytesPerLine + srcX; newImageData.data[idx] = (byte)(alpha * srcData1.data[idx] + (1- alpha) * srcData2.data[idx]); } } return newImageData; } |
需要说明的是,以上所列举的清单中大部分都没有对透明度数据进行处理,读者如对带有透明度数据图片进行处理的时候,可自行添加相关代码,其处理过程跟对 ImageData 数据处理过程类似。
|
从上面的介绍中,可以知道 SWT 对 Image 的支持非常好,开发者可以方便的对图象进行各种操作。对于想用 Java 来进行图像处理的编程人员来说, SWT Images 是一个不可多得选择。