最近在读取客户发过来的tiff文件是,底层竟然报错了,错误: bandOffsets.length is wrong! 没办法,因为错误消息出现在tiff的read中,因此就对底层序中tiff读取的代码进行了研究。
借助这篇文章,我们需要先了解Tiff文件的具体结构,可以参考这篇文章, TIFF文件结构详解 https://blog.csdn.net/oYinHeZhiGuang/article/details/121710467 讲的很好!
下面我们来看下imageio-ext中的tiff读取代码,主要类TiffImageReader,我们来看下Java程序是如何读取tiff文件的。
构造方法:
public TIFFImageReader(ImageReaderSpi originatingProvider) {
super(originatingProvider);
}
这个类需要通过一个ImageReaderSpi来实例化,其实这种SPI的设计模式,Java的很多开源项目都在用到,这里我们通过TIFFImageReaderSpi这个类即可。
其次设置文件的路径,以及其它一些参数,通过该类的如下方法:
public void setInput(Object input,
boolean seekForwardOnly,
boolean ignoreMetadata)
这个方法,里面有input就是需要读取的文件, seekForwardOnly设置为true表示: 只能从这个输入源按升序读取图像和元数据。 ignoreMetadata设置为true表示读取忽略元数据
接下来就是对tiff元数据的读取,具体参见getImageMetadata(int imageIndex)这个方法:
public IIOMetadata getImageMetadata(int imageIndex) throws IIOException {
seekToImage(imageIndex, true);
TIFFImageMetadata im =
new TIFFImageMetadata(imageMetadata.getRootIFD().getTagSetList());
Node root =
imageMetadata.getAsTree(TIFFImageMetadata.nativeMetadataFormatName);
im.setFromTree(TIFFImageMetadata.nativeMetadataFormatName, root);
if (noData != null) {
im.setNoData(new double[] {noData, noData});
}
if (scales != null && offsets != null) {
im.setScales(scales);
im.setOffsets(offsets);
}
return im;
}
其中的seekToImage(imageIndex, true)为最主要的逻辑处理,这个方法中,第一个参数,imageIndex为tiff多页中的第几个,第二参数设置标示该tiff页是否已经被解析过
private void seekToImage(int imageIndex, boolean optimized) throws IIOException {
checkIndex(imageIndex);
// TODO we should do this initialization just once!!!
int index = locateImage(imageIndex);
if (index != imageIndex) {
throw new IndexOutOfBoundsException("imageIndex out of bounds!");
}
final Integer i= Integer.valueOf(index);
//optimized branch
if(!optimized){
readMetadata();
initializeFromMetadata();
return;
}
// in case we have cache the info for this page
if(pagesInfo.containsKey(i)){
// initialize from cachedinfo only if needed
// TODO Improve
if(imageMetadata == null || !initialized) {// this means the curindex has changed
final PageInfo info = pagesInfo.get(i);
final TIFFImageMetadata metadata = info.imageMetadata.get();
if (metadata != null) {
initializeFromCachedInfo(info, metadata);
return;
}
pagesInfo.put(i,null);
}
}
readMetadata();
initializeFromMetadata();
}
这个方法当中,第一次加载tiff,通过readMetadata()和initializeFromMetadata()将tiff的元信息缓存起来,方便后面再次读取。
读取过程
主要是要结合Tiff的格式进行理解,大