MapTracker.java
package com.github.celldynamics.quimp.geom;
import java.awt.Point;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Predicate;
import com.github.celldynamics.quimp.utils.QuimPArrayUtils;
/**
* Compute forward and backward tracking maps from origin and coordinates maps.
*
* <p>Origin and coordinates maps are stored in both <i>QCONF</i> and <i>paQP</i> data files. Allow
* to
* track given outline point forward in backward using these maps.
*
* @author p.baniukiewicz
*
*/
public class MapTracker {
/**
* Denote bad index.
*/
public static final int BAD_INDEX = -1;
/**
* Decides whether include starting point in tracking. By default Matlab procedures do not
* include it. Therefore, trackXX(int, int, int) returns first tracked point AFTER initial one.
*/
public boolean includeFirst = false;
/**
* The forward map.
*/
int[][] forwardMap;
/**
* The backward map.
*/
int[][] backwardMap;
/**
* Number of rows in <i>Map</i> - equals to number of frames.
*/
int rowsFrames;
/**
* Number of columns in <i>Map</i> - equals to number of outline points set by resolution in Q
* Analysis.
*/
int colsIndexes;
/**
* Construct tracking maps.
*
* <p>Prepare tracking maps that are not stored by default in
* {@link com.github.celldynamics.quimp.filesystem.DataContainer}. This code is based on Matlab
* routine buildTrackMaps.m.
*
* <p><b>Note</b>
*
* <p>All frames are numbered from 0 as well as outline indexes. Nonexisting indexes are marked as
* -1.
*
* @param originMap originMap stored in
* {@link com.github.celldynamics.quimp.filesystem.DataContainer}
* @param coordMap coordMap stored in
* {@link com.github.celldynamics.quimp.filesystem.DataContainer}
* @see com.github.celldynamics.quimp.filesystem.DataContainer
* @see com.github.celldynamics.quimp.plugin.qanalysis.STmap
*/
public MapTracker(double[][] originMap, double[][] coordMap) {
forwardMap = QuimPArrayUtils.initInteger2dArray(originMap.length, originMap[0].length);
backwardMap = QuimPArrayUtils.initInteger2dArray(forwardMap.length, forwardMap[0].length);
QuimPArrayUtils.fill2Darray(forwardMap, -1);
QuimPArrayUtils.fill2Darray(backwardMap, -1);
rowsFrames = forwardMap.length;
colsIndexes = forwardMap[0].length;
double[] minV = new double[3];
double[] minI = new double[3];
// backward map
for (int i = 1; i < rowsFrames; i++) {
for (int j = 0; j < colsIndexes; j++) {
double p = originMap[i][j];
double[] diffA = rowDiff(p, coordMap[i - 1]);
double[] diffB = rowDiff(p, rowAdd(-1, coordMap[i - 1]));
double[] diffC = rowDiff(p, rowAdd(+1, coordMap[i - 1]));
double[] minDiffA = QuimPArrayUtils.minArrayIndexElement(diffA);
double[] minDiffB = QuimPArrayUtils.minArrayIndexElement(diffB);
double[] minDiffC = QuimPArrayUtils.minArrayIndexElement(diffC);
// copy min values to array
minV[0] = minDiffA[0];
minV[1] = minDiffB[0];
minV[2] = minDiffC[0];
minI[0] = minDiffA[1];
minI[1] = minDiffB[1];
minI[2] = minDiffC[1];
double[] minMinV = QuimPArrayUtils.minArrayIndexElement(minV);
backwardMap[i][j] = (int) minI[(int) minMinV[1]]; // copy index of smallest among A,B,C
}
}
// forward map
for (int i = 0; i < rowsFrames - 1; i++) {
for (int j = 0; j < colsIndexes; j++) {
double p = coordMap[i][j];
double[] diffA = rowDiff(p, originMap[i + 1]);
double[] diffB = rowDiff(p, rowAdd(-1, originMap[i + 1]));
double[] diffC = rowDiff(p, rowAdd(+1, originMap[i + 1]));
double[] minDiffA = QuimPArrayUtils.minArrayIndexElement(diffA);
double[] minDiffB = QuimPArrayUtils.minArrayIndexElement(diffB);
double[] minDiffC = QuimPArrayUtils.minArrayIndexElement(diffC);
// copy min values to array
minV[0] = minDiffA[0];
minV[1] = minDiffB[0];
minV[2] = minDiffC[0];
minI[0] = minDiffA[1];
minI[1] = minDiffB[1];
minI[2] = minDiffC[1];
double[] minMinV = QuimPArrayUtils.minArrayIndexElement(minV);
forwardMap[i][j] = (int) minI[(int) minMinV[1]]; // copy index of smallest among A,B,C
}
}
}
/**
* Compute <tt>p</tt>-<tt>row</tt>.
*
* <p>Based on Matlab routine buildTrackMaps.m
*
* @param p single value that row will be subtracted from
* @param row row to subtract from p
* @return Vector of <tt>p</tt>-<tt>row</tt> as a copy.
*/
private double[] rowDiff(double p, double[] row) {
double[] cpy = new double[row.length];
for (int i = 0; i < cpy.length; i++) {
cpy[i] = Math.abs(p - row[i]);
}
return cpy;
}
/**
* Add value <tt>val</tt> to vector <tt>row</tt>
*
* <p>Based on Matlab routine buildTrackMaps.m
*
* @param val Value to add
* @param row Input matrix
* @return <tt>row</tt>+<tt>val</tt> as a copy.
*/
private double[] rowAdd(double val, double[] row) {
double[] cpy = new double[row.length];
System.arraycopy(row, 0, cpy, 0, row.length);
for (int i = 0; i < cpy.length; i++) {
cpy[i] += val;
}
return cpy;
}
/**
* Get position of <tt>membraneIndex</tt> on frame <tt>currentFrame+1</tt>.
*
* @param frame frame number, counted from 0
* @param membraneIndex index of point on membrane on frame <tt>currentFrame</tt>
* @return corresponding index on next frame. Returns -1 when there is neither next frame nor
* index.
*/
public int getNext(int frame, int membraneIndex) {
if (frame >= rowsFrames || membraneIndex >= colsIndexes || frame < 0 || membraneIndex < 0) {
return BAD_INDEX;
}
return forwardMap[frame][membraneIndex]; // membrane index on frame
}
/**
* Get position of <tt>membraneIndex</tt> on frame <tt>currentFrame-1</tt>.
*
* @param currentFrame frame number, counted from 0
* @param membraneIndex index of point on membrane on frame <tt>currentFrame</tt>
* @return corresponding index on previous frame. Returns -1 when there is neither next frame
* nor index.
*/
public int getPrev(int currentFrame, int membraneIndex) {
if (currentFrame >= rowsFrames || membraneIndex >= colsIndexes || currentFrame < 0
|| membraneIndex < 0) {
return BAD_INDEX;
}
return backwardMap[currentFrame][membraneIndex];
}
/**
* Track given point forward.
*
* @param currentFrame Starting frame (not included in results - depends on
* <tt>includeFirst</tt> flag)
* @param membraneIndex Tracked membrane index
* @param timeSpan Number of frames to track
* @return Indexes of point <tt>membraneIndex</tt> in frames <tt>currentFrame+1</tt> to
* <tt>currentFrame+timeSpan</tt>
* @deprecated Use trackForwardValid(int, int, int) instead
*/
@Deprecated
public int[] trackForward(int currentFrame, int membraneIndex, int timeSpan) {
if (includeFirst) {
timeSpan++;
}
int[] ret = new int[timeSpan];
if (includeFirst) {
ret[0] = membraneIndex;
} else {
ret[0] = getNext(currentFrame, membraneIndex);
}
for (int t = 1; t < timeSpan; t++) {
ret[t] = getNext(currentFrame + t, (int) ret[t - 1]);
}
return ret;
}
/**
* Track given point forward.
*
* @param currentFrame Starting frame (not included in results - depends on
* <tt>includeFirst</tt> flag)
* @param membraneIndex Tracked membrane index
* @param timeSpan Number of frames to track
* @return Indexes of point <tt>membraneIndex</tt> in frames <tt>currentFrame+1</tt> to
* <tt>currentFrame+timeSpan</tt>. Only correct. Do no return negative indexes but may
* return empty array. Keep order [frame,index]
*/
public List<Point> trackForwardValid(int currentFrame, int membraneIndex, int timeSpan) {
if (includeFirst) {
timeSpan++;
}
ArrayList<Point> ret = new ArrayList<>();
if (includeFirst) {
ret.add(new Point(currentFrame, membraneIndex));
} else {
ret.add(new Point(currentFrame + 1, getNext(currentFrame, membraneIndex)));
}
for (int t = 1; t < timeSpan; t++) {
ret.add(new Point(ret.get(t - 1).x + 1, getNext(currentFrame + t, (int) ret.get(t - 1).y)));
}
ret.removeIf(new PredicateBadIndex());
return ret;
}
/**
* Track given point backward.
*
* @param currentFrame Starting frame (not included in results - depends on
* <tt>includeFirst</tt> flag)
* @param membraneIndex Tracked membrane index
* @param timeSpan Number of frames to track
* @return Indexes of point <tt>membraneIndex</tt> in frames <tt>currentFrame-1</tt> to
* <tt>currentFrame-timeSpan</tt>
* @deprecated Use trackBackwardValid(int, int, int) instead
*/
@Deprecated
public int[] trackBackward(int currentFrame, int membraneIndex, int timeSpan) {
if (includeFirst) {
timeSpan++;
}
int[] ret = new int[timeSpan];
if (includeFirst) {
ret[timeSpan - 1] = membraneIndex;
} else {
ret[timeSpan - 1] = getPrev(currentFrame, membraneIndex);
}
for (int t = timeSpan - 2; t >= 0; t--) {
ret[t] = getPrev(currentFrame - (timeSpan - t - 1), (int) ret[t + 1]);
}
return ret;
}
/**
* Track given point backward.
*
* @param currentFrame Starting frame (not included in results - depends on
* <tt>includeFirst</tt> flag)
* @param membraneIndex Tracked membrane index
* @param timeSpan Number of frames to track
* @return Indexes of point <tt>membraneIndex</tt> in frames <tt>currentFrame-1</tt> to
* <tt>currentFrame-timeSpan</tt> Only correct. Do no return negative indexes but may
* return empty array. Keep order [frame,index]
*/
public List<Point> trackBackwardValid(int currentFrame, int membraneIndex, int timeSpan) {
if (includeFirst) {
timeSpan++;
}
ArrayList<Point> ret = new ArrayList<>();
if (includeFirst) {
ret.add(new Point(currentFrame, membraneIndex));
} else {
ret.add(new Point(currentFrame - 1, getPrev(currentFrame, membraneIndex)));
}
for (int t = 1; t < timeSpan; t++) {
ret.add(new Point(ret.get(t - 1).x - 1, getPrev(currentFrame - t, (int) ret.get(t - 1).y)));
}
Collections.reverse(ret);
ret.removeIf(new PredicateBadIndex());
return ret;
}
/**
* Helper that generates range of frames for given input parameters.
*
* <p>These are frames that {@link #trackForward(int, int, int)} returns indexes for. Input
* parameters must be the same as for {@link #trackForward(int, int, int)}.
*
* @param currentFrame Starting frame (not included in results)
* @param timeSpan timeSpan Number of frames to track
* @return Array of frame numbers
* @deprecated Use {@link #trackForwardValid(int, int, int)} instead
*/
@Deprecated
public int[] getForwardFrames(int currentFrame, int timeSpan) {
int f;
if (includeFirst) {
timeSpan++;
}
int[] ret = new int[timeSpan];
if (includeFirst) {
f = currentFrame;
} else {
f = currentFrame + 1;
}
int l = 0;
do {
ret[l++] = f++;
} while (l < timeSpan);
return ret;
}
/**
* Helper that generates range of frames for given input parameters.
*
* <p>These are frames that {@link #trackBackward(int, int, int)} returns indexes for. Input
* parameters must be the same as for {@link #trackBackward(int, int, int)}.
*
* @param currentFrame Starting frame (not included in results)
* @param timeSpan timeSpan Number of frames to track
* @return Array of frame numbers
* @deprecated Use {@link #trackBackwardValid(int, int, int)} instead
*/
@Deprecated
public int[] getBackwardFrames(int currentFrame, int timeSpan) {
int f;
if (includeFirst) {
timeSpan++;
}
int[] ret = new int[timeSpan];
if (includeFirst) {
f = currentFrame;
} else {
f = currentFrame - 1;
}
int l = timeSpan - 1;
do {
ret[l--] = f--;
} while (l >= 0);
return ret;
}
/**
* Predicate class for detection bad indexes in tracking methods.
*
* @author p.baniukiewicz
*
*/
class PredicateBadIndex implements Predicate<Point> {
@Override
public boolean test(Point t) {
if (t.y == MapTracker.BAD_INDEX) {
return true;
} else {
return false;
}
}
}
}