/*-
* Copyright 2015 Diamond Light Source Ltd.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package uk.ac.diamond.scisoft.analysis.image;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.january.dataset.IDataset;
/**
* Peem analysis class with useful methods to calculate angle rotation of an image given Peem specific detector
* properties
*/
public class ImagePeemUtils {
private static Map<Integer, Integer> dict;
static {
dict = new HashMap<Integer, Integer>();
dict.put(10, -15);
dict.put(20, 20);
dict.put(40, 45);
dict.put(50, 51);
dict.put(80, 65);
}
/**
* Calculates the rotation angle of an image given a field of view value and rotation of a peem manipulator (theta)
* The resulting rotation angle is the difference between the rotation of the microscope magnetic lenses (alpha) and
* theta. The alpha angle can be deduced from the FOV, using the lookup table with a set of FOV/alpha corresponding
* values.<br>
* If a FOV entered is not in the table, then a linear interpolation is used to deduct the corresponding alpha
* angle.
*
* @param fov
* @param theta
* @return angle
*/
public static double getAngleFromFOV(double fov, double theta) {
double alpha = 0;
// if fov not in the map, calculate it with linear interpolation
// using the following equation:
// alpha = alpha1 + (fov - fov1)*((alpha2 - alpha1)/(fov2 - fov1))
if (!dict.containsKey((int)fov)){
List<Integer> fovs = new ArrayList<Integer>(dict.keySet());
Collections.sort(fovs);
for (int i = 0; i < fovs.size(); i++) {
if (i + 1 < fovs.size() && fov > fovs.get(i) && fov < fovs.get(i + 1)) {
double alpha1 = dict.get(fovs.get(i));
double fov1 = fovs.get(i);
double alpha2 = dict.get(fovs.get(i + 1));
double fov2 = fovs.get(i + 1);
alpha = alpha1 + ((fov - fov1) * ((alpha2 - alpha1) / (fov2 - fov1)));
break;
}
}
if (fov > fovs.get(fovs.size() - 1)) {
double alpha1 = dict.get(fovs.get(fovs.size() - 2));
double fov1 = fovs.get(fovs.size() - 2);
double alpha2 = dict.get(fovs.get(fovs.size() - 1));
double fov2 = fovs.get(fovs.size() - 1);
alpha = alpha1 + ((fov - fov1) * ((alpha2 - alpha1) / (fov2 - fov1)));
}
} else {
alpha = dict.get((int)fov);
}
return alpha - theta;
}
private static double round(double value) {
BigDecimal bd = new BigDecimal(Double.toString(value));
bd = bd.setScale(5, BigDecimal.ROUND_HALF_UP);
return bd.doubleValue();
}
/**
* Given two 1 dimensional datasets containing a list of X positions and Y positions, this method returns an array of
* steps between a X/Y position and its predecessor
*
* @param psx
* @param psy
* @return translations in microns
*/
public static double[][][] getMotorTranslationsArray(IDataset psx, IDataset psy) {
int[] matrixSize = getColumnAndRowNumber(psx, psy);
int rows = matrixSize[1];
int columns = matrixSize[0];
List<Double> xShifts = new ArrayList<Double>(rows);
xShifts.add(0.0);
// get xshifts
for(int i = columns; i < psx.getSize(); i=i+columns) {
double previousX = psx.getDouble(i - columns);
double currentX = psx.getDouble(i);
double translX = (currentX - previousX) * 1000;
double rounded = round(translX);
xShifts.add(rounded);
}
// get yshifts
List<Double> yShifts = new ArrayList<Double>(columns);
yShifts.add(0.0);
for (int i = 1; i < psy.getSize(); i++) {
if (i % columns == 0) {
yShifts.add(0.0);
} else {
double previousY = psy.getDouble(i - 1);
double currentY = psy.getDouble(i);
double translY = (currentY - previousY) * 1000;
double rounded = round(translY);
yShifts.add(rounded);
}
}
// Fill translation array
double[][][] translations = new double[columns][rows][2];
for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {
double x = xShifts.get(i);
double y = yShifts.get(j);
if (x == 0 && y != 0)
x = y;
if (x != 0 && y == 0)
y = x;
translations[j][i][0] = x;
translations[j][i][1] = y;
}
}
return translations;
}
/**
* Fill an array with translation values
*
* @param value
* @param rows
* @param columns
* @return Filled array
*/
public static double[][][] fillArray(double[] value, int rows, int columns) {
double[][][] array = new double[columns][rows][2];
for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {
array[j][i] = value;
}
}
return array;
}
/**
* Given two 1 dimensional datasets containing a list of X positions and Y positions, this method returns a list of
* steps between a X/Y position and its predecessor
*
* @param psx
* @param psy
* @return translations in microns
*/
public static List<double[]> getMotorTranslations(IDataset psx, IDataset psy) {
int[] matrixSize = getColumnAndRowNumber(psx, psy);
List<double[]> translations = new ArrayList<double[]>();
int rows = matrixSize[1];
int columns = matrixSize[0];
int count = 0;
int xshift = rows;
for (int i = 0; i < columns; i++) {
for (int j = 0; j < rows; j++) {
double currentY = psy.getDouble(count);
double previousY = 0;
if (count >= 1)
previousY = psy.getDouble(count - 1);
if (j == 0) {
currentY = 0;
previousY = 0;
}
double currentX = psx.getDouble(count);
double previousX = 0;
if (i == 0) {
currentX = 0;
previousX = 0;
}
if (count >= xshift)
previousX = psx.getDouble(count - xshift);
double translX = (currentX - previousX) * 1000;
double translY = (currentY - previousY) * 1000;
if (translX == 0 && translY != 0)
translX = translY;
else if (translY == 0 && translX != 0)
translY = translX;
translations.add(new double[] { translX, translY });
count++;
}
}
return translations;
}
/**
* Returns the number of columns and rows given a list of X/Y positions by counting the number of times a
* particular position is identical to its predecessor.
*
* @param psx
* @param psy
* @return column and row array
*/
@SuppressWarnings("unused")
public static int[] getColumnAndRowNumber(IDataset psx, IDataset psy) {
int columns = 0, rows = 0, yIndex = 0;
// just return the integer value of the square root
int[] shape = psx.getShape();
rows = (int)Math.sqrt(shape[0]);
columns = shape[0] / rows;
return new int[] { columns, rows };
}
}