package ij.util;
import ij.*;
import ij.process.*;
import ij.plugin.DICOM;
public class DicomTools {
private static final int MAX_DIGITS = 5;
private static String[] sliceLabels;
public static ImageStack sort(ImageStack stack) {
if (IJ.debugMode) IJ.log("Sorting by DICOM image number");
if (stack.size()==1) return stack;
String[] strings = getSortStrings(stack, "0020,0013");
if (strings==null) return stack;
StringSorter.sort(strings);
ImageStack stack2 = null;
if (stack.isVirtual())
stack2 = ((VirtualStack)stack).sortDicom(strings, sliceLabels, MAX_DIGITS);
else
stack2 = sortStack(stack, strings);
return stack2!=null?stack2:stack;
}
private static ImageStack sortStack(ImageStack stack, String[] strings) {
ImageProcessor ip = stack.getProcessor(1);
ImageStack stack2 = new ImageStack(ip.getWidth(), ip.getHeight(), ip.getColorModel());
for (int i=0; i<stack.size(); i++) {
int slice = (int)Tools.parseDouble(strings[i].substring(strings[i].length()-MAX_DIGITS), 0.0);
if (slice==0) return null;
stack2.addSlice(sliceLabels[slice-1], stack.getPixels(slice));
}
stack2.update(stack.getProcessor(1));
return stack2;
}
private static String[] getSortStrings(ImageStack stack, String tag) {
double series = getSeriesNumber(getSliceLabel(stack,1));
int n = stack.size();
boolean checkRescaleSlope = (stack instanceof VirtualStack)?((VirtualStack)stack).getBitDepth()==16:false;
if (Prefs.ignoreRescaleSlope)
checkRescaleSlope = false;
boolean showError = false;
String[] values = new String[n];
sliceLabels = new String[n];
for (int i=1; i<=n; i++) {
String tags = getSliceLabel(stack,i);
if (tags==null) return null;
sliceLabels[i-1] = tags;
double value = getNumericTag(tags, tag);
if (Double.isNaN(value)) {
if (IJ.debugMode) IJ.log(" "+tag+" tag missing in slice "+i);
if (showError) rescaleSlopeError(stack);
return null;
}
if (getSeriesNumber(tags)!=series) {
if (IJ.debugMode) IJ.log(" all slices must be part of the same series");
if (showError) rescaleSlopeError(stack);
return null;
}
values[i-1] = toString(value, MAX_DIGITS) + toString(i, MAX_DIGITS);
if (checkRescaleSlope) {
double rescaleSlope = getNumericTag(tags, "0028,1053");
if (rescaleSlope!=1.0)
showError = true;
}
}
if (showError) rescaleSlopeError(stack);
return values;
}
private static void rescaleSlopeError(ImageStack stack) {
((VirtualStack)stack).setBitDepth(32);
}
private static String toString(double value, int width) {
String s = " " + IJ.d2s(value,0);
return s.substring(s.length()-MAX_DIGITS);
}
private static String getSliceLabel(ImageStack stack, int n) {
String info = stack.getSliceLabel(n);
if ((info==null || info.length()<100) && stack.isVirtual()) {
String dir = ((VirtualStack)stack).getDirectory();
String name = ((VirtualStack)stack).getFileName(n);
DICOM reader = new DICOM();
info = reader.getInfo(dir+name);
if (info!=null)
info = name + "\n" + info;
}
return info;
}
public static double getVoxelDepth(ImageStack stack) {
if (stack.isVirtual()) stack.getProcessor(1);
String pos0 = getTag(stack.getSliceLabel(1), "0020,0032");
String posn = null;
double voxelDepth = -1.0;
if (pos0!=null) {
String[] xyz = pos0.split("\\\\");
if (xyz.length!=3) return voxelDepth;
double z0 = Double.parseDouble(xyz[2]);
if (stack.isVirtual()) stack.getProcessor(stack.size());
posn = getTag(stack.getSliceLabel(stack.size()), "0020,0032");
if (posn==null) return voxelDepth;
xyz = posn.split("\\\\");
if (xyz.length!=3) return voxelDepth;
double zn = Double.parseDouble(xyz[2]);
voxelDepth = Math.abs((zn - z0) / (stack.size() - 1));
}
if (IJ.debugMode) IJ.log("DicomTools.getVoxelDepth: "+voxelDepth+" "+pos0+" "+posn);
return voxelDepth;
}
public static String getTag(ImagePlus imp, String id) {
String metadata = null;
if (imp.getStackSize()>1) {
ImageStack stack = imp.getStack();
String label = stack.getSliceLabel(imp.getCurrentSlice());
if (label!=null && label.indexOf('\n')>0) metadata = label;
}
if (metadata==null)
metadata = (String)imp.getProperty("Info");
return getTag(metadata, id);
}
public static String getTagName(String id) {
return DICOM.getTagName(id);
}
private static double getSeriesNumber(String tags) {
double series = getNumericTag(tags, "0020,0011");
if (Double.isNaN(series)) series = 0;
return series;
}
private static double getNumericTag(String hdr, String tag) {
String value = getTag(hdr, tag);
if (value==null) return Double.NaN;
int index3 = value.indexOf("\\");
if (index3>0)
value = value.substring(0, index3);
return Tools.parseDouble(value);
}
private static String getTag(String hdr, String tag) {
if (hdr==null) return null;
int index1 = hdr.indexOf(tag);
if (index1==-1) return null;
if (hdr.charAt(index1+11)=='>') {
index1 = hdr.indexOf(tag, index1+10);
if (index1==-1) return null;
}
index1 = hdr.indexOf(":", index1);
if (index1==-1) return null;
int index2 = hdr.indexOf("\n", index1);
String value = hdr.substring(index1+1, index2);
return value;
}
}