package ij.plugin.filter;
import ij.*;
import ij.process.*;
import ij.gui.*;
import ij.util.Tools;
import ij.io.FileOpener;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import ij.measure.Calibration;
import ij.plugin.frame.Recorder;
public class ImageProperties implements PlugInFilter, TextListener {
private final String SAME = "-";
ImagePlus imp;
static final int NANOMETER=0, MICROMETER=1, MILLIMETER=2, CENTIMETER=3,
METER=4, KILOMETER=5, INCH=6, FOOT=7, MILE=8, PIXEL=9, OTHER_UNIT=10;
int oldUnitIndex;
double oldUnitsPerCm;
Vector nfields, sfields;
boolean duplicatePixelWidth = true;
String calUnit;
double calPixelWidth, calPixelHeight, calPixelDepth;
TextField pixelWidthField, pixelHeightField, pixelDepthField;
int textChangedCount;
public int setup(String arg, ImagePlus imp) {
this.imp = imp;
return DOES_ALL+NO_CHANGES;
}
public void run(ImageProcessor ip) {
showDialog(imp);
}
void showDialog(ImagePlus imp) {
String options = Macro.getOptions();
boolean legacyMacro = false;
if (options!=null ) {
String options2 = options.replaceAll(" depth=", " slices=");
options2 = options2.replaceAll(" interval=", " frame=");
Macro.setOptions(options2);
if (options.contains("unit="))
legacyMacro = true;
}
Calibration cal = imp.getCalibration();
Calibration calOrig = cal.copy();
oldUnitIndex = getUnitIndex(cal.getUnit());
oldUnitsPerCm = getUnitsPerCm(oldUnitIndex);
int stackSize = imp.getImageStackSize();
int channels = imp.getNChannels();
int slices = imp.getNSlices();
int frames = imp.getNFrames();
boolean global1 = imp.getGlobalCalibration()!=null;
boolean global2;
int digits = cal.pixelWidth<1.0||cal.pixelHeight<1.0||cal.pixelDepth<1.0?7:4;
String xunit = cal.getXUnit();
String yunit = cal.getYUnit();
String zunit = cal.getZUnit();
String project = imp.getProp("CompositeProjection");
if (project==null) project="";
if (project.contains("Min")||project.contains("min")) project="Min";
if (project.contains("Max")||project.contains("max")) project="Max";
if (!(project.equals("Min")||project.equals("Max"))) project="Sum";
GenericDialog gd = new GenericDialog(imp.getTitle());
gd.addNumericField("Channels (c):", channels, 0);
gd.addNumericField("Slices (z):", slices, 0);
gd.addNumericField("Frames (t):", frames, 0);
gd.setInsets(0, 5, 0);
gd.addMessage("Note: c*z*t must equal "+stackSize, null, Color.darkGray);
gd.setInsets(15, 0, 0);
if (legacyMacro)
gd.addStringField("Unit of length:", cal.getUnit());
int fieldWidth = 9;
gd.addNumericField("Pixel_width:", cal.pixelWidth, digits, fieldWidth, null);
gd.addToSameRow();
gd.addStringField("_", xunit, 5);
gd.addNumericField("Pixel_height:", cal.pixelHeight, digits, fieldWidth, null);
gd.addToSameRow();
gd.addStringField("_", yunit.equals(xunit)?SAME:yunit, 5);
gd.addNumericField("Voxel_depth:", cal.pixelDepth, digits, fieldWidth, null);
gd.addToSameRow();
gd.addStringField("_", zunit.equals(xunit)?SAME:zunit, 5);
gd.setInsets(10, 0, 5);
double interval = cal.frameInterval;
String intervalStr = IJ.d2s(interval, (int)interval==interval?0:2) + " " + cal.getTimeUnit();
gd.addStringField("Frame interval:", intervalStr);
String xo = cal.xOrigin==(int)cal.xOrigin?IJ.d2s(cal.xOrigin,0):IJ.d2s(cal.xOrigin,2);
String yo = cal.yOrigin==(int)cal.yOrigin?IJ.d2s(cal.yOrigin,0):IJ.d2s(cal.yOrigin,2);
String zo = "";
if (imp.getNSlices()>1) {
zo = cal.zOrigin==(int)cal.zOrigin?IJ.d2s(cal.zOrigin,0):IJ.d2s(cal.zOrigin,2);
zo = "," + zo;
}
gd.addStringField("Origin (pixels):", xo+","+yo+zo);
gd.setInsets(5, 20, 0);
gd.addCheckbox("Invert Y coordinates", cal.getInvertY());
gd.addCheckbox("Global", global1);
nfields = gd.getNumericFields();
if (nfields!=null) {
pixelWidthField = (TextField)nfields.elementAt(3);
pixelHeightField = (TextField)nfields.elementAt(4);
pixelDepthField = (TextField)nfields.elementAt(5);
for (int i=0; i<nfields.size(); i++)
((TextField)nfields.elementAt(i)).addTextListener(this);
}
sfields = gd.getStringFields();
if (sfields!=null) {
for (int i=0; i<sfields.size(); i++)
((TextField)sfields.elementAt(i)).addTextListener(this);
}
calUnit = cal.getUnit();
calPixelWidth = cal.pixelWidth;
calPixelHeight = cal.pixelHeight;
calPixelDepth = cal.pixelDepth;
gd.showDialog();
if (gd.wasCanceled())
return;
channels = (int)gd.getNextNumber();
if (channels<1) channels = 1;
slices = (int)gd.getNextNumber();
if (slices<1) slices = 1;
frames = (int)gd.getNextNumber();
if (frames<1) frames = 1;
if (channels*slices*frames==stackSize)
imp.setDimensions(channels, slices, frames);
else
IJ.error("Properties", "The product of channels ("+channels+"), slices ("+slices
+")\n and frames ("+frames+") must equal the stack size ("+stackSize+").");
String unit = "";
if (legacyMacro)
unit = gd.getNextString();
double pixelWidth = gd.getNextNumber();
String xunit2 = gd.getNextString();
double pixelHeight = gd.getNextNumber();
String yunit2 = gd.getNextString();
double pixelDepth = gd.getNextNumber();
String zunit2 = gd.getNextString();
boolean reset = false;
boolean xUnitChanged=false,yUnitChanged=false,zUnitChanged=false;
if (legacyMacro) {
if (!unit.equals(cal.getUnit())) {
cal.setYUnit(null);
cal.setZUnit(null);
}
cal.setUnit(unit);
} else {
xUnitChanged = !xunit2.equals(xunit);
if (xUnitChanged)
cal.setXUnit(xunit2);
yUnitChanged = !yunit2.equals(yunit) && !yunit2.equals(SAME);
if (yUnitChanged)
cal.setYUnit(yunit2);
zUnitChanged = !zunit2.equals(zunit) && !zunit2.equals(SAME);
if (zUnitChanged)
cal.setZUnit(zunit2);
unit = xunit2;
}
if (unit.equals("") || unit.equalsIgnoreCase("none") || pixelWidth==0.0) {
cal.setUnit(null);
cal.pixelWidth = 1.0;
cal.pixelHeight = 1.0;
cal.pixelDepth = 1.0;
} else {
cal.pixelWidth = pixelWidth;
cal.pixelHeight = pixelHeight;
cal.pixelDepth = pixelDepth;
}
gd.setSmartRecording(interval==0);
String frameInterval = validateInterval(gd.getNextString());
String[] intAndUnit = Tools.split(frameInterval, " -");
interval = Tools.parseDouble(intAndUnit[0]);
cal.frameInterval = Double.isNaN(interval)?0.0:interval;
String timeUnit = intAndUnit.length>=2?intAndUnit[1]:"sec";
if (timeUnit.equals("sec")&&cal.frameInterval<=2.0&&cal.frameInterval>=1.0/30.0)
cal.fps = 1.0/cal.frameInterval;
if (timeUnit.equals("usec"))
timeUnit = IJ.micronSymbol + "sec";
cal.setTimeUnit(timeUnit);
gd.setSmartRecording(cal.xOrigin==0&&cal.yOrigin==0&&cal.zOrigin==0);
String[] origin = Tools.split(gd.getNextString(), " ,");
gd.setSmartRecording(false);
double x = origin.length>=1?Tools.parseDouble(origin[0]):Double.NaN;
double y = origin.length>=2?Tools.parseDouble(origin[1]):Double.NaN;
double z = origin.length>=3?Tools.parseDouble(origin[2]):Double.NaN;
cal.xOrigin= Double.isNaN(x)?0.0:x;
cal.yOrigin= Double.isNaN(y)?cal.xOrigin:y;
cal.zOrigin= Double.isNaN(z)?0.0:z;
cal.setInvertY(gd.getNextBoolean());
global2 = gd.getNextBoolean();
if (!cal.equals(calOrig))
imp.setCalibration(cal);
imp.setGlobalCalibration(global2?cal:null);
if (global2 || global2!=global1)
WindowManager.repaintImageWindows();
else
imp.repaintWindow();
if (global2 && global2!=global1)
FileOpener.setShowConflictMessage(true);
if (Recorder.record) {
if (Recorder.scriptMode()) {
if (xUnitChanged)
Recorder.recordCall("imp.getCalibration().setXUnit(\""+xunit2+"\");", true);
if (yUnitChanged)
Recorder.recordCall("imp.getCalibration().setYUnit(\""+yunit2+"\");", true);
if (zUnitChanged)
Recorder.recordCall("imp.getCalibration().setZUnit(\""+zunit2+"\");", true);
} else {
if (xUnitChanged)
Recorder.record("Stack.setXUnit", xunit2);
if (yUnitChanged)
Recorder.record("Stack.setYUnit", yunit2);
if (zUnitChanged)
Recorder.record("Stack.setZUnit", zunit2);
}
}
}
String validateInterval(String interval) {
if (interval.indexOf(" ")!=-1)
return interval;
int firstLetter = -1;
for (int i=0; i<interval.length(); i++) {
char c = interval.charAt(i);
if (Character.isLetter(c)) {
firstLetter = i;
break;
}
}
if (firstLetter>0 && firstLetter<interval.length()-1)
interval = interval.substring(0,firstLetter)+" "+interval.substring(firstLetter, interval.length());
return interval;
}
double getNewScale(String newUnit, double oldScale) {
if (oldUnitsPerCm==0.0) return 0.0;
double newScale = 0.0;
int newUnitIndex = getUnitIndex(newUnit);
if (newUnitIndex!=oldUnitIndex) {
double newUnitsPerCm = getUnitsPerCm(newUnitIndex);
if (oldUnitsPerCm!=0.0 && newUnitsPerCm!=0.0) {
newScale = oldScale * (oldUnitsPerCm/newUnitsPerCm);
}
}
return newScale;
}
static int getUnitIndex(String unit) {
unit = unit.toLowerCase(Locale.US);
if (unit.equals("cm")||unit.startsWith("cent"))
return CENTIMETER;
else if (unit.equals("mm")||unit.startsWith("milli"))
return MILLIMETER;
else if (unit.startsWith("inch"))
return INCH;
else if (unit.startsWith(""+IJ.micronSymbol)||unit.startsWith("u")||unit.startsWith("micro"))
return MICROMETER;
else if (unit.equals("nm")||unit.startsWith("nano"))
return NANOMETER;
else if (unit.equals("m") || unit.startsWith("meter"))
return METER;
else if (unit.equals("km")||unit.startsWith("kilo"))
return KILOMETER;
else if (unit.equals("ft")||unit.equals("foot")||unit.equals("feet"))
return FOOT;
else if (unit.equals("mi")||unit.startsWith("mile"))
return MILE;
else
return OTHER_UNIT;
}
static double getUnitsPerCm(int unitIndex) {
switch (unitIndex) {
case NANOMETER: return 10000000.0;
case MICROMETER: return 10000.0;
case MILLIMETER: return 10.0;
case CENTIMETER: return 1.0;
case METER: return 0.01;
case KILOMETER: return 0.00001;
case INCH: return 0.3937;
case FOOT: return 0.0328083;
case MILE: return 0.000006213;
default: return 0.0;
}
}
public void textValueChanged(TextEvent e) {
textChangedCount++;
Object source = e.getSource();
int channels = (int)Tools.parseDouble(((TextField)nfields.elementAt(2)).getText(),-99);
int depth = (int)Tools.parseDouble(((TextField)nfields.elementAt(3)).getText(),-99);
int frames = (int)Tools.parseDouble(((TextField)nfields.elementAt(4)).getText(),-99);
double newPixelWidth = calPixelWidth;
String newWidthText = pixelWidthField.getText();
if (source==pixelWidthField)
newPixelWidth = Tools.parseDouble(newWidthText,-99);
double newPixelHeight = calPixelHeight;
if (source==pixelHeightField) {
String newHeightText = pixelHeightField.getText();
newPixelHeight = Tools.parseDouble(newHeightText,-99);
if (!newHeightText.equals(newWidthText))
duplicatePixelWidth = false;
}
double newPixelDepth = calPixelDepth;
if (source==pixelDepthField) {
String newDepthText = pixelDepthField.getText();
newPixelDepth = Tools.parseDouble(newDepthText,-99);
if (!newDepthText.equals(newWidthText))
duplicatePixelWidth = false;
}
if (textChangedCount==1 && (calPixelHeight!=1.0||calPixelDepth!=1.0))
duplicatePixelWidth = false;
if (source==pixelWidthField && newPixelWidth!=-99 && duplicatePixelWidth) {
pixelHeightField.setText(newWidthText);
pixelDepthField.setText(newWidthText);
calPixelHeight = calPixelWidth;
calPixelDepth = calPixelWidth;
}
calPixelWidth = newPixelWidth;
calPixelHeight = newPixelHeight;
calPixelDepth = newPixelDepth;
TextField unitField = (TextField)sfields.elementAt(0);
String newUnit = unitField.getText();
if (!newUnit.equals(calUnit)) {
double oldXScale = newPixelWidth!=0?1.0/newPixelWidth:0;
double oldYScale = newPixelHeight!=0?1.0/newPixelHeight:0;
double oldZScale = newPixelDepth!=0?1.0/newPixelDepth:0;
double newXScale = getNewScale(newUnit, oldXScale);
double newYScale = getNewScale(newUnit, oldYScale);
double newZScale = getNewScale(newUnit, oldZScale);
if (newXScale!=0.0) {
double w = 1.0/newXScale;
double h = 1.0/newYScale;
double d = 1.0/newZScale;
int digits = w<1.0||h<1.0||d<1.0?7:4;
pixelWidthField.setText(IJ.d2s(1/newXScale,digits));
pixelHeightField.setText(IJ.d2s(1/newYScale,digits));
pixelDepthField.setText(IJ.d2s(1/newZScale,digits));
calPixelWidth = 1.0/newXScale;
calPixelHeight = 1.0/newYScale;
calPixelDepth = 1.0/newZScale;
oldUnitIndex = getUnitIndex(newUnit);
oldUnitsPerCm = getUnitsPerCm(oldUnitIndex);
}
calUnit = newUnit;
}
}
}