import ij.*; import ij.io.FileInfo; import ij.io.ImageReader; import ij.io.OpenDialog; import ij.io.RandomAccessStream; import ij.io.TiffDecoder; import ij.process.*; import java.awt.image.ColorModel; import java.awt.image.IndexColorModel; import java.io.BufferedInputStream; 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.ArrayList; import java.util.Properties; import java.util.zip.GZIPInputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import ij.plugin.*; import ij.util.Tools; public class ZIP_Reader_Standalone extends ImagePlus implements PlugIn { public void run(String arg) { boolean display = false; File test = new File(arg); display = !test.exists(); OpenDialog od = new OpenDialog("Open zip file...", arg); if(od!=null){ String file = od.getFileName(); if (file == null) return; String directory = od.getDirectory(); directory = directory.replace('\\', '/'); // Windows safe if (!directory.endsWith("/")) directory += "/"; arg = directory + file; } try { ZipProjectionSource fileSource = new ZipProjectionSource(); fileSource.initStream(arg); ImagePlusDataSink sink = new ImagePlusDataSink(); ImageProcessor imp = fileSource.getNextProjection(); int i =0; while (imp != null){ sink.process(imp, i); i++; imp = fileSource.getNextProjection(); } sink.close(); setStack(sink.getResult().getStack()); setTitle(new File(arg).getName()); fileSource.close(); if(display) show(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } private class ZipProjectionSource extends FileProjectionSource { boolean DICOMMultiFrameMode = false; ZipInputStream zis = null; @SuppressWarnings("unused") public void initStream (String filename) throws IOException{ zis = new ZipInputStream(new FileInputStream(filename)); is = zis; if (zis==null) { throw new IOException("Could not read zip header"); } ZipEntry entry = zis.getNextEntry(); if (entry==null) throw new IOException("Zip was empty"); String name = entry.getName(); if (name.endsWith(".tif")) { TiffDecoder td = new TiffDecoder(zis, name); if (IJ.debugMode) td.enableDebugging(); FileInfo [] info = td.getTiffInfo(); fi = info[0]; } else if (name.endsWith(".ima")) { DicomProjectionSource dicomSource = new DicomProjectionSource(); dicomSource.initStream(zis); fi = dicomSource.fi; } else if (name.endsWith(".dcm")) { DICOMMultiFrameMode = true; DicomProjectionSource dicomSource = new DicomProjectionSource(); dicomSource.initStream(zis); fi = dicomSource.fi; } else { zis.close(); throw new IOException("No matching fileformat found in zip file"); } File f = new File(filename); fi.fileFormat = FileInfo.ZIP_ARCHIVE; fi.fileName = f.getName(); fi.directory = f.getParent()+File.separator; if (fi!=null && fi.width>0 && fi.height>0 && fi.offset>0) { init(); } else { throw new IOException("Format does not match"); } } @Override public ImageProcessor getNextProjection(){ if (!DICOMMultiFrameMode){ return super.getNextProjection(); } else { try { ZipEntry entry = zis.getNextEntry(); if (entry == null){ return null; } DicomProjectionSource dicomSource = new DicomProjectionSource(); dicomSource.initStream(zis); fi = dicomSource.fi; ImageProcessor ip = dicomSource.getNextProjection(); currentIndex++; if (showProgress){ IJ.showProgress((0.0 + currentIndex) / fi.nImages); } return ip; } catch (IOException e1) { e1.printStackTrace(); throw new RuntimeException("Error while reading projection " + this.currentIndex); } } } } public class DicomProjectionSource extends FileProjectionSource { public void initStream (String filename) throws IOException{ File file = new File(filename); String fileName = file.getName(); if (fileName==null) return; BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(file)); initStream(inputStream); } public void initStream(InputStream inputStream) throws IOException{ DicomDecoder dd = new DicomDecoder(inputStream); fi = dd.getFileInfo(); if (fi!=null && fi.width>0 && fi.height>0 && fi.offset>0) { init(); } else { throw new IOException("Format does not match"); } } } public class ImagePlusDataSink { /** * */ private static final long serialVersionUID = -931042578800415977L; private ImageProcessorBuffer imageBuffer; private ImageStack stack = null; private boolean closed = false; private boolean init = false; private boolean debug = false; ImagePlus projectionVolume; public String getName() { return "Image Plus Projection Data Sink"; } public ImagePlus getResult() { //System.out.println("get result called: " + closed); while (!closed){ try { Thread.sleep(1); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } //System.out.println("Image returned." + closed); projectionVolume = new ImagePlus(); if (stack != null) projectionVolume.setStack("Projection Data Sink", stack); adjustViewRange(); return projectionVolume; } public void adjustViewRange(){ if (projectionVolume != null){ if (projectionVolume.getChannelProcessor() != null){ double [] stats = minAndMaxOfImageProcessor(projectionVolume.getChannelProcessor()); if (stats[0] != stats[1]) { projectionVolume.setDisplayRange(stats[0], stats[1]); projectionVolume.updateAndDraw(); } } } } public double [] minAndMaxOfImageProcessor(ImageProcessor imp){ double [] revan = new double [2]; revan[0] = Double.MAX_VALUE; revan[1] = -Double.MAX_VALUE; for (int i = 0; i < imp.getWidth(); i++){ for (int j = 0; j < imp.getHeight(); j++){ if (imp.getPixelValue(i, j) < revan[0]) { revan[0] = imp.getPixelValue(i, j); } if (imp.getPixelValue(i, j) > revan[1]) { revan[1] = imp.getPixelValue(i, j); } } } return revan; } private synchronized void updateStack(ImageProcessor image, int projectionNumber){ if (stack == null) { int stackSize = imageBuffer.size(); stack = new ImageStack(image.getWidth(), image.getHeight(), stackSize); } stack.setPixels(image.getPixels(), projectionNumber + 1); } public void process(ImageProcessor projection, int projectionNumber) throws Exception { if ((!init) || (imageBuffer == null)){ if (debug) System.out.println("ImagePlusDataSink: got projection " + projectionNumber); imageBuffer = new ImageProcessorBuffer(); init = true; closed = false; } if (debug) System.out.println("ImagePlusDataSink: got projection " + projectionNumber + " buffersize " + imageBuffer.size()); imageBuffer.add(projection, projectionNumber); } @Override public String toString() { return getName(); } public void prepareForSerialization(){ stack = null; init = false; closed = false; imageBuffer = null; } public void close() throws Exception { if (!closed) { if (debug) System.out.println("ImagePlusDataSink: Closing Stream at " + imageBuffer.size()); ImageProcessor [] process = imageBuffer.toArray(); int newWidth = process[0].getWidth(); int newHeight = process[0].getHeight(); for (int i=1; i < process.length; i++){ int check = process[i].getWidth(); newWidth = (check > newWidth) ? check: newWidth; check = process[i].getHeight(); newHeight = (check > newHeight)? check: newHeight; } // make sure the thread pool is done for (int i = 0; i < process.length; i++){ ImageProcessor ip = process[i]; if (process[i] == null) { System.out.println("ImagePlusDataSink: Image " + i + " was null"); } if ((ip.getHeight() != newHeight)|| (ip.getWidth() != newWidth)) { FloatProcessor newIP = new FloatProcessor(newWidth, newHeight); for(int k = 0; k < ip.getHeight();k++){ for (int l= 0; l < ip.getWidth();l++){ newIP.putPixelValue(l, k, ip.getPixelValue(l, k)); } } ip = newIP; } updateStack(ip, i); } closed = true; } } } public abstract class FileProjectionSource { protected FileInfo fi = null; protected long skip; protected int currentIndex = -1; protected boolean showProgress; protected ImageReader reader; protected InputStream is; protected ColorModel cm; public FileProjectionSource [] getProjectionSources(){ FileProjectionSource [] sources = {new ZipProjectionSource(), new DicomProjectionSource()}; return sources; } public FileProjectionSource openProjectionStream(String filename) throws IOException { FileProjectionSource [] sources = getProjectionSources(); FileProjectionSource 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. System.out.println(sources[i] +" failed because " +e.getLocalizedMessage()); //e.printStackTrace(); } } if (source == null) { throw new IOException("No matching file format found."); } return source; } public abstract void initStream(String filename) throws IOException; /* public synchronized void getNextProjection(IndividualImagePipelineFilteringTool tool){ ImageProcessor proc = getNextProjection(); if (proc != null){ tool.setImageProcessor(proc); 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 ImageProcessor getNextProjection() { ImageProcessor ip; 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[]) ip = new FloatProcessor(fi.width, fi.height, (float[]) pixels, cm); else throw new IllegalArgumentException("Unknown stack type"); skip = fi.gapBetweenImages; currentIndex++; if (showProgress){ IJ.showProgress((0.0 + currentIndex) / fi.nImages); } return ip; } 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) { IJ.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 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 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; } } public class ImageProcessorBuffer { ArrayList<ImageProcessor> images; ArrayList<Integer> indices; private boolean debug = false; /** * creates a new ImageProcessorBuffer object. */ public ImageProcessorBuffer (){ images = new ArrayList<ImageProcessor>(); indices = new ArrayList<Integer>(); } /** * adds the Image at index i; Previous entry will be overwritten. * @param image * @param index */ public synchronized void add(ImageProcessor image, int index){ Integer key = new Integer(index); if (!indices.contains(key)){ // Insert if (debug) System.out.println("ImageProcessorBuffer: Duplicate Index replacing " + key); images.add(image); indices.add(key); } else { // Overwrite for (int i = 0; i < indices.size(); i++){ if (indices.get(i).equals(key)){ if (debug) System.out.println("ImageProcessorBuffer: Duplicate Index replacing " + key); images.set(i, image); break; } } } } /** * Returns the internal index of the given external index. * It is used to access the arrayLists. * * @param externalIndex the external index position * @return the position in the internal ArrayList. */ private synchronized int internalIndex(int externalIndex){ int internalIndex = -1; if (debug) System.out.println("ImageProcessorBuffer: Requested Index " + externalIndex + "; current size = " +indices.size()); for (int i = 0; i < indices.size(); i++){ if (indices.get(i) != null) { if (indices.get(i).intValue() == externalIndex){ internalIndex = i; if (debug) System.out.println("ImageProcessorBuffer: Index delivered " + externalIndex + "; current size = " +indices.size()); break; } } else { System.out.println("ImageProcessorBuffer internal error."); System.exit(-1); } } return internalIndex; } /** * Returns the ImageProcessor at index index. * Returns null if the index is not found. * @param index the index * @return the ImageProcessor */ public synchronized ImageProcessor get(int index){ ImageProcessor revan = null; int pos = internalIndex(index); if (pos != -1) revan = images.get(pos); return revan; } /** * Removes the image at the given index. * @param index the index. */ public synchronized void remove (int index){ int pos = internalIndex(index); images.set(pos, null); } /** * Returns a sorted array of the buffered ImageProcessors * @return the array. */ public synchronized ImageProcessor [] toArray(){ if (debug) System.out.println("ImageProcessorBuffer: Creating array with length " + images.size()); ImageProcessor[] array = new ImageProcessor[images.size()]; for (int i = 0; i< array.length; i++){ int index = indices.get(i).intValue(); array[index] = images.get(i); } return array; } /** * Returns a sorted ImagePlus of the buffered ImageProcessors * @return the ImagePlus. */ /* public synchronized ImagePlus toImagePlus(String title){ if (debug) System.out.println("ImageProcessorBuffer: Creating ImagePlus with length " + images.size()); ImagePlus image = new ImagePlus(); ImageStack stack = new ImageStack(images.get(0).getWidth(), images.get(0).getHeight(), images.size()); stack.setColorModel(ImageUtil.getDefaultColorModel()); for (int i = 0; i< images.size(); i++){ int index = indices.get(i).intValue(); stack.setPixels(images.get(i).getPixels(), index + 1); } image.setStack(title, stack); return image; } */ /** * returns the number of stored ImageProcessors * @return the number */ public synchronized int size(){ return images.size(); } } public class DicomDecoder { private static final int PIXEL_REPRESENTATION = 0x00280103; private static final int TRANSFER_SYNTAX_UID = 0x00020010; private static final int SLICE_THICKNESS = 0x00180050; 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 RED_PALETTE = 0x00281201; private static final int GREEN_PALETTE = 0x00281202; private static final int BLUE_PALETTE = 0x00281203; 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, DA=0x4441, DS=0x4453, DT=0x4454, FD=0x4644, FL=0x464C, IS=0x4953, LO=0x4C4F, LT=0x4C54, PN=0x504E, SH=0x5348, SL=0x534C, SS=0x5353, ST=0x5354, TM=0x544D, UI=0x5549, UL=0x554C, US=0x5553, UT=0x5554, OB=0x4F42, OW=0x4F57, SQ=0x5351, UN=0x554E, QQ=0x3F3F; private Properties dictionary; private String directory, fileName; private static final int ID_OFFSET = 128; //location of "DICM" private static final String DICM = "DICM"; private BufferedInputStream f; private int location = 0; private boolean littleEndian = true; private int elementLength; private int vr; // Value Representation private static final int IMPLICIT_VR = 0x2D2D; // '--' private byte[] vrLetters = new byte[2]; private double[] voltage; private double[] current; private double[] time; private double dCU; private double dose; private int previousGroup; public int getPreviousGroup() { return previousGroup; } public String getPreviousInfo() { return previousInfo; } private String previousInfo; private StringBuffer dicomInfo = new StringBuffer(1000); private boolean dicmFound; // "DICM" found at offset 128 private boolean oddLocations; // one or more tags at odd locations private boolean bigEndianTransferSyntax = false; double windowCenter, windowWidth; double rescaleIntercept, rescaleSlope; boolean inSequence; private BufferedInputStream inputStream; private boolean debug = false; public DicomDecoder(String directory, String fileName) { this.directory = directory; this.fileName = fileName; String path = null; if (dictionary==null && IJ.getApplet()==null) { path = Prefs.getHomeDir()+File.separator+"DICOM_Dictionary.txt"; File f = new File(path); if (f.exists()) try { dictionary = new Properties(); InputStream is = new BufferedInputStream(new FileInputStream(f)); dictionary.load(is); is.close(); if (debug) IJ.log("DicomDecoder: using "+dictionary.size()+" tag dictionary at "+path); } catch (Exception e) { dictionary = null; } } if (dictionary==null) { DicomDictionary d = new DicomDictionary(); dictionary = d.getDictionary(); if (debug) IJ.log("DicomDecoder: "+path+" not found; using "+dictionary.size()+" tag built in dictionary"); } } public DicomDecoder(InputStream inputStream) { String path = null; if (dictionary==null && IJ.getApplet()==null) { path = Prefs.getHomeDir()+File.separator+"DICOM_Dictionary.txt"; File f = new File(path); if (f.exists()) try { dictionary = new Properties(); InputStream is = new BufferedInputStream(new FileInputStream(f)); dictionary.load(is); is.close(); if (debug) IJ.log("DicomDecoder: using "+dictionary.size()+" tag dictionary at "+path); } catch (Exception e) { dictionary = null; } } this.inputStream = new BufferedInputStream(inputStream); if (dictionary==null) { DicomDictionary d = new DicomDictionary(); dictionary = d.getDictionary(); if (debug) IJ.log("DicomDecoder: "+path+" not found; using "+dictionary.size()+" tag built in dictionary"); } } String getString(int length) throws IOException { byte[] buf = new byte[length]; int pos = 0; while (pos<length) { int count = f.read(buf, pos, length-pos); if (count == -1) throw new IOException("End of Stream reached"); pos += count; } location += length; return new String(buf); } int getByte() throws IOException { int b = f.read(); if (b ==-1) throw new IOException("unexpected EOF"); ++location; //System.out.println(b); return b; } int getShort() throws IOException { int b0 = getByte(); int b1 = getByte(); if (littleEndian) return ((b1 << 8) + b0); else return ((b0 << 8) + b1); } final int getInt() throws IOException { int b0 = getByte(); int b1 = getByte(); int b2 = getByte(); int b3 = getByte(); if (littleEndian) return ((b3<<24) + (b2<<16) + (b1<<8) + b0); else return ((b0<<24) + (b1<<16) + (b2<<8) + b3); } double getDouble() throws IOException { int b0 = getByte(); int b1 = getByte(); int b2 = getByte(); int b3 = getByte(); int b4 = getByte(); int b5 = getByte(); int b6 = getByte(); int b7 = getByte(); long res = 0; if (littleEndian) { res += b0; res += ( ((long)b1) << 8); res += ( ((long)b2) << 16); res += ( ((long)b3) << 24); res += ( ((long)b4) << 32); res += ( ((long)b5) << 40); res += ( ((long)b6) << 48); res += ( ((long)b7) << 56); } else { res += b7; res += ( ((long)b6) << 8); res += ( ((long)b5) << 16); res += ( ((long)b4) << 24); res += ( ((long)b3) << 32); res += ( ((long)b2) << 40); res += ( ((long)b1) << 48); res += ( ((long)b0) << 56); } return Double.longBitsToDouble(res); } float getFloat() throws IOException { int b0 = getByte(); int b1 = getByte(); int b2 = getByte(); int b3 = getByte(); int res = 0; if (littleEndian) { res += b0; res += ( ((long)b1) << 8); res += ( ((long)b2) << 16); res += ( ((long)b3) << 24); } else { res += b3; res += ( ((long)b2) << 8); res += ( ((long)b1) << 16); res += ( ((long)b0) << 24); } return Float.intBitsToFloat(res); } byte[] getLut(int length) throws IOException { if ((length&1)!=0) { // odd getString(length); return null; } length /= 2; byte[] lut = new byte[length]; for (int i=0; i<length; i++) lut[i] = (byte)(getShort()>>>8); return lut; } int getLength() throws IOException { int b0 = getByte(); int b1 = getByte(); int b2 = getByte(); int b3 = getByte(); // 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 = (b0<<8) + b1; switch (vr) { case OB: case OW: case SQ: case UN: case UT: // Explicit VR with 32-bit length if other two bytes are zero if ( (b2 == 0) || (b3 == 0) ) return getInt(); // Implicit VR with 32-bit length vr = IMPLICIT_VR; if (littleEndian) return ((b3<<24) + (b2<<16) + (b1<<8) + b0); else return ((b0<<24) + (b1<<16) + (b2<<8) + b3); 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 QQ: // Explicit vr with 16-bit length if (littleEndian) return ((b3<<8) + b2); else return ((b2<<8) + b3); default: // Implicit VR with 32-bit length... vr = IMPLICIT_VR; if (littleEndian) return ((b3<<24) + (b2<<16) + (b1<<8) + b0); else return ((b0<<24) + (b1<<16) + (b2<<8) + b3); } } int getNextTag() throws IOException { int groupWord = getShort(); if (groupWord==0x0800 && bigEndianTransferSyntax) { littleEndian = false; groupWord = 0x0008; } int elementWord = getShort(); int tag = groupWord<<16 | elementWord; elementLength = getLength(); // hack needed to read some GE files // The element length must be even! if (elementLength==13 && !oddLocations) elementLength = 10; // "Undefined" element length. // This is a sort of bracket that encloses a sequence of elements. if (elementLength==-1) { elementLength = 0; inSequence = true; } //IJ.log("getNextTag: "+tag+" "+elementLength); return tag; } public FileInfo getFileInfo() throws IOException { long skipCount; FileInfo fi = new FileInfo(); int bitsAllocated = 16; fi.fileFormat = FileInfo.RAW; fi.fileName = fileName; if (directory != null) { if (directory.indexOf("://")>0) { // is URL URL u = new URL(directory+fileName); setInputStream(new BufferedInputStream(u.openStream())); fi.inputStream = getInputStream(); } else if (getInputStream()!=null) fi.inputStream = getInputStream(); else fi.directory = directory; } else if (getInputStream()!=null) fi.inputStream = getInputStream(); fi.width = 0; fi.height = 0; fi.offset = 0; fi.intelByteOrder = true; fi.fileType = FileInfo.GRAY16_UNSIGNED; fi.fileFormat = FileInfo.DICOM; int samplesPerPixel = 1; int planarConfiguration = 0; String photoInterpretation = ""; if (getInputStream()!=null) { // Use large buffer to allow URL stream to be reset after reading header f = getInputStream(); f.mark(400000); } else f = new BufferedInputStream(new FileInputStream(directory + fileName)); if (IJ.debugMode) { IJ.log(""); IJ.log("DicomDecoder: decoding "+fileName); } skipCount = (long)ID_OFFSET; while (skipCount > 0) skipCount -= f.skip( skipCount ); location += ID_OFFSET; if (!getString(4).equals(DICM)) { if (getInputStream()==null) f.close(); if (getInputStream()!=null) f.reset(); else f = new BufferedInputStream(new FileInputStream(directory + fileName)); location = 0; if (IJ.debugMode) IJ.log(DICM + " not found at offset "+ID_OFFSET+"; reseting to offset 0"); } else { dicmFound = true; if (IJ.debugMode) IJ.log(DICM + " found at offset " + ID_OFFSET); } boolean decodingTags = true; boolean signed = false; while (decodingTags) { int tag = getNextTag(); if ((location&1)!=0) // DICOM tags must be at even locations oddLocations = true; if (inSequence) { addInfo(tag, null); continue; } String s; switch (tag) { case 0x00181020: s = getString(elementLength); addInfo(tag, s); break; case TRANSFER_SYNTAX_UID: s = getString(elementLength); addInfo(tag, s); if (s.indexOf("1.2.4")>-1||s.indexOf("1.2.5")>-1) { f.close(); String msg = "ImageJ cannot open compressed DICOM images.\n \n"; msg += "Transfer Syntax UID = "+s; throw new IOException(msg); } if (s.indexOf("1.2.840.10008.1.2.2")>=0) bigEndianTransferSyntax = true; break; case NUMBER_OF_FRAMES: s = getString(elementLength); addInfo(tag, s); double frames = s2d(s); if (frames>1.0) fi.nImages = (int)frames; break; case SAMPLES_PER_PIXEL: samplesPerPixel = getShort(); addInfo(tag, samplesPerPixel); break; case PHOTOMETRIC_INTERPRETATION: photoInterpretation = getString(elementLength); addInfo(tag, photoInterpretation); break; case PLANAR_CONFIGURATION: planarConfiguration = getShort(); addInfo(tag, planarConfiguration); break; case ROWS: fi.height = getShort(); //Configuration.getGlobalConfiguration().setIntensifierSize(fi.height); addInfo(tag, fi.height); break; case COLUMNS: fi.width = getShort(); addInfo(tag, fi.width); break; case PIXEL_SPACING: String scale = getString(elementLength); getSpatialScale(fi, scale); addInfo(tag, scale); break; case SLICE_THICKNESS: case SLICE_SPACING: String spacing = getString(elementLength); fi.pixelDepth = s2d(spacing); addInfo(tag, spacing); break; case BITS_ALLOCATED: bitsAllocated = getShort(); if (bitsAllocated==8) fi.fileType = FileInfo.GRAY8; else if (bitsAllocated==32) fi.fileType = FileInfo.GRAY32_UNSIGNED; addInfo(tag, bitsAllocated); break; case PIXEL_REPRESENTATION: int pixelRepresentation = getShort(); if (pixelRepresentation==1) { fi.fileType = FileInfo.GRAY16_SIGNED; signed = true; } addInfo(tag, pixelRepresentation); break; case WINDOW_CENTER: String center = getString(elementLength); int index = center.indexOf('\\'); if (index!=-1) center = center.substring(index+1); windowCenter = s2d(center); addInfo(tag, center); break; case WINDOW_WIDTH: String width = getString(elementLength); index = width.indexOf('\\'); if (index!=-1) width = width.substring(index+1); windowWidth = s2d(width); addInfo(tag, width); break; case RESCALE_INTERCEPT: String intercept = getString(elementLength); rescaleIntercept = s2d(intercept); addInfo(tag, intercept); break; case RESCALE_SLOPE: String slop = getString(elementLength); rescaleSlope = s2d(slop); addInfo(tag, slop); break; case RED_PALETTE: fi.reds = getLut(elementLength); addInfo(tag, elementLength/2); break; case GREEN_PALETTE: fi.greens = getLut(elementLength); addInfo(tag, elementLength/2); break; case BLUE_PALETTE: fi.blues = getLut(elementLength); addInfo(tag, elementLength/2); break; case PIXEL_DATA: // Start of image data... if (elementLength!=0) { fi.offset = location; addInfo(tag, location); decodingTags = false; } else addInfo(tag, null); break; case 0x7F880010: // What is this? - RAK if (elementLength!=0) { fi.offset = location+4; decodingTags = false; } addInfo(tag, null); break; default: boolean exploreMode = false; if (exploreMode){ System.out.println(tag2hex(tag) + " " + elementLength+ " Bytes: " + this.getString(elementLength)); } else { addInfo(tag, null); } } } // while(decodingTags) if (fi.fileType==FileInfo.GRAY8) { if (fi.reds!=null && fi.greens!=null && fi.blues!=null && fi.reds.length==fi.greens.length && fi.reds.length==fi.blues.length) { fi.fileType = FileInfo.COLOR8; fi.lutSize = fi.reds.length; } } if (fi.fileType==FileInfo.GRAY32_UNSIGNED && signed) fi.fileType = FileInfo.GRAY32_INT; if (samplesPerPixel==3 && photoInterpretation.startsWith("RGB")) { if (planarConfiguration==0) fi.fileType = FileInfo.RGB; else if (planarConfiguration==1) fi.fileType = FileInfo.RGB_PLANAR; } else if (photoInterpretation.endsWith("1 ")) fi.whiteIsZero = true; if (!littleEndian) fi.intelByteOrder = false; if (IJ.debugMode) { IJ.log("width: " + fi.width); IJ.log("height: " + fi.height); IJ.log("images: " + fi.nImages); IJ.log("bits allocated: " + bitsAllocated); IJ.log("offset: " + fi.offset); } if (getInputStream()!=null) f.reset(); else f.close(); return fi; } public double[] getVoltage() { return voltage; } public double[] getCurrent() { return current; } public double[] getTime() { return time; } public double getdCU() { return dCU; } public double getDose() { return dose; } String getDicomInfo() { String s = new String(dicomInfo); char[] chars = new char[s.length()]; s.getChars(0, s.length(), chars, 0); for (int i=0; i<chars.length; i++) { if (chars[i]<' ' && chars[i]!='\n') chars[i] = ' '; } return new String(chars); } void addInfo(int tag, String value) throws IOException { String info = getHeaderInfo(tag, value); if (inSequence && info!=null && vr!=SQ) info = ">" + info; if (info!=null && tag!=ITEM) { int group = tag>>>16; //if (group!=previousGroup && (previousInfo!=null&&previousInfo.indexOf("Sequence:")==-1)) // dicomInfo.append("\n"); previousGroup = group; previousInfo = info; dicomInfo.append(tag2hex(tag)+info+"\n"); } if (debug) { if (info==null) info = ""; vrLetters[0] = (byte)(vr >> 8); vrLetters[1] = (byte)(vr & 0xFF); String VR = new String(vrLetters); IJ.log("(" + tag2hex(tag) + VR + " " + elementLength + " bytes from " + (location-elementLength)+") " + info); } } void addInfo(int tag, int value) throws IOException { addInfo(tag, Integer.toString(value)); } String getHeaderInfo(int tag, String value) throws IOException { if (tag==ITEM_DELIMINATION || tag==SEQUENCE_DELIMINATION) { inSequence = false; if (!IJ.debugMode) return null; } String key = i2hex(tag); //while (key.length()<8) // key = '0' + key; String id = (String)dictionary.get(key); if (id!=null) { if (vr==IMPLICIT_VR && id!=null) vr = (id.charAt(0)<<8) + id.charAt(1); id = id.substring(2); } if (tag==ITEM) return id!=null?id+":":null; if (value!=null) return id+": "+value; switch (vr) { case FD: if (elementLength==8) value = Double.toString(getDouble()); else for (int i=0; i<elementLength; i++) getByte(); break; case FL: if (elementLength==4) value = Float.toString(getFloat()); else for (int i=0; i<elementLength; i++) getByte(); break; //case UT: //throw new IOException("ImageJ cannot read UT (unlimited text) DICOMs"); 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 = getString(elementLength); break; case US: if (elementLength==2) value = Integer.toString(getShort()); else { value = ""; int n = elementLength/2; for (int i=0; i<n; i++) value += Integer.toString(getShort())+" "; } break; case IMPLICIT_VR: value = getString(elementLength); if (elementLength>44) value=null; break; case SQ: value = ""; boolean privateTag = ((tag>>16)&1)!=0; if (tag!=ICON_IMAGE_SEQUENCE && !privateTag) break; // else fall through and skip icon image sequence or private sequence default: long skipCount = (long)elementLength; while (skipCount > 0) skipCount -= f.skip(skipCount); location += elementLength; value = ""; } if (value!=null && id==null && !value.equals("")) return "---: "+value; else if (id==null) return null; else return id+": "+value; } char[] buf8 = new char[8]; /** Converts an int to an 8 byte hex string. */ String i2hex(int i) { for (int pos=7; pos>=0; pos--) { buf8[pos] = Tools.hexDigits[i&0xf]; i >>>= 4; } return new String(buf8); } char[] buf10; String tag2hex(int tag) { if (buf10==null) { buf10 = new char[11]; buf10[4] = ','; buf10[9] = ' '; } int pos = 8; while (pos>=0) { buf10[pos] = Tools.hexDigits[tag&0xf]; tag >>>= 4; pos--; if (pos==4) pos--; // skip coma } return new String(buf10); } double s2d(String s) { Double d; try {d = new Double(s);} catch (NumberFormatException e) {d = null;} if (d!=null) return(d.doubleValue()); else return(0.0); } void getSpatialScale(FileInfo fi, String scale) { double xscale=0, yscale=0; int i = scale.indexOf('\\'); if (i>0) { yscale = s2d(scale.substring(0, i)); xscale = s2d(scale.substring(i+1)); } if (xscale!=0.0 && yscale!=0.0) { fi.pixelWidth = xscale; fi.pixelHeight = yscale; fi.unit = "mm"; } } public boolean dicmFound() { return dicmFound; } public void setInputStream(BufferedInputStream inputStream) { this.inputStream = inputStream; } public BufferedInputStream getInputStream() { return inputStream; } } class DicomDictionary { Properties getDictionary() { Properties p = new Properties(); for (int i=0; i<dict.length; i++) { p.put(dict[i].substring(0,8), dict[i].substring(9)); } return p; } String[] dict = { "00020002=UIMedia Storage SOP Class UID", "00020003=UIMedia Storage SOP Inst UID", "00020010=UITransfer Syntax UID", "00020012=UIImplementation Class UID", "00020013=SHImplementation Version Name", "00020016=AESource Application Entity Title", "00080005=CSSpecific Character Set", "00080008=CSImage Type", "00080010=CSRecognition Code", "00080012=DAInstance Creation Date", "00080013=TMInstance Creation Time", "00080014=UIInstance Creator UID", "00080016=UISOP Class UID", "00080018=UISOP Instance UID", "00080020=DAStudy Date", "00080021=DASeries Date", "00080022=DAAcquisition Date", "00080023=DAImage Date", "00080024=DAOverlay Date", "00080025=DACurve Date", "00080030=TMStudy Time", "00080031=TMSeries Time", "00080032=TMAcquisition Time", "00080033=TMImage Time", "00080034=TMOverlay Time", "00080035=TMCurve Time", "00080040=USData Set Type", "00080041=LOData Set Subtype", "00080042=CSNuclear Medicine Series Type", "00080050=SHAccession Number", "00080052=CSQuery/Retrieve Level", "00080054=AERetrieve AE Title", "00080058=AEFailed SOP Instance UID List", "00080060=CSModality", "00080064=CSConversion Type", "00080068=CSPresentation Intent Type", "00080070=LOManufacturer", "00080080=LOInstitution Name", "00080081=STInstitution Address", "00080082=SQInstitution Code Sequence", "00080090=PNReferring Physician's Name", "00080092=STReferring Physician's Address", "00080094=SHReferring Physician's Telephone Numbers", "00080100=SHCode Value", "00080102=SHCoding Scheme Designator", "00080104=LOCode Meaning", "00080201=SHTimezone Offset From UTC", "00081010=SHStation Name", "00081030=LOStudy Description", "00081032=SQProcedure Code Sequence", "0008103E=LOSeries Description", "00081040=LOInstitutional Department Name", "00081048=PNPhysician(s) of Record", "00081050=PNAttending Physician's Name", "00081060=PNName of Physician(s) Reading Study", "00081070=PNOperator's Name", "00081080=LOAdmitting Diagnoses Description", "00081084=SQAdmitting Diagnosis Code Sequence", "00081090=LOManufacturer's Model Name", "00081100=SQReferenced Results Sequence", "00081110=SQReferenced Study Sequence", "00081111=SQReferenced Study Component Sequence", "00081115=SQReferenced Series Sequence", "00081120=SQReferenced Patient Sequence", "00081125=SQReferenced Visit Sequence", "00081130=SQReferenced Overlay Sequence", "00081140=SQReferenced Image Sequence", "00081145=SQReferenced Curve Sequence", "00081150=UIReferenced SOP Class UID", "00081155=UIReferenced SOP Instance UID", "00082111=STDerivation Description", "00082112=SQSource Image Sequence", "00082120=SHStage Name", "00082122=ISStage Number", "00082124=ISNumber of Stages", "00082129=ISNumber of Event Timers", "00082128=ISView Number", "0008212A=ISNumber of Views in Stage", "00082130=DSEvent Elapsed Time(s)", "00082132=LOEvent Timer Name(s)", "00082142=ISStart Trim", "00082143=ISStop Trim", "00082144=ISRecommended Display Frame Rate", "00082200=CSTransducer Position", "00082204=CSTransducer Orientation", "00082208=CSAnatomic Structure", "00100010=PNPatient's Name", "00100020=LOPatient ID", "00100021=LOIssuer of Patient ID", "00100030=DAPatient's Birth Date", "00100032=TMPatient's Birth Time", "00100040=CSPatient's Sex", "00101000=LOOther Patient IDs", "00101001=PNOther Patient Names", "00101005=PNPatient's Maiden Name", "00101010=ASPatient's Age", "00101020=DSPatient's Size", "00101030=DSPatient's Weight", "00101040=LOPatient's Address", "00102150=LOCountry of Residence", "00102152=LORegion of Residence", "00102180=SHOccupation", "001021A0=CSSmoking Status", "001021B0=LTAdditional Patient History", "00104000=LTPatient Comments", "00180010=LOContrast/Bolus Agent", "00180015=CSBody Part Examined", "00180020=CSScanning Sequence", "00180021=CSSequence Variant", "00180022=CSScan Options", "00180023=CSMR Acquisition Type", "00180024=SHSequence Name", "00180025=CSAngio Flag", "00180030=LORadionuclide", "00180031=LORadiopharmaceutical", "00180032=DSEnergy Window Centerline", "00180033=DSEnergy Window Total Width", "00180034=LOIntervention Drug Name", "00180035=TMIntervention Drug Start Time", "00180040=ISCine Rate", "00180050=DSSlice Thickness", "00180060=DSkVp", "00180070=ISCounts Accumulated", "00180071=CSAcquisition Termination Condition", "00180072=DSEffective Series Duration", "00180073=CSAcquisition Start Condition", "00180074=ISAcquisition Start Condition Data", "00180075=ISAcquisition Termination Condition Data", "00180080=DSRepetition Time", "00180081=DSEcho Time", "00180082=DSInversion Time", "00180083=DSNumber of Averages", "00180084=DSImaging Frequency", "00180085=SHImaged Nucleus", "00180086=ISEcho Numbers(s)", "00180087=DSMagnetic Field Strength", "00180088=DSSpacing Between Slices", "00180089=ISNumber of Phase Encoding Steps", "00180090=DSData Collection Diameter", "00180091=ISEcho Train Length", "00180093=DSPercent Sampling", "00180094=DSPercent Phase Field of View", "00180095=DSPixel Bandwidth", "00181000=LODevice Serial Number", "00181004=LOPlate ID", "00181010=LOSecondary Capture Device ID", "00181012=DADate of Secondary Capture", "00181014=TMTime of Secondary Capture", "00181016=LOSecondary Capture Device Manufacturer", "00181018=LOSecondary Capture Device Manufacturer's Model Name", "00181019=LOSecondary Capture Device Software Version(s)", "00181020=LOSoftware Versions(s)", "00181022=SHVideo Image Format Acquired", "00181023=LODigital Image Format Acquired", "00181030=LOProtocol Name", "00181040=LOContrast/Bolus Route", "00181041=DSContrast/Bolus Volume", "00181042=TMContrast/Bolus Start Time", "00181043=TMContrast/Bolus Stop Time", "00181044=DSContrast/Bolus Total Dose", "00181045=ISSyringe Counts", "00181050=DSSpatial Resolution", "00181060=DSTrigger Time", "00181061=LOTrigger Source or Type", "00181062=ISNominal Interval", "00181063=DSFrame Time", "00181064=LOFraming Type", "00181065=DSFrame Time Vector", "00181066=DSFrame Delay", "00181070=LORadionuclide Route", "00181071=DSRadionuclide Volume", "00181072=TMRadionuclide Start Time", "00181073=TMRadionuclide Stop Time", "00181074=DSRadionuclide Total Dose", "00181075=DSRadionuclide Half Life", "00181076=DSRadionuclide Positron Fraction", "00181080=CSBeat Rejection Flag", "00181081=ISLow R-R Value", "00181082=ISHigh R-R Value", "00181083=ISIntervals Acquired", "00181084=ISIntervals Rejected", "00181085=LOPVC Rejection", "00181086=ISSkip Beats", "00181088=ISHeart Rate", "00181090=ISCardiac Number of Images", "00181094=ISTrigger Window", "00181100=DSReconstruction Diameter", "00181110=DSDistance Source to Detector", "00181111=DSDistance Source to Patient", "00181120=DSGantry/Detector Tilt", "00181130=DSTable Height", "00181131=DSTable Traverse", "00181140=CSRotation Direction", "00181141=DSAngular Position", "00181142=DSRadial Position", "00181143=DSScan Arc", "00181144=DSAngular Step", "00181145=DSCenter of Rotation Offset", "00181146=DSRotation Offset", "00181147=CSField of View Shape", "00181149=ISField of View Dimensions(s)", "00181150=ISExposure Time", "00181151=ISX-ray Tube Current", "00181152=ISExposure", "00181153=ISExposure in uAs", "00181154=DSAverage Pulse Width", "00181155=CSRadiation Setting", "00181156=CSRectification Type", "0018115A=CSRadiation Mode", "0018115E=DSImage Area Dose Product", "00181160=SHFilter Type", "00181161=LOType of Filters", "00181162=DSIntensifier Size", "00181164=DSImager Pixel Spacing", "00181166=CSGrid", "00181170=ISGenerator Power", "00181180=SHCollimator/grid Name", "00181181=CSCollimator Type", "00181182=ISFocal Distance", "00181183=DSX Focus Center", "00181184=DSY Focus Center", "00181190=DSFocal Spot(s)", "00181191=CSAnode Target Material", "001811A0=DSBody Part Thickness", "001811A2=DSCompression Force", "00181200=DADate of Last Calibration", "00181201=TMTime of Last Calibration", "00181210=SHConvolution Kernel", "00181242=ISActual Frame Duration", "00181243=ISCount Rate", "00181250=SHReceiving Coil", "00181251=SHTransmitting Coil", "00181260=SHPlate Type", "00181261=LOPhosphor Type", "00181300=ISScan Velocity", "00181301=CSWhole Body Technique", "00181302=ISScan Length", "00181310=USAcquisition Matrix", "00181312=CSPhase Encoding Direction", "00181314=DSFlip Angle", "00181315=CSVariable Flip Angle Flag", "00181316=DSSAR", "00181318=DSdB/dt", "00181400=LOAcquisition Device Processing Description", "00181401=LOAcquisition Device Processing Code", "00181402=CSCassette Orientation", "00181403=CSCassette Size", "00181404=USExposures on Plate", "00181405=ISRelative X-ray Exposure", "00181450=CSColumn Angulation", "00181500=CSPositioner Motion", "00181508=CSPositioner Type", "00181510=DSPositioner Primary Angle", "00181511=DSPositioner Secondary Angle", "00181520=DSPositioner Primary Angle Increment", "00181521=DSPositioner Secondary Angle Increment", "00181530=DSDetector Primary Angle", "00181531=DSDetector Secondary Angle", "00181600=CSShutter Shape", "00181602=ISShutter Left Vertical Edge", "00181604=ISShutter Right Vertical Edge", "00181606=ISShutter Upper Horizontal Edge", "00181608=ISShutter Lower Horizontal Edge", "00181610=ISCenter of Circular Shutter", "00181612=ISRadius of Circular Shutter", "00181620=ISVertices of the Polygonal Shutter", "00181700=ISCollimator Shape", "00181702=ISCollimator Left Vertical Edge", "00181704=ISCollimator Right Vertical Edge", "00181706=ISCollimator Upper Horizontal Edge", "00181708=ISCollimator Lower Horizontal Edge", "00181710=ISCenter of Circular Collimator", "00181712=ISRadius of Circular Collimator", "00181720=ISVertices of the Polygonal Collimator", "00185000=SHOutput Power", "00185010=LOTransducer Data", "00185012=DSFocus Depth", "00185020=LOPreprocessing Function", "00185021=LOPostprocessing Function", "00185022=DSMechanical Index", "00185024=DSThermal Index", "00185026=DSCranial Thermal Index", "00185027=DSSoft Tissue Thermal Index", "00185028=DSSoft Tissue-focus Thermal Index", "00185029=DSSoft Tissue-surface Thermal Index", "00185050=ISDepth of Scan Field", "00185100=CSPatient Position", "00185101=CSView Position", "00185104=SQProjection Eponymous Name Code Sequence", "00185210=DSImage Transformation Matrix", "00185212=DSImage Translation Vector", "00186000=DSSensitivity", "00186011=SQSequence of Ultrasound Regions", "00186012=USRegion Spatial Format", "00186014=USRegion Data Type", "00186016=ULRegion Flags", "00186018=ULRegion Location Min X0", "0018601A=ULRegion Location Min Y0", "0018601C=ULRegion Location Max X1", "0018601E=ULRegion Location Max Y1", "00186020=SLReference Pixel X0", "00186022=SLReference Pixel Y0", "00186024=USPhysical Units X Direction", "00186026=USPhysical Units Y Direction", "00181628=FDReference Pixel Physical Value X", "0018602A=FDReference Pixel Physical Value Y", "0018602C=FDPhysical Delta X", "0018602E=FDPhysical Delta Y", "00186030=ULTransducer Frequency", "00186031=CSTransducer Type", "00186032=ULPulse Repetition Frequency", "00186034=FDDoppler Correction Angle", "00186036=FDSterring Angle", "00186038=ULDoppler Sample Volume X Position", "0018603A=ULDoppler Sample Volume Y Position", "0018603C=ULTM-Line Position X0", "0018603E=ULTM-Line Position Y0", "00186040=ULTM-Line Position X1", "00186042=ULTM-Line Position Y1", "00186044=USPixel Component Organization", "00186046=ULPixel Component Mask", "00186048=ULPixel Component Range Start", "0018604A=ULPixel Component Range Stop", "0018604C=USPixel Component Physical Units", "0018604E=USPixel Component Data Type", "00186050=ULNumber of Table Break Points", "00186052=ULTable of X Break Points", "00186054=FDTable of Y Break Points", "00186056=ULNumber of Table Entries", "00186058=ULTable of Pixel Values", "0018605A=ULTable of Parameter Values", "00187000=CSDetector Conditions Nominal Flag", "00187001=DSDetector Temperature", "00187004=CSDetector Type", "00187005=CSDetector Configuration", "00187006=LTDetector Description", "00187008=LTDetector Mode", "0018700A=SHDetector ID", "0018700C=DADate of Last Detector Calibration", "0018700E=TMTime of Last Detector Calibration", "00187010=ISExposures on Detector Since Last Calibration", "00187011=ISExposures on Detector Since Manufactured", "00187012=DSDetector Time Since Last Exposure", "00187014=DSDetector Active Time", "00187016=DSDetector Activation Offset From Exposure", "0018701A=DSDetector Binning", "00187020=DSDetector Element Physical Size", "00187022=DSDetector Element Spacing", "00187024=CSDetector Active Shape", "00187026=DSDetector Active Dimension(s)", "00187028=DSDetector Active Origin", "00187030=DSField of View Origin", "00187032=DSField of View Rotation", "00187034=CSField of View Horizontal Flip", "00187040=LTGrid Absorbing Material", "00187041=LTGrid Spacing Material", "00187042=DSGrid Thickness", "00187044=DSGrid Pitch", "00187046=ISGrid Aspect Ratio", "00187048=DSGrid Period", "0018704C=DSGrid Focal Distance", "00187050=LTFilter Material LT", "00187052=DSFilter Thickness Minimum", "00187054=DSFilter Thickness Maximum", "00187060=CSExposure Control Mode", "00187062=LTExposure Control Mode Description", "00187064=CSExposure Status", "00187065=DSPhototimer Setting", "0020000D=UIStudy Instance UID", "0020000E=UISeries Instance UID", "00200010=SHStudy ID", "00200011=ISSeries Number", "00200012=ISAcquisition Number", "00200013=ISImage Number", "00200014=ISIsotope Number", "00200015=ISPhase Number", "00200016=ISInterval Number", "00200017=ISTime Slot Number", "00200018=ISAngle Number", "00200020=CSPatient Orientation", "00200022=USOverlay Number", "00200024=USCurve Number", "00200030=DSImage Position", "00200032=DSImage Position (Patient)", "00200037=DSImage Orientation (Patient)", "00200050=DSLocation", "00200052=UIFrame of Reference UID", "00200060=CSLaterality", "00200070=LOImage Geometry Type", "00200080=UIMasking Image UID", "00200100=ISTemporal Position Identifier", "00200105=ISNumber of Temporal Positions", "00200110=DSTemporal Resolution", "00201000=ISSeries in Study", "00201002=ISImages in Acquisition", "00201004=ISAcquisition in Study", "00201040=LOPosition Reference Indicator", "00201041=DSSlice Location", "00201070=ISOther Study Numbers", "00201200=ISNumber of Patient Related Studies", "00201202=ISNumber of Patient Related Series", "00201204=ISNumber of Patient Related Images", "00201206=ISNumber of Study Related Series", "00201208=ISNumber of Study Related Images", "00204000=LTImage Comments", "00211002=FLtest X", "00211005=UTDose", "0021100A=UTdCU", "0021100F=UTX-Ray Tube Voltage Current and Time", "00280002=USSamples per Pixel", "00280004=CSPhotometric Interpretation", "00280006=USPlanar Configuration", "00280008=ISNumber of Frames", "00280009=ATFrame Increment Pointer", "00280010=USRows", "00280011=USColumns", "00280030=DSPixel Spacing", "00280031=DSZoom Factor", "00280032=DSZoom Center", "00280034=ISPixel Aspect Ratio", "00280051=CSCorrected Image", "00280100=USBits Allocated", "00280101=USBits Stored", "00280102=USHigh Bit", "00280103=USPixel Representation", "00280106=USSmallest Image Pixel Value", "00280107=USLargest Image Pixel Value", "00280108=USSmallest Pixel Value in Series", "00280109=USLargest Pixel Value in Series", "00280120=USPixel Padding Value", "00280300=CSQuality Control Image", "00280301=CSBurned In Annotation", "00281040=CSPixel Intensity Relationship", "00281041=SSPixel Intensity Relationship Sign", "00281050=DSWindow Center", "00281051=DSWindow Width", "00281052=DSRescale Intercept", "00281053=DSRescale Slope", "00281054=LORescale Type", "00281055=LOWindow Center & Width Explanation", "00281101=USRed Palette Color Lookup Table Descriptor", "00281102=USGreen Palette Color Lookup Table Descriptor", "00281103=USBlue Palette Color Lookup Table Descriptor", "00281201=USRed Palette Color Lookup Table Data", "00281202=USGreen Palette Color Lookup Table Data", "00281203=USBlue Palette Color Lookup Table Data", "00282110=CSLossy Image Compression", "00283000=SQModality LUT Sequence", "00283002=USLUT Descriptor", "00283003=LOLUT Explanation", "00283004=LOMadality LUT Type", "00283006=USLUT Data", "00283010=SQVOI LUT Sequence", "30020011=DSImage Plane Pixel Spacing", "30020022=DSRadiation Machine SAD", "30020026=DSRT IMAGE SID", "0032000A=CSStudy Status ID", "0032000C=CSStudy Priority ID", "00320012=LOStudy ID Issuer", "00320032=DAStudy Verified Date", "00320033=TMStudy Verified Time", "00320034=DAStudy Read Date", "00320035=TMStudy Read Time", "00321000=DAScheduled Study Start Date", "00321001=TMScheduled Study Start Time", "00321010=DAScheduled Study Stop Date", "00321011=TMScheduled Study Stop Time", "00321020=LOScheduled Study Location", "00321021=AEScheduled Study Location AE Title(s)", "00321030=LOReason for Study", "00321032=PNRequesting Physician", "00321033=LORequesting Service", "00321040=DAStudy Arrival Date", "00321041=TMStudy Arrival Time", "00321050=DAStudy Completion Date", "00321051=TMStudy Completion Time", "00321055=CSStudy Component Status ID", "00321060=LORequested Procedure Description", "00321064=SQRequested Procedure Code Sequence", "00321070=LORequested Contrast Agent", "00324000=LTStudy Comments", "00400001=AEScheduled Station AE Title", "00400002=DAScheduled Procedure Step Start Date", "00400003=TMScheduled Procedure Step Start Time", "00400004=DAScheduled Procedure Step End Date", "00400005=TMScheduled Procedure Step End Time", "00400006=PNScheduled Performing Physician's Name", "00400007=LOScheduled Procedure Step Description", "00400008=SQScheduled Action Item Code Sequence", "00400009=SHScheduled Procedure Step ID", "00400010=SHScheduled Station Name", "00400011=SHScheduled Procedure Step Location", "00400012=LOPre-Medication", "00400020=CSScheduled Procedure Step Status", "00400100=SQScheduled Procedure Step Sequence", "00400220=SQReferenced Standalone SOP Instance Sequence", "00400241=AEPerformed Station AE Title", "00400242=SHPerformed Station Name", "00400243=SHPerformed Location", "00400244=DAPerformed Procedure Step Start Date", "00400245=TMPerformed Procedure Step Start Time", "00400250=DAPerformed Procedure Step End Date", "00400251=TMPerformed Procedure Step End Time", "00400252=CSPerformed Procedure Step Status", "00400253=SHPerformed Procedure Step ID", "00400254=LOPerformed Procedure Step Description", "00400255=LOPerformed Procedure Type Description", "00400260=SQPerformed Action Item Sequence", "00400270=SQScheduled Step Attributes Sequence", "00400275=SQRequest Attributes Sequence", "00400280=STComments on the Performed Procedure Steps", "00400293=SQQuantity Sequence", "00400294=DSQuantity", "00400295=SQMeasuring Units Sequence", "00400296=SQBilling Item Sequence", "00400300=USTotal Time of Fluoroscopy", "00400301=USTotal Number of Exposures", "00400302=USEntrance Dose", "00400303=USExposed Area", "00400306=DSDistance Source to Entrance", "00400307=DSDistance Source to Support", "00400310=STComments on Radiation Dose", "00400312=DSX-Ray Output", "00400314=DSHalf Value Layer", "00400316=DSOrgan Dose", "00400318=CSOrgan Exposed", "00400320=SQBilling Procedure Step Sequence", "00400321=SQFilm Consumption Sequence", "00400324=SQBilling Supplies and Devices Sequence", "00400330=SQReferenced Procedure Step Sequence", "00400340=SQPerformed Series Sequence", "00400400=LTComments on the Scheduled Procedure Step", "0040050A=LOSpecimen Accession Number", "00400550=SQSpecimen Sequence", "00400551=LOSpecimen Identifier", "0040059A=SQSpecimen Type Code Sequence", "00400555=SQAcquisition Context Sequence", "00400556=STAcquisition Context Description", "004006FA=LOSlide Identifier", "0040071A=SQImage Center Point Coordinates Sequence", "0040072A=DSX offset in Slide Coordinate System", "0040073A=DSY offset in Slide Coordinate System", "0040074A=DSZ offset in Slide Coordinate System", "004008D8=SQPixel Spacing Sequence", "004008DA=SQCoordinate System Axis Code Sequence", "004008EA=SQMeasurement Units Code Sequence", "00401001=SHRequested Procedure ID", "00401002=LOReason for the Requested Procedure", "00401003=SHRequested Procedure Priority", "00401004=LOPatient Transport Arrangements", "00401005=LORequested Procedure Location", "00401006= 1Placer Order Number / Procedure S", "00401007= 1Filler Order Number / Procedure S", "00401008=LOConfidentiality Code", "00401009=SHReporting Priority", "00401010=PNNames of Intended Recipients of Results", "00401400=LTRequested Procedure Comments", "00402001=LOReason for the Imaging Service Request", "00402004=DAIssue Date of Imaging Service Request", "00402005=TMIssue Time of Imaging Service Request", "00402006= 1Placer Order Number / Imaging Service Request S", "00402007= 1Filler Order Number / Imaging Service Request S", "00402008=PNOrder Entered By", "00402009=SHOrder Enterers Location", "00402010=SHOrder Callback Phone Number", "00402016=LOPlacer Order Number / Imaging Service Request", "00402017=LOFiller Order Number / Imaging Service Request", "00402400=LTImaging Service Request Comments", "00403001=LOConfidentiality Constraint on Patient Data Description", "00408302=DSEntrance Dose in mGy", "0040A010=CSRelationship Type", "0040A027=LOVerifying Organization", "0040A030=DTVerification DateTime", "0040A032=DTObservation DateTime", "0040A040=CSValue Type", "0040A043=SQConcept-name Code Sequence", "0040A050=CSContinuity Of Content", "0040A073=SQVerifying Observer Sequence", "0040A075=PNVerifying Observer Name", "0040A088=SQVerifying Observer Identification Code Sequence", "0040A0B0=USReferenced Waveform Channels", "0040A120=DTDateTime", "0040A121=DADate", "0040A122=TMTime", "0040A123=PNPerson Name", "0040A124=UIUID", "0040A130=CSTemporal Range Type", "0040A132=ULReferenced Sample Positions", "0040A136=USReferenced Frame Numbers", "0040A138=DSReferenced Time Offsets", "0040A13A=DTReferenced Datetime", "0040A160=UTText Value", "0040A168=SQConcept Code Sequence", "0040A180=USAnnotation Group Number", "0040A195=SQModifier Code Sequence", "0040A300=SQMeasured Value Sequence", "0040A30A=DSNumeric Value", "0040A360=SQPredecessor Documents Sequence", "0040A370=SQReferenced Request Sequence", "0040A372=SQPerformed Procedure Code Sequence", "0040A375=SQCurrent Requested Procedure Evidence Sequence", "0040A385=SQPertinent Other Evidence Sequence", "0040A491=CSCompletion Flag", "0040A492=LOCompletion Flag Description", "0040A493=CSVerification Flag", "0040A504=SQContent Template Sequence", "0040A525=SQIdentical Documents Sequence", "0040A730=SQContent Sequence", "0040B020=SQAnnotation Sequence", "0040DB00=CSTemplate Identifier", "0040DB06=DTTemplate Version", "0040DB07=DTTemplate Local Version", "0040DB0B=CSTemplate Extension Flag", "0040DB0C=UITemplate Extension Organization UID", "0040DB0D=UITemplate Extension Creator UID", "0040DB73=ULReferenced Content Item Identifier", "00540011=USNumber of Energy Windows", "00540012=SQEnergy Window Information Sequence", "00540013=SQEnergy Window Range Sequence", "00540014=DSEnergy Window Lower Limit", "00540015=DSEnergy Window Upper Limit", "00540016=SQRadiopharmaceutical Information Sequence", "00540017=ISResidual Syringe Counts", "00540018=SHEnergy Window Name", "00540020=USDetector Vector", "00540021=USNumber of Detectors", "00540022=SQDetector Information Sequence", "00540030=USPhase Vector", "00540031=USNumber of Phases", "00540032=SQPhase Information Sequence", "00540033=USNumber of Frames in Phase", "00540036=ISPhase Delay", "00540038=ISPause Between Frames", "00540039=CSPhase Description", "00540050=USRotation Vector", "00540051=USNumber of Rotations", "00540052=SQRotation Information Sequence", "00540053=USNumber of Frames in Rotation", "00540060=USR-R Interval Vector", "00540061=USNumber of R-R Intervals", "00540062=SQGated Information Sequence", "00540063=SQData Information Sequence", "00540070=USTime Slot Vector", "00540071=USNumber of Time Slots", "00540072=SQTime Slot Information Sequence", "00540073=DSTime Slot Time", "00540080=USSlice Vector", "00540081=USNumber of Slices", "00540090=USAngular View Vector", "00540100=USTime Slice Vector", "00540101=USNumber of Time Slices", "00540200=DSStart Angle", "00540202=CSType of Detector Motion", "00540210=ISTrigger Vector", "00540211=USNumber of Triggers in Phase", "00540220=SQView Code Sequence", "00540222=SQView Modifier Code Sequence", "00540300=SQRadionuclide Code Sequence", "00540302=SQAdministration Route Code Sequence", "00540304=SQRadiopharmaceutical Code Sequence", "00540306=SQCalibration Data Sequence", "00540308=USEnergy Window Number", "00540400=SHImage ID", "00540410=SQPatient Orientation Code Sequence", "00540412=SQPatient Orientation Modifier Code Sequence", "00540414=SQPatient Gantry Relationship Code Sequence", "00540500=CSSlice Progression Direction", "00541000=CSSeries Type", "00541001=CSUnits", "00541002=CSCounts Source", "00541004=CSReprojection Method", "00541100=CSRandoms Correction Method", "00541101=LOAttenuation Correction Method", "00541102=CSDecay Correction", "00541103=LOReconstruction Method", "00541104=LODetector Lines of Response Used", "00541105=LOScatter Correction Method", "00541200=DSAxial Acceptance", "00541201=ISAxial Mash", "00541202=ISTransverse Mash", "00541203=DSDetector Element Size", "00541210=DSCoincidence Window Width", "00541220=CSSecondary Counts Type", "00541300=DSFrame Reference Time", "00541310=ISPrimary (Prompts) Counts Accumulated", "00541311=ISSecondary Counts Accumulated", "00541320=DSSlice Sensitivity Factor", "00541321=DSDecay Factor", "00541322=DSDose Calibration Factor", "00541323=DSScatter Fraction Factor", "00541324=DSDead Time Factor", "00541330=USImage Index", "00541400=CSCounts Included", "00541401=CSDead Time Correction Flag", "30020002=SHRT Image Label", "30020003=LORT Image Name", "30020004=STRT Image Description", "3002000A=CSReported Values Origin", "3002000C=CSRT Image Plane", "3002000D=DSX-Ray Image Receptor Translation", "3002000E=DSX-Ray Image Receptor Angle", "30020011=DSImage Plane Pixel Spacing", "30020012=DSRT Image Position", "30020020=SHRadiation Machine Name", "30020022=DSRadiation Machine SAD", "30020026=DSRT Image SID", "30020029=ISFraction Number", "30020030=SQExposure Sequence", "30020032=DSMeterset Exposure", "300A011E=DSGantry Angle", "300A0120=DSBeam Limiting Device Angle", "300A0122=DSPatient Support Angle", "300A0128=DSTable Top Vertical Position", "300A0129=DSTable Top Longitudinal Position", "300A012A=DSTable Top Lateral Position", "300A00B3=CSPrimary Dosimeter Unit", "300A00F0=ISNumber of Blocks", "300C0006=ISReferenced Beam Number", "300C0008=DSStart Cumulative Meterset Weight", "300C0022=ISReferenced Fraction Group Number", "7FE00010=OXPixel Data", "FFFEE000=DLItem", "FFFEE00D=DLItem Delimitation Item", "FFFEE0DD=DLSequence Delimitation Item" }; } } /* * Copyright (C) 2010-2014 - Andreas Maier * CONRAD is developed as an Open Source project under the GNU General Public License (GPL). */