// // DicomReader.java // /* OME Bio-Formats package for reading and converting biological file formats. Copyright (C) 2005-@year@ UW-Madison LOCI and Glencoe Software, Inc. 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, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package loci.formats.in; import java.io.IOException; import java.util.Arrays; import java.util.Hashtable; import java.util.Vector; import loci.common.DataTools; import loci.common.DateTools; import loci.common.Location; import loci.common.RandomAccessInputStream; import loci.formats.CoreMetadata; import loci.formats.FilePattern; import loci.formats.FormatException; import loci.formats.FormatReader; import loci.formats.FormatTools; import loci.formats.MetadataTools; import loci.formats.UnsupportedCompressionException; import loci.formats.codec.Codec; import loci.formats.codec.CodecOptions; import loci.formats.codec.JPEG2000Codec; import loci.formats.codec.JPEGCodec; import loci.formats.codec.PackbitsCodec; import loci.formats.meta.MetadataStore; import ome.xml.model.primitives.PositiveFloat; /** * DicomReader is the file format reader for DICOM files. * Much of this code is adapted from ImageJ's DICOM reader; see * http://rsb.info.nih.gov/ij/developer/source/ij/plugin/DICOM.java.html * * <dl><dt><b>Source code:</b></dt> * <dd><a href="http://trac.openmicroscopy.org.uk/ome/browser/bioformats.git/components/bio-formats/src/loci/formats/in/DicomReader.java">Trac</a>, * <a href="http://git.openmicroscopy.org/?p=bioformats.git;a=blob;f=components/bio-formats/src/loci/formats/in/DicomReader.java;hb=HEAD">Gitweb</a></dd></dl> */ public class DicomReader extends FormatReader { // -- Constants -- public static final String DICOM_MAGIC_STRING = "DICM"; private static final String[] DICOM_SUFFIXES = { "dic", "dcm", "dicom", "j2ki", "j2kr" }; private static final Hashtable<Integer, String> TYPES = buildTypes(); private static final int PIXEL_REPRESENTATION = 0x00280103; private static final int TRANSFER_SYNTAX_UID = 0x00020010; private static final int SLICE_SPACING = 0x00180088; private static final int SAMPLES_PER_PIXEL = 0x00280002; private static final int PHOTOMETRIC_INTERPRETATION = 0x00280004; private static final int PLANAR_CONFIGURATION = 0x00280006; private static final int NUMBER_OF_FRAMES = 0x00280008; private static final int ROWS = 0x00280010; private static final int COLUMNS = 0x00280011; private static final int PIXEL_SPACING = 0x00280030; private static final int BITS_ALLOCATED = 0x00280100; private static final int WINDOW_CENTER = 0x00281050; private static final int WINDOW_WIDTH = 0x00281051; private static final int RESCALE_INTERCEPT = 0x00281052; private static final int RESCALE_SLOPE = 0x00281053; private static final int ICON_IMAGE_SEQUENCE = 0x00880200; private static final int ITEM = 0xFFFEE000; private static final int ITEM_DELIMINATION = 0xFFFEE00D; private static final int SEQUENCE_DELIMINATION = 0xFFFEE0DD; private static final int PIXEL_DATA = 0x7FE00010; private static final int AE = 0x4145, AS = 0x4153, AT = 0x4154, CS = 0x4353; private static final int DA = 0x4441, DS = 0x4453, DT = 0x4454, FD = 0x4644; private static final int FL = 0x464C, IS = 0x4953, LO = 0x4C4F, LT = 0x4C54; private static final int PN = 0x504E, SH = 0x5348, SL = 0x534C, SS = 0x5353; private static final int ST = 0x5354, TM = 0x544D, UI = 0x5549, UL = 0x554C; private static final int US = 0x5553, UT = 0x5554, OB = 0x4F42, OW = 0x4F57; private static final int SQ = 0x5351, UN = 0x554E, QQ = 0x3F3F; private static final int IMPLICIT_VR = 0x2d2d; // -- Fields -- /** Bits per pixel. */ private int bitsPerPixel; private int location; private int elementLength; private int vr; private boolean oddLocations; private boolean inSequence; private boolean bigEndianTransferSyntax; private byte[][] lut; private short[][] shortLut; private long[] offsets; private int maxPixelValue; private double rescaleSlope = 1.0, rescaleIntercept = 0.0; private boolean isJP2K = false; private boolean isJPEG = false; private boolean isRLE = false; private boolean isDeflate = false; private boolean inverted; private String date, time, imageType; private String pixelSizeX, pixelSizeY; private Hashtable<Integer, Vector<String>> fileList; private int imagesPerFile; private String originalDate, originalTime, originalInstance; private int originalSeries; private DicomReader helper; // -- Constructor -- /** Constructs a new DICOM reader. */ // "Digital Imaging and Communications in Medicine" is nasty long. public DicomReader() { super("DICOM", new String[] { "dic", "dcm", "dicom", "jp2", "j2ki", "j2kr", "raw", "ima"}); suffixNecessary = false; suffixSufficient = false; domains = new String[] {FormatTools.MEDICAL_DOMAIN}; datasetDescription = "One or more .dcm or .dicom files"; } // -- IFormatReader API methods -- /* @see loci.formats.IFormatReader#isThisType(String, boolean) */ public boolean isThisType(String name, boolean open) { // extension is sufficient as long as it is DIC, DCM, DICOM, J2KI, or J2KR if (checkSuffix(name, DICOM_SUFFIXES)) return true; return super.isThisType(name, open); } /* @see loci.formats.IFormatReader#isThisType(RandomAccessInputStream) */ public boolean isThisType(RandomAccessInputStream stream) throws IOException { final int blockLen = 2048; if (!FormatTools.validStream(stream, blockLen, true)) return false; stream.seek(128); if (stream.readString(4).equals(DICOM_MAGIC_STRING)) return true; stream.seek(0); try { int tag = getNextTag(stream); return TYPES.get(new Integer(tag)) != null; } catch (NullPointerException e) { } catch (FormatException e) { } return false; } /* @see loci.formats.IFormatReader#get8BitLookupTable() */ public byte[][] get8BitLookupTable() { FormatTools.assertId(currentId, true, 1); if (getPixelType() != FormatTools.INT8 && getPixelType() != FormatTools.UINT8) { return null; } return lut; } /* @see loci.formats.IFormatReader#get16BitLookupTable() */ public short[][] get16BitLookupTable() { FormatTools.assertId(currentId, true, 1); if (getPixelType() != FormatTools.INT16 && getPixelType() != FormatTools.UINT16) { return null; } return shortLut; } /* @see loci.formats.IFormatReader#getSeriesUsedFiles(boolean) */ public String[] getSeriesUsedFiles(boolean noPixels) { FormatTools.assertId(currentId, true, 1); if (noPixels || fileList == null) return null; Integer[] keys = fileList.keySet().toArray(new Integer[0]); Arrays.sort(keys); Vector<String> files = fileList.get(keys[getSeries()]); return files == null ? null : files.toArray(new String[files.size()]); } public int fileGroupOption(String id) throws FormatException, IOException { return CAN_GROUP; } /** * @see loci.formats.IFormatReader#openBytes(int, byte[], int, int, int, int) */ 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); Integer[] keys = fileList.keySet().toArray(new Integer[0]); Arrays.sort(keys); if (fileList.get(keys[getSeries()]).size() > 1) { int fileNumber = no / imagesPerFile; no = no % imagesPerFile; String file = fileList.get(keys[getSeries()]).get(fileNumber); helper.setId(file); return helper.openBytes(no, buf, x, y, w, h); } int ec = isIndexed() ? 1 : getSizeC(); int bpp = FormatTools.getBytesPerPixel(getPixelType()); int bytes = getSizeX() * getSizeY() * bpp * ec; in.seek(offsets[no]); if (isRLE) { // plane is compressed using run-length encoding CodecOptions options = new CodecOptions(); options.maxBytes = getSizeX() * getSizeY(); for (int c=0; c<ec; c++) { PackbitsCodec codec = new PackbitsCodec(); byte[] t = null; if (bpp > 1) { int plane = bytes / (bpp * ec); byte[][] tmp = new byte[bpp][]; for (int i=0; i<bpp; i++) { tmp[i] = codec.decompress(in, options); if (no < imagesPerFile - 1 || i < bpp - 1) { while (in.read() == 0); in.seek(in.getFilePointer() - 1); } } t = new byte[bytes / ec]; for (int i=0; i<plane; i++) { for (int j=0; j<bpp; j++) { int byteIndex = isLittleEndian() ? bpp - j - 1 : j; if (i < tmp[byteIndex].length) { t[i * bpp + j] = tmp[byteIndex][i]; } } } } else { t = codec.decompress(in, options); if (t.length < (bytes / ec)) { byte[] tmp = t; t = new byte[bytes / ec]; System.arraycopy(tmp, 0, t, 0, tmp.length); } if (no < imagesPerFile - 1 || c < ec - 1) { while (in.read() == 0); in.seek(in.getFilePointer() - 1); } } int rowLen = w * bpp; int srcRowLen = getSizeX() * bpp; int srcPlane = getSizeY() * srcRowLen; for (int row=0; row<h; row++) { int src = (row + y) * srcRowLen + x * bpp; int dest = (h * c + row) * rowLen; int len = (int) Math.min(rowLen, t.length - src - 1); if (len < 0) break; System.arraycopy(t, src, buf, dest, len); } } } else if (isJPEG || isJP2K) { // plane is compressed using JPEG or JPEG-2000 long end = no < offsets.length - 1 ? offsets[no + 1] : in.length(); byte[] b = new byte[(int) (end - in.getFilePointer())]; in.read(b); if (b[2] != (byte) 0xff) { byte[] tmp = new byte[b.length + 1]; tmp[0] = b[0]; tmp[1] = b[1]; tmp[2] = (byte) 0xff; System.arraycopy(b, 2, tmp, 3, b.length - 2); b = tmp; } if ((b[3] & 0xff) >= 0xf0) { b[3] -= (byte) 0x30; } int pt = b.length - 2; while (pt >= 0 && b[pt] != (byte) 0xff || b[pt + 1] != (byte) 0xd9) { pt--; } if (pt < b.length - 2) { byte[] tmp = b; b = new byte[pt + 2]; System.arraycopy(tmp, 0, b, 0, b.length); } Codec codec = null; CodecOptions options = new CodecOptions(); options.littleEndian = isLittleEndian(); options.interleaved = isInterleaved(); if (isJPEG) codec = new JPEGCodec(); else codec = new JPEG2000Codec(); b = codec.decompress(b, options); int rowLen = w * bpp; int srcRowLen = getSizeX() * bpp; int srcPlane = getSizeY() * srcRowLen; for (int c=0; c<ec; c++) { for (int row=0; row<h; row++) { System.arraycopy(b, c * srcPlane + (row + y) * srcRowLen + x * bpp, buf, h * rowLen * c + row * rowLen, rowLen); } } } else if (isDeflate) { // TODO throw new UnsupportedCompressionException( "Deflate data is not supported."); } else { // plane is not compressed readPlane(in, x, y, w, h, buf); } if (inverted) { // pixels are stored such that white -> 0; invert the values so that // white -> 255 (or 65535) if (bpp == 1) { for (int i=0; i<buf.length; i++) { buf[i] = (byte) (255 - buf[i]); } } else if (bpp == 2) { if (maxPixelValue == -1) maxPixelValue = 65535; boolean little = isLittleEndian(); for (int i=0; i<buf.length; i+=2) { short s = DataTools.bytesToShort(buf, i, 2, little); DataTools.unpackBytes(maxPixelValue - s, buf, i, 2, little); } } } // NB: do *not* apply the rescale function return buf; } /* @see loci.formats.IFormatReader#close(boolean) */ public void close(boolean fileOnly) throws IOException { super.close(fileOnly); if (helper != null) helper.close(fileOnly); if (!fileOnly) { bitsPerPixel = location = elementLength = vr = 0; oddLocations = inSequence = bigEndianTransferSyntax = false; isJPEG = isJP2K = isRLE = isDeflate = false; lut = null; offsets = null; shortLut = null; maxPixelValue = 0; rescaleSlope = 1.0; rescaleIntercept = 0.0; pixelSizeX = pixelSizeY = null; imagesPerFile = 0; fileList = null; inverted = false; date = time = imageType = null; originalDate = originalTime = originalInstance = null; originalSeries = 0; helper = null; } } // -- Internal FormatReader API methods -- /* @see loci.formats.FormatReader#initFile(String) */ protected void initFile(String id) throws FormatException, IOException { super.initFile(id); in = new RandomAccessInputStream(id); in.order(true); helper = new DicomReader(); helper.setGroupFiles(false); core[0].littleEndian = true; location = 0; isJPEG = false; isRLE = false; bigEndianTransferSyntax = false; oddLocations = false; inSequence = false; bitsPerPixel = 0; elementLength = 0; vr = 0; lut = null; offsets = null; inverted = false; // some DICOM files have a 128 byte header followed by a 4 byte identifier LOGGER.info("Verifying DICOM format"); MetadataLevel level = getMetadataOptions().getMetadataLevel(); in.seek(128); if (in.readString(4).equals("DICM")) { if (level != MetadataLevel.MINIMUM) { // header exists, so we'll read it in.seek(0); addSeriesMeta("Header information", in.readString(128)); in.skipBytes(4); } location = 128; } else in.seek(0); LOGGER.info("Reading tags"); long baseOffset = 0; boolean decodingTags = true; boolean signed = false; while (decodingTags) { if (in.getFilePointer() + 4 >= in.length()) { break; } LOGGER.debug("Reading tag from {}", in.getFilePointer()); int tag = getNextTag(in); if (elementLength <= 0) continue; oddLocations = (location & 1) != 0; LOGGER.debug(" tag={} len={} fp=", new Object[] {tag, elementLength, in.getFilePointer()}); String s = null; switch (tag) { case TRANSFER_SYNTAX_UID: // this tag can indicate which compression scheme is used s = in.readString(elementLength); addInfo(tag, s); if (s.startsWith("1.2.840.10008.1.2.4.9")) isJP2K = true; else if (s.startsWith("1.2.840.10008.1.2.4")) isJPEG = true; else if (s.startsWith("1.2.840.10008.1.2.5")) isRLE = true; else if (s.equals("1.2.8.10008.1.2.1.99")) isDeflate = true; else if (s.indexOf("1.2.4") > -1 || s.indexOf("1.2.5") > -1) { throw new UnsupportedCompressionException( "Sorry, compression type " + s + " not supported"); } if (s.indexOf("1.2.840.10008.1.2.2") >= 0) { bigEndianTransferSyntax = true; } break; case NUMBER_OF_FRAMES: s = in.readString(elementLength); addInfo(tag, s); double frames = Double.parseDouble(s); if (frames > 1.0) imagesPerFile = (int) frames; break; case SAMPLES_PER_PIXEL: addInfo(tag, in.readShort()); break; case PLANAR_CONFIGURATION: int config = in.readShort(); core[0].interleaved = config == 0; addInfo(tag, config); break; case ROWS: if (getSizeY() == 0) core[0].sizeY = in.readShort(); else in.skipBytes(2); addInfo(tag, getSizeY()); break; case COLUMNS: if (getSizeX() == 0) core[0].sizeX = in.readShort(); else in.skipBytes(2); addInfo(tag, getSizeX()); break; case PHOTOMETRIC_INTERPRETATION: case PIXEL_SPACING: case SLICE_SPACING: case RESCALE_INTERCEPT: case WINDOW_CENTER: case RESCALE_SLOPE: addInfo(tag, in.readString(elementLength)); break; case BITS_ALLOCATED: if (bitsPerPixel == 0) bitsPerPixel = in.readShort(); else in.skipBytes(2); addInfo(tag, bitsPerPixel); break; case PIXEL_REPRESENTATION: short ss = in.readShort(); signed = ss == 1; addInfo(tag, ss); break; case 537262910: case WINDOW_WIDTH: String t = in.readString(elementLength); if (t.trim().length() == 0) maxPixelValue = -1; else { try { maxPixelValue = new Double(t.trim()).intValue(); } catch (NumberFormatException e) { maxPixelValue = -1; } } addInfo(tag, t); break; case PIXEL_DATA: case ITEM: case 0xffee000: if (elementLength != 0) { baseOffset = in.getFilePointer(); addInfo(tag, location); decodingTags = false; } else addInfo(tag, null); break; case 0x7f880010: if (elementLength != 0) { baseOffset = location + 4; decodingTags = false; } break; case 0x7fe00000: in.skipBytes(elementLength); break; case 0: in.seek(in.getFilePointer() - 4); break; default: long oldfp = in.getFilePointer(); addInfo(tag, s); in.seek(oldfp + elementLength); } if (in.getFilePointer() >= (in.length() - 4)) { decodingTags = false; } } if (imagesPerFile == 0) imagesPerFile = 1; core[0].bitsPerPixel = bitsPerPixel; while (bitsPerPixel % 8 != 0) bitsPerPixel++; if (bitsPerPixel == 24 || bitsPerPixel == 48) { bitsPerPixel /= 3; core[0].bitsPerPixel /= 3; } core[0].pixelType = FormatTools.pixelTypeFromBytes(bitsPerPixel / 8, signed, false); int bpp = FormatTools.getBytesPerPixel(getPixelType()); int plane = getSizeX() * getSizeY() * (lut == null ? getSizeC() : 1) * bpp; LOGGER.info("Calculating image offsets"); // calculate the offset to each plane in.seek(baseOffset - 12); int len = in.readInt(); if (len >= 0 && len + in.getFilePointer() < in.length()) { in.skipBytes(len); int check = in.readShort() & 0xffff; if (check == 0xfffe) { baseOffset = in.getFilePointer() + 2; } } offsets = new long[imagesPerFile]; for (int i=0; i<imagesPerFile; i++) { if (isRLE) { if (i == 0) in.seek(baseOffset); else { in.seek(offsets[i - 1]); CodecOptions options = new CodecOptions(); options.maxBytes = plane / bpp; for (int q=0; q<bpp; q++) { new PackbitsCodec().decompress(in, options); while (in.read() == 0); in.seek(in.getFilePointer() - 1); } } in.skipBytes(i == 0 ? 64 : 53); while (in.read() == 0); offsets[i] = in.getFilePointer() - 1; } else if (isJPEG || isJP2K) { // scan for next JPEG magic byte sequence if (i == 0) offsets[i] = baseOffset; else offsets[i] = offsets[i - 1] + 3; byte secondCheck = isJPEG ? (byte) 0xd8 : (byte) 0x4f; in.seek(offsets[i]); byte[] buf = new byte[8192]; int n = in.read(buf); boolean found = false; while (!found) { for (int q=0; q<n-2; q++) { if (buf[q] == (byte) 0xff && buf[q + 1] == secondCheck && buf[q + 2] == (byte) 0xff) { if (isJPEG || (isJP2K && buf[q + 3] == 0x51)) { found = true; offsets[i] = in.getFilePointer() + q - n; break; } } } if (!found) { for (int q=0; q<4; q++) { buf[q] = buf[buf.length + q - 4]; } n = in.read(buf, 4, buf.length - 4) + 4; } } } else offsets[i] = baseOffset + plane*i; } makeFileList(); LOGGER.info("Populating metadata"); if (fileList.size() > 1) { core = new CoreMetadata[fileList.size()]; } Integer[] keys = fileList.keySet().toArray(new Integer[0]); Arrays.sort(keys); for (int i=0; i<core.length; i++) { if (core.length == 1) { core[i].sizeZ = imagesPerFile * fileList.get(keys[i]).size(); if (core[i].sizeC == 0) core[i].sizeC = 1; core[i].rgb = core[i].sizeC > 1; core[i].sizeT = 1; core[i].dimensionOrder = "XYCZT"; core[i].metadataComplete = true; core[i].falseColor = false; if (isRLE) core[i].interleaved = false; } else { helper.setId(fileList.get(keys[i]).get(0)); core[i] = helper.getCoreMetadata()[0]; core[i].sizeZ *= fileList.get(keys[i]).size(); } core[i].imageCount = core[i].sizeZ; } // The metadata store we're working with. MetadataStore store = makeFilterMetadata(); MetadataTools.populatePixels(store, this); String stamp = null; if (date != null && time != null) { stamp = date + " " + time; stamp = DateTools.formatDate(stamp, "yyyy.MM.dd HH:mm:ss.SSSSSS"); } if (stamp == null || stamp.trim().equals("")) stamp = null; for (int i=0; i<core.length; i++) { if (stamp != null) store.setImageAcquiredDate(stamp, i); else MetadataTools.setDefaultCreationDate(store, id, i); store.setImageName("Series " + i, i); } if (level != MetadataLevel.MINIMUM) { for (int i=0; i<core.length; i++) { store.setImageDescription(imageType, i); if (pixelSizeX != null) { Double sizeX = new Double(pixelSizeX); if (sizeX > 0) { store.setPixelsPhysicalSizeX(new PositiveFloat(sizeX), i); } } if (pixelSizeY != null) { Double sizeY = new Double(pixelSizeY); if (sizeY > 0) { store.setPixelsPhysicalSizeY(new PositiveFloat(sizeY), i); } } } } } // -- Helper methods -- private void addInfo(int tag, String value) throws IOException { String oldValue = value; String info = getHeaderInfo(tag, value); if (info != null && tag != ITEM) { info = info.trim(); if (info.equals("")) info = oldValue == null ? "" : oldValue.trim(); String key = TYPES.get(tag); if (key == null) { key = formatTag(tag); } if (key.equals("Samples per pixel")) { core[0].sizeC = Integer.parseInt(info); if (getSizeC() > 1) core[0].rgb = true; } else if (key.equals("Photometric Interpretation")) { if (info.equals("PALETTE COLOR")) { core[0].indexed = true; core[0].sizeC = 1; core[0].rgb = false; lut = new byte[3][]; shortLut = new short[3][]; } else if (info.startsWith("MONOCHROME")) { inverted = info.endsWith("1"); } } else if (key.equals("Acquisition Date")) originalDate = info; else if (key.equals("Acquisition Time")) originalTime = info; else if (key.equals("Instance Number")) { if (info.trim().length() > 0) { originalInstance = info; } } else if (key.equals("Series Number")) { try { originalSeries = Integer.parseInt(info); } catch (NumberFormatException e) { } } else if (key.indexOf("Palette Color LUT Data") != -1) { String color = key.substring(0, key.indexOf(" ")).trim(); int ndx = color.equals("Red") ? 0 : color.equals("Green") ? 1 : 2; long fp = in.getFilePointer(); in.seek(in.getFilePointer() - elementLength + 1); shortLut[ndx] = new short[elementLength / 2]; lut[ndx] = new byte[elementLength / 2]; for (int i=0; i<lut[ndx].length; i++) { shortLut[ndx][i] = in.readShort(); lut[ndx][i] = (byte) (shortLut[ndx][i] & 0xff); } in.seek(fp); } else if (key.equals("Content Time")) time = info; else if (key.equals("Content Date")) date = info; else if (key.equals("Image Type")) imageType = info; else if (key.equals("Rescale Intercept")) { rescaleIntercept = Double.parseDouble(info); } else if (key.equals("Rescale Slope")) { rescaleSlope = Double.parseDouble(info); } else if (key.equals("Pixel Spacing")) { pixelSizeX = info.substring(0, info.indexOf("\\")); pixelSizeY = info.substring(info.lastIndexOf("\\") + 1); } if (((tag & 0xffff0000) >> 16) != 0x7fe0) { key = formatTag(tag) + " " + key; if (metadata.containsKey(key)) { // make sure that values are not overwritten Object v = getMetadataValue(key); metadata.remove(key); addSeriesMeta(key + " #1", v); addSeriesMeta(key + " #2", info); } else if (metadata.containsKey(key + " #1")) { int index = 2; while (metadata.containsKey(key + " #" + index)) index++; addSeriesMeta(key + " #" + index, info); } else addSeriesMeta(key, info); } } } private void addInfo(int tag, int value) throws IOException { addInfo(tag, Integer.toString(value)); } private String getHeaderInfo(int tag, String value) throws IOException { if (tag == ITEM_DELIMINATION || tag == SEQUENCE_DELIMINATION) { inSequence = false; } String id = TYPES.get(new Integer(tag)); if (id != null) { if (vr == IMPLICIT_VR && id != null) { vr = (id.charAt(0) << 8) + id.charAt(1); } if (id.length() > 2) id = id.substring(2); } if (tag == ITEM) return id != null ? id : null; if (value != null) return value; boolean skip = false; switch (vr) { case AE: case AS: case AT: case CS: case DA: case DS: case DT: case IS: case LO: case LT: case PN: case SH: case ST: case TM: case UI: value = in.readString(elementLength); break; case US: if (elementLength == 2) value = Integer.toString(in.readShort()); else { value = ""; int n = elementLength / 2; for (int i=0; i<n; i++) { value += Integer.toString(in.readShort()) + " "; } } break; case IMPLICIT_VR: value = in.readString(elementLength); if (elementLength <= 4 || elementLength > 44) value = null; break; case SQ: value = ""; boolean privateTag = ((tag >> 16) & 1) != 0; if (tag == ICON_IMAGE_SEQUENCE || privateTag) skip = true; break; default: skip = true; } if (skip) { long skipCount = (long) elementLength; if (in.getFilePointer() + skipCount <= in.length()) { in.skipBytes((int) skipCount); } location += elementLength; value = ""; } if (value != null && id == null && !value.equals("")) return value; else if (id == null) return null; else return value; } private int getLength(RandomAccessInputStream stream, int tag) throws IOException { byte[] b = new byte[4]; stream.read(b); // We cannot know whether the VR is implicit or explicit // without the full DICOM Data Dictionary for public and // private groups. // We will assume the VR is explicit if the two bytes // match the known codes. It is possible that these two // bytes are part of a 32-bit length for an implicit VR. vr = ((b[0] & 0xff) << 8) | (b[1] & 0xff); switch (vr) { case OB: case OW: case SQ: case UN: // Explicit VR with 32-bit length if other two bytes are zero if ((b[2] == 0) || (b[3] == 0)) { return stream.readInt(); } vr = IMPLICIT_VR; return DataTools.bytesToInt(b, stream.isLittleEndian()); case AE: case AS: case AT: case CS: case DA: case DS: case DT: case FD: case FL: case IS: case LO: case LT: case PN: case SH: case SL: case SS: case ST: case TM: case UI: case UL: case US: case UT: case QQ: // Explicit VR with 16-bit length if (tag == 0x00283006) { return DataTools.bytesToInt(b, 2, 2, stream.isLittleEndian()); } int n1 = DataTools.bytesToShort(b, 2, 2, stream.isLittleEndian()); int n2 = DataTools.bytesToShort(b, 2, 2, !stream.isLittleEndian()); n1 &= 0xffff; n2 &= 0xffff; if (n1 < 0 || n1 + stream.getFilePointer() > stream.length()) return n2; if (n2 < 0 || n2 + stream.getFilePointer() > stream.length()) return n1; return n1; case 0xffff: vr = IMPLICIT_VR; return 8; default: vr = IMPLICIT_VR; int len = DataTools.bytesToInt(b, stream.isLittleEndian()); if (len + stream.getFilePointer() > stream.length() || len < 0) { len = DataTools.bytesToInt(b, 2, 2, stream.isLittleEndian()); len &= 0xffff; } return len; } } private int getNextTag(RandomAccessInputStream stream) throws FormatException, IOException { long fp = stream.getFilePointer(); int groupWord = stream.readShort() & 0xffff; if (groupWord == 0x0800 && bigEndianTransferSyntax) { core[0].littleEndian = false; groupWord = 0x0008; stream.order(false); } else if (groupWord == 0xfeff || groupWord == 0xfffe) { stream.skipBytes(6); return getNextTag(stream); } int elementWord = stream.readShort(); int tag = ((groupWord << 16) & 0xffff0000) | (elementWord & 0xffff); elementLength = getLength(stream, tag); if (elementLength > stream.length()) { stream.seek(fp); core[0].littleEndian = !stream.isLittleEndian(); stream.order(!stream.isLittleEndian()); groupWord = stream.readShort() & 0xffff; elementWord = stream.readShort(); tag = ((groupWord << 16) & 0xffff0000) | (elementWord & 0xffff); elementLength = getLength(stream, tag); if (elementLength > stream.length()) { throw new FormatException("Invalid tag length " + elementLength); } return tag; } if (elementLength < 0 && groupWord == 0x7fe0) { stream.skipBytes(12); elementLength = stream.readInt(); if (elementLength < 0) elementLength = stream.readInt(); } if (elementLength == 0 && (groupWord == 0x7fe0 || tag == 0x291014)) { elementLength = getLength(stream, tag); } // HACK - needed to read some GE files // The element length must be even! if (!oddLocations && (elementLength % 2) == 1) elementLength++; // "Undefined" element length. // This is a sort of bracket that encloses a sequence of elements. if (elementLength == -1) { elementLength = 0; inSequence = true; } return tag; } private void makeFileList() throws FormatException, IOException { LOGGER.info("Building file list"); if (fileList == null && originalInstance != null && originalDate != null && originalTime != null && isGroupFiles()) { currentId = new Location(currentId).getAbsolutePath(); fileList = new Hashtable<Integer, Vector<String>>(); Integer s = new Integer(originalSeries); fileList.put(s, new Vector<String>()); int instanceNumber = Integer.parseInt(originalInstance) - 1; if (instanceNumber == 0) fileList.get(s).add(currentId); else { while (instanceNumber > fileList.get(s).size()) { fileList.get(s).add(null); } fileList.get(s).add(currentId); } // look for matching files in the current directory Location currentFile = new Location(currentId).getAbsoluteFile(); Location directory = currentFile.getParentFile(); scanDirectory(directory, false); // move up a directory and look for other directories that // could contain matching files directory = directory.getParentFile(); String[] subdirs = directory.list(true); if (subdirs != null) { for (String subdir : subdirs) { Location f = new Location(directory, subdir).getAbsoluteFile(); if (!f.isDirectory()) continue; scanDirectory(f, true); } } Integer[] keys = fileList.keySet().toArray(new Integer[0]); Arrays.sort(keys); for (Integer key : keys) { for (int j=0; j<fileList.get(key).size(); j++) { if (fileList.get(key).get(j) == null) { fileList.get(key).remove(j); j--; } } } } else if (fileList == null) { fileList = new Hashtable<Integer, Vector<String>>(); fileList.put(new Integer(0), new Vector<String>()); fileList.get(0).add(currentId); } } // -- Utility methods -- /** * Scan the given directory for files that belong to this dataset. */ private void scanDirectory(Location dir, boolean checkSeries) throws FormatException, IOException { Location currentFile = new Location(currentId).getAbsoluteFile(); FilePattern pattern = new FilePattern(currentFile.getName(), dir.getAbsolutePath()); String[] patternFiles = pattern.getFiles(); if (patternFiles == null) patternFiles = new String[0]; Arrays.sort(patternFiles); String[] files = dir.list(true); if (files == null) return; Arrays.sort(files); for (String f : files) { String file = new Location(dir, f).getAbsolutePath(); LOGGER.debug("Checking file {}", file); if (!f.equals(currentId) && !file.equals(currentId) && isThisType(file) && Arrays.binarySearch(patternFiles, file) >= 0) { addFileToList(file, checkSeries); } } } /** * Determine if the given file belongs in the same dataset as this file. */ private void addFileToList(String file, boolean checkSeries) throws FormatException, IOException { RandomAccessInputStream stream = new RandomAccessInputStream(file); if (!isThisType(stream)) { stream.close(); return; } stream.order(true); stream.seek(128); if (!stream.readString(4).equals("DICM")) stream.seek(0); int fileSeries = -1; String date = null, time = null, instance = null; while (date == null || time == null || instance == null || (checkSeries && fileSeries < 0)) { long fp = stream.getFilePointer(); if (fp + 4 >= stream.length() || fp < 0) break; int tag = getNextTag(stream); String key = TYPES.get(new Integer(tag)); if ("Instance Number".equals(key)) { instance = stream.readString(elementLength).trim(); if (instance.length() == 0) instance = null; } else if ("Acquisition Time".equals(key)) { time = stream.readString(elementLength); } else if ("Acquisition Date".equals(key)) { date = stream.readString(elementLength); } else if ("Series Number".equals(key)) { fileSeries = Integer.parseInt(stream.readString(elementLength).trim()); } else stream.skipBytes(elementLength); } stream.close(); if (date == null || time == null || instance == null || (checkSeries && fileSeries == originalSeries)) { return; } int stamp = 0; try { stamp = Integer.parseInt(time); } catch (NumberFormatException e) { } int timestamp = 0; try { timestamp = Integer.parseInt(originalTime); } catch (NumberFormatException e) { } if (date.equals(originalDate) && (Math.abs(stamp - timestamp) < 150)) { int position = Integer.parseInt(instance) - 1; if (position < 0) position = 0; if (fileList.get(fileSeries) == null) { fileList.put(new Integer(fileSeries), new Vector<String>()); } if (position < fileList.get(fileSeries).size()) { while (position < fileList.get(fileSeries).size() && fileList.get(fileSeries).get(position) != null) { position++; } if (position < fileList.get(fileSeries).size()) { fileList.get(fileSeries).setElementAt(file, position); } else fileList.get(fileSeries).add(file); } else { while (position > fileList.get(fileSeries).size()) { fileList.get(fileSeries).add(null); } fileList.get(fileSeries).add(file); } } } private String formatTag(int tag) { String s = Integer.toHexString(tag); while (s.length() < 8) { s = "0" + s; } return s.substring(0, 4) + "," + s.substring(4); } /** * Assemble the data dictionary. * This is incomplete at best, since there are literally thousands of * fields defined by the DICOM specifications. */ private static Hashtable<Integer, String> buildTypes() { Hashtable<Integer, String> dict = new Hashtable<Integer, String>(); dict.put(new Integer(0x00020002), "Media Storage SOP Class UID"); dict.put(new Integer(0x00020003), "Media Storage SOP Instance UID"); dict.put(new Integer(0x00020010), "Transfer Syntax UID"); dict.put(new Integer(0x00020012), "Implementation Class UID"); dict.put(new Integer(0x00020013), "Implementation Version Name"); dict.put(new Integer(0x00020016), "Source Application Entity Title"); dict.put(new Integer(0x00080005), "Specific Character Set"); dict.put(new Integer(0x00080008), "Image Type"); dict.put(new Integer(0x00080010), "Recognition Code"); dict.put(new Integer(0x00080012), "Instance Creation Date"); dict.put(new Integer(0x00080013), "Instance Creation Time"); dict.put(new Integer(0x00080014), "Instance Creator UID"); dict.put(new Integer(0x00080016), "SOP Class UID"); dict.put(new Integer(0x00080018), "SOP Instance UID"); dict.put(new Integer(0x0008001a), "Related General SOP Class UID"); dict.put(new Integer(0x0008001b), "Original Specialized SOP Class UID"); dict.put(new Integer(0x00080020), "Study Date"); dict.put(new Integer(0x00080021), "Series Date"); dict.put(new Integer(0x00080022), "Acquisition Date"); dict.put(new Integer(0x00080023), "Content Date"); dict.put(new Integer(0x00080024), "Overlay Date"); dict.put(new Integer(0x00080025), "Curve Date"); dict.put(new Integer(0x0008002a), "Acquisition Date/Time"); dict.put(new Integer(0x00080030), "Study Time"); dict.put(new Integer(0x00080031), "Series Time"); dict.put(new Integer(0x00080032), "Acquisition Time"); dict.put(new Integer(0x00080033), "Content Time"); dict.put(new Integer(0x00080034), "Overlay Time"); dict.put(new Integer(0x00080035), "Curve Time"); dict.put(new Integer(0x00080041), "Data Set Subtype"); dict.put(new Integer(0x00080050), "Accession Number"); dict.put(new Integer(0x00080052), "Query/Retrieve Level"); dict.put(new Integer(0x00080054), "Retrieve AE Title"); dict.put(new Integer(0x00080056), "Instance Availability"); dict.put(new Integer(0x00080058), "Failed SOP Instance UID List"); dict.put(new Integer(0x00080060), "Modality"); dict.put(new Integer(0x00080061), "Modalities in Study"); dict.put(new Integer(0x00080062), "SOP Classes in Study"); dict.put(new Integer(0x00080064), "Conversion Type"); dict.put(new Integer(0x00080068), "Presentation Intent Type"); dict.put(new Integer(0x00080070), "Manufacturer"); dict.put(new Integer(0x00080080), "Institution Name"); dict.put(new Integer(0x00080081), "Institution Address"); dict.put(new Integer(0x00080082), "Institution Code Sequence"); dict.put(new Integer(0x00080090), "Referring Physician's Name"); dict.put(new Integer(0x00080092), "Referring Physician's Address"); dict.put(new Integer(0x00080094), "Referring Physician's Telephone"); dict.put(new Integer(0x00080096), "Referring Physician ID"); dict.put(new Integer(0x00080100), "Code Value"); dict.put(new Integer(0x00080102), "Coding Scheme Designator"); dict.put(new Integer(0x00080103), "Coding Scheme Version"); dict.put(new Integer(0x00080104), "Code Meaning"); dict.put(new Integer(0x00080105), "Mapping Resource"); dict.put(new Integer(0x00080106), "Context Group Version"); dict.put(new Integer(0x00080107), "Context Group Local Version"); dict.put(new Integer(0x0008010b), "Context Group Extension Flag"); dict.put(new Integer(0x0008010c), "Coding Scheme UID"); dict.put(new Integer(0x0008010d), "Context Group Extension Creator UID"); dict.put(new Integer(0x0008010f), "Context ID"); dict.put(new Integer(0x00080110), "Coding Scheme ID"); dict.put(new Integer(0x00080112), "Coding Scheme Registry"); dict.put(new Integer(0x00080114), "Coding Scheme External ID"); dict.put(new Integer(0x00080115), "Coding Scheme Name"); dict.put(new Integer(0x00080116), "Responsible Organization"); dict.put(new Integer(0x00080201), "Timezone Offset from UTC"); dict.put(new Integer(0x00081010), "Station Name"); dict.put(new Integer(0x00081030), "Study Description"); dict.put(new Integer(0x00081032), "Procedure Code Sequence"); dict.put(new Integer(0x0008103e), "Series Description"); dict.put(new Integer(0x00081040), "Institutional Department Name"); dict.put(new Integer(0x00081048), "Physician(s) of Record"); dict.put(new Integer(0x00081049), "Physician(s) of Record ID"); dict.put(new Integer(0x00081050), "Performing Physician's Name"); dict.put(new Integer(0x00081052), "Performing Physican ID"); dict.put(new Integer(0x00081060), "Name of Physician(s) Reading Study"); dict.put(new Integer(0x00081062), "Physician(s) Reading Study ID"); dict.put(new Integer(0x00081070), "Operator's Name"); dict.put(new Integer(0x00081072), "Operator ID"); dict.put(new Integer(0x00081080), "Admitting Diagnoses Description"); dict.put(new Integer(0x00081084), "Admitting Diagnoses Code Sequence"); dict.put(new Integer(0x00081090), "Manufacturer's Model Name"); dict.put(new Integer(0x00081100), "Referenced Results Sequence"); dict.put(new Integer(0x00081110), "Referenced Study Sequence"); dict.put(new Integer(0x00081111), "Referenced Performed Procedure Step"); dict.put(new Integer(0x00081115), "Referenced Series Sequence"); dict.put(new Integer(0x00081120), "Referenced Patient Sequence"); dict.put(new Integer(0x00081125), "Referenced Visit Sequence"); dict.put(new Integer(0x00081130), "Referenced Overlay Sequence"); dict.put(new Integer(0x0008113a), "Referenced Waveform Sequence"); dict.put(new Integer(0x00081140), "Referenced Image Sequence"); dict.put(new Integer(0x00081145), "Referenced Curve Sequence"); dict.put(new Integer(0x0008114a), "Referenced Instance Sequence"); dict.put(new Integer(0x00081150), "Referenced SOP Class UID"); dict.put(new Integer(0x00081155), "Referenced SOP Instance UID"); dict.put(new Integer(0x0008115a), "SOP Classes Supported"); dict.put(new Integer(0x00081160), "Referenced Frame Number"); dict.put(new Integer(0x00081195), "Transaction UID"); dict.put(new Integer(0x00081197), "Failure Reason"); dict.put(new Integer(0x00081198), "Failed SOP Sequence"); dict.put(new Integer(0x00081199), "Referenced SOP Sequence"); dict.put(new Integer(0x00081200), "Studies Containing Other Referenced Instances Sequence"); dict.put(new Integer(0x00081250), "Related Series Sequence"); dict.put(new Integer(0x00082111), "Derivation Description"); dict.put(new Integer(0x00082112), "Source Image Sequence"); dict.put(new Integer(0x00082120), "Stage Name"); dict.put(new Integer(0x00082122), "Stage Number"); dict.put(new Integer(0x00082124), "Number of Stages"); dict.put(new Integer(0x00082127), "View Name"); dict.put(new Integer(0x00082128), "View Number"); dict.put(new Integer(0x00082129), "Number of Event Timers"); dict.put(new Integer(0x0008212a), "Number of Views in Stage"); dict.put(new Integer(0x00082130), "Event Elapsed Time(s)"); dict.put(new Integer(0x00082132), "Event Timer Name(s)"); dict.put(new Integer(0x00082142), "Start Trim"); dict.put(new Integer(0x00082143), "Stop Trim"); dict.put(new Integer(0x00082144), "Recommended Display Frame Rate"); dict.put(new Integer(0x00082218), "Anatomic Region Sequence"); dict.put(new Integer(0x00082220), "Anatomic Region Modifier Sequence"); dict.put(new Integer(0x00082228), "Primary Anatomic Structure Sequence"); dict.put(new Integer(0x00082229), "Anatomic Structure Sequence"); dict.put(new Integer(0x00082230), "Primary Anatomic Structure Modifier"); dict.put(new Integer(0x00082240), "Transducer Position Sequence"); dict.put(new Integer(0x00082242), "Transducer Position Modifier Sequence"); dict.put(new Integer(0x00082244), "Transducer Orientation Sequence"); dict.put(new Integer(0x00082246), "Transducer Orientation Modifier"); dict.put(new Integer(0x00083001), "Alternate Representation Sequence"); dict.put(new Integer(0x00089007), "Frame Type"); dict.put(new Integer(0x00089092), "Referenced Image Evidence Sequence"); dict.put(new Integer(0x00089121), "Referenced Raw Data Sequence"); dict.put(new Integer(0x00089123), "Creator-Version UID"); dict.put(new Integer(0x00089124), "Derivation Image Sequence"); dict.put(new Integer(0x00089154), "Source Image Evidence Sequence"); dict.put(new Integer(0x00089205), "Pixel Representation"); dict.put(new Integer(0x00089206), "Volumetric Properties"); dict.put(new Integer(0x00089207), "Volume Based Calculation Technique"); dict.put(new Integer(0x00089208), "Complex Image Component"); dict.put(new Integer(0x00089209), "Acquisition Contrast"); dict.put(new Integer(0x00089215), "Derivation Code Sequence"); dict.put(new Integer(0x00089237), "Reference Grayscale Presentation State"); dict.put(new Integer(0x00100010), "Patient's Name"); dict.put(new Integer(0x00100020), "Patient ID"); dict.put(new Integer(0x00100021), "Issuer of Patient ID"); dict.put(new Integer(0x00100030), "Patient's Birth Date"); dict.put(new Integer(0x00100032), "Patient's Birth Time"); dict.put(new Integer(0x00100040), "Patient's Sex"); dict.put(new Integer(0x00100050), "Patient's Insurance Plane Code"); dict.put(new Integer(0x00100101), "Patient's Primary Language Code"); dict.put(new Integer(0x00100102), "Patient's Primary Language Modifier"); dict.put(new Integer(0x00101000), "Other Patient IDs"); dict.put(new Integer(0x00101001), "Other Patient Names"); dict.put(new Integer(0x00101005), "Patient's Birth Name"); dict.put(new Integer(0x00101010), "Patient's Age"); dict.put(new Integer(0x00101020), "Patient's Size"); dict.put(new Integer(0x00101030), "Patient's Weight"); dict.put(new Integer(0x00101040), "Patient's Address"); dict.put(new Integer(0x00101060), "Patient's Mother's Birth Name"); dict.put(new Integer(0x00101080), "Military Rank"); dict.put(new Integer(0x00101081), "Branch of Service"); dict.put(new Integer(0x00101090), "Medical Record Locator"); dict.put(new Integer(0x00102000), "Medical Alerts"); dict.put(new Integer(0x00102110), "Contrast Allergies"); dict.put(new Integer(0x00102150), "Country of Residence"); dict.put(new Integer(0x00102152), "Region of Residence"); dict.put(new Integer(0x00102154), "Patient's Telephone Numbers"); dict.put(new Integer(0x00102160), "Ethnic Group"); dict.put(new Integer(0x00102180), "Occupation"); dict.put(new Integer(0x001021a0), "Smoking Status"); dict.put(new Integer(0x001021b0), "Additional Patient History"); dict.put(new Integer(0x001021c0), "Pregnancy Status"); dict.put(new Integer(0x001021d0), "Last Menstrual Date"); dict.put(new Integer(0x001021f0), "Patient's Religious Preference"); dict.put(new Integer(0x00104000), "Patient Comments"); dict.put(new Integer(0x00120010), "Clinical Trial Sponsor Name"); dict.put(new Integer(0x00120020), "Clinical Trial Protocol ID"); dict.put(new Integer(0x00120021), "Clinical Trial Protocol Name"); dict.put(new Integer(0x00120030), "Clinical Trial Site ID"); dict.put(new Integer(0x00120031), "Clinical Trial Site Name"); dict.put(new Integer(0x00120040), "Clinical Trial Subject ID"); dict.put(new Integer(0x00120042), "Clinical Trial Subject Reading ID"); dict.put(new Integer(0x00120050), "Clinical Trial Time Point ID"); dict.put(new Integer(0x00120051), "Clinical Trial Time Point Description"); dict.put(new Integer(0x00120060), "Clinical Trial Coordinating Center"); dict.put(new Integer(0x00180010), "Contrast/Bolus Agent"); dict.put(new Integer(0x00180012), "Contrast/Bolus Agent Sequence"); dict.put(new Integer(0x00180014), "Contrast/Bolus Admin. Route Sequence"); dict.put(new Integer(0x00180015), "Body Part Examined"); dict.put(new Integer(0x00180020), "Scanning Sequence"); dict.put(new Integer(0x00180021), "Sequence Variant"); dict.put(new Integer(0x00180022), "Scan Options"); dict.put(new Integer(0x00180023), "MR Acquisition Type"); dict.put(new Integer(0x00180024), "Sequence Name"); dict.put(new Integer(0x00180025), "Angio Flag"); dict.put(new Integer(0x00180026), "Intervention Drug Information Sequence"); dict.put(new Integer(0x00180027), "Intervention Drug Stop Time"); dict.put(new Integer(0x00180028), "Intervention Drug Dose"); dict.put(new Integer(0x00180029), "Intervention Drug Sequence"); dict.put(new Integer(0x0018002a), "Additional Drug Sequence"); dict.put(new Integer(0x00180031), "Radiopharmaceutical"); dict.put(new Integer(0x00180034), "Intervention Drug Name"); dict.put(new Integer(0x00180035), "Intervention Drug Start Time"); dict.put(new Integer(0x00180036), "Intervention Sequence"); dict.put(new Integer(0x00180038), "Intervention Status"); dict.put(new Integer(0x0018003a), "Intervention Description"); dict.put(new Integer(0x00180040), "Cine Rate"); dict.put(new Integer(0x00180050), "Slice Thickness"); dict.put(new Integer(0x00180060), "KVP"); dict.put(new Integer(0x00180070), "Counts Accumulated"); dict.put(new Integer(0x00180071), "Acquisition Termination Condition"); dict.put(new Integer(0x00180072), "Effective Duration"); dict.put(new Integer(0x00180073), "Acquisition Start Condition"); dict.put(new Integer(0x00180074), "Acquisition Start Condition Data"); dict.put(new Integer(0x00180075), "Acquisition Termination Condition Data"); dict.put(new Integer(0x00180080), "Repetition Time"); dict.put(new Integer(0x00180081), "Echo Time"); dict.put(new Integer(0x00180082), "Inversion Time"); dict.put(new Integer(0x00180083), "Number of Averages"); dict.put(new Integer(0x00180084), "Imaging Frequency"); dict.put(new Integer(0x00180085), "Imaged Nucleus"); dict.put(new Integer(0x00180086), "Echo Number(s)"); dict.put(new Integer(0x00180087), "Magnetic Field Strength"); dict.put(new Integer(0x00180088), "Spacing Between Slices"); dict.put(new Integer(0x00180089), "Number of Phase Encoding Steps"); dict.put(new Integer(0x00180090), "Data Collection Diameter"); dict.put(new Integer(0x00180091), "Echo Train Length"); dict.put(new Integer(0x00180093), "Percent Sampling"); dict.put(new Integer(0x00180094), "Percent Phase Field of View"); dict.put(new Integer(0x00180095), "Pixel Bandwidth"); dict.put(new Integer(0x00181000), "Device Serial Number"); dict.put(new Integer(0x00181004), "Plate ID"); dict.put(new Integer(0x00181010), "Secondary Capture Device ID"); dict.put(new Integer(0x00181011), "Hardcopy Creation Device ID"); dict.put(new Integer(0x00181012), "Date of Secondary Capture"); dict.put(new Integer(0x00181014), "Time of Secondary Capture"); dict.put(new Integer(0x00181016), "Secondary Capture Device Manufacturer"); dict.put(new Integer(0x00181017), "Hardcopy Device Manufacturer"); dict.put(new Integer(0x00181018), "Secondary Capture Device Model Name"); dict.put(new Integer(0x00181019), "Secondary Capture Device Software Version"); dict.put(new Integer(0x0018101a), "Hardcopy Device Software Version"); dict.put(new Integer(0x0018101b), "Hardcopy Device Model Name"); dict.put(new Integer(0x00181020), "Software Version(s)"); dict.put(new Integer(0x00181022), "Video Image Format Acquired"); dict.put(new Integer(0x00181023), "Digital Image Format Acquired"); dict.put(new Integer(0x00181030), "Protocol Name"); dict.put(new Integer(0x00181040), "Contrast/Bolus Route"); dict.put(new Integer(0x00181041), "Contrast/Bolus Volume"); dict.put(new Integer(0x00181042), "Contrast/Bolus Start Time"); dict.put(new Integer(0x00181043), "Contrast/Bolus Stop Time"); dict.put(new Integer(0x00181044), "Contrast/Bolus Total Dose"); dict.put(new Integer(0x00181045), "Syringe Counts"); dict.put(new Integer(0x00181046), "Contrast Flow Rate"); dict.put(new Integer(0x00181047), "Contrast Flow Duration"); dict.put(new Integer(0x00181048), "Contrast/Bolus Ingredient"); dict.put(new Integer(0x00181049), "Contrast Ingredient Concentration"); dict.put(new Integer(0x00181050), "Spatial Resolution"); dict.put(new Integer(0x00181060), "Trigger Time"); dict.put(new Integer(0x00181061), "Trigger Source or Type"); dict.put(new Integer(0x00181062), "Nominal Interval"); dict.put(new Integer(0x00181063), "Frame Time"); dict.put(new Integer(0x00181064), "Framing Type"); dict.put(new Integer(0x00181065), "Frame Time Vector"); dict.put(new Integer(0x00181066), "Frame Delay"); dict.put(new Integer(0x00181067), "Image Trigger Delay"); dict.put(new Integer(0x00181068), "Multiplex Group Time Offset"); dict.put(new Integer(0x00181069), "Trigger Time Offset"); dict.put(new Integer(0x0018106a), "Synchronization Trigger"); dict.put(new Integer(0x0018106c), "Synchronization Channel"); dict.put(new Integer(0x0018106e), "Trigger Sample Position"); dict.put(new Integer(0x00181070), "Radiopharmaceutical Route"); dict.put(new Integer(0x00181071), "Radiopharmaceutical Volume"); dict.put(new Integer(0x00181072), "Radiopharmaceutical Start Time"); dict.put(new Integer(0x00181073), "Radiopharmaceutical Stop Time"); dict.put(new Integer(0x00181074), "Radionuclide Total Dose"); dict.put(new Integer(0x00181075), "Radionuclide Half Life"); dict.put(new Integer(0x00181076), "Radionuclide Positron Fraction"); dict.put(new Integer(0x00181077), "Radiopharmaceutical Specific Activity"); dict.put(new Integer(0x00181080), "Beat Rejection Flag"); dict.put(new Integer(0x00181081), "Low R-R Value"); dict.put(new Integer(0x00181082), "High R-R Value"); dict.put(new Integer(0x00181083), "Intervals Acquired"); dict.put(new Integer(0x00181084), "Intervals Rejected"); dict.put(new Integer(0x00181085), "PVC Rejection"); dict.put(new Integer(0x00181086), "Skip Beats"); dict.put(new Integer(0x00181088), "Heart Rate"); dict.put(new Integer(0x00181090), "Cardiac Number of Images"); dict.put(new Integer(0x00181094), "Trigger Window"); dict.put(new Integer(0x00181100), "Reconstruction Diameter"); dict.put(new Integer(0x00181110), "Distance Source to Detector"); dict.put(new Integer(0x00181111), "Distance Source to Patient"); dict.put(new Integer(0x00181114), "Estimated Radiographic Mag. Factor"); dict.put(new Integer(0x00181120), "Gantry/Detector Tilt"); dict.put(new Integer(0x00181121), "Gantry/Detector Skew"); dict.put(new Integer(0x00181130), "Table Height"); dict.put(new Integer(0x00181131), "Table Traverse"); dict.put(new Integer(0x00181134), "Table Motion"); dict.put(new Integer(0x00181135), "Table Vertical Increment"); dict.put(new Integer(0x00181136), "Table Lateral Increment"); dict.put(new Integer(0x00181137), "Table Longitudinal Increment"); dict.put(new Integer(0x00181138), "Table Angle"); dict.put(new Integer(0x0018113a), "Table Type"); dict.put(new Integer(0x00181140), "Rotation Direction"); dict.put(new Integer(0x00181141), "Angular Position"); dict.put(new Integer(0x00181142), "Radial Position"); dict.put(new Integer(0x00181143), "Scan Arc"); dict.put(new Integer(0x00181144), "Angular Step"); dict.put(new Integer(0x00181145), "Center of Rotation Offset"); dict.put(new Integer(0x00181147), "Field of View Shape"); dict.put(new Integer(0x00181149), "Field of View Dimension(s)"); dict.put(new Integer(0x00181150), "Exposure Time"); dict.put(new Integer(0x00181151), "X-ray Tube Current"); dict.put(new Integer(0x00181152), "Exposure"); dict.put(new Integer(0x00181153), "Exposure in uAs"); dict.put(new Integer(0x00181154), "Average Pulse Width"); dict.put(new Integer(0x00181155), "Radiation Setting"); dict.put(new Integer(0x00181156), "Rectification Type"); dict.put(new Integer(0x0018115a), "Radiation Mode"); dict.put(new Integer(0x0018115e), "Image Area Dose Product"); dict.put(new Integer(0x00181160), "Filter Type"); dict.put(new Integer(0x00181161), "Type of Filters"); dict.put(new Integer(0x00181162), "Intensifier Size"); dict.put(new Integer(0x00181164), "Imager Pixel Spacing"); dict.put(new Integer(0x00181166), "Grid"); dict.put(new Integer(0x00181170), "Generator Power"); dict.put(new Integer(0x00181180), "Collimator/Grid Name"); dict.put(new Integer(0x00181181), "Collimator Type"); dict.put(new Integer(0x00181182), "Focal Distance"); dict.put(new Integer(0x00181183), "X Focus Center"); dict.put(new Integer(0x00181184), "Y Focus Center"); dict.put(new Integer(0x00181190), "Focal Spot(s)"); dict.put(new Integer(0x00181191), "Anode Target Material"); dict.put(new Integer(0x001811a0), "Body Part Thickness"); dict.put(new Integer(0x001811a2), "Compression Force"); dict.put(new Integer(0x00181200), "Date of Last Calibration"); dict.put(new Integer(0x00181201), "Time of Last Calibration"); dict.put(new Integer(0x00181210), "Convolution Kernel"); dict.put(new Integer(0x00181242), "Actual Frame Duration"); dict.put(new Integer(0x00181243), "Count Rate"); dict.put(new Integer(0x00181244), "Preferred Playback Sequencing"); dict.put(new Integer(0x00181250), "Receive Coil Name"); dict.put(new Integer(0x00181251), "Transmit Coil Name"); dict.put(new Integer(0x00181260), "Plate Type"); dict.put(new Integer(0x00181261), "Phosphor Type"); dict.put(new Integer(0x00181300), "Scan Velocity"); dict.put(new Integer(0x00181301), "Whole Body Technique"); dict.put(new Integer(0x00181302), "Scan Length"); dict.put(new Integer(0x00181310), "Acquisition Matrix"); dict.put(new Integer(0x00181312), "In-plane Phase Encoding Direction"); dict.put(new Integer(0x00181314), "Flip Angle"); dict.put(new Integer(0x00181315), "Variable Flip Angle Flag"); dict.put(new Integer(0x00181316), "SAR"); dict.put(new Integer(0x00181318), "dB/dt"); dict.put(new Integer(0x00181400), "Acquisition Device Processing Descr."); dict.put(new Integer(0x00181401), "Acquisition Device Processing Code"); dict.put(new Integer(0x00181402), "Cassette Orientation"); dict.put(new Integer(0x00181403), "Cassette Size"); dict.put(new Integer(0x00181404), "Exposures on Plate"); dict.put(new Integer(0x00181405), "Relative X-ray Exposure"); dict.put(new Integer(0x00181450), "Column Angulation"); dict.put(new Integer(0x00181460), "Tomo Layer Height"); dict.put(new Integer(0x00181470), "Tomo Angle"); dict.put(new Integer(0x00181480), "Tomo Time"); dict.put(new Integer(0x00181490), "Tomo Type"); dict.put(new Integer(0x00181491), "Tomo Class"); dict.put(new Integer(0x00181495), "Number of Tomosynthesis Source Images"); dict.put(new Integer(0x00181500), "Positioner Motion"); dict.put(new Integer(0x00181508), "Positioner Type"); dict.put(new Integer(0x00181510), "Positioner Primary Angle"); dict.put(new Integer(0x00181511), "Positioner Secondary Angle"); dict.put(new Integer(0x00181520), "Positioner Primary Angle Increment"); dict.put(new Integer(0x00181521), "Positioner Secondary Angle Increment"); dict.put(new Integer(0x00181530), "Detector Primary Angle"); dict.put(new Integer(0x00181531), "Detector Secondary Angle"); dict.put(new Integer(0x00181600), "Shutter Shape"); dict.put(new Integer(0x00181602), "Shutter Left Vertical Edge"); dict.put(new Integer(0x00181604), "Shutter Right Vertical Edge"); dict.put(new Integer(0x00181606), "Shutter Upper Horizontal Edge"); dict.put(new Integer(0x00181608), "Shutter Lower Horizontal Edge"); dict.put(new Integer(0x00181610), "Center of Circular Shutter"); dict.put(new Integer(0x00181612), "Radius of Circular Shutter"); dict.put(new Integer(0x00181620), "Vertices of the Polygonal Shutter"); dict.put(new Integer(0x00181622), "Shutter Presentation Value"); dict.put(new Integer(0x00181623), "Shutter Overlay Group"); dict.put(new Integer(0x00181700), "Collimator Shape"); dict.put(new Integer(0x00181702), "Collimator Left Vertical Edge"); dict.put(new Integer(0x00181704), "Collimator Right Vertical Edge"); dict.put(new Integer(0x00181706), "Collimator Upper Horizontal Edge"); dict.put(new Integer(0x00181708), "Collimator Lower Horizontal Edge"); dict.put(new Integer(0x00181710), "Center of Circular Collimator"); dict.put(new Integer(0x00181712), "Radius of Circular Collimator"); dict.put(new Integer(0x00181720), "Vertices of the polygonal Collimator"); dict.put(new Integer(0x00181800), "Acquisition Time Synchronized"); dict.put(new Integer(0x00181801), "Time Source"); dict.put(new Integer(0x00181802), "Time Distribution Protocol"); dict.put(new Integer(0x00181803), "NTP Source Address"); dict.put(new Integer(0x00182001), "Page Number Vector"); dict.put(new Integer(0x00182002), "Frame Label Vector"); dict.put(new Integer(0x00182003), "Frame Primary Angle Vector"); dict.put(new Integer(0x00182004), "Frame Secondary Angle Vector"); dict.put(new Integer(0x00182005), "Slice Location Vector"); dict.put(new Integer(0x00182006), "Display Window Label Vector"); dict.put(new Integer(0x00182010), "Nominal Scanned Pixel Spacing"); dict.put(new Integer(0x00182020), "Digitizing Device Transport Direction"); dict.put(new Integer(0x00182030), "Rotation of Scanned Film"); dict.put(new Integer(0x00183100), "IVUS Acquisition"); dict.put(new Integer(0x00183101), "IVUS Pullback Rate"); dict.put(new Integer(0x00183102), "IVUS Gated Rate"); dict.put(new Integer(0x00183103), "IVUS Pullback Start Frame Number"); dict.put(new Integer(0x00183104), "IVUS Pullback Stop Frame Number"); dict.put(new Integer(0x00183105), "Lesion Number"); dict.put(new Integer(0x00185000), "Output Power"); dict.put(new Integer(0x00185010), "Transducer Data"); dict.put(new Integer(0x00185012), "Focus Depth"); dict.put(new Integer(0x00185020), "Processing Function"); dict.put(new Integer(0x00185021), "Postprocessing Fuction"); dict.put(new Integer(0x00185022), "Mechanical Index"); dict.put(new Integer(0x00185024), "Bone Thermal Index"); dict.put(new Integer(0x00185026), "Cranial Thermal Index"); dict.put(new Integer(0x00185027), "Soft Tissue Thermal Index"); dict.put(new Integer(0x00185028), "Soft Tissue-focus Thermal Index"); dict.put(new Integer(0x00185029), "Soft Tissue-surface Thermal Index"); dict.put(new Integer(0x00185050), "Depth of scan field"); dict.put(new Integer(0x00185100), "Patient Position"); dict.put(new Integer(0x00185101), "View Position"); dict.put(new Integer(0x00185104), "Projection Eponymous Name Code"); dict.put(new Integer(0x00186000), "Sensitivity"); dict.put(new Integer(0x00186011), "Sequence of Ultrasound Regions"); dict.put(new Integer(0x00186012), "Region Spatial Format"); dict.put(new Integer(0x00186014), "Region Data Type"); dict.put(new Integer(0x00186016), "Region Flags"); dict.put(new Integer(0x00186018), "Region Location Min X0"); dict.put(new Integer(0x0018601a), "Region Location Min Y0"); dict.put(new Integer(0x0018601c), "Region Location Max X1"); dict.put(new Integer(0x0018601e), "Region Location Max Y1"); dict.put(new Integer(0x00186020), "Reference Pixel X0"); dict.put(new Integer(0x00186022), "Reference Pixel Y0"); dict.put(new Integer(0x00186024), "Physical Units X Direction"); dict.put(new Integer(0x00186026), "Physical Units Y Direction"); dict.put(new Integer(0x00186028), "Reference Pixel Physical Value X"); dict.put(new Integer(0x0018602a), "Reference Pixel Physical Value Y"); dict.put(new Integer(0x0018602c), "Physical Delta X"); dict.put(new Integer(0x0018602e), "Physical Delta Y"); dict.put(new Integer(0x00186030), "Transducer Frequency"); dict.put(new Integer(0x00186031), "Transducer Type"); dict.put(new Integer(0x00186032), "Pulse Repetition Frequency"); dict.put(new Integer(0x00186034), "Doppler Correction Angle"); dict.put(new Integer(0x00186036), "Steering Angle"); dict.put(new Integer(0x00186039), "Doppler Sample Volume X Position"); dict.put(new Integer(0x0018603b), "Doppler Sample Volume Y Position"); dict.put(new Integer(0x0018603d), "TM-Line Position X0"); dict.put(new Integer(0x0018603f), "TM-Line Position Y0"); dict.put(new Integer(0x00186041), "TM-Line Position X1"); dict.put(new Integer(0x00186043), "TM-Line Position Y1"); dict.put(new Integer(0x00186044), "Pixel Component Organization"); dict.put(new Integer(0x00186046), "Pixel Component Mask"); dict.put(new Integer(0x00186048), "Pixel Component Range Start"); dict.put(new Integer(0x0018604a), "Pixel Component Range Stop"); dict.put(new Integer(0x0018604c), "Pixel Component Physical Units"); dict.put(new Integer(0x0018604e), "Pixel Component Data Type"); dict.put(new Integer(0x00186050), "Number of Table Break Points"); dict.put(new Integer(0x00186052), "Table of X Break Points"); dict.put(new Integer(0x00186054), "Table of Y Break Points"); dict.put(new Integer(0x00186056), "Number of Table Entries"); dict.put(new Integer(0x00186058), "Table of Pixel Values"); dict.put(new Integer(0x0018605a), "Table of Parameter Values"); dict.put(new Integer(0x00186060), "R Wave Time Vector"); dict.put(new Integer(0x00187000), "Detector Conditions Nominal Flag"); dict.put(new Integer(0x00187001), "Detector Temperature"); dict.put(new Integer(0x00187004), "Detector Type"); dict.put(new Integer(0x00187005), "Detector Configuration"); dict.put(new Integer(0x00187006), "Detector Description"); dict.put(new Integer(0x00187008), "Detector Mode"); dict.put(new Integer(0x0018700a), "Detector ID"); dict.put(new Integer(0x0018700c), "Date of Last Detector Calibration"); dict.put(new Integer(0x0018700e), "Time of Last Detector Calibration"); dict.put(new Integer(0x00187012), "Detector Time Since Last Exposure"); dict.put(new Integer(0x00187014), "Detector Active Time"); dict.put(new Integer(0x00187016), "Detector Activation Offset"); dict.put(new Integer(0x0018701a), "Detector Binning"); dict.put(new Integer(0x00187020), "Detector Element Physical Size"); dict.put(new Integer(0x00187022), "Detector Element Spacing"); dict.put(new Integer(0x00187024), "Detector Active Shape"); dict.put(new Integer(0x00187026), "Detector Active Dimension(s)"); dict.put(new Integer(0x00187028), "Detector Active Origin"); dict.put(new Integer(0x0018702a), "Detector Manufacturer Name"); dict.put(new Integer(0x0018702b), "Detector Model Name"); dict.put(new Integer(0x00187030), "Field of View Origin"); dict.put(new Integer(0x00187032), "Field of View Rotation"); dict.put(new Integer(0x00187034), "Field of View Horizontal Flip"); dict.put(new Integer(0x00187040), "Grid Absorbing Material"); dict.put(new Integer(0x00187041), "Grid Spacing Material"); dict.put(new Integer(0x00187042), "Grid Thickness"); dict.put(new Integer(0x00187044), "Grid Pitch"); dict.put(new Integer(0x00187046), "Grid Aspect Ratio"); dict.put(new Integer(0x00187048), "Grid Period"); dict.put(new Integer(0x0018704c), "Grid Focal Distance"); dict.put(new Integer(0x00187050), "Filter Material"); dict.put(new Integer(0x00187052), "Filter Thickness Min"); dict.put(new Integer(0x00187054), "Filter Thickness Max"); dict.put(new Integer(0x00187060), "Exposure Control Mode"); dict.put(new Integer(0x0020000d), "Study Instance UID"); dict.put(new Integer(0x0020000e), "Series Instance UID"); dict.put(new Integer(0x00200011), "Series Number"); dict.put(new Integer(0x00200012), "Acquisition Number"); dict.put(new Integer(0x00200013), "Instance Number"); dict.put(new Integer(0x00200020), "Patient Orientation"); dict.put(new Integer(0x00200030), "Image Position"); dict.put(new Integer(0x00200032), "Image Position (Patient)"); dict.put(new Integer(0x00200037), "Image Orientation (Patient)"); dict.put(new Integer(0x00200050), "Location"); dict.put(new Integer(0x00200052), "Frame of Reference UID"); dict.put(new Integer(0x00200070), "Image Geometry Type"); dict.put(new Integer(0x00201001), "Acquisitions in Series"); dict.put(new Integer(0x00201020), "Reference"); dict.put(new Integer(0x00201041), "Slice Location"); // skipped a bunch of stuff here - not used dict.put(new Integer(0x00280002), "Samples per pixel"); dict.put(new Integer(0x00280003), "Samples per pixel used"); dict.put(new Integer(0x00280004), "Photometric Interpretation"); dict.put(new Integer(0x00280006), "Planar Configuration"); dict.put(new Integer(0x00280008), "Number of frames"); dict.put(new Integer(0x00280009), "Frame Increment Pointer"); dict.put(new Integer(0x0028000a), "Frame Dimension Pointer"); dict.put(new Integer(0x00280010), "Rows"); dict.put(new Integer(0x00280011), "Columns"); dict.put(new Integer(0x00280012), "Planes"); dict.put(new Integer(0x00280014), "Ultrasound Color Data Present"); dict.put(new Integer(0x00280030), "Pixel Spacing"); dict.put(new Integer(0x00280031), "Zoom Factor"); dict.put(new Integer(0x00280032), "Zoom Center"); dict.put(new Integer(0x00280034), "Pixel Aspect Ratio"); dict.put(new Integer(0x00280051), "Corrected Image"); dict.put(new Integer(0x00280100), "Bits Allocated"); dict.put(new Integer(0x00280101), "Bits Stored"); dict.put(new Integer(0x00280102), "High Bit"); dict.put(new Integer(0x00280103), "Pixel Representation"); dict.put(new Integer(0x00280106), "Smallest Image Pixel Value"); dict.put(new Integer(0x00280107), "Largest Image Pixel Value"); dict.put(new Integer(0x00280108), "Smallest Pixel Value in Series"); dict.put(new Integer(0x00280109), "Largest Pixel Value in Series"); dict.put(new Integer(0x00280110), "Smallest Image Pixel Value in Plane"); dict.put(new Integer(0x00280111), "Largest Image Pixel Value in Plane"); dict.put(new Integer(0x00280120), "Pixel Padding Value"); dict.put(new Integer(0x00280300), "Quality Control Image"); dict.put(new Integer(0x00280301), "Burned in Annotation"); dict.put(new Integer(0x00281040), "Pixel Intensity Relationship"); dict.put(new Integer(0x00281041), "Pixel Intensity Relationship Sign"); dict.put(new Integer(0x00281050), "Window Center"); dict.put(new Integer(0x00281051), "Window Width"); dict.put(new Integer(0x00281052), "Rescale Intercept"); dict.put(new Integer(0x00281053), "Rescale Slope"); dict.put(new Integer(0x00281054), "Rescale Type"); dict.put(new Integer(0x00281055), "Window Center and Width Explanation"); dict.put(new Integer(0x00281090), "Recommended Viewing Mode"); dict.put(new Integer(0x00281101), "Red Palette Color LUT Descriptor"); dict.put(new Integer(0x00281102), "Green Palette Color LUT Descriptor"); dict.put(new Integer(0x00281103), "Blue Palette Color LUT Descriptor"); dict.put(new Integer(0x00281199), "Palette Color LUT UID"); dict.put(new Integer(0x00281201), "Red Palette Color LUT Data"); dict.put(new Integer(0x00281202), "Green Palette Color LUT Data"); dict.put(new Integer(0x00281203), "Blue Palette Color LUT Data"); dict.put(new Integer(0x00281221), "Segmented Red Palette Color LUT Data"); dict.put(new Integer(0x00281222), "Segmented Green Palette Color LUT Data"); dict.put(new Integer(0x00281223), "Segmented Blue Palette Color LUT Data"); dict.put(new Integer(0x00281300), "Implant Present"); dict.put(new Integer(0x00281350), "Partial View"); dict.put(new Integer(0x00281351), "Partial View Description"); dict.put(new Integer(0x00282110), "Lossy Image Compression"); dict.put(new Integer(0x00282112), "Lossy Image Compression Ratio"); dict.put(new Integer(0x00282114), "Lossy Image Compression Method"); dict.put(new Integer(0x00283000), "Modality LUT Sequence"); dict.put(new Integer(0x00283002), "LUT Descriptor"); dict.put(new Integer(0x00283003), "LUT Explanation"); dict.put(new Integer(0x00283004), "Modality LUT Type"); dict.put(new Integer(0x00283006), "LUT Data"); dict.put(new Integer(0x00283010), "VOI LUT Sequence"); dict.put(new Integer(0x00283110), "Softcopy VOI LUT Sequence"); dict.put(new Integer(0x00285000), "Bi-Plane Acquisition Sequence"); dict.put(new Integer(0x00286010), "Representative Frame Number"); dict.put(new Integer(0x00286020), "Frame Numbers of Interest (FOI)"); dict.put(new Integer(0x00286022), "Frame(s) of Interest Description"); dict.put(new Integer(0x00286023), "Frame of Interest Type"); dict.put(new Integer(0x00286040), "R Wave Pointer"); dict.put(new Integer(0x00286100), "Mask Subtraction Sequence"); dict.put(new Integer(0x00286101), "Mask Operation"); dict.put(new Integer(0x00286102), "Applicable Frame Range"); dict.put(new Integer(0x00286110), "Mask Frame Numbers"); dict.put(new Integer(0x00286112), "Contrast Frame Averaging"); dict.put(new Integer(0x00286114), "Mask Sub-pixel Shift"); dict.put(new Integer(0x00286120), "TID Offset"); dict.put(new Integer(0x00286190), "Mask Operation Explanation"); dict.put(new Integer(0x00289001), "Data Point Rows"); dict.put(new Integer(0x00289002), "Data Point Columns"); dict.put(new Integer(0x00289003), "Signal Domain Columns"); dict.put(new Integer(0x00289108), "Data Representation"); dict.put(new Integer(0x00289110), "Pixel Measures Sequence"); dict.put(new Integer(0x00289132), "Frame VOI LUT Sequence"); dict.put(new Integer(0x00289145), "Pixel Value Transformation Sequence"); dict.put(new Integer(0x00289235), "Signal Domain Rows"); // skipping some more stuff dict.put(new Integer(0x00540011), "Number of Energy Windows"); dict.put(new Integer(0x00540021), "Number of Detectors"); dict.put(new Integer(0x00540051), "Number of Rotations"); dict.put(new Integer(0x00540080), "Slice Vector"); dict.put(new Integer(0x00540081), "Number of Slices"); dict.put(new Integer(0x00540202), "Type of Detector Motion"); dict.put(new Integer(0x00540400), "Image ID"); dict.put(new Integer(0x20100100), "Border Density"); return dict; } }