use of ij3d.Content in project TrakEM2 by trakem2.
the class Display3D method showOrthoslices.
public static void showOrthoslices(final ImagePlus imp, final String title, final int wx, final int wy, final float scale2D, final Layer first) {
final Display3D d3d = get(first.getParent());
d3d.universe.removeContent(title);
final Content ct = d3d.universe.addOrthoslice(imp, null, title, 0, new boolean[] { true, true, true }, 1);
final Calibration cal = imp.getCalibration();
final Transform3D t = new Transform3D(new double[] { 1, 0, 0, wx * cal.pixelWidth * scale2D, 0, 1, 0, wy * cal.pixelHeight * scale2D, // not pixelDepth!
0, // not pixelDepth!
0, // not pixelDepth!
scale2D, // not pixelDepth!
first.getZ() * cal.pixelWidth * scale2D, 0, 0, 0, 1 });
// why scale2D has to be there at all reflects a horrible underlying setting of the calibration, plus of the scaling in the Display3D.
Utils.log(t);
ct.applyTransform(t);
ct.setLocked(true);
}
use of ij3d.Content in project TrakEM2 by trakem2.
the class Display3D method addFatPoint.
/**
* Expects uncalibrated wx,wy,wz, (i.e. pixel values), to be calibrated by @param ls calibration.
*/
public static final Future<Content> addFatPoint(final String title, final LayerSet ls, final double wx, final double wy, final double wz, final double wr, final Color color) {
final Display3D d3d = Display3D.get(ls);
d3d.universe.removeContent(title);
final Content ct = d3d.universe.createContent(new CustomTriangleMesh(d3d.createFatPoint(wx, wy, wz, wr, ls.getCalibrationCopy()), new Color3f(color), 0), title);
ct.setLocked(true);
return d3d.addContent(ct);
}
use of ij3d.Content in project TrakEM2 by trakem2.
the class Display3D method createMesh.
/**
* Returns a function that returns a Content object.
* Does NOT add the Content to the universe; it merely creates it.
*/
public Callable<Content> createMesh(final ProjectThing pt, final Displayable displ, final int resample) {
// OBSOLETE
final double scale = 1.0;
return new Callable<Content>() {
@Override
public Content call() {
Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
try {
// the list 'triangles' is really a list of Point3f, which define a triangle every 3 consecutive points. (TODO most likely Bene Schmid got it wrong: I don't think there's any need to have the points duplicated if they overlap in space but belong to separate triangles.)
final List<Point3f> triangles;
// boolean no_culling_ = false;
final Class<?> c;
final boolean line_mesh;
final int line_mesh_mode;
if (null == displ) {
c = null;
line_mesh = false;
line_mesh_mode = Integer.MAX_VALUE;
} else {
c = displ.getClass();
line_mesh = Tree.class.isAssignableFrom(c) || Polyline.class == c;
if (Tree.class.isAssignableFrom(c))
line_mesh_mode = CustomLineMesh.PAIRWISE;
else if (Polyline.class == c)
line_mesh_mode = CustomLineMesh.CONTINUOUS;
else
// disabled
line_mesh_mode = Integer.MAX_VALUE;
}
List<Point3f> extra_triangles = null;
List<Color3f> triangle_colors = null, extra_triangle_colors = null;
int rs = resample;
if (displ instanceof AreaContainer) {
if (// will adjust this.resample, and return it (even if it's a default value)
-1 == resample)
// will adjust this.resample, and return it (even if it's a default value)
rs = Display3D.this.resample = adjustResampling();
else
rs = Display3D.this.resample;
}
if (AreaList.class == c) {
triangles = ((AreaList) displ).generateTriangles(scale, rs);
// triangles = removeNonManifold(triangles);
} else if (Ball.class == c) {
final double[][][] globe = Ball.generateGlobe(12, 12);
triangles = ((Ball) displ).generateTriangles(scale, globe);
} else if (displ instanceof Line3D) {
// Pipe and Polyline
// adjustResampling(); // fails horribly, needs first to correct mesh-generation code
triangles = ((Line3D) displ).generateTriangles(scale, 12, 1);
} else if (displ instanceof Tree<?>) {
// A 3D wire skeleton, using CustomLineMesh
final Tree.MeshData skeleton = ((Tree<?>) displ).generateSkeleton(scale, 12, 1);
triangles = skeleton.verts;
triangle_colors = skeleton.colors;
if (displ instanceof Treeline) {
final Tree.MeshData tube = ((Treeline) displ).generateMesh(scale, 12);
extra_triangles = tube.verts;
extra_triangle_colors = tube.colors;
} else if (displ instanceof AreaTree) {
final Tree.MeshData mesh = ((AreaTree) displ).generateMesh(scale, rs);
extra_triangles = mesh.verts;
extra_triangle_colors = mesh.colors;
}
// avoid issues with MultiMesh
if (null != extra_triangles && extra_triangles.isEmpty())
extra_triangles = null;
} else if (Connector.class == c) {
final Tree.MeshData octopus = ((Connector) displ).generateMesh(scale, 12);
triangles = octopus.verts;
triangle_colors = octopus.colors;
} else if (null == displ && pt.getType().equals("profile_list")) {
triangles = Profile.generateTriangles(pt, scale);
// no_culling_ = true;
} else {
Utils.log("Unrecognized type for 3D mesh generation: " + (null != displ ? displ.getClass() : null) + " : " + displ);
triangles = null;
}
// safety checks
if (null == triangles) {
Utils.log("Some error ocurred: can't create triangles for " + displ);
return null;
}
if (0 == triangles.size()) {
Utils.log2("Skipping empty mesh for " + displ.getTitle());
return null;
}
if (!line_mesh && 0 != triangles.size() % 3) {
Utils.log2("Skipping non-multiple-of-3 vertices list generated for " + displ.getTitle());
return null;
}
final Color color;
final float alpha;
final String title;
if (null != displ) {
color = displ.getColor();
alpha = displ.getAlpha();
title = makeTitle(displ);
} else if (pt.getType().equals("profile_list")) {
// for profile_list: get from the first (what a kludge; there should be a ZDisplayable ProfileList object)
final Object obp = ((ProjectThing) pt.getChildren().get(0)).getObject();
if (null == obp)
return null;
final Displayable di = (Displayable) obp;
color = di.getColor();
alpha = di.getAlpha();
title = makeProfileListTitle(pt);
} else {
title = pt.toString() + " #" + pt.getId();
color = null;
alpha = 1.0f;
}
// Why for all? Above no_culling_ is set to true or false, depending upon type. --> Because with transparencies it looks proper and better when no_culling is true.
// for ALL
final boolean no_culling = true;
Content ct = null;
try {
final Color3f c3 = new Color3f(color);
// If it exists, remove and add as new:
universe.removeContent(title);
final CustomMesh cm;
if (line_mesh) {
// ct = universe.createContent(new CustomLineMesh(triangles, line_mesh_mode, c3, 0), title);
cm = new CustomLineMesh(triangles, line_mesh_mode, c3, 0);
} else if (no_culling) {
// create a mesh with the same color and zero transparency (that is, full opacity)
final CustomTriangleMesh mesh = new CustomTriangleMesh(triangles, c3, 0);
// Set mesh properties for double-sided triangles
final PolygonAttributes pa = mesh.getAppearance().getPolygonAttributes();
pa.setCullFace(PolygonAttributes.CULL_NONE);
pa.setBackFaceNormalFlip(true);
mesh.setColor(c3);
// After setting properties, add to the viewer
// ct = universe.createContent(mesh, title);
cm = mesh;
} else {
// ct = universe.createContent(new CustomTriangleMesh(triangles, c3, 0), title);
cm = new CustomTriangleMesh(triangles, c3, 0);
}
if (null != triangle_colors)
cm.setColor(triangle_colors);
if (null == extra_triangles || 0 == extra_triangles.size()) {
ct = universe.createContent(cm, title);
} else {
final CustomTriangleMesh extra = new CustomTriangleMesh(extra_triangles, c3, 0);
if (null != extra_triangle_colors) {
// Set mesh properties for double-sided triangles
final PolygonAttributes pa = extra.getAppearance().getPolygonAttributes();
pa.setCullFace(PolygonAttributes.CULL_NONE);
pa.setBackFaceNormalFlip(true);
extra.setColor(extra_triangle_colors);
}
ct = universe.createContent(new CustomMultiMesh(Arrays.asList(new CustomMesh[] { cm, extra })), title);
}
// Set general content properties
ct.setTransparency(1f - alpha);
// Default is unlocked (editable) transformation; set it to locked:
ct.setLocked(true);
// register mesh title
synchronized (ht_pt_meshes) {
ht_pt_meshes.put(pt, ct.getName());
}
} catch (final Throwable e) {
Utils.logAll("Mesh generation failed for \"" + title + "\" from " + pt);
IJError.print(e);
e.printStackTrace();
}
Utils.log2(pt.toString() + " n points: " + triangles.size());
return ct;
} catch (final Exception e) {
IJError.print(e);
return null;
}
}
};
}
use of ij3d.Content 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 ij3d.Content in project TrakEM2 by trakem2.
the class Compare method variabilityAnalysis.
/**
* @param reference_project If null, then the first one found in the Project.getProjects() lists is used.
* @param regex A String (can be null) to filter objects by, to limit what gets processed.
* If regex is not null, then only ProjectThing nodes with the matching regex are analyzed (shallow: none of their children are questioned, but pipes will be built from them all).
* @param generate_plots Whether to generate the variability plots at all.
* @param show_plots If generate_plots, whether to show the plots in a stack image window or to save them.
* @param show_3D Whether to show any 3D data.
* @param show_condensed_3D If show_3D, whether to show the condensed vector strings, i.e. the "average" pipes.
* @param show_sources_3D If show_3D, whether to show the source pipes from which the condensed vector string was generated.
* @param sources_color_table Which colors to give to the pipes of which Project.
* @param show_envelope_3D If show_3D, whether to generate the variability envelope.
* @param envelope_alpha If show_envelope_3D, the envelope takes an alpha value between 0 (total transparency) and 1 (total opacity)
* @param delta_envelope The delta to resample the envelope to. When smaller than or equal to 1, no envelope resampling occurs.
* @param show_axes_3D If show_3D, whether to display the reference axes as well.
* @param heat_map If show_3D, whether to color the variability with a Fire LUT.
* If not show_condensed_3D, then the variability is shown in color-coded 3D spheres placed at the entry point to the neuropile.
* @param map_condensed If not null, all VectorString3D are put into this map.
* @param projects The projects to use.
*/
public static Bureaucrat variabilityAnalysis(final Project reference_project, final String regex, final String[] ignore, final boolean show_cata_dialog, final boolean generate_plots, final boolean show_plots, final String plot_dir_, final boolean show_3D, final boolean show_condensed_3D, final boolean show_sources_3D, final Map<Project, Color> sources_color_table, final boolean show_envelope_3D, final float envelope_alpha, final double delta_envelope, final int envelope_type, final boolean show_axes_3D, final boolean heat_map, final Map<String, VectorString3D> map_condensed, final Project[] projects) {
// gather all open projects
final Project[] p = null == projects ? Project.getProjects().toArray(new Project[0]) : projects;
// make the reference_project be the first in the array
if (null != reference_project && reference_project != p[0]) {
for (int i = 0; i < p.length; i++) {
if (reference_project == p[i]) {
p[i] = p[0];
p[0] = reference_project;
break;
}
}
}
final Worker worker = new Worker("Comparing all to all") {
@Override
public void run() {
startedWorking();
try {
Utils.log2("Asking for CATAParameters...");
final CATAParameters cp = new CATAParameters();
cp.regex = regex;
cp.delta_envelope = delta_envelope;
cp.envelope_type = envelope_type;
if (show_cata_dialog && !cp.setup(false, regex, true, true)) {
finishedWorking();
return;
}
// so source points are stored in VectorString3D for each resampled and interpolated point
cp.with_source = true;
// Store a series of results, depending on options
final HashMap<String, Display3D> results = new HashMap<String, Display3D>();
String plot_dir = plot_dir_;
if (generate_plots && !show_plots) {
// Save plots
if (null == plot_dir) {
final DirectoryChooser dc = new DirectoryChooser("Choose plots directory");
plot_dir = dc.getDirectory();
if (null == plot_dir) {
finishedWorking();
return;
}
}
if (IJ.isWindows())
plot_dir = plot_dir.replace('\\', '/');
if (!plot_dir.endsWith("/"))
plot_dir += "/";
}
Utils.log2("Gathering chains...");
// Gather chains that do not match the ignore regexes
// will transform them as well to the reference found in the first project in the p array
Object[] ob = gatherChains(p, cp, ignore);
ArrayList<Chain> chains = (ArrayList<Chain>) ob[0];
// to keep track of each project's chains
final ArrayList[] p_chains = (ArrayList[]) ob[1];
ob = null;
if (null == chains) {
finishedWorking();
return;
}
Utils.log2("Collecting bundles...");
final HashMap<Project, HashMap<String, VectorString3D>> axes = new HashMap<Project, HashMap<String, VectorString3D>>();
// Sort out into groups by unique names of lineage bundles
final HashMap<String, ArrayList<Chain>> bundles = new HashMap<String, ArrayList<Chain>>();
for (final Chain chain : chains) {
String title = chain.getCellTitle();
final String t = title.toLowerCase();
// unnamed
if (0 == t.indexOf('[') || 0 == t.indexOf('#'))
continue;
Utils.log("Accepting " + title);
title = title.substring(0, title.indexOf(' '));
// lineage bundle instance chains
ArrayList<Chain> bc = bundles.get(title);
if (null == bc) {
bc = new ArrayList<Chain>();
bundles.put(title, bc);
}
bc.add(chain);
}
Utils.log2("Found " + bundles.size() + " bundles.");
chains = null;
if (null != cp.regex && show_axes_3D && axes.size() < 3) {
// Must find the Mushroom Body lobes separately
final String cp_regex = cp.regex;
cp.regex = "mb";
final Object[] o = gatherChains(p, cp, ignore);
final ArrayList<Chain> lobes = (ArrayList<Chain>) o[0];
Utils.logAll("Found " + lobes.size() + " chains for lobes");
for (final Chain chain : lobes) {
final String t = chain.getCellTitle().toLowerCase();
if (-1 != t.indexOf("peduncle") || -1 != t.indexOf("medial lobe") || -1 != t.indexOf("dorsal lobe")) {
Utils.logAll("adding " + t);
final Project pr = chain.pipes.get(0).getProject();
HashMap<String, VectorString3D> m = axes.get(pr);
if (null == m) {
m = new HashMap<String, VectorString3D>();
axes.put(pr, m);
}
m.put(t, chain.vs);
continue;
}
}
cp.regex = cp_regex;
} else {
Utils.logAll("Not: cp.regex = " + cp.regex + " show_axes_3D = " + show_axes_3D + " axes.size() = " + axes.size());
}
final HashMap<String, VectorString3D> condensed = new HashMap<String, VectorString3D>();
Utils.log2("Condensing each bundle...");
// Condense each into a single VectorString3D
for (final Map.Entry<String, ArrayList<Chain>> entry : bundles.entrySet()) {
final ArrayList<Chain> bc = entry.getValue();
if (bc.size() < 2) {
Utils.log2("Skipping single: " + entry.getKey());
continue;
}
final VectorString3D[] vs = new VectorString3D[bc.size()];
for (int i = 0; i < vs.length; i++) vs[i] = bc.get(i).vs;
final VectorString3D c = condense(cp, vs, this);
c.setCalibration(p[0].getRootLayerSet().getCalibrationCopy());
condensed.put(entry.getKey(), c);
if (this.hasQuitted())
return;
}
// Store:
if (null != map_condensed) {
map_condensed.putAll(condensed);
}
if (generate_plots) {
Utils.log2("Plotting stdDev for each condensed bundle...");
// Y axis: the stdDev at each point, computed from the group of points that contribute to each
for (final Map.Entry<String, VectorString3D> e : condensed.entrySet()) {
final String name = e.getKey();
final VectorString3D c = e.getValue();
final Plot plot = makePlot(cp, name, c);
// FAILS//plot.addLabel(10, cp.plot_height-5, name); // must be added after setting size
if (show_plots)
plot.show();
else if (null != plot_dir)
new FileSaver(plot.getImagePlus()).saveAsPng(plot_dir + name.replace('/', '-') + ".png");
}
}
if (show_3D) {
final HashMap<String, Color> heat_table = new HashMap<String, Color>();
if (heat_map || show_envelope_3D) {
// Create a Fire LUT
final ImagePlus lutimp = new ImagePlus("lut", new ByteProcessor(4, 4));
IJ.run(lutimp, "Fire", "");
final IndexColorModel icm = (IndexColorModel) lutimp.getProcessor().getColorModel();
final byte[] reds = new byte[256];
final byte[] greens = new byte[256];
final byte[] blues = new byte[256];
icm.getReds(reds);
icm.getGreens(greens);
icm.getBlues(blues);
final List<String> names = new ArrayList<String>(bundles.keySet());
Collections.sort(names);
// find max stdDev
double max = 0;
final HashMap<String, Double> heats = new HashMap<String, Double>();
for (final String name : names) {
final VectorString3D vs_merged = condensed.get(name);
if (null == vs_merged) {
Utils.logAll("WARNING could not find a condensed pipe for " + name);
continue;
}
final double[] stdDev = vs_merged.getStdDevAtEachPoint();
// double avg = 0;
// for (int i=0; i<stdDev.length; i++) avg += stdDev[i];
// avg = avg/stdDev.length;
Arrays.sort(stdDev);
// median is more representative than average
final double median = stdDev[stdDev.length / 2];
if (max < median)
max = median;
heats.put(name, median);
}
for (final Map.Entry<String, Double> e : heats.entrySet()) {
final String name = e.getKey();
final double median = e.getValue();
// scale between 0 and max to get a Fire LUT color:
int index = (int) ((median / max) * 255);
if (index > 255)
index = 255;
final Color color = new Color(0xff & reds[index], 0xff & greens[index], 0xff & blues[index]);
Utils.log2(new StringBuilder(name).append('\t').append(median).append('\t').append(reds[index]).append('\t').append(greens[index]).append('\t').append(blues[index]).toString());
heat_table.put(name, color);
}
}
final LayerSet common_ls = new LayerSet(p[0], -1, "Common", 10, 10, 0, 0, 0, 512, 512, false, 2, new AffineTransform());
final Display3D d3d = Display3D.get(common_ls);
float env_alpha = envelope_alpha;
if (env_alpha < 0) {
Utils.log2("WARNING envelope_alpha is invalid: " + envelope_alpha + "\n Using 0.4f instead");
env_alpha = 0.4f;
} else if (env_alpha > 1)
env_alpha = 1.0f;
for (final String name : bundles.keySet()) {
final ArrayList<Chain> bc = bundles.get(name);
final VectorString3D vs_merged = condensed.get(name);
if (null == vs_merged) {
Utils.logAll("WARNING: could not find a condensed vs for " + name);
continue;
}
if (show_sources_3D) {
if (null != sources_color_table) {
final HashSet<String> titles = new HashSet<String>();
for (final Chain chain : bc) {
final Color c = sources_color_table.get(chain.getRoot().getProject());
final String title = chain.getCellTitle();
String t = title;
int i = 2;
while (titles.contains(t)) {
t = title + "-" + i;
i += 1;
}
titles.add(t);
Display3D.addMesh(common_ls, chain.vs, t, null != c ? c : Color.gray);
}
} else {
for (final Chain chain : bc) Display3D.addMesh(common_ls, chain.vs, chain.getCellTitle(), Color.gray);
}
}
if (show_condensed_3D) {
Display3D.addMesh(common_ls, vs_merged, name + "-condensed", heat_map ? heat_table.get(name) : Color.red);
}
if (show_envelope_3D) {
double[] widths = makeEnvelope(cp, vs_merged);
if (cp.delta_envelope > 1) {
vs_merged.addDependent(widths);
vs_merged.resample(cp.delta_envelope);
widths = vs_merged.getDependent(0);
}
Display3D.addMesh(common_ls, vs_merged, name + "-envelope", heat_map ? heat_table.get(name) : Color.red, widths, env_alpha);
} else if (heat_map) {
// Show spheres in place of envelopes, at the starting tip (neuropile entry point)
final double x = vs_merged.getPoints(0)[0];
final double y = vs_merged.getPoints(1)[0];
final double z = vs_merged.getPoints(2)[0];
final double r = 10;
final Color color = heat_table.get(name);
if (null == color) {
Utils.logAll("WARNING: heat table does not have a color for " + name);
continue;
}
final Content sphere = d3d.getUniverse().addMesh(ij3d.Mesh_Maker.createSphere(x, y, z, r), new Color3f(heat_table.get(name)), name + "-sphere", 1);
}
}
if (show_axes_3D) {
for (int i = 0; i < p.length; i++) {
final Map<String, VectorString3D> m = axes.get(p[i]);
if (null == m) {
Utils.log2("No axes found for project " + p[i]);
continue;
}
for (final Map.Entry<String, VectorString3D> e : m.entrySet()) {
Display3D.addMesh(common_ls, e.getValue(), e.getKey() + "-" + i, Color.gray);
}
}
}
results.put("d3d", Display3D.get(common_ls));
}
this.result = results;
Utils.log2("Done.");
} catch (final Exception e) {
IJError.print(e);
} finally {
finishedWorking();
}
}
};
return Bureaucrat.createAndStart(worker, p[0]);
}
Aggregations