package edu.stanford.rsl.conrad.io; import java.awt.image.ColorModel; import java.awt.image.IndexColorModel; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.util.zip.GZIPInputStream; import edu.stanford.rsl.conrad.data.numeric.Grid2D; import edu.stanford.rsl.conrad.pipeline.IndividualImagePipelineFilteringTool; import edu.stanford.rsl.conrad.pipeline.ProjectionSource; import edu.stanford.rsl.conrad.utils.CONRAD; import ij.IJ; import ij.LookUpTable; import ij.Macro; import ij.Prefs; import ij.io.FileInfo; import ij.io.ImageReader; import ij.io.RandomAccessStream; import ij.process.ByteProcessor; import ij.process.ColorProcessor; import ij.process.ImageProcessor; import ij.process.ShortProcessor; /** * Class to model an abstract projection source which accesses the file system to stream the data. * * @author akmaier * */ public abstract class FileProjectionSource implements ProjectionSource { protected FileInfo fi = null; protected long skip; protected int currentIndex = -1; protected boolean showProgress; protected ImageReader reader; protected InputStream is; protected ColorModel cm; public static ProjectionSource [] getProjectionSources(){ ProjectionSource [] sources = {new TiffProjectionSource(), new ZipProjectionSource(), new SEQProjectionSource(), new DicomProjectionSource(), new NRRDProjectionSource(), new DennerleinProjectionSource()}; return sources; } public static ProjectionSource openProjectionStream(String filename) throws IOException { ProjectionSource [] sources = getProjectionSources(); ProjectionSource source = null; for (int i = 0; i < sources.length; i++){ try { sources[i].initStream(filename); source = sources[i]; break; } catch (Exception e) { // May throw some exception, if it is the wrong file type. CONRAD.log(sources[i] +" failed because " +e.getLocalizedMessage()); // e.printStackTrace(); } } if (source == null) { throw new IOException("No matching file format found."); } return source; } public synchronized void getNextProjection(IndividualImagePipelineFilteringTool tool){ Grid2D grid = getNextProjection(); if (grid != null){ tool.setImageProcessor(grid); tool.setImageIndex(getCurrentProjectionNumber()); } else { tool.setImageProcessor(null); tool.setImageIndex(-1); } } public void setShowProgress(boolean showProgress) { this.showProgress = showProgress; } public boolean isShowProgress() { return showProgress; } public int getCurrentProjectionNumber(){ return currentIndex; } public Grid2D getNextProjection() { ImageProcessor ip = null; Object pixels = reader.readPixels(is, skip); if (pixels == null) { return null; } if (pixels instanceof byte[]) ip = new ByteProcessor(fi.width, fi.height, (byte[]) pixels, cm); else if (pixels instanceof short[]) ip = new ShortProcessor(fi.width, fi.height, (short[]) pixels, cm); else if (pixels instanceof int[]) ip = new ColorProcessor(fi.width, fi.height, (int[]) pixels); else if (pixels instanceof float[]) { // no conversion needed } else throw new IllegalArgumentException("Unknown stack type"); skip = fi.gapBetweenImages; currentIndex++; if (showProgress){ IJ.showProgress((0.0 + currentIndex) / fi.nImages); } if(ip != null) pixels = ip.toFloat(0, null).getPixels(); Grid2D grid = new Grid2D((float [])pixels, fi.width, fi.height); return grid; } protected void init() { cm = createColorModel(fi); //if (fi.nImages>1){ initStack(); //} } /** Returns an IndexColorModel for the image specified by this FileInfo. */ protected ColorModel createColorModel(FileInfo fi) { if (fi.fileType==FileInfo.COLOR8 && fi.lutSize>0) return new IndexColorModel(8, fi.lutSize, fi.reds, fi.greens, fi.blues); else return LookUpTable.createGrayscaleColorModel(fi.whiteIsZero); } /** Opens a stack of images. */ protected void initStack() { skip = fi.getOffset(); try { reader = new ImageReader(fi); is = createInputStream(fi); if (is==null) return ; } catch (Exception e) { CONRAD.log("" + e); e.printStackTrace(); } catch(OutOfMemoryError e) { e.printStackTrace(); IJ.outOfMemory(fi.fileName); } } /** Returns an InputStream for the image described by this FileInfo. */ protected InputStream createInputStream(FileInfo fi) throws IOException, MalformedURLException { InputStream is = null; boolean gzip = fi.fileName!=null && (fi.fileName.endsWith(".gz")||fi.fileName.endsWith(".GZ")); if (fi.inputStream!=null) is = fi.inputStream; else if (fi.url!=null && !fi.url.equals("")) is = new URL(fi.url+fi.fileName).openStream(); else { if (fi.directory.length()>0 && !fi.directory.endsWith(Prefs.separator)) fi.directory += Prefs.separator; File f = new File(fi.directory + fi.fileName); if (gzip) fi.compression = FileInfo.COMPRESSION_UNKNOWN; if (f==null || f.isDirectory() || !validateFileInfo(f, fi)) is = null; else is = new FileInputStream(f); } if (is!=null) { if (fi.compression>=FileInfo.LZW) is = new RandomAccessStream(is); else if (gzip) is = new GZIPInputStream(is, 50000); } return is; } protected static boolean validateFileInfo(File f, FileInfo fi) { long offset = fi.getOffset(); long length = 0; if (fi.width<=0 || fi.height<0) { error("Width or height <= 0.", fi, offset, length); return false; } if (offset>=0 && offset<1000L) return true; if (offset<0L) { error("Offset is negative.", fi, offset, length); return false; } if (fi.fileType==FileInfo.BITMAP || fi.compression!=FileInfo.COMPRESSION_NONE) return true; length = f.length(); long size = fi.width*fi.height*fi.getBytesPerPixel(); size = fi.nImages>1?size:size/4; if (fi.height==1) size = 0; // allows plugins to read info of unknown length at end of file if (offset+size>length) { error("Offset + image size > file length.", fi, offset, length); return false; } return true; } protected static void error(String msg, FileInfo fi, long offset, long length) { IJ.error("FileOpener", "FileInfo parameter error. \n" +msg + "\n \n" +" Width: " + fi.width + "\n" +" Height: " + fi.height + "\n" +" Offset: " + offset + "\n" +" Bytes/pixel: " + fi.getBytesPerPixel() + "\n" +(length>0?" File length: " + length + "\n":"") ); } /** Reads the pixel data from an image described by a FileInfo object. */ protected Object readPixels(FileInfo fi) { Object pixels = null; try { InputStream is = createInputStream(fi); if (is==null) return null; ImageReader reader = new ImageReader(fi); pixels = reader.readPixels(is); is.close(); } catch (Exception e) { if (!Macro.MACRO_CANCELED.equals(e.getMessage())) IJ.handleException(e); } return pixels; } public void close() throws IOException{ is.close(); } /** * converts a byte sequence to integer * @param values the byte array * @param index the index in 32bit integer metric * @return the integer */ public long convertToUnsignedInt(byte[] values, int index) { int offset = index * 4; //System.out.println(values[offset +0] + " " + values[offset +1] + " " + values[offset +2] + " " +values[offset +3]); int val = values[offset+3]; if (val < 0) val += 256; long value = val; value = value << 8; val = values[offset+2]; if (val < 0) val += 256; value += val; value = value << 8; val = values[offset+1]; if (val < 0) val += 256; value += val; value = value << 8; val = values[offset]; if (val < 0) val += 256; value += val; return value; } /** * converts a byte sequence to integer * @param values the byte array * @param index the index in 32bit integer metric * @return the integer */ public int convertToUnsignedShort(byte[] values, int index) { int offset = index * 2; //System.out.println(values[offset +0] + " " + values[offset +1] + " " + values[offset +2] + " " +values[offset +3]); int val = values[offset+1]; if (val < 0) val += 256; int value = val; value = value << 8; val = values[offset]; if (val < 0) val += 256; value += val; return value; } } /* * Copyright (C) 2010-2014 Andreas Maier * CONRAD is developed as an Open Source project under the GNU General Public License (GPL). */