Java图像基础知识
前言
提起java的图像处理,可能很多人都觉的java不太适合这方面的业务,毕竟很多图形学和图像处理都是基于C或者C++来实现的,比如OpenCV的2D图像处理、Matlab的图像处理、OpenGL的2D和3D的图形和图像的渲染等。原因主要是这些底层语言在处理图像时有着先天性的优势,就是可调用GPU资源来加速图像的处理。
而Java语言明显不具备这个能力,但是如果我们的场景本身比较小,不需要大批量的渲染加载,只是简单的处理计算,对性能没有要求的,那么使用Java来处理图形和图像的相关业务也是可以的。
提到Java的图像处理,第一时间想到的就是Java的awt包下提供的图形和图像的处理类,除此之外,前面的文章中我们曾经提到的一个java的图像处理库JAI,全称是java advanced image,奈何这个库只发行了2个版本就停止,不过好在后面又有大佬维护了一个jai-ext的java图像库。
本篇文章主要介绍下java图像处理过程中的一些基础知识。
基础知识
像素:像素是组成图像的基础单位,我们把一张图片无限放大,看到的每一个小格子就
是一个像素。比如一张图片768* 512,则表示图片宽度768个像素,高度是512个像素。而在GIS中计算分辨率时,总是会提到一个DPI的值,这个值在Arcgis中为96,在OGC标准中一般取91,指的是每英寸有多少个像素,这个值可保证客户端的比例尺与服务端图像的比例尺一致。
颜色空间(ColorSpace):颜色空间是一个抽象数学模型,用于描述颜色的属性和范围
它定义了颜色值的表示方式以及这些值如何映射到视觉感知,其实决定了最终这些像素值是如何呈现在我们硬件上的,比如我们的电脑显示器,印刷等。
常见的颜色空间
//RBG空间
@Native public static final int TYPE_RGB = 5;
//灰度空间
@Native public static final int TYPE_GRAY = 6;
//hsv空间(色相、饱和度、明度)
@Native public static final int TYPE_HSV = 7;
//HLS空间(色相、亮度、饱和度)
@Native public static final int TYPE_HLS = 8;
//打印模式的颜色空间
@Native public static final int TYPE_CMYK = 9;
颜色模型:有了颜色空间后,我们就需要一个类将像素值和颜色值对应起来,这就是
ColorModel(颜色模型),java提供了多中颜色模型的实现类,主要区别是像素值是直接存储RGB值还是存储索引值,其中ComponentColorModel 是直接基于像素值的颜色模型,IndexColorModel 是存储索引值,然后通过在调色板中查找来获取最终的颜色值。
下面我们重点看下ComponentColorModel 的构造函数,来深入了解下这个类的使用
其构造函数有五个参数,分别是:
colorSpace:颜色空间,前面介绍过了。
bits:位深数组,表示每一个通道的占用的bit位数,这个值越大,代表最后的像素值越大,色彩值越丰富,能够表示的色彩就越细腻,比如常见的三通道8位,能表示的色彩值最大255*255*255。
hasAlpha:是否有alpha通道
isAlphaPremultiplied:这是处理透明的一个参数
transparency: 透明类型 ,其中1表示完全不透明 2表示完全透明3表示介于两者之间,也就是透明度可调。
transferType: 像素的数据类型,比如可以是DataBuffer.TYPE_BYTE、DataBuffer.TYPE_INT、DataBuffer.TYPE_SHORT、DataBuffer.TYPE_FLOAT等。
IndexColorModel适合于颜色数量有限且已知的情况,通过较小的索引值就可以高效地表示大量像素,从而节省内存空间,一般用于网页图形和压缩图像格式中。
下面介绍另一个重要参数SampleModel:该参数定义像素数据的组织结构、存储模式、样本的排列顺序和访问规则,它和上面的ColorModel一起决定了图像的最终显示效果。
其实现类最常用的ComponentSampleModel其构造参数如下:
其中datatype:数据类型,即就是像素值的表示单位,比如常见的RGB三通道,使用TYPE_BYTE来表示,即就是每个通道8位,用0-255来表示,常见的DEM地形数据,也会直接使用TYPE_SHORT或者TYPE_FLOAT来定义。
w: 图片宽度
h: 图片高度
pixelStride:像素步幅,也就是采样间隔,一般都是numBands,比如三通道图像就是3,也就是像素数组中每三个数据代表一个像素的值。
scanlineStride: 线性步幅,表示切换到下一行的像素时,数组的位置变换,一般是numBands*width
bandOffsets:波段偏移量,一般都是从0开始,表示每个像素步幅在通道上的偏移量,比如RGB数据,一般都是new int[] {0,1,2}
介绍了上面的几个参数后,那么使用Java来创建一个图像就只差一步了,就是构造一个像素数组,这个数组的构造和前面的像素类型有关,比如要创建768*512大小的图片,颜色模型TYPE_BYTE的数据类型,则需要创建一个大小为768*512*3大小的byte数组。
接下来我们来看下根据以上参数,构造一个BufferedImage对象
/**
* 图像基础知识
*/
public class ImageProcess {
@Test
public void createImage(){
int width = 768;
int height = 512;
ColorModel colorModel = buildColorModel(DataBuffer.TYPE_BYTE);
SampleModel sampleModel = buildSampleModel(DataBuffer.TYPE_BYTE,width,height);
//构造像素数组
int[] buffers = new int[width*height*3];
Random random = new Random();
for(int i=width*height;i<width*height*2;i++){
buffers[i] = random.nextInt(256);
}
//DataBuffer
DataBuffer dataBuffer = new DataBufferInt(buffers,width*height*3);
//创建像素
WritableRaster raster = Raster.createWritableRaster(sampleModel,dataBuffer,new Point(0,0));
//创建图像
BufferedImage image = new BufferedImage(colorModel,raster,false,null);
}
public ColorModel buildColorModel(int dataType){
ColorSpace colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB);
int[] bits = {8,8,8};
ColorModel colorModel = new ComponentColorModel(colorSpace,bits,false,false,
Transparency.TRANSLUCENT, dataType);
return colorModel;
}
public SampleModel buildSampleModel(int dataType,int width,int height){
SampleModel sampleModel = new ComponentSampleModel(dataType, width,height,
3,width*3,new int[]{0,1,2});
return sampleModel;
}
}
好了今天关于Java图像的基础知识就介绍到这里,下一节将介绍Geotools以及JAI-EXT的开源库中对图像的处理方式,当然本文并未完全介绍java 2D图像的相关内容,篇幅有限,大家在过程中如有任何疑问,可在评论区交流。