package ij.measure;
import ij.*;
import ij.plugin.filter.Analyzer;
public class Calibration implements Cloneable {
public static final int STRAIGHT_LINE=0,POLY2=1,POLY3=2,POLY4=3,
EXPONENTIAL=4,POWER=5,LOG=6,RODBARD=7,GAMMA_VARIATE=8, LOG2=9, RODBARD2=10,
EXP_WITH_OFFSET=11, GAUSSIAN=12, EXP_RECOVERY=13;
public static final int NONE=20, UNCALIBRATED_OD=21, CUSTOM=22;
public static final String DEFAULT_VALUE_UNIT = "Gray Value";
private static final int UNKNOWN = 0;
public double pixelWidth = 1.0;
public double pixelHeight = 1.0;
public double pixelDepth = 1.0;
public double frameInterval;
public double fps;
private static boolean loopBackAndForth;
public boolean loop = loopBackAndForth;
public double xOrigin;
public double yOrigin;
public double zOrigin;
public String info;
private double[] coefficients;
private String unit = "pixel";
private String yunit;
private String zunit;
private String valueUnit = DEFAULT_VALUE_UNIT;
private String timeUnit = "sec";
private int function = NONE;
private float[] cTable;
private boolean invertedLut;
private int bitDepth = UNKNOWN;
private boolean zeroClip;
private boolean invertY;
public Calibration(ImagePlus imp) {
if (imp!=null) {
bitDepth = imp.getBitDepth();
if (bitDepth!=UNKNOWN)
invertedLut=imp.isInvertedLut();
}
}
public Calibration() {
}
public boolean scaled() {
return pixelWidth!=1.0 || pixelHeight!=1.0 || pixelDepth!=1.0 || !unit.equals("pixel");
}
public boolean scaledOrOffset() {
return pixelWidth!=1.0 || pixelHeight!=1.0 || pixelDepth!=1.0
|| xOrigin!=0.0 || yOrigin!=0.0 || zOrigin!=0.0;
}
public void setUnit(String unit) {
this.unit = sanitizeUnit(unit);
}
public void setXUnit(String unit) {
setUnit(unit);
}
public void setYUnit(String unit) {
if (unit==null || unit.equals(""))
yunit = null;
else
yunit = sanitizeUnit(unit);
}
public void setZUnit(String unit) {
if (unit==null || unit.equals(""))
zunit = null;
else
zunit = sanitizeUnit(unit );
}
private static String sanitizeUnit(String unit) {
if (unit==null || unit.equals(""))
return "pixel";
else if (unit.equals("um"))
unit = "\u00B5m";
else if (unit.equals("A"))
unit = ""+IJ.angstromSymbol;
return unit;
}
public String getUnit() {
return unit;
}
public String getXUnit() {
return unit;
}
public String getYUnit() {
return yunit!=null?yunit:unit;
}
public String getZUnit() {
return zunit!=null?zunit:unit;
}
private static String pluralForm(String unit) {
String units;
if (unit.equals("pixel"))
units = "pixels";
else if (unit.equals("micron"))
units = "microns";
else if (unit.equals("inch"))
units = "inches";
else
units = unit;
return units;
}
public String getUnits() {
return pluralForm(getUnit());
}
public String getXUnits() {
return pluralForm(getXUnit());
}
public String getYUnits() {
return pluralForm(getYUnit());
}
public String getZUnits() {
return pluralForm(getZUnit());
}
public void setTimeUnit(String unit) {
if (unit==null || unit.equals(""))
timeUnit = "sec";
else
timeUnit = unit;
}
public String getTimeUnit() {
return timeUnit;
}
public double getX(double x) {
return (x-xOrigin)*pixelWidth;
}
public double getY(double y) {
return (y-yOrigin)*pixelHeight;
}
public double getY(double y, int imageHeight) {
if (invertY || (Analyzer.getMeasurements()&Measurements.INVERT_Y)!=0) {
if (yOrigin!=0.0)
return (yOrigin-y)*pixelHeight;
else
return (imageHeight-y-1)*pixelHeight;
} else
return (y-yOrigin)*pixelHeight;
}
public double getZ(double z) {
return (z-zOrigin)*pixelDepth;
}
public double getRawX(double x) {
return x/pixelWidth + xOrigin;
}
public double getRawY(double y) {
return y/pixelHeight + yOrigin;
}
public double getRawZ(double z) {
return z/pixelDepth + zOrigin;
}
public double getRawY(double y, int imageHeight) {
if (invertY || (Analyzer.getMeasurements()&Measurements.INVERT_Y)!=0) {
if (yOrigin!=0.0)
return yOrigin-y/pixelHeight;
else
return imageHeight -y/pixelHeight - 1;
} else
return y/pixelHeight + yOrigin;
}
public void setFunction(int function, double[] coefficients, String unit) {
setFunction(function, coefficients, unit, false);
}
public void setFunction(int function, double[] coefficients, String unit, boolean zeroClip) {
if (function==NONE)
{disableDensityCalibration(); return;}
if (coefficients==null && function>=STRAIGHT_LINE && function<=EXP_RECOVERY)
return;
this.function = function;
this.coefficients = coefficients;
this.zeroClip = zeroClip;
if (unit!=null)
valueUnit = unit;
cTable = null;
}
public void setImage(ImagePlus imp) {
if (imp==null)
return;
int type = imp.getType();
int newBitDepth = imp.getBitDepth();
if (newBitDepth==16 && imp.getLocalCalibration().isSigned16Bit()) {
double[] coeff = new double[2]; coeff[0] = -32768.0; coeff[1] = 1.0;
setFunction(Calibration.STRAIGHT_LINE, coeff, DEFAULT_VALUE_UNIT);
} else if ((newBitDepth!=bitDepth&&bitDepth!=UNKNOWN) || type==ImagePlus.GRAY32 || type==ImagePlus.COLOR_RGB) {
String saveUnit = valueUnit;
disableDensityCalibration();
if (type==ImagePlus.GRAY32) valueUnit = saveUnit;
}
bitDepth = newBitDepth;
}
public void disableDensityCalibration() {
function = NONE;
coefficients = null;
cTable = null;
valueUnit = DEFAULT_VALUE_UNIT;
}
public String getValueUnit() {
return valueUnit;
}
public void setValueUnit(String unit) {
if (unit!=null)
valueUnit = unit;
}
public double[] getCoefficients() {
return coefficients;
}
public boolean calibrated() {
return function!=NONE;
}
public int getFunction() {
return function;
}
public float[] getCTable() {
if (cTable==null)
makeCTable();
return cTable;
}
public void setCTable(float[] table, String unit) {
if (table==null) {
disableDensityCalibration();
return;
}
if (bitDepth==UNKNOWN) {
if (table.length==256)
bitDepth = 8;
else if (table.length==65536)
bitDepth = 16;
}
if (bitDepth==16 && table.length!=65536)
throw new IllegalArgumentException("Table.length!=65536");
cTable = table;
function = CUSTOM;
coefficients = null;
zeroClip = false;
if (unit!=null) valueUnit = unit;
}
void makeCTable() {
if (bitDepth==16)
{make16BitCTable(); return;}
if (bitDepth==UNKNOWN)
bitDepth = 8;
if (bitDepth!=8)
return;
if (function==UNCALIBRATED_OD) {
cTable = new float[256];
for (int i=0; i<256; i++)
cTable[i] = (float)od(i);
} else if (function>=STRAIGHT_LINE && function<=EXP_RECOVERY && coefficients!=null) {
cTable = new float[256];
double value;
for (int i=0; i<256; i++) {
value = CurveFitter.f(function, coefficients, i);
if (zeroClip && value<0.0)
cTable[i] = 0f;
else
cTable[i] = (float)value;
}
} else
cTable = null;
}
void make16BitCTable() {
if (function>=STRAIGHT_LINE && function<=EXP_RECOVERY && coefficients!=null) {
cTable = new float[65536];
for (int i=0; i<65536; i++)
cTable[i] = (float)CurveFitter.f(function, coefficients, i);
} else
cTable = null;
}
double od(double v) {
if (invertedLut) {
if (v==255.0) v = 254.5;
return 0.434294481*Math.log(255.0/(255.0-v));
} else {
if (v==0.0) v = 0.5;
return 0.434294481*Math.log(255.0/v);
}
}
public double getCValue(int value) {
if (function==NONE)
return value;
if (function>=STRAIGHT_LINE && function<=EXP_RECOVERY && coefficients!=null) {
double v = CurveFitter.f(function, coefficients, value);
if (zeroClip && v<0.0)
return 0.0;
else
return v;
}
if (cTable==null)
makeCTable();
if (cTable!=null && value>=0 && value<cTable.length)
return cTable[value];
else
return value;
}
public double getCValue(double value) {
if (function==NONE)
return value;
else {
if (function>=STRAIGHT_LINE && function<=EXP_RECOVERY && coefficients!=null) {
double v = CurveFitter.f(function, coefficients, value);
if (zeroClip && v<0.0)
return 0.0;
else
return v;
} else
return getCValue((int)value);
}
}
public double getRawValue(double value) {
if (function==NONE)
return value;
if (function==STRAIGHT_LINE && coefficients!=null && coefficients.length==2 && coefficients[1]!=0.0)
return (value-coefficients[0])/coefficients[1];
if (cTable==null)
makeCTable();
float fvalue = (float)value;
float smallestDiff = Float.MAX_VALUE;
float diff;
int index = 0;
for (int i=0; i<cTable.length; i++) {
diff = fvalue - cTable[i];
if (diff<0f) diff = -diff;
if (diff<smallestDiff) {
smallestDiff = diff;
index = i;
}
}
return index;
}
public Calibration copy() {
return (Calibration)clone();
}
public synchronized Object clone() {
try {return super.clone();}
catch (CloneNotSupportedException e) {return null;}
}
public boolean equals(Calibration cal) {
if (cal==null)
return false;
boolean equal = true;
if (cal.pixelWidth!=pixelWidth || cal.pixelHeight!=pixelHeight || cal.pixelDepth!=pixelDepth)
equal = false;
if (!cal.unit.equals(unit))
equal = false;
if (!cal.valueUnit.equals(valueUnit) || cal.function!=function)
equal = false;
return equal;
}
public boolean isSigned16Bit() {
return (bitDepth==16 && function>=STRAIGHT_LINE && function<=EXP_RECOVERY && coefficients!=null
&& coefficients[0]==-32768.0 && coefficients[1]==1.0);
}
public void setSigned16BitCalibration() {
double[] coeff = new double[2];
coeff[0] = -32768.0;
coeff[1] = 1.0;
setFunction(STRAIGHT_LINE, coeff, "Gray Value");
}
public boolean zeroClip() {
return zeroClip;
}
public void setInvertY(boolean invertYCoordinates) {
invertY = invertYCoordinates;
}
public boolean getInvertY() {
return invertY;
}
public static void setLoopBackAndForth(boolean loop) {
loopBackAndForth = loop;
}
public String toString() {
return
"w=" + pixelWidth
+ ", h=" + pixelHeight
+ ", d=" + pixelDepth
+ ", unit=" + unit
+ ", f=" + function
+ ", nc=" + (coefficients!=null?""+coefficients.length:"null")
+ ", table=" + (cTable!=null?""+cTable.length:"null")
+ ", vunit=" + valueUnit
+ ", bd=" + bitDepth;
}
}