use of ini.trakem2.display.Patch in project TrakEM2 by trakem2.
the class Display method insertStack.
/**
* @param stack_patch is just a Patch of a series of Patch that make a stack of Patches.
*/
private boolean insertStack(final ProjectThing target_landmarks, final Project source, final ProjectThing source_landmarks, final Patch stack_patch) {
final List<Ball> l1 = new ArrayList<Ball>();
final List<Ball> l2 = new ArrayList<Ball>();
// source is the one that has the stack_patch
final Collection<ProjectThing> b1s = source_landmarks.findChildrenOfType("ball");
// target is this
final Collection<ProjectThing> b2s = target_landmarks.findChildrenOfType("ball");
final HashSet<String> seen = new HashSet<String>();
for (final ProjectThing b1 : b1s) {
final Ball ball1 = (Ball) b1.getObject();
if (null == ball1) {
Utils.log("ERROR: there's an empty 'ball' node in target project" + project.toString());
return false;
}
final String title1 = ball1.getTitle();
for (final ProjectThing b2 : b2s) {
final Ball ball2 = (Ball) b2.getObject();
if (null == ball2) {
Utils.log("ERROR: there's an empty 'ball' node in source project" + source.toString());
return false;
}
if (title1.equals(ball2.getTitle())) {
if (seen.contains(title1))
continue;
seen.add(title1);
l1.add(ball1);
l2.add(ball2);
}
}
}
if (l1.size() < 4) {
Utils.log("ERROR: found only " + l1.size() + " common landmarks: needs at least 4!");
return false;
}
// Extract coordinates of source project landmarks, in patch stack coordinate space
final List<double[]> c1 = new ArrayList<double[]>();
for (final Ball ball1 : l1) {
final Map<Layer, double[]> m = ball1.getRawBalls();
if (1 != m.size()) {
Utils.log("ERROR: ball object " + ball1 + " from target project " + project + " has " + m.size() + " balls instead of just 1.");
return false;
}
final Map.Entry<Layer, double[]> e = m.entrySet().iterator().next();
final Layer layer = e.getKey();
final double[] xyr = e.getValue();
final double[] fin = new double[] { xyr[0], xyr[1] };
final AffineTransform affine = ball1.getAffineTransformCopy();
try {
affine.preConcatenate(stack_patch.getAffineTransform().createInverse());
} catch (final Exception nite) {
IJError.print(nite);
return false;
}
final double[] fout = new double[2];
affine.transform(fin, 0, fout, 0, 1);
c1.add(new double[] { fout[0], fout[1], layer.getParent().indexOf(layer) });
}
// Extract coordinates of target (this) project landmarks, in calibrated world space
final List<double[]> c2 = new ArrayList<double[]>();
for (final Ball ball2 : l2) {
final double[][] b = ball2.getBalls();
if (1 != b.length) {
Utils.log("ERROR: ball object " + ball2 + " from source project " + source + " has " + b.length + " balls instead of just 1.");
return false;
}
final double[] fin = new double[] { b[0][0], b[0][1] };
final AffineTransform affine = ball2.getAffineTransformCopy();
final double[] fout = new double[2];
affine.transform(fin, 0, fout, 0, 1);
c2.add(new double[] { fout[0], fout[1], b[0][2] });
}
// Print landmarks:
Utils.log("Landmarks:");
for (Iterator<double[]> it1 = c1.iterator(), it2 = c2.iterator(); it1.hasNext(); ) {
Utils.log(Utils.toString(it1.next()) + " <--> " + Utils.toString(it2.next()));
}
// Create point matches
final List<PointMatch> pm = new ArrayList<PointMatch>();
for (Iterator<double[]> it1 = c1.iterator(), it2 = c2.iterator(); it1.hasNext(); ) {
pm.add(new mpicbg.models.PointMatch(new mpicbg.models.Point(it1.next()), new mpicbg.models.Point(it2.next())));
}
// Estimate AffineModel3D
final AffineModel3D aff3d = new AffineModel3D();
try {
aff3d.fit(pm);
} catch (final Exception e) {
IJError.print(e);
return false;
}
// Create and add the Stack
final String path = stack_patch.getImageFilePath();
final Stack st = new Stack(project, new File(path).getName(), 0, 0, getLayerSet().getLayers().get(0), path);
st.setInvertibleCoordinateTransform(aff3d);
getLayerSet().add(st);
return true;
}
use of ini.trakem2.display.Patch in project TrakEM2 by trakem2.
the class Display3D method showVolume.
public static void showVolume(final Patch p) {
final Display3D d3d = get(p.getLayerSet());
d3d.adjustResampling();
// d3d.universe.resetView();
final String title = makeTitle(p) + " volume";
// remove if present
d3d.universe.removeContent(title);
final PatchStack ps = p.makePatchStack();
final ImagePlus imp = get8BitStack(ps);
final Content ct = d3d.universe.addVoltex(imp, null, title, 0, new boolean[] { true, true, true }, d3d.resample);
setTransform(ct, ps.getPatch(0));
// locks the added content
ct.setLocked(true);
}
use of ini.trakem2.display.Patch in project TrakEM2 by trakem2.
the class Display3D method showOrthoslices.
public static void showOrthoslices(final Patch p) {
final Display3D d3d = get(p.getLayerSet());
d3d.adjustResampling();
// d3d.universe.resetView();
final String title = makeTitle(p) + " orthoslices";
// remove if present
d3d.universe.removeContent(title);
final PatchStack ps = p.makePatchStack();
final ImagePlus imp = get8BitStack(ps);
final Content ct = d3d.universe.addOrthoslice(imp, null, title, 0, new boolean[] { true, true, true }, d3d.resample);
setTransform(ct, ps.getPatch(0));
// locks the added content
ct.setLocked(true);
}
use of ini.trakem2.display.Patch in project TrakEM2 by trakem2.
the class ManualAlignMode method apply.
@Override
public boolean apply() {
// Check there's more than one layer
if (m.size() < 2) {
Utils.showMessage("Need more than one layer to align!");
return false;
}
// Check that the current layer is one of the layers with landmarks.
// Will be used as reference
final Layer ref_layer = display.getLayer();
if (null == m.get(ref_layer)) {
Utils.showMessage("Please scroll to a layer with landmarks,\nto be used as reference.");
return false;
}
// Check that all layers have the same number of landmarks
int n_landmarks = -1;
for (final Map.Entry<Layer, Landmarks> e : m.entrySet()) {
final Landmarks lm = e.getValue();
if (-1 == n_landmarks) {
n_landmarks = lm.points.size();
continue;
}
if (n_landmarks != lm.points.size()) {
Utils.showMessage("Can't apply: there are different amounts of landmarks per layer.\nSee the log window.");
for (final Map.Entry<Layer, Landmarks> ee : m.entrySet()) {
Utils.log(ee.getValue().points.size() + " landmarks in layer " + ee.getKey());
}
return false;
}
}
// Sort Layers by Z
final TreeMap<Layer, Landmarks> sorted = new TreeMap<Layer, Landmarks>(new Comparator<Layer>() {
@Override
public boolean equals(final Object ob) {
return this == ob;
}
@Override
public int compare(final Layer l1, final Layer l2) {
// Ascending order
final double dz = l1.getZ() - l2.getZ();
if (dz < 0)
return -1;
else if (dz > 0)
return 1;
else
return 0;
}
});
sorted.putAll(m);
int iref = 0;
for (final Layer la : sorted.keySet()) {
if (la != ref_layer)
iref++;
else
break;
}
// Ok, now ask for a model
final GenericDialog gd = new GenericDialog("Model");
gd.addChoice("Model:", Align.Param.modelStrings, Align.Param.modelStrings[1]);
gd.addCheckbox("Propagate to first layer", 0 != iref);
((Component) gd.getCheckboxes().get(0)).setEnabled(0 != iref);
gd.addCheckbox("Propagate to last layer", sorted.size() - 1 != iref);
((Component) gd.getCheckboxes().get(1)).setEnabled(sorted.size() - 1 != iref);
gd.showDialog();
if (gd.wasCanceled())
return false;
final int model_index = gd.getNextChoiceIndex();
final boolean propagate_to_first = gd.getNextBoolean();
final boolean propagate_to_last = gd.getNextBoolean();
int min;
// Create a model as desired
final AbstractAffineModel2D<?> model;
switch(model_index) {
case 0:
min = 1;
model = new TranslationModel2D();
break;
case 1:
min = 2;
model = new RigidModel2D();
break;
case 2:
min = 2;
model = new SimilarityModel2D();
break;
case 3:
min = 3;
model = new AffineModel2D();
break;
default:
Utils.log("Unknown model index!");
return false;
}
if (n_landmarks < min) {
Utils.showMessage("Need at least " + min + " landmarks for a " + Align.Param.modelStrings[model_index] + " model");
return false;
}
Bureaucrat.createAndStart(new Worker.Task("Aligning layers with landmarks") {
@Override
public void exec() {
// Find layers with landmarks, in increasing Z.
// Match in pairs.
// So, get two submaps: from ref_layer to first, and from ref_layer to last
// strictly lower Z than ref_layer
final SortedMap<Layer, Landmarks> first_chunk_ = new TreeMap<Layer, Landmarks>(sorted.headMap(ref_layer));
// .. so add ref_layer
first_chunk_.put(ref_layer, m.get(ref_layer));
// equal or larger Z than ref_layer
final SortedMap<Layer, Landmarks> second_chunk = sorted.tailMap(ref_layer);
final SortedMap<Layer, Landmarks> first_chunk;
// Reverse order of first_chunk
if (first_chunk_.size() > 1) {
final SortedMap<Layer, Landmarks> fc = new TreeMap<Layer, Landmarks>(new Comparator<Layer>() {
@Override
public boolean equals(final Object ob) {
return this == ob;
}
@Override
public int compare(final Layer l1, final Layer l2) {
// Descending order
final double dz = l2.getZ() - l1.getZ();
if (dz < 0)
return -1;
else if (dz > 0)
return 1;
else
return 0;
}
});
fc.putAll(first_chunk_);
first_chunk = fc;
} else {
first_chunk = first_chunk_;
}
final LayerSet ls = ref_layer.getParent();
final Collection<Layer> affected_layers = new HashSet<Layer>(m.keySet());
// Gather all Patch instances that will be affected
final ArrayList<Patch> patches = new ArrayList<Patch>();
for (final Layer la : m.keySet()) patches.addAll(la.getAll(Patch.class));
if (propagate_to_first && first_chunk.size() > 1) {
final Collection<Layer> affected = ls.getLayers().subList(0, ls.indexOf(first_chunk.lastKey()));
for (final Layer la : affected) {
patches.addAll(la.getAll(Patch.class));
}
affected_layers.addAll(affected);
}
if (propagate_to_last && second_chunk.size() > 1) {
final Collection<Layer> affected = ls.getLayers().subList(ls.indexOf(second_chunk.lastKey()) + 1, ls.size());
for (final Layer la : affected) {
patches.addAll(la.getAll(Patch.class));
}
}
// Transform segmentations along with patches
AlignTask.transformPatchesAndVectorData(patches, new Runnable() {
@Override
public void run() {
// Apply!
// TODO: when adding non-linear transforms, use this single line for undo instead of all below:
// (these transforms may be non-linear as well, which alter mipmaps.)
// ls.addTransformStepWithData(affected_layers);
// Setup undo:
// Find all images in the range of affected layers,
// plus all Displayable of those layers (but Patch instances in a separate DoTransforms step,
// to avoid adding a "data" undo for them, which would recreate mipmaps when undone).
// plus all ZDisplayable that paint in those layers
final HashSet<Displayable> ds = new HashSet<Displayable>();
final ArrayList<Displayable> patches = new ArrayList<Displayable>();
for (final Layer layer : affected_layers) {
for (final Displayable d : layer.getDisplayables()) {
if (d.getClass() == Patch.class) {
patches.add(d);
} else {
ds.add(d);
}
}
}
for (final ZDisplayable zd : ls.getZDisplayables()) {
for (final Layer layer : affected_layers) {
if (zd.paintsAt(layer)) {
ds.add((Displayable) zd);
break;
}
}
}
if (ds.size() > 0) {
final Displayable.DoEdits step = ls.addTransformStepWithData(ds);
if (patches.size() > 0) {
final ArrayList<DoStep> a = new ArrayList<DoStep>();
a.add(new Displayable.DoTransforms().addAll(patches));
step.addDependents(a);
}
}
if (first_chunk.size() > 1) {
final AffineTransform aff = align(first_chunk, model);
if (propagate_to_first) {
for (final Layer la : ls.getLayers().subList(0, ls.indexOf(first_chunk.lastKey()))) {
// exclusive last
la.apply(Patch.class, aff);
}
}
}
if (second_chunk.size() > 1) {
final AffineTransform aff = align(second_chunk, model);
if (propagate_to_last) {
for (final Layer la : ls.getLayers().subList(ls.indexOf(second_chunk.lastKey()) + 1, ls.size())) {
// exclusive last
la.apply(Patch.class, aff);
}
}
}
Display.repaint();
// Store current state
if (ds.size() > 0) {
final Displayable.DoEdits step2 = ls.addTransformStepWithData(ds);
if (patches.size() > 0) {
final ArrayList<DoStep> a2 = new ArrayList<DoStep>();
a2.add(new Displayable.DoTransforms().addAll(patches));
step2.addDependents(a2);
}
}
}
});
}
}, display.getProject());
return true;
}
use of ini.trakem2.display.Patch in project TrakEM2 by trakem2.
the class Patch method exportXML.
/**
* Opens and closes the tag and exports data. The image is saved in the directory provided in @param any as a String.
*/
@Override
public void exportXML(final StringBuilder sb_body, final String indent, final XMLOptions options) {
// TODO the Loader should handle the saving of images, not this class.
final String in = indent + "\t";
String path = null;
String path2 = null;
if (options.export_images) {
path = options.patches_dir + title;
// save image without overwriting, and add proper extension (.zip)
path2 = project.getLoader().exportImage(this, path, false);
// path2 will be null if the file exists already
}
sb_body.append(indent).append("<t2_patch\n");
String rel_path = null;
if (null != path && path.equals(path2)) {
// this happens when a DB project is exported. It may be a different path when it's a FS loader
// Utils.log2("p id=" + id + " path==path2");
rel_path = path2;
// TrakEM2 uses paths that always have '/' and never '\', so using java.io.File.separatorChar would be an error.
int i_slash = rel_path.lastIndexOf('/');
if (i_slash > 0) {
i_slash = rel_path.lastIndexOf('/', i_slash - 1);
if (-1 != i_slash) {
rel_path = rel_path.substring(i_slash + 1);
}
}
} else {
// Utils.log2("Setting rel_path to " + path2);
rel_path = path2;
}
// For FSLoader projects, saving a second time will save images as null unless calling it
if (null == rel_path) {
// Utils.log2("path2 was null");
final Object ob = project.getLoader().getPath(this);
path2 = null == ob ? null : (String) ob;
if (null == path2) {
// Utils.log2("ERROR: No path for Patch id=" + id + " and title: " + title);
// at least some clue for recovery
rel_path = title;
} else {
rel_path = path2;
}
}
// Utils.log("Patch path is: " + rel_path);
super.exportXML(sb_body, in, options);
final String[] RGB = Utils.getHexRGBColor(color);
int type = this.type;
if (-1 == this.type) {
Utils.log2("Retrieving type for p = " + this);
final ImagePlus imp = project.getLoader().fetchImagePlus(this);
if (null != imp)
type = imp.getType();
}
sb_body.append(in).append("type=\"").append(type).append("\"\n").append(in).append("file_path=\"").append(rel_path).append("\"\n").append(in).append("style=\"fill-opacity:").append(alpha).append(";stroke:#").append(RGB[0]).append(RGB[1]).append(RGB[2]).append(";\"\n").append(in).append("o_width=\"").append(o_width).append("\"\n").append(in).append("o_height=\"").append(o_height).append("\"\n");
if (null != original_path) {
sb_body.append(in).append("original_path=\"").append(original_path).append("\"\n");
}
sb_body.append(in).append("min=\"").append(min).append("\"\n");
sb_body.append(in).append("max=\"").append(max).append("\"\n");
final String pps = getPreprocessorScriptPath();
if (null != pps)
sb_body.append(in).append("pps=\"").append(project.getLoader().makeRelativePath(pps)).append("\"\n");
sb_body.append(in).append("mres=\"").append(meshResolution).append("\"\n");
if (hasCoordinateTransform()) {
sb_body.append(in).append("ct_id=\"").append(ct_id).append("\"\n");
}
if (hasAlphaMask()) {
sb_body.append(in).append("alpha_mask_id=\"").append(alpha_mask_id).append("\"\n");
}
sb_body.append(indent).append(">\n");
if (hasCoordinateTransform()) {
if (options.include_coordinate_transform) {
// Write an XML entry for the CoordinateTransform
char[] ct_chars = null;
try {
ct_chars = readCoordinateTransformFile();
} catch (final Exception e) {
IJError.print(e);
}
if (null != ct_chars) {
sb_body.append(ct_chars).append('\n');
} else {
Utils.log("ERROR: could not write the CoordinateTransform to the XML file!");
}
}
}
if (null != filters && filters.length > 0) {
// specify their own line termination
for (final IFilter f : filters) sb_body.append(f.toXML(in));
}
super.restXML(sb_body, in, options);
sb_body.append(indent).append("</t2_patch>\n");
}
Aggregations