use of ini.trakem2.display.Display in project TrakEM2 by trakem2.
the class Display3D method show.
/**
* Scan the {@link ProjectThing} children and assign the renderable ones to an existing {@link Display3D} for their {@link LayerSet}, or open a new one. If {@code true == wait && -1 != resample}, then the method returns only when the mesh/es have been added.
*/
public static Future<Vector<Future<Content>>> show(final ProjectThing pt, final boolean wait, final int resample) {
if (null == pt)
return null;
final Future<Vector<Future<Content>>> fu = launchers.submit(new Callable<Vector<Future<Content>>>() {
@Override
public Vector<Future<Content>> call() {
// Scan the given ProjectThing for 3D-viewable items
// So: find arealist, pipe, ball, and profile_list types
final HashSet<ProjectThing> hs = pt.findBasicTypeChildren();
if (null == hs || 0 == hs.size()) {
Utils.logAll("Node " + pt + " does not contain any 3D-displayable children");
return null;
}
// Remove profile if it lives under a profile_list
for (final Iterator<ProjectThing> it = hs.iterator(); it.hasNext(); ) {
final ProjectThing pt = it.next();
if (null != pt.getObject() && pt.getObject().getClass() == Profile.class && pt.getParent().getType().equals("profile_list")) {
it.remove();
}
}
setWaitingCursor();
// Start new scheduler to publish/add meshes to the 3D Viewer every 5 seconds and when done.
final Hashtable<Display3D, Vector<Content>> contents = new Hashtable<Display3D, Vector<Content>>();
final ScheduledExecutorService updater = Executors.newScheduledThreadPool(1);
final AtomicInteger counter = new AtomicInteger();
updater.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
// Obtain a copy of the contents queue
final HashMap<Display3D, Vector<Content>> m = new HashMap<Display3D, Vector<Content>>();
synchronized (contents) {
m.putAll(contents);
contents.clear();
}
if (m.isEmpty())
return;
// Add all to the corresponding Display3D
for (final Map.Entry<Display3D, Vector<Content>> e : m.entrySet()) {
e.getKey().universe.addContentLater(e.getValue());
counter.getAndAdd(e.getValue().size());
}
Utils.showStatus(new StringBuilder("Rendered ").append(counter.get()).append('/').append(hs.size()).toString());
}
}, 100, 4000, TimeUnit.MILLISECONDS);
// A list of all generated Content objects
final Vector<Future<Content>> list = new Vector<Future<Content>>();
for (final Iterator<ProjectThing> it = hs.iterator(); it.hasNext(); ) {
// obtain the Displayable object under the node
final ProjectThing child = it.next();
final Object obc = child.getObject();
final Displayable displ = obc.getClass().equals(String.class) ? null : (Displayable) obc;
if (null != displ) {
if (displ.getClass().equals(Profile.class)) {
// handled by profile_list Thing
continue;
}
if (!displ.isVisible()) {
Utils.log("Skipping non-visible node " + displ);
continue;
}
}
// obtain the containing LayerSet
final Display3D d3d;
if (null != displ)
d3d = Display3D.get(displ.getLayerSet());
else if (child.getType().equals("profile_list")) {
final ArrayList<ProjectThing> al_children = child.getChildren();
if (null == al_children || 0 == al_children.size())
continue;
// else, get the first Profile and get its LayerSet
d3d = Display3D.get(((Displayable) ((ProjectThing) al_children.get(0)).getObject()).getLayerSet());
} else {
Utils.log("Don't know what to do with node " + child);
d3d = null;
}
if (null == d3d) {
Utils.log("Could not get a proper 3D display for node " + displ);
// java3D not installed most likely
return null;
}
boolean already;
synchronized (d3d.ht_pt_meshes) {
already = d3d.ht_pt_meshes.containsKey(child);
}
if (already) {
if (child.getObject() instanceof ZDisplayable) {
Utils.log("Updating 3D view of " + child.getObject());
} else {
Utils.log("Updating 3D view of " + child);
}
}
list.add(d3d.executors.submit(new Callable<Content>() {
@Override
public Content call() {
Content c = null;
try {
c = d3d.createMesh(child, displ, resample).call();
Vector<Content> vc;
synchronized (contents) {
vc = contents.get(d3d);
if (null == vc)
vc = new Vector<Content>();
contents.put(d3d, vc);
}
vc.add(c);
} catch (final Exception e) {
IJError.print(e);
}
return c;
}
}));
// If it's the last one:
if (!it.hasNext()) {
// Add the concluding task, that waits on all and shuts down the scheduler
d3d.executors.submit(new Runnable() {
@Override
public void run() {
// Wait until all are done
for (final Future<Content> c : list) {
try {
c.get();
} catch (final Throwable t) {
IJError.print(t);
}
}
try {
// Shutdown scheduler and execute remaining tasks
for (final Runnable r : updater.shutdownNow()) {
r.run();
}
} catch (final Throwable e) {
IJError.print(e);
}
// Reset cursor
doneWaiting();
Utils.showStatus(new StringBuilder("Done rendering ").append(counter.get()).append('/').append(hs.size()).toString());
}
});
}
}
return list;
}
});
if (wait && -1 != resample) {
try {
fu.get();
} catch (final Throwable t) {
IJError.print(t);
}
}
return fu;
}
use of ini.trakem2.display.Display in project TrakEM2 by trakem2.
the class Display3D method removeFrom3D.
/**
* Remove all basic type children contained in {@code pt} and its children, recursively.
*
* @param pt
*/
public static void removeFrom3D(final ProjectThing pt) {
final HashSet<ProjectThing> hs = pt.findBasicTypeChildren();
if (null == hs || 0 == hs.size()) {
Utils.logAll("Nothing to remove from 3D.");
return;
}
// Ignore Profile instances ("profile_list" takes care of them)
for (final ProjectThing child : hs) {
if (child.getByType().equals("profile"))
continue;
// Find the LayerSet
LayerSet lset = null;
if (child.getType().equals("profile_list")) {
if (!child.hasChildren())
continue;
for (final ProjectThing p : child.getChildren()) {
if (null != p.getObject() && p.getObject() instanceof Profile) {
lset = ((Displayable) p.getObject()).getLayerSet();
break;
}
}
if (null == lset)
continue;
} else if (child.getType().equals("profile")) {
// Taken care of by "profile list"
continue;
} else {
final Displayable d = (Displayable) child.getObject();
if (null == d) {
Utils.log("Null object for ProjectThing " + child);
continue;
}
lset = d.getLayerSet();
}
if (null == lset) {
Utils.log("No LayerSet found for " + child);
continue;
}
final Display3D d3d = getDisplay(lset);
if (null == d3d) {
Utils.log("No Display 3D found for " + child);
// no Display3D window open
continue;
}
final String oldTitle = d3d.ht_pt_meshes.remove(child);
if (null == oldTitle) {
Utils.log("Could not find a title for " + child);
continue;
}
Utils.log("Removed from 3D view: " + oldTitle);
d3d.getUniverse().removeContent(oldTitle);
}
}
use of ini.trakem2.display.Display in project TrakEM2 by trakem2.
the class ImageJCommandListener method commandExecuting.
// I know, I could create a hashtable and then map methods of this class to each command key ... this is just easier, and performance-wise nobody cares
// Or even a hastable with String command keys and then a number as value, and use a gigantic switch block. So much pain to write. WHAT I REALLY WANT is a switch that takes a String and is fast because it has its own hash setup.
public String commandExecuting(final String command) {
// Utils.log2("Command: " + command);
// 1 - check source
ImagePlus current = WindowManager.getCurrentImage();
// not a trakem2 display: continue happily
if (!(current instanceof FakeImagePlus))
return command;
// 2 - identify project
final FakeImagePlus fimp = (FakeImagePlus) current;
final Display display = fimp.getDisplay();
final LayerSet layer_set = display.getLayer().getParent();
final Project project = display.getProject();
final ProjectTree ptree = project.getProjectTree();
final Displayable active = display.getActive();
final Selection selection = display.getSelection();
// FILE menu
if (command.equals("Save")) {
project.save();
return null;
} else // EDIT menu
if (command.equals("Undo")) {
// TODO forward to the active image, if any
niy(command);
return null;
} else if (command.equals("Cut")) {
// TODO forward to the active image, if any
niy(command);
return null;
} else if (command.equals("Copy")) {
// TODO forward to the active image, if any
niy(command);
return null;
} else if (command.equals("Copy to System")) {
// TODO forward to the active image, if any
niy(command);
return null;
} else if (command.equals("Paste")) {
// TODO forward to the active image, if any
niy(command);
return null;
} else if (command.equals("Clear")) {
// TODO forward to the active image, if any
niy(command);
return null;
} else if (command.equals("Clear Outside")) {
// TODO forward to the active image, if any
niy(command);
return null;
} else if (command.equals("Fill")) {
// TODO forward to the active image, if any
niy(command);
return null;
} else if (command.equals("Draw")) {
// TODO forward to the active image, if any
niy(command);
return null;
} else if (command.equals("Invert")) {
// TODO forward to the active image, if any
niy(command);
return null;
} else // EDIT - SELECTION menu
if (command.equals("Select All")) {
if (ProjectToolbar.SELECT == Toolbar.getToolId()) {
selection.selectAll();
return null;
}
return command;
} else if (command.equals("Select None")) {
if (ProjectToolbar.SELECT == Toolbar.getToolId()) {
display.select(null);
return null;
}
return command;
} else if (command.equals("Restore Selection")) {
if (ProjectToolbar.SELECT == Toolbar.getToolId()) {
selection.restore();
return null;
}
return command;
} else // IMAGE - TYPE menu
if (command.equals("8-bit")) {
// TODO forward to the active image, if any
niy(command);
return null;
} else if (command.equals("16-bit")) {
// TODO forward to the active image, if any
niy(command);
return null;
} else if (command.equals("32-bit")) {
// TODO forward to the active image, if any
niy(command);
return null;
} else if (command.equals("8-bit Color")) {
// TODO forward to the active image, if any
niy(command);
return null;
} else if (command.equals("RGB Color")) {
// TODO forward to the active image, if any
niy(command);
return null;
} else if (command.equals("RGB Stack") || command.equals("HSB Stack")) {
Utils.showMessage("Can't convert to " + command);
return null;
} else // IMAGE - ADJUST menu
if (command.equals("Brightness/Contrast...")) {
// TODO forward to the active image, if any
niy(command);
return null;
} else if (command.equals("Window/Level...")) {
// TODO forward to the active image, if any
niy(command);
return null;
} else if (command.equals("Color Balance...")) {
// TODO forward to the active image, if any
niy(command);
return null;
} else if (command.equals("Threshold...")) {
// TODO forward to the active image, if any
niy(command);
return null;
} else if (command.equals("Size...")) {
if (null != active)
selection.specify();
return null;
} else if (command.equals("Canvas Size...")) {
display.resizeCanvas();
return null;
} else // IMAGE menu
if (command.equals("Show Info...")) {
// TODO perhaps it should show only for images ...
if (null == active) {
ptree.showInfo(project.getRootProjectThing());
} else {
ProjectThing pt = project.findProjectThing(active);
if (null != pt)
ptree.showInfo(pt);
}
return null;
} else // IMAGE - COLOR menu
if (in(command, new String[] { "RGB Split", "RGB Merge...", "Stack to RGB", "Make Composite" })) {
notAvailable(command);
return null;
} else if (command.equals("Show LUT")) {
return setTempCurrentImage(command, active);
} else if (command.equals("Edit LUT...")) {
// TODO forward to the active image, if any
niy(command);
return null;
} else if (command.equals("Average Color")) {
// TODO forward to the active image, if any
niy(command);
return null;
} else if (command.equals("RGB to CIELAB")) {
// TODO forward to the active image, if any
niy(command);
return null;
} else if (command.equals("RGB to Luminance")) {
// TODO forward to the active image, if any
niy(command);
return null;
} else // IMAGE STACK menu
if (in(command, new String[] { "Add Slice", "Delete Slice" })) {
Utils.showMessage("Go to the Layer Tree and right-click to add/delete a layer.");
return null;
} else if (command.equals("Next Slice [>]")) {
display.nextLayer(IJ.shiftKeyDown() ? Event.SHIFT_MASK : 0);
return null;
} else if (command.equals("Previous Slice [<]")) {
display.previousLayer(IJ.shiftKeyDown() ? Event.SHIFT_MASK : 0);
return null;
} else if (in(command, new String[] { "Set Slice", "Images to Stack", "Stack to Images", "Make Montage..." })) {
notAvailable(command);
return null;
} else if (command.equals("Reslice [/]...")) {
// TODO
niy(command);
return null;
} else if (command.equals("Z Project...")) {
// TODO
niy(command);
return null;
} else if (command.equals("3D Project...")) {
// TODO
niy(command);
return null;
} else if (command.equals("Plot Z-axis Profile")) {
// TODO
niy(command);
return null;
} else if (command.equals("Start Animation [\\]")) {
// TODO
niy(command);
return null;
} else if (command.equals("Stop Animation")) {
// TODO
niy(command);
return null;
} else // IMAGE menu again
if (command.equals("Crop")) {
notAvailable(command);
return null;
} else if (in(command, new String[] { "Translate...", "Scale..." })) {
if (null != active)
selection.specify();
return null;
} else if (command.equals("Duplicate...")) {
if (null != active && active.getClass().equals(Patch.class)) {
// TODO stacks?
// 2.5 security factor: for awt in non-1.6.0 machines
project.getLoader().releaseToFit((long) (project.getLoader().estimateImageFileSize((Patch) active, 0) * 2.5));
new ImagePlus(active.getTitle(), ((Patch) active).getImageProcessor().duplicate()).show();
}
return null;
} else if (command.equals("Rename...")) {
if (null != active) {
active.adjustProperties();
Display.updateSelection();
}
return null;
} else // IMAGE ROTATE menu
if (command.equals("Flip Horizontally")) {
selection.apply(2, new double[] { -1, 1 });
return null;
} else if (command.equals("Flip Vertically")) {
selection.apply(2, new double[] { 1, -1 });
return null;
} else if (command.equals("Rotate 90 Degrees Right")) {
selection.apply(1, new double[] { 90 });
return null;
} else if (command.equals("Rotate 90 Degrees Left")) {
selection.apply(1, new double[] { -90 });
return null;
} else if (command.equals("Arbitrarily...")) {
if (null != active)
selection.specify();
return null;
} else // IMAGE ZOOM menu
if (command.equals("To Selection")) {
Roi roi = fimp.getRoi();
if (null != roi) {
Rectangle b = roi.getBounds();
b.x -= b.width / 2;
b.y -= b.height / 2;
b.width *= 2;
b.height *= 2;
display.getCanvas().showCentered(b);
}
return null;
} else if (command.equals("View 100%")) {
// TODO
niy(command);
return null;
} else // ANALYZE menu
if (command.equals("Measure")) {
// Minimal measurement: area of closed ROIs, length of unclosed ROIs, calibrated.
Roi roi = fimp.getRoi();
if (null != roi) {
Calibration cal = fimp.getCalibration();
AffineTransform caff = new AffineTransform();
caff.scale(cal.pixelWidth, cal.pixelHeight);
if (M.isAreaROI(roi)) {
Area area = M.getArea(roi);
area = area.createTransformedArea(caff);
ResultsTable rt = Utils.createResultsTable("ROI area", new String[] { "area", "perimeter" });
rt.incrementCounter();
rt.addLabel("units", cal.getUnit());
rt.addValue(0, Math.abs(AreaCalculations.area(area.getPathIterator(null))));
rt.addValue(1, roi.getLength());
rt.show("ROI area");
} else {
ResultsTable rt = Utils.createResultsTable("ROI length", new String[] { "length" });
rt.incrementCounter();
rt.addLabel("units", cal.getUnit());
rt.addValue(0, roi.getLength());
rt.show("ROI length");
}
return null;
} else if (null != active) {
// Measure the active displayable
if (active.getClass() == Patch.class) {
// measure like 'm' would in ImageJ for an image
ImagePlus imp = ((Patch) active).getImagePlus();
imp.setCalibration(active.getLayer().getParent().getCalibrationCopy());
IJ.run(imp, "Measure", "");
} else {
// Call measure like ProjectThing does
ProjectThing pt = active.getProject().findProjectThing(active);
if (active instanceof Profile)
((ProjectThing) pt.getParent()).measure();
else
pt.measure();
}
return null;
}
Utils.log("Draw a ROI or select an object!");
return null;
} else if (in(command, new String[] { "Analyze Particles...", "Histogram", "Plot Profile", "Surface Plot...", "Color Inspector 3D", "3D Surface Plot", "Color Histogram" })) {
return setTempCurrentImage(command, active);
} else if (command.equals("Label")) {
notAvailable(command);
return null;
} else // PLUGINS menu
if (command.equals("Volume Viewer")) {
return runOnVirtualLayerSet(command, layer_set, display);
} else if (command.equals("3D Viewer")) {
// it's virtual and non-caching, will appear as a regular ImageJ stack
layer_set.createLayerStack(Displayable.class, ImagePlus.COLOR_RGB, display.getDisplayChannelAlphas()).getImagePlus().show();
return command;
} else // PROCESS menu and submenus
if (in(command, new String[] { "FFT", "Fast FFT (2D/3D)" })) {
return setTempCurrentImage(command, active);
} else if (in(command, new String[] { "Bandpass Filter...", "Custom Filter...", "FD Math...", "Swap Quadrants", "Convolve...", "Gaussian Blur...", "Median...", "Mean...", "Minimum...", "Maximum...", "Unsharp Mask...", "Variance...", "Show Circular Masks...", "Subtract Background..." })) {
return duplicate(command, active);
} else if (in(command, new String[] { "Smooth", "Sharpen", "Find Edges", "Enhance Contrast", "Add Noise", "Add Specified Noise...", "Salt and Pepper", "Despeckle", "Remove Outliers...", "North", "Northeast", "East", "Southeast", "South", "Southwest", "West", "Northwest", "Make Binary", "Convert to Mask", "Find Maxima...", "Erode", "Dilate", "Open ", "Close-", "Outline", "Fill Holes", "Skeletonize", "Distance Map", "Ultimate Points", "Watershed", "Add...", "Subtract...", "Multiply...", "Divide...", "AND...", "OR...", "XOR...", "Min...", "Max...", "Gamma...", "Log", "Exp", "Square", "Square Root", "Reciprocal", "NaN Background", "Abs" })) {
return duplicate(command, active);
}
/*else {
// continue happily
//Utils.log2("Skipping " + command);
}*/
// If it's part of "Save As", ignore it
Menu menu = Menus.getSaveAsMenu();
for (int i = menu.getItemCount() - 1; i > -1; i--) {
if (command.equals(menu.getItem(i).getActionCommand())) {
notAvailable(command);
return null;
}
}
// Give it back to ImageJ
return command;
}
use of ini.trakem2.display.Display in project TrakEM2 by trakem2.
the class Loader method makePrescaledTiles.
/**
* Generate 256x256 tiles, as many as necessary, to cover the given srcRect, starting at max_scale. Designed to be slow but memory-capable.
*
* filename = z + "/" + row + "_" + column + "_" + s + ".jpg";
*
* row and column run from 0 to n stepsize 1
* that is, row = y / ( 256 * 2^s ) and column = x / ( 256 * 2^s )
*
* z : z-level (slice)
* x,y: the row and column
* s: scale, which is 1 / (2^s), in integers: 0, 1, 2 ...
*
* var MAX_S = Math.floor( Math.log( MAX_Y + 1 ) / Math.LN2 ) - Math.floor( Math.log( Y_TILE_SIZE ) / Math.LN2 ) - 1;
*
* The module should not be more than 5
* At al levels, there should be an even number of rows and columns, except for the coarsest level.
* The coarsest level should be at least 5x5 tiles.
*
* Best results obtained when the srcRect approaches or is a square. Black space will pad the right and bottom edges when the srcRect is not exactly a square.
* Only the area within the srcRect is ever included, even if actual data exists beyond.
*
* @return The watcher thread, for joining purposes, or null if the dialog is canceled or preconditions are not passed.
* @throws IllegalArgumentException if the type is not ImagePlus.GRAY8 or Imageplus.COLOR_RGB.
*/
public Bureaucrat makePrescaledTiles(final Layer[] layers, final Class<?> clazz, final Rectangle srcRect, double max_scale_, final int c_alphas, final int type, String target_dir, final boolean from_original_images, final Saver saver, final int tileSide) {
if (null == layers || 0 == layers.length)
return null;
switch(type) {
case ImagePlus.GRAY8:
case ImagePlus.COLOR_RGB:
break;
default:
throw new IllegalArgumentException("Can only export for web with 8-bit or RGB");
}
// choose target directory
if (null == target_dir) {
final DirectoryChooser dc = new DirectoryChooser("Choose target directory");
target_dir = dc.getDirectory();
if (null == target_dir)
return null;
}
if (IJ.isWindows())
target_dir = target_dir.replace('\\', '/');
if (!target_dir.endsWith("/"))
target_dir += "/";
if (max_scale_ > 1) {
Utils.log("Prescaled Tiles: using max scale of 1.0");
// no point
max_scale_ = 1;
}
final String dir = target_dir;
final double max_scale = max_scale_;
final Worker worker = new Worker("Creating prescaled tiles") {
private void cleanUp() {
finishedWorking();
}
@Override
public void run() {
startedWorking();
try {
// project name
// String pname = layer[0].getProject().getTitle();
// create 'z' directories if they don't exist: check and ask!
// start with the highest scale level
final int[] best = determineClosestPowerOfTwo(srcRect.width > srcRect.height ? srcRect.width : srcRect.height);
final int edge_length = best[0];
final int n_edge_tiles = edge_length / tileSide;
Utils.log2("srcRect: " + srcRect);
Utils.log2("edge_length, n_edge_tiles, best[1] " + best[0] + ", " + n_edge_tiles + ", " + best[1]);
// thumbnail dimensions
// LayerSet ls = layer[0].getParent();
final double ratio = srcRect.width / (double) srcRect.height;
double thumb_scale = 1.0;
if (ratio >= 1) {
// width is larger or equal than height
thumb_scale = 192.0 / srcRect.width;
} else {
thumb_scale = 192.0 / srcRect.height;
}
// Figure out layer indices, given that layers are not necessarily evenly spaced
final TreeMap<Integer, Layer> indices = new TreeMap<Integer, Layer>();
final ArrayList<Integer> missingIndices = new ArrayList<Integer>();
final double resolution_z_px;
final int smallestIndex, largestIndex;
if (1 == layers.length) {
indices.put(0, layers[0]);
resolution_z_px = layers[0].getZ();
smallestIndex = 0;
largestIndex = 0;
} else {
// Ensure layers are sorted by Z index and are unique pointers and unique in Z coordinate:
final TreeMap<Double, Layer> t = new TreeMap<Double, Layer>();
for (final Layer l1 : new HashSet<Layer>(Arrays.asList(layers))) {
final Layer l2 = t.get(l1.getZ());
if (null == l2) {
t.put(l1.getZ(), l1);
} else {
// Ignore the layer with less objects
if (l1.getDisplayables().size() > l2.getDisplayables().size()) {
t.put(l1.getZ(), l1);
Utils.log("Ignoring duplicate layer: " + l2);
}
}
}
// What is the mode thickness, measured by Z(i-1) - Z(i)?
// (Distance between the Z of two consecutive layers)
final HashMap<Double, Integer> counts = new HashMap<Double, Integer>();
final Layer prev = t.get(t.firstKey());
double modeThickness = 0;
int modeThicknessCount = 0;
for (final Layer la : t.tailMap(prev.getZ(), false).values()) {
// Thickness with 3-decimal precision only
final double d = ((int) ((la.getZ() - prev.getZ()) * 1000 + 0.5)) / 1000.0;
Integer c = counts.get(d);
//
if (null == c)
c = 0;
++c;
counts.put(d, c);
//
if (c > modeThicknessCount) {
modeThicknessCount = c;
modeThickness = d;
}
}
// Not pixelDepth
resolution_z_px = modeThickness * prev.getParent().getCalibration().pixelWidth;
// Assign an index to each layer, approximating each layer at modeThickness intervals
for (final Layer la : t.values()) {
indices.put((int) (la.getZ() / modeThickness + 0.5), la);
}
// First and last
smallestIndex = indices.firstKey();
largestIndex = indices.lastKey();
Utils.logAll("indices: " + smallestIndex + ", " + largestIndex);
// Which indices are missing?
for (int i = smallestIndex + 1; i < largestIndex; ++i) {
if (!indices.containsKey(i)) {
missingIndices.add(i);
}
}
}
// JSON metadata for CATMAID
{
final StringBuilder sb = new StringBuilder("{");
final LayerSet ls = layers[0].getParent();
final Calibration cal = ls.getCalibration();
sb.append("\"volume_width_px\": ").append(srcRect.width).append(',').append('\n').append("\"volume_height_px\": ").append(srcRect.height).append(',').append('\n').append("\"volume_sections\": ").append(largestIndex - smallestIndex + 1).append(',').append('\n').append("\"extension\": \"").append(saver.getExtension()).append('\"').append(',').append('\n').append("\"resolution_x\": ").append(cal.pixelWidth).append(',').append('\n').append("\"resolution_y\": ").append(cal.pixelHeight).append(',').append('\n').append("\"resolution_z\": ").append(resolution_z_px).append(',').append('\n').append("\"units\": \"").append(cal.getUnit()).append('"').append(',').append('\n').append("\"offset_x_px\": 0,\n").append("\"offset_y_px\": 0,\n").append("\"offset_z_px\": ").append(indices.get(indices.firstKey()).getZ() * cal.pixelWidth / cal.pixelDepth).append(',').append('\n').append("\"missing_layers\": [");
for (final Integer i : missingIndices) sb.append(i - smallestIndex).append(',');
// remove last comma
sb.setLength(sb.length() - 1);
sb.append("]}");
if (!Utils.saveToFile(new File(dir + "metadata.json"), sb.toString())) {
Utils.logAll("WARNING: could not save " + dir + "metadata.json\nThe contents was:\n" + sb.toString());
}
}
for (final Map.Entry<Integer, Layer> entry : indices.entrySet()) {
if (this.quit) {
cleanUp();
return;
}
final int index = entry.getKey() - smallestIndex;
final Layer layer = entry.getValue();
// 1 - create a directory 'z' named as the layer's index
String tile_dir = dir + index;
File fdir = new File(tile_dir);
final int tag = 1;
// Ensure there is a usable directory:
while (fdir.exists() && !fdir.isDirectory()) {
fdir = new File(tile_dir + "_" + tag);
}
if (!fdir.exists()) {
fdir.mkdir();
Utils.log("Created directory " + fdir);
}
// if the directory exists already just reuse it, overwritting its files if so.
final String tmp = fdir.getAbsolutePath().replace('\\', '/');
if (!tile_dir.equals(tmp))
Utils.log("\tWARNING: directory will not be in the standard location.");
// debug:
Utils.log2("tile_dir: " + tile_dir + "\ntmp: " + tmp);
tile_dir = tmp;
if (!tile_dir.endsWith("/"))
tile_dir += "/";
// 2 - create layer thumbnail, max 192x192
ImagePlus thumb = getFlatImage(layer, srcRect, thumb_scale, c_alphas, type, clazz, true);
saver.save(thumb, tile_dir + "small");
// ImageSaver.saveAsJpeg(thumb.getProcessor(), tile_dir + "small.jpg", jpeg_quality, ImagePlus.COLOR_RGB != type);
flush(thumb);
thumb = null;
// 3 - fill directory with tiles
if (edge_length < tileSide) {
// edge_length is the largest length of the tileSide x tileSide tile map that covers an area equal or larger than the desired srcRect (because all tiles have to be tileSide x tileSide in size)
// create single tile per layer
makeTile(layer, srcRect, max_scale, c_alphas, type, clazz, tile_dir + "0_0_0", saver);
} else {
// create pyramid of tiles
if (from_original_images) {
Utils.log("Exporting from web using original images");
// Create a giant 8-bit image of the whole layer from original images
double scale = 1;
Utils.log("Export srcRect: " + srcRect);
// WARNING: the snapshot will most likely be smaller than the virtual square image being chopped into tiles
ImageProcessor snapshot = null;
if (ImagePlus.COLOR_RGB == type) {
Utils.log("WARNING: ignoring alpha masks for 'use original images' and 'RGB color' options");
snapshot = Patch.makeFlatImage(type, layer, srcRect, scale, (ArrayList<Patch>) (List) layer.getDisplayables(Patch.class, true), Color.black, true);
} else if (ImagePlus.GRAY8 == type) {
// Respect alpha masks and display range:
Utils.log("WARNING: ignoring scale for 'use original images' and '8-bit' options");
snapshot = ExportUnsignedShort.makeFlatImage((ArrayList<Patch>) (List) layer.getDisplayables(Patch.class, true), srcRect, 0).convertToByte(true);
} else {
Utils.log("ERROR: don't know how to generate mipmaps for type '" + type + "'");
cleanUp();
return;
}
int scale_pow = 0;
int n_et = n_edge_tiles;
final ExecutorService exe = Utils.newFixedThreadPool("export-for-web");
final ArrayList<Future<?>> fus = new ArrayList<Future<?>>();
try {
while (n_et >= best[1]) {
final int snapWidth = snapshot.getWidth();
final int snapHeight = snapshot.getHeight();
final ImageProcessor source = snapshot;
for (int row = 0; row < n_et; row++) {
for (int col = 0; col < n_et; col++) {
final String path = new StringBuilder(tile_dir).append(row).append('_').append(col).append('_').append(scale_pow).toString();
final int tileXStart = col * tileSide;
final int tileYStart = row * tileSide;
final int pixelOffset = tileYStart * snapWidth + tileXStart;
fus.add(exe.submit(new Callable<Boolean>() {
@Override
public Boolean call() {
if (ImagePlus.GRAY8 == type) {
final byte[] pixels = (byte[]) source.getPixels();
final byte[] p = new byte[tileSide * tileSide];
for (int y = 0, sourceIndex = pixelOffset; y < tileSide && tileYStart + y < snapHeight; sourceIndex = pixelOffset + y * snapWidth, y++) {
final int offsetL = y * tileSide;
for (int x = 0; x < tileSide && tileXStart + x < snapWidth; sourceIndex++, x++) {
p[offsetL + x] = pixels[sourceIndex];
}
}
return saver.save(new ImagePlus(path, new ByteProcessor(tileSide, tileSide, p, GRAY_LUT)), path);
} else {
final int[] pixels = (int[]) source.getPixels();
final int[] p = new int[tileSide * tileSide];
for (int y = 0, sourceIndex = pixelOffset; y < tileSide && tileYStart + y < snapHeight; sourceIndex = pixelOffset + y * snapWidth, y++) {
final int offsetL = y * tileSide;
for (int x = 0; x < tileSide && tileXStart + x < snapWidth; sourceIndex++, x++) {
p[offsetL + x] = pixels[sourceIndex];
}
}
return saver.save(new ImagePlus(path, new ColorProcessor(tileSide, tileSide, p)), path);
}
}
}));
}
}
//
scale_pow++;
// works as magnification
scale = 1 / Math.pow(2, scale_pow);
n_et /= 2;
//
Utils.wait(fus);
fus.clear();
// Scale snapshot in half with area averaging
final ImageProcessor nextSnapshot;
if (ImagePlus.GRAY8 == type) {
nextSnapshot = new ByteProcessor((int) (srcRect.width * scale), (int) (srcRect.height * scale));
final byte[] p1 = (byte[]) snapshot.getPixels();
final byte[] p2 = (byte[]) nextSnapshot.getPixels();
final int width1 = snapshot.getWidth();
final int width2 = nextSnapshot.getWidth();
final int height2 = nextSnapshot.getHeight();
int i = 0;
for (int y1 = 0, y2 = 0; y2 < height2; y1 += 2, y2++) {
final int offset1a = y1 * width1;
final int offset1b = (y1 + 1) * width1;
for (int x1 = 0, x2 = 0; x2 < width2; x1 += 2, x2++) {
p2[i++] = (byte) (((p1[offset1a + x1] & 0xff) + (p1[offset1a + x1 + 1] & 0xff) + (p1[offset1b + x1] & 0xff) + (p1[offset1b + x1 + 1] & 0xff)) / 4);
}
}
} else {
nextSnapshot = new ColorProcessor((int) (srcRect.width * scale), (int) (srcRect.height * scale));
final int[] p1 = (int[]) snapshot.getPixels();
final int[] p2 = (int[]) nextSnapshot.getPixels();
final int width1 = snapshot.getWidth();
final int width2 = nextSnapshot.getWidth();
final int height2 = nextSnapshot.getHeight();
int i = 0;
for (int y1 = 0, y2 = 0; y2 < height2; y1 += 2, y2++) {
final int offset1a = y1 * width1;
final int offset1b = (y1 + 1) * width1;
for (int x1 = 0, x2 = 0; x2 < width2; x1 += 2, x2++) {
final int ka = p1[offset1a + x1], kb = p1[offset1a + x1 + 1], kc = p1[offset1b + x1], kd = p1[offset1b + x1 + 1];
// Average each channel independently
p2[i++] = (((// red
((ka >> 16) & 0xff) + ((kb >> 16) & 0xff) + ((kc >> 16) & 0xff) + ((kd >> 16) & 0xff)) / 4) << 16) + (((// green
((ka >> 8) & 0xff) + ((kb >> 8) & 0xff) + ((kc >> 8) & 0xff) + ((kd >> 8) & 0xff)) / 4) << 8) + (// blue
(ka & 0xff) + (kb & 0xff) + (kc & 0xff) + (kd & 0xff)) / 4;
}
}
}
// Assign for next iteration
snapshot = nextSnapshot;
// Scale snapshot with a TransformMesh
/*
AffineModel2D aff = new AffineModel2D();
aff.set(0.5f, 0, 0, 0.5f, 0, 0);
ImageProcessor scaledSnapshot = new ByteProcessor((int)(snapshot.getWidth() * scale), (int)(snapshot.getHeight() * scale));
final CoordinateTransformMesh mesh = new CoordinateTransformMesh( aff, 32, snapshot.getWidth(), snapshot.getHeight() );
final mpicbg.ij.TransformMeshMapping<CoordinateTransformMesh> mapping = new mpicbg.ij.TransformMeshMapping<CoordinateTransformMesh>( mesh );
mapping.mapInterpolated(snapshot, scaledSnapshot, Runtime.getRuntime().availableProcessors());
// Assign for next iteration
snapshot = scaledSnapshot;
snapshotPixels = (byte[]) scaledSnapshot.getPixels();
*/
}
} catch (final Throwable t) {
IJError.print(t);
} finally {
exe.shutdown();
}
} else {
// max_scale; // WARNING if scale is different than 1, it will FAIL to set the next scale properly.
double scale = 1;
int scale_pow = 0;
// cached for local modifications in the loop, works as loop controler
int n_et = n_edge_tiles;
while (n_et >= best[1]) {
// best[1] is the minimal root found, i.e. 1,2,3,4,5 from which then powers of two were taken to make up for the edge_length
// 0 < scale <= 1, so no precision lost
final int tile_side = (int) (256 / scale);
for (int row = 0; row < n_et; row++) {
for (int col = 0; col < n_et; col++) {
final int i_tile = row * n_et + col;
Utils.showProgress(i_tile / (double) (n_et * n_et));
if (0 == i_tile % 100) {
// RGB int[] images
releaseToFit(tile_side * tile_side * 4 * 2);
}
if (this.quit) {
cleanUp();
return;
}
final Rectangle tile_src = new // TODO row and col are inverted
Rectangle(// TODO row and col are inverted
srcRect.x + tile_side * row, srcRect.y + tile_side * col, tile_side, // in absolute coords, magnification later.
tile_side);
// crop bounds
if (tile_src.x + tile_src.width > srcRect.x + srcRect.width)
tile_src.width = srcRect.x + srcRect.width - tile_src.x;
if (tile_src.y + tile_src.height > srcRect.y + srcRect.height)
tile_src.height = srcRect.y + srcRect.height - tile_src.y;
// negative tile sizes will be made into black tiles
// (negative dimensions occur for tiles beyond the edges of srcRect, since the grid of tiles has to be of equal number of rows and cols)
// should be row_col_scale, but results in transposed tiles in googlebrains, so I reversed the order.
makeTile(layer, tile_src, scale, c_alphas, type, clazz, new StringBuilder(tile_dir).append(col).append('_').append(row).append('_').append(scale_pow).toString(), saver);
}
}
scale_pow++;
// works as magnification
scale = 1 / Math.pow(2, scale_pow);
n_et /= 2;
}
}
}
}
} catch (final Exception e) {
IJError.print(e);
} finally {
Utils.showProgress(1);
}
cleanUp();
finishedWorking();
}
};
// watcher thread
return Bureaucrat.createAndStart(worker, layers[0].getProject());
}
use of ini.trakem2.display.Display in project TrakEM2 by trakem2.
the class Loader method importImages.
/**
* <p>Import images from the given text file, which is expected to contain 4 columns or optionally 9 columns:</p>
* <ul>
* <li>column 1: image file path (if base_dir is not null, it will be prepended)</li>
* <li>column 2: x coord [px]</li>
* <li>column 3: y coord [px]</li>
* <li>column 4: z coord [px] (layer_thickness will be multiplied to it if not zero)</li>
* </ul>
* <p>optional columns, if a property is not known, it can be set to "-" which makes TrakEM2 open the file and find out by itself</p>
* <ul>
* <li>column 5: width [px]</li>
* <li>column 6: height [px]</li>
* <li>column 7: min intensity [double] (for screen display)</li>
* <li>column 8: max intensity [double] (for screen display)</li>
* <li>column 9: type [integer] (pixel types according to ImagepPlus types: 0=8bit int gray, 1=16bit int gray, 2=32bit float gray, 3=8bit indexed color, 4=32-bit RGB color</li>
* </ul>
*
* <p>This function implements the "Import from text file" command.</p>
*
* <p>Layers will be automatically created as needed inside the LayerSet to which the given ref_layer belongs.</p>
* <p>
* The text file can contain comments that start with the # sign.
* </p>
* <p>
* Images will be imported in parallel, using as many cores as your machine has.
* </p>
* @param calibration_ transforms the read coordinates into pixel coordinates, including x,y,z, and layer thickness.
* @param scale_ Between 0 and 1. When lower than 1, a preprocessor script is created for the imported images, to scale them down.
*/
public Bureaucrat importImages(Layer ref_layer, String abs_text_file_path_, String column_separator_, double layer_thickness_, double calibration_, boolean homogenize_contrast_, float scale_, int border_width_) {
// check parameters: ask for good ones if necessary
if (null == abs_text_file_path_) {
final String[] file = Utils.selectFile("Select text file");
// user canceled dialog
if (null == file)
return null;
abs_text_file_path_ = file[0] + file[1];
}
if (null == column_separator_ || 0 == column_separator_.length() || Double.isNaN(layer_thickness_) || layer_thickness_ <= 0 || Double.isNaN(calibration_) || calibration_ <= 0) {
final Calibration cal = ref_layer.getParent().getCalibrationCopy();
final GenericDialog gdd = new GenericDialog("Options");
final String[] separators = new String[] { "tab", "space", "comma (,)" };
gdd.addMessage("Choose a layer to act as the zero for the Z coordinates:");
Utils.addLayerChoice("Base layer", ref_layer, gdd);
gdd.addChoice("Column separator: ", separators, separators[0]);
// default: 60 nm
gdd.addNumericField("Layer thickness: ", cal.pixelDepth, 2);
gdd.addNumericField("Calibration (data to pixels): ", 1, 2);
gdd.addCheckbox("Homogenize contrast layer-wise", homogenize_contrast_);
gdd.addSlider("Scale:", 0, 100, 100);
gdd.addNumericField("Hide border with alpha mask", 0, 0, 6, "pixels");
gdd.showDialog();
if (gdd.wasCanceled())
return null;
layer_thickness_ = gdd.getNextNumber();
if (layer_thickness_ < 0 || Double.isNaN(layer_thickness_)) {
Utils.log("Improper layer thickness value.");
return null;
}
calibration_ = gdd.getNextNumber();
if (0 == calibration_ || Double.isNaN(calibration_)) {
Utils.log("Improper calibration value.");
return null;
}
// not pixelDepth!
layer_thickness_ /= cal.pixelWidth;
ref_layer = ref_layer.getParent().getLayer(gdd.getNextChoiceIndex());
column_separator_ = "\t";
switch(gdd.getNextChoiceIndex()) {
case 1:
column_separator_ = " ";
break;
case 2:
column_separator_ = ",";
break;
default:
break;
}
homogenize_contrast_ = gdd.getNextBoolean();
final double sc = gdd.getNextNumber();
if (Double.isNaN(sc))
scale_ = 1.0f;
else
scale_ = ((float) sc) / 100.0f;
final int border = (int) gdd.getNextNumber();
if (border < 0) {
Utils.log("Nonsensical border value: " + border);
return null;
}
border_width_ = border;
}
if (Float.isNaN(scale_) || scale_ < 0 || scale_ > 1) {
Utils.log("Non-sensical scale: " + scale_ + "\nUsing scale of 1 instead.");
scale_ = 1;
}
// make vars accessible from inner threads:
final Layer base_layer = ref_layer;
final String abs_text_file_path = abs_text_file_path_;
final String column_separator = column_separator_;
final double layer_thickness = layer_thickness_;
final double calibration = calibration_;
final boolean homogenize_contrast = homogenize_contrast_;
final float scale = (float) scale_;
final int border_width = border_width_;
return Bureaucrat.createAndStart(new Worker.Task("Importing images", true) {
@Override
public void exec() {
try {
// 1 - read text file
final String[] lines = Utils.openTextFileLines(abs_text_file_path);
if (null == lines || 0 == lines.length) {
Utils.log2("No images to import from " + abs_text_file_path);
return;
}
ContrastEnhancerWrapper cew = null;
if (homogenize_contrast) {
cew = new ContrastEnhancerWrapper();
cew.showDialog();
}
final String sep2 = column_separator + column_separator;
// 2 - set a base dir path if necessary
String base_dir = null;
// to wait on mipmap regeneration
final Vector<Future<?>> fus = new Vector<Future<?>>();
final LayerSet layer_set = base_layer.getParent();
final double z_zero = base_layer.getZ();
final AtomicInteger n_imported = new AtomicInteger(0);
final Set<Layer> touched_layers = new HashSet<Layer>();
final int NP = Runtime.getRuntime().availableProcessors();
int np = NP;
switch(np) {
case 1:
case 2:
break;
default:
np = np / 2;
break;
}
final ExecutorService ex = Utils.newFixedThreadPool(np, "import-images");
final List<Future<?>> imported = new ArrayList<Future<?>>();
final Worker wo = this;
final String script_path;
// If scale is at least 1/100 lower than 1, then:
if (Math.abs(scale - (int) scale) > 0.01) {
// Assume source and target sigma of 0.5
final double sigma = Math.sqrt(Math.pow(1 / scale, 2) - 0.25);
final String script = new StringBuilder().append("import ij.ImagePlus;\n").append("import ij.process.ImageProcessor;\n").append("import ij.plugin.filter.GaussianBlur;\n").append("GaussianBlur blur = new GaussianBlur();\n").append(// as in ij.plugin.filter.GaussianBlur
"double accuracy = (imp.getType() == ImagePlus.GRAY8 || imp.getType() == ImagePlus.COLOR_RGB) ? 0.002 : 0.0002;\n").append("imp.getProcessor().setInterpolationMethod(ImageProcessor.NONE);\n").append("blur.blurGaussian(imp.getProcessor(),").append(sigma).append(',').append(sigma).append(",accuracy);\n").append("imp.setProcessor(imp.getTitle(), imp.getProcessor().resize((int)(imp.getWidth() * ").append(scale).append("), (int)(imp.getHeight() * ").append(scale).append(")));").toString();
File f = new File(getStorageFolder() + "resize-" + scale + ".bsh");
int v = 1;
while (f.exists()) {
f = new File(getStorageFolder() + "resize-" + scale + "." + v + ".bsh");
v++;
}
script_path = Utils.saveToFile(f, script) ? f.getAbsolutePath() : null;
if (null == script_path) {
Utils.log("Could NOT save a preprocessor script for image scaling\nat path " + f.getAbsolutePath());
}
} else {
script_path = null;
}
Utils.log("Scaling script path is " + script_path);
final AtomicReference<Triple<Integer, Integer, ByteProcessor>> last_mask = new AtomicReference<Triple<Integer, Integer, ByteProcessor>>();
// 3 - parse each line
for (int i = 0; i < lines.length; i++) {
if (Thread.currentThread().isInterrupted() || hasQuitted()) {
this.quit();
return;
}
// process line
// first thing is the backslash removal, before they get processed at all
String line = lines[i].replace('\\', '/').trim();
final int ic = line.indexOf('#');
// remove comment at end of line if any
if (-1 != ic)
line = line.substring(0, ic);
if (0 == line.length() || '#' == line.charAt(0))
continue;
// reduce line, so that separators are really unique
while (-1 != line.indexOf(sep2)) {
line = line.replaceAll(sep2, column_separator);
}
final String[] column = line.split(column_separator);
if (column.length < 4) {
Utils.log("Less than 4 columns: can't import from line " + i + " : " + line);
continue;
}
// obtain coordinates
double x = 0, y = 0, z = 0;
try {
x = Double.parseDouble(column[1].trim());
y = Double.parseDouble(column[2].trim());
z = Double.parseDouble(column[3].trim());
} catch (final NumberFormatException nfe) {
Utils.log("Non-numeric value in a numeric column at line " + i + " : " + line);
continue;
}
x *= calibration;
y *= calibration;
z = z * calibration + z_zero;
// obtain path
String path = column[0].trim();
if (0 == path.length())
continue;
// check if path is relative
if ((!IJ.isWindows() && '/' != path.charAt(0)) || (IJ.isWindows() && 1 != path.indexOf(":/"))) {
// path is relative.
if (null == base_dir) {
// may not be null if another thread that got the lock first set it to non-null
// Ask for source directory
final DirectoryChooser dc = new DirectoryChooser("Choose source directory");
final String dir = dc.getDirectory();
if (null == dir) {
// quit all threads
return;
}
base_dir = Utils.fixDir(dir);
}
}
if (null != base_dir)
path = base_dir + path;
final File f = new File(path);
if (!f.exists()) {
Utils.log("No file found for path " + path);
continue;
}
// will create a new Layer if necessary
final Layer layer = layer_set.getLayer(z, layer_thickness, true);
touched_layers.add(layer);
final String imagefilepath = path;
final double xx = x * scale;
final double yy = y * scale;
final Callable<Patch> creator;
if (column.length >= 9) {
creator = new Callable<Patch>() {
private final int parseInt(final String t) {
if (t.equals("-"))
return -1;
return Integer.parseInt(t);
}
private final double parseDouble(final String t) {
if (t.equals("-"))
return Double.NaN;
return Double.parseDouble(t);
}
@Override
public Patch call() throws Exception {
int o_width = parseInt(column[4].trim());
int o_height = parseInt(column[5].trim());
double min = parseDouble(column[6].trim());
double max = parseDouble(column[7].trim());
int type = parseInt(column[8].trim());
if (-1 == type || -1 == o_width || -1 == o_height) {
// Read them from the file header
final ImageFileHeader ifh = new ImageFileHeader(imagefilepath);
o_width = ifh.width;
o_height = ifh.height;
type = ifh.type;
if (!ifh.isSupportedType()) {
Utils.log("Incompatible image type: " + imagefilepath);
return null;
}
}
ImagePlus imp = null;
if (Double.isNaN(min) || Double.isNaN(max)) {
imp = openImagePlus(imagefilepath);
min = imp.getProcessor().getMin();
max = imp.getProcessor().getMax();
}
final Patch patch = new Patch(layer.getProject(), new File(imagefilepath).getName(), o_width, o_height, o_width, o_height, type, 1.0f, Color.yellow, false, min, max, new AffineTransform(1, 0, 0, 1, xx, yy), imagefilepath);
if (null != script_path && null != imp) {
// For use in setting the preprocessor script
cacheImagePlus(patch.getId(), imp);
}
return patch;
}
};
} else {
creator = new Callable<Patch>() {
@Override
public Patch call() throws Exception {
IJ.redirectErrorMessages();
final ImageFileHeader ifh = new ImageFileHeader(imagefilepath);
final int o_width = ifh.width;
final int o_height = ifh.height;
final int type = ifh.type;
if (!ifh.isSupportedType()) {
Utils.log("Incompatible image type: " + imagefilepath);
return null;
}
double min = 0;
double max = 255;
switch(type) {
case ImagePlus.GRAY16:
case ImagePlus.GRAY32:
// Determine suitable min and max
// TODO Stream through the image, do not load it!
final ImagePlus imp = openImagePlus(imagefilepath);
if (null == imp) {
Utils.log("Ignoring unopenable image from " + imagefilepath);
return null;
}
min = imp.getProcessor().getMin();
max = imp.getProcessor().getMax();
break;
}
// add Patch
final Patch patch = new Patch(layer.getProject(), new File(imagefilepath).getName(), o_width, o_height, o_width, o_height, type, 1.0f, Color.yellow, false, min, max, new AffineTransform(1, 0, 0, 1, xx, yy), imagefilepath);
return patch;
}
};
}
// Otherwise, images would end up loaded twice for no reason
if (0 == (i % (NP + NP))) {
final ArrayList<Future<?>> a = new ArrayList<Future<?>>(NP + NP);
synchronized (fus) {
// .add is also synchronized, fus is a Vector
int k = 0;
while (!fus.isEmpty() && k < NP) {
a.add(fus.remove(0));
k++;
}
}
for (final Future<?> fu : a) {
try {
if (wo.hasQuitted())
return;
fu.get();
} catch (final Throwable t) {
t.printStackTrace();
}
}
}
imported.add(ex.submit(new Runnable() {
@Override
public void run() {
if (wo.hasQuitted())
return;
/* */
IJ.redirectErrorMessages();
Patch patch;
try {
patch = creator.call();
} catch (final Exception e) {
e.printStackTrace();
Utils.log("Could not load patch from " + imagefilepath);
return;
}
// Set the script if any
if (null != script_path) {
try {
patch.setPreprocessorScriptPath(script_path);
} catch (final Throwable t) {
Utils.log("FAILED to set a scaling preprocessor script to patch " + patch);
IJError.print(t);
}
}
// Set an alpha mask to crop away the borders
if (border_width > 0) {
final Triple<Integer, Integer, ByteProcessor> m = last_mask.get();
if (null != m && m.a == patch.getOWidth() && m.b == patch.getOHeight()) {
// Reuse
patch.setAlphaMask(m.c);
} else {
// Create new mask
final ByteProcessor mask = new ByteProcessor(patch.getOWidth(), patch.getOHeight());
mask.setValue(255);
mask.setRoi(new Roi(border_width, border_width, mask.getWidth() - 2 * border_width, mask.getHeight() - 2 * border_width));
mask.fill();
patch.setAlphaMask(mask);
// Store as last
last_mask.set(new Triple<Integer, Integer, ByteProcessor>(mask.getWidth(), mask.getHeight(), mask));
}
}
if (!homogenize_contrast) {
fus.add(regenerateMipMaps(patch));
}
synchronized (layer) {
layer.add(patch, true);
}
wo.setTaskName("Imported " + (n_imported.incrementAndGet() + 1) + "/" + lines.length);
}
}));
}
Utils.wait(imported);
ex.shutdown();
if (0 == n_imported.get()) {
Utils.log("No images imported.");
return;
}
base_layer.getParent().setMinimumDimensions();
Display.repaint(base_layer.getParent());
recreateBuckets(touched_layers);
if (homogenize_contrast) {
setTaskName("Enhance contrast");
// layer-wise (layer order is irrelevant):
cew.applyLayerWise(touched_layers);
cew.shutdown();
}
Utils.wait(fus);
} catch (final Exception e) {
IJError.print(e);
}
}
}, base_layer.getProject());
}
Aggregations