/* * #%L * OME Bio-Formats package for reading and converting biological file formats. * %% * Copyright (C) 2005 - 2015 Open Microscopy Environment: * - Board of Regents of the University of Wisconsin-Madison * - Glencoe Software, Inc. * - University of Dundee * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program. If not, see * <http://www.gnu.org/licenses/gpl-2.0.html>. * #L% */ package loci.formats.in; import java.io.File; import java.io.IOException; import java.util.ArrayList; import loci.common.DataTools; import loci.common.DateTools; import loci.common.Location; import loci.common.RandomAccessInputStream; import loci.formats.CoreMetadata; import loci.formats.FormatException; import loci.formats.FormatReader; import loci.formats.FormatTools; import loci.formats.MetadataTools; import loci.formats.meta.MetadataStore; import ome.xml.model.primitives.PositiveFloat; import ome.xml.model.primitives.Timestamp; import ome.units.quantity.Length; /** * InveonReader is the file format reader for Inveon files. */ public class InveonReader extends FormatReader { // -- Constants -- private static final String HEADER = "Header file for data file"; // -- Fields -- private String datFile; private ArrayList<Long> dataPointers = new ArrayList<Long>(); // -- Constructor -- /** Constructs a new Inveon reader. */ public InveonReader() { super("Inveon", new String[] {"hdr"}); domains = new String[] {FormatTools.MEDICAL_DOMAIN}; suffixSufficient = false; hasCompanionFiles = true; datasetDescription = "One .hdr file plus one similarly-named file"; } // -- IFormatReader API methods -- /* @see loci.formats.IFormatReader#isThisType(String, boolean) */ @Override public boolean isThisType(String name, boolean open) { if (checkSuffix(name, "hdr")) { return super.isThisType(name, open); } Location file = new Location(name + ".hdr"); if (!file.exists()) { return false; } return super.isThisType(file.getAbsolutePath(), open); } /* @see loci.formats.IFormatReader#isThisType(RandomAccessInputStream) */ @Override public boolean isThisType(RandomAccessInputStream stream) throws IOException { final int blockLen = 128; if (!FormatTools.validStream(stream, blockLen, false)) return false; return (stream.readString(blockLen)).indexOf(HEADER) >= 0; } /* @see loci.formats.IFormatReader#getUsedFiles(boolean) */ @Override public String[] getUsedFiles(boolean noPixels) { FormatTools.assertId(currentId, true, 1); if (noPixels) return new String[] {currentId}; return new String[] {currentId, datFile}; } /** * @see loci.formats.IFormatReader#openBytes(int, byte[], int, int, int, int) */ @Override public byte[] openBytes(int no, byte[] buf, int x, int y, int w, int h) throws FormatException, IOException { FormatTools.checkPlaneParameters(this, no, buf.length, x, y, w, h); long planeSize = (long) FormatTools.getPlaneSize(this); int index = getCoreIndex(); RandomAccessInputStream dat = new RandomAccessInputStream(datFile); try { dat.order(isLittleEndian()); dat.seek(dataPointers.get(index) + no * planeSize); readPlane(dat, x, y, w, h, buf); } finally { dat.close(); } return buf; } /* @see loci.formats.IFormatReader#close(boolean) */ @Override public void close(boolean fileOnly) throws IOException { super.close(fileOnly); if (!fileOnly) { datFile = null; dataPointers.clear(); } } // -- Internal FormatReader API methods -- /* @see loci.formats.FormatReader#initFile(String) */ @Override protected void initFile(String id) throws FormatException, IOException { if (!checkSuffix(id, "hdr")) { id += ".hdr"; } super.initFile(id); String headerData = DataTools.readFile(id); String[] lines = headerData.split("\n"); String date = null; String institution = null, investigator = null; String model = null; String description = null; Double pixelSizeX = null; Double pixelSizeY = null; Double pixelSizeZ = null; int frames = 0; for (String line : lines) { line = line.trim(); if (!line.startsWith("#")) { int space = line.indexOf(" "); if (space < 0) { continue; } String key = line.substring(0, space); String value = line.substring(space + 1); if (key.equals("institution")) { institution = value; } else if (key.equals("investigator")) { investigator = value; } else if (key.equals("study")) { description = value; } else if (key.equals("model")) { value = transformModel(value); model = value; } else if (key.equals("modality")) { value = transformModality(value); } else if (key.equals("modality_configuration")) { value = transformModalityConfiguration(value); } else if (key.equals("file_type")) { value = transformFileType(value); } else if (key.equals("acquisition_mode")) { value = transformAcquisitionMode(value); } else if (key.equals("bed_control")) { value = transformBedControl(value); } else if (key.equals("bed_motion")) { value = transformBedMotion(value); } else if (key.equals("registration_available")) { value = transformRegistrationAvailable(value); } else if (key.equals("normalization_applied")) { value = transformNormalizationApplied(value); } else if (key.equals("recon_algorithm")) { value = transformReconAlgorithm(value); } else if (key.equals("x_filter")) { value = transformFilter(value); } else if (key.equals("y_filter")) { value = transformFilter(value); } else if (key.equals("z_filter")) { value = transformFilter(value); } else if (key.equals("subject_orientation")) { value = transformSubjectOrientation(value); } else if (key.equals("subject_length_units")) { value = transformSubjectLengthUnits(value); } else if (key.equals("subject_weight_units")) { value = transformSubjectWeightUnits(value); } else if (key.equals("gantry_rotation")) { value = transformGantryRotation(value); } else if (key.equals("rotation_direction")) { value = transformRotationDirection(value); } else if (key.equals("ct_warping")) { value = transformCTWarping(value); } else if (key.equals("ct_projection_interpolation")) { value = transformCTProjectionInterpolation(value); } else if (key.equals("event_type")) { value = transformEventType(value); } else if (key.equals("projection") || key.equals("ct_projection_center_offset") || key.equals("ct_projection_horizontal_bed_offset")) { space = value.indexOf(" "); int index = Integer.parseInt(value.substring(0, space)); value = value.substring(space + 1); key += " " + index; } else if (key.equals("user")) { space = value.indexOf(" "); key = value.substring(0, space); value = value.substring(space + 1); } else if (key.equals("file_name")) { // remove path from stored file name, if present value = value.replace('/', File.separatorChar); value = value.replace('\\', File.separatorChar); value = value.substring(value.lastIndexOf(File.separator) + 1); Location header = new Location(currentId).getAbsoluteFile(); datFile = new Location(header.getParent(), value).getAbsolutePath(); } else if (key.equals("time_frames")) { int sizeT = Integer.parseInt(value); for (int i=0; i<core.size(); i++) { core.get(i).sizeT = sizeT; } } else if (key.equals("total_frames")) { frames = Integer.parseInt(value); } else if (key.equals("number_of_bed_positions")) { int nPos = (int) Math.min(frames, Integer.parseInt(value)); if (nPos > 1) { CoreMetadata original = core.get(0); core.clear(); for (int i=0; i<nPos; i++) { core.add(original); } } } else if (key.equals("data_type")) { setDataType(value); } else if (key.equals("x_dimension")) { int sizeX = Integer.parseInt(value); for (int i=0; i<core.size(); i++) { core.get(i).sizeX = sizeX; } } else if (key.equals("y_dimension")) { int sizeY = Integer.parseInt(value); for (int i=0; i<core.size(); i++) { core.get(i).sizeY = sizeY; } } else if (key.equals("z_dimension")) { int sizeZ = Integer.parseInt(value); for (int i=0; i<core.size(); i++) { core.get(i).sizeZ = sizeZ; } } else if (key.equals("scan_time")) { date = value; } else if (key.equals("data_file_pointer")) { String[] values = value.split(" "); int[] ints = new int[values.length]; for (int i=0; i<ints.length; i++) { ints[i] = Integer.parseInt(values[i]); } byte[] b = DataTools.intsToBytes(ints, false); dataPointers.add(DataTools.bytesToLong(b, false)); } // pixel sizes stored in mm else if (key.equals("pixel_size_x")) { pixelSizeX = new Double(value) * 1000; } else if (key.equals("pixel_size_y")) { pixelSizeY = new Double(value) * 1000; } else if (key.equals("pixel_size_z")) { pixelSizeZ = new Double(value) * 1000; } addGlobalMeta(key, value); } } for (int i=0; i<core.size(); i++) { CoreMetadata ms = core.get(i); if (ms.sizeZ == 0) { ms.sizeZ = 1; } if (ms.sizeT == 0) { ms.sizeT = 1; } ms.sizeC = 1; ms.rgb = false; ms.interleaved = false; ms.indexed = false; ms.dimensionOrder = "XYZCT"; ms.imageCount = ms.sizeZ * ms.sizeC * ms.sizeT; } MetadataStore store = makeFilterMetadata(); MetadataTools.populatePixels(store, this); String experimenter = null, instrument = null; if (getMetadataOptions().getMetadataLevel() != MetadataLevel.MINIMUM) { experimenter = MetadataTools.createLSID("Experimenter", 0); store.setExperimenterID(experimenter, 0); store.setExperimenterUserName(investigator, 0); store.setExperimenterInstitution(institution, 0); instrument = MetadataTools.createLSID("Instrument", 0); store.setInstrumentID(instrument, 0); store.setMicroscopeModel(model, 0); } for (int i=0; i<core.size(); i++) { if (date != null) { String newDate = DateTools.formatDate(date, "EEE MMM dd HH:mm:ss yyyy"); if (newDate != null) { store.setImageAcquisitionDate(new Timestamp(newDate), i); } } if (getMetadataOptions().getMetadataLevel() != MetadataLevel.MINIMUM) { if (experimenter != null) { store.setImageExperimenterRef(experimenter, i); } if (instrument != null) { store.setImageInstrumentRef(instrument, i); } store.setImageDescription(description, i); Length sizeX = FormatTools.getPhysicalSizeX(pixelSizeX); Length sizeY = FormatTools.getPhysicalSizeY(pixelSizeY); Length sizeZ = FormatTools.getPhysicalSizeZ(pixelSizeZ); if (sizeX != null) { store.setPixelsPhysicalSizeX(sizeX, i); } if (sizeY != null) { store.setPixelsPhysicalSizeY(sizeY, i); } if (sizeZ != null) { store.setPixelsPhysicalSizeZ(sizeZ, i); } } } } // -- Helper methods -- // Enumeration data is taken from the comments of the .hdr files. private String transformModel(String value) { int model = Integer.parseInt(value); switch (model) { case 2000: return "Primate"; case 2001: return "Rodent"; case 2002: return "microPET2"; case 2500: return "Focus_220"; case 2501: return "Focus_120"; case 3000: return "mCAT"; case 3500: return "mCATII"; case 4000: return "mSPECT"; case 5000: return "Inveon_Dedicated_PET"; case 5001: return "Inveon_MM_Platform"; case 6000: return "MR_PET_Head_Insert"; case 8000: return "Tuebingen_PET_MR"; } return "Unknown"; } private String transformModality(String value) { int modality = Integer.parseInt(value); switch (modality) { case 0: return "PET acquisition"; case 1: return "CT acquisition"; case 2: return "SPECT acquisition"; } return "Unknown"; } private String transformModalityConfiguration(String value) { int modalityConfiguration = Integer.parseInt(value); switch (modalityConfiguration) { case 3000: return "mCAT"; case 3500: return "mCATII"; case 3600: return "Inveon_MM_Std_CT"; case 3601: return "Inveon_MM_HiRes_Std_CT"; case 3602: return "Inveon_MM_Std_LFOV_CT"; case 3603: return "Inveon_MM_HiRes_LFOV_CT"; } return "Unknown"; } private String transformFileType(String value) { int type = Integer.parseInt(value); switch (type) { case 1: return "List mode"; case 2: return "Sinogram"; case 3: return "Normalization"; case 4: return "Attenuation correction"; case 5: return "Image data"; case 6: return "Blank data"; // 7 omitted intentionally case 8: return "Mu map"; case 9: return "Scatter correction"; case 10: return "Crystal efficiency"; case 11: return "Crystal interference correction"; case 12: return "Transaxial geometric correction"; case 13: return "Axial geometric correction"; case 14: return "CT projection"; case 15: return "SPECT raw projection"; case 16: return "SPECT energy data from projections"; case 17: return "SPECT normalization"; } return "Unknown"; } private String transformAcquisitionMode(String value) { int mode = Integer.parseInt(value); switch (mode) { case 1: return "Blank"; case 2: return "Emission"; case 3: return "Dynamic"; case 4: return "Gated"; case 5: return "Continuous bed motion"; case 6: return "Singles transmission"; case 7: return "Windowed coincidence transmission"; case 8: return "Non-windowed coincidence transmission"; case 9: return "CT projection"; case 10: return "CT calibration"; case 11: return "SPECT planar projection"; case 12: return "SPECT multi-projection"; case 13: return "SPECT calibration"; case 14: return "SPECT normalization"; case 15: return "SPECT detector setup"; case 16: return "SPECT scout view"; } return "Unknown"; } private String transformBedControl(String value) { int control = Integer.parseInt(value); switch (control) { case 1: return "Dedicated PET"; case 2: return "microCAT II"; case 3: return "Multimodality bed control"; case 4: return "microPET bed control"; } return "Unknown"; } private String transformBedMotion(String value) { int motion = Integer.parseInt(value); switch (motion) { case 1: return "Continuous"; case 2: return "Multiple bed positions"; } return "Unknown"; } private String transformRegistrationAvailable(String value) { int available = Integer.parseInt(value); switch (available) { case 1: return "CT"; case 2: return "PET"; } return "None"; } private String transformNormalizationApplied(String value) { int normalization = Integer.parseInt(value); switch (normalization) { case 1: return "Point source inversion"; case 2: return "Point source component based"; case 3: return "Cylinder source inversion"; case 4: return "Cylinder source component based"; case 5: return "Dark/bright field log normalization (CT)"; case 6: return "SPECT flood inversion based"; } return "None"; } private String transformReconAlgorithm(String value) { int algorithm = Integer.parseInt(value); switch (algorithm) { case 1: return "Filtered Backprojection"; case 2: return "OSEM2d"; case 3: return "OSEM3d"; // 4 and 5 omitted intentionally case 6: return "OSEM3D followed by MAP or FastMAP"; case 7: return "MAPTR for transmission image"; case 8: return "MAP 3D reconstruction"; case 9: return "Feldkamp cone beam"; } return "Unknown"; } private String transformFilter(String value) { int space = value.indexOf(" "); int filter = Integer.parseInt(value.substring(0, space)); String cutoff = " (cutoff = " + value.substring(space + 1) + ")"; String filterType = "Unknown"; switch (filter) { case 0: return "None"; case 1: return "Ramp filter (backprojection)"; case 2: return "First-order Butterworth window"; case 3: return "Hanning window"; case 4: return "Hamming window"; case 5: return "Parzen window"; case 6: return "Shepp filter"; case 7: return "Second-order Butterworth window"; } return filterType + cutoff; } private String transformSubjectOrientation(String value) { int orientation = Integer.parseInt(value); switch (orientation) { case 1: return "Feet first, prone"; case 2: return "Head first, prone"; case 3: return "Feet first, supine"; case 4: return "Head first, supine"; case 5: return "Feet first, right"; case 6: return "Head first, right"; case 7: return "Feet first, left"; case 8: return "Head first, left"; } return "Unknown"; } private String transformSubjectLengthUnits(String value) { int units = Integer.parseInt(value); switch (units) { case 1: return "millimeters"; case 2: return "centimeters"; case 3: return "inches"; } return "Unknown"; } private String transformSubjectWeightUnits(String value) { int units = Integer.parseInt(value); switch (units) { case 1: return "grams"; case 2: return "ounces"; case 3: return "kilograms"; case 4: return "pounds"; } return "Unknown"; } private String transformGantryRotation(String value) { int rotation = Integer.parseInt(value); switch (rotation) { case 0: return "No gantry rotation"; case 1: return "Rotation with discrete steps"; case 2: return "Continuous rotation"; } return "Unknown"; } private String transformRotationDirection(String value) { return value.equals("0") ? "Clockwise" : "Counterclockwise"; } private String transformCTWarping(String value) { int warping = Integer.parseInt(value); switch (warping) { case 1: return "None"; case 2: return "Bilinear"; case 3: return "Nearest neighbor"; } return "Unknown"; } private String transformCTProjectionInterpolation(String value) { int interpolation = Integer.parseInt(value); switch (interpolation) { case 1: return "Bilinear"; case 2: return "Nearest neighbor"; } return "Unknown"; } private String transformEventType(String value) { int type = Integer.parseInt(value); switch (type) { case 1: return "Singles"; case 2: return "Prompt events (coincidences)"; case 3: return "Delay events"; case 4: return "Trues (prompts - delays)"; case 5: return "Energy spectrum data"; } return "Unknown"; } private void setDataType(String value) { int type = Integer.parseInt(value); int pixelType = FormatTools.INT8; boolean littleEndian = true; switch (type) { case 2: pixelType = FormatTools.INT16; break; case 3: pixelType = FormatTools.INT32; break; case 4: pixelType = FormatTools.FLOAT; break; case 5: pixelType = FormatTools.FLOAT; littleEndian = false; break; case 6: pixelType = FormatTools.INT16; littleEndian = false; break; case 7: pixelType = FormatTools.INT32; littleEndian = false; break; } for (int i=0; i<core.size(); i++) { CoreMetadata ms = core.get(i); ms.pixelType = pixelType; ms.littleEndian = littleEndian; } } }