Implemented partial support for CoordinateTransforms for Patch.
[trakem2.git] / Microcube_Maker.java
blob8d2201e1fc1796fec4fee8dedca77c1b7619cf4a
1 import ini.trakem2.Project;
2 import ini.trakem2.ControlWindow;
3 import ini.trakem2.persistence.Loader;
4 import ini.trakem2.persistence.FSLoader;
5 import ini.trakem2.display.Layer;
6 import ini.trakem2.display.LayerSet;
7 import ini.trakem2.display.Patch;
8 import ini.trakem2.display.Displayable;
9 import ini.trakem2.utils.Utils;
10 import ini.trakem2.imaging.Registration;
12 import ij.ImagePlus;
13 import ij.io.FileSaver;
14 import ij.plugin.PlugIn;
15 import ij.Macro;
17 import java.awt.Rectangle;
18 import java.io.File;
19 import java.util.ArrayList;
20 import java.util.List;
22 /** Albert Cardona 2007
24 * Takes as arguments:
25 * - the file path to a TrakEM2 XML project
26 * - the top-left 3D point of the microcube, as x1 y1 z1
27 * - the bottom-right point of the microcube, as x2 y2 z2
28 * - the scale
29 * - boolean to re-register stack
30 * - file path to tif stack to be generated (will be overwritten if it exists)
32 * (in total, 10 arguments)
34 * Layers are taken from the Z coordinates of the points.
35 * If a layer is not found for the exact given Z, it will get the nearest layer to that Z.
37 * Assumptions:
38 * - That the microcube stack will fit in RAM memory.
39 * - That the desired outcome is 8-bit gray
40 * - That the project is on a FSLoader
41 * - That the project may already be open
43 * Example of usage:
45 * $ java -Xmx1000m -classpath .:../../ij.jar:./plugins/TrakEM2_.jar Microcube_Maker /path/to/project.xml 123 456 78.9 987 654 87.0 0.5 true /path/to/stack.tif
47 * Example of usage as a plugin:
49 * java -Xmx512m -classpath /home/albert/Applications/ImageJ/ij.jar:/home/albert/Applications/ImageJ/plugins/TrakEM2_.jar -Dplugins.dir=/home/albert/Applications/ImageJ ij.ImageJ -eval "run(\"Microcube Maker\", \"/home/albert/Desktop/ministack4-mm.xml 520 300 0 1020 800 150 1.0 true /home/albert/temp/test-mc/result.tif\");"
52 public class Microcube_Maker implements PlugIn {
54 public void run(String arg) {
55 if (null == arg || 0 == arg.trim().length()) {
56 arg = Macro.getOptions();
58 p("Called as plugin with args:\n\t" + arg);
59 main(arg.split(" "));
62 static public void main(String[] arg) {
63 try {
64 makeMicrocube(arg);
65 } catch (Exception e) {
66 p("ERROR: could not create microcube stack file.");
67 e.printStackTrace();
71 static private final void makeMicrocube(final String[] arg) {
72 if (arg.length < 10) {
73 p("Example usage: java -Xmx1000m -classpath .:../../ij.jar:./plugins/TrakEM2_.jar Microcube_Maker /path/to/project.xml 123 456 78.9 987 654 87.0 0.5 true /path/to/stack.tif \nArguments are: <project path> <x1> <y1> <z1> <x2> <y2> <z2> <scale> <re-register layers: true|false> <path_to_stack>\n\n ... where x,y coords are pixel coordinates, and z are layer indices.");
74 return;
77 Project project = null;
78 boolean was_open = false;
80 try {
81 // parse args
82 String path = arg[0].trim();
83 if (FSLoader.isURL(path)) {
84 // ok, we'll see if it an be opened
85 } else if (!new File(path).exists()) {
86 p("Project XML file path is invalid or not found: " + path);
87 return;
89 double x1, y1, z1,
90 x2, y2, z2,
91 scale;
92 try {
93 x1 = Double.parseDouble(arg[1]);
94 y1 = Double.parseDouble(arg[2]);
95 z1 = Integer.parseInt(arg[3]);
96 x2 = Double.parseDouble(arg[4]);
97 y2 = Double.parseDouble(arg[5]);
98 z2 = Integer.parseInt(arg[6]);
99 scale = Double.parseDouble(arg[7]);
101 } catch (NumberFormatException nfe) {
102 p("Improper numerical argument for a coordinate.");
103 nfe.printStackTrace();
104 return;
106 final boolean align = Boolean.parseBoolean(arg[8].trim().toLowerCase());
108 ControlWindow.setGUIEnabled(false);
110 // check if the project is already open
111 project = FSLoader.getOpenProject(path);
112 if (null == project) {
113 project = Project.openFSProject(path);
114 } else {
115 was_open = true;
117 if (null == project) {
118 p("Could not open TrakEM2 project at path " + path);
119 return;
121 // define ROI
122 int x = (int)(x1 < x2 ? x1 : x2);
123 int y = (int)(y1 < y2 ? y1 : y2);
124 int w = (int)Math.abs(x1 < x2 ? x2 - x1 : x1 - x2);
125 int h = (int)Math.abs(y1 < y2 ? y2 - y1 : y1 - y2);
126 final Rectangle roi = new Rectangle(x, y, w, h);
127 // define first and last layer
128 LayerSet ls = project.getRootLayerSet();
129 Layer la1 = ls.getLayer((int)Math.abs(z1)); // WARNING: Calibration
130 Layer la2 = ls.getLayer((int)Math.abs(z2));
131 if (z1 > z2) {
132 Layer tmp = la1;
133 la1 = la2;
134 la2 = tmp;
137 // Re-register, with proper enlarged sizes and subsequent crop to ensure there are no black areas in the downloaded microcube.
138 // One way to do it: BEST way, since the focus remains within the selected stack. Potential problem: if the slices don't match at all due to excessive deformations. But it's hard.
139 // - first cut stack
140 // - register
141 // - calculate enlarged box, and crop that from the original for each layer, but putting in the proper transforms.
142 // Another way to do it:
143 // - cut a bit more than will be needed
144 // - register
145 // - calculate enlarged box, crop that from the original - because black areas may still creep in
146 // Yet another way:
147 // - create a subproject from a double-side size area, centered on desired area
148 // - run the layer registration that will shift tile positions as well
149 // - bring in any other patches not originally included, to fill up any black areas
150 // - grab stack.
152 // Reality: need same ids for images to avoid duplicating cache and to reuse the same feature serialized files. So, hacking time:
154 Object ob;
156 if (!align) {
157 ob = ls.grab(ls.indexOf(la1), ls.indexOf(la2), roi, scale, Patch.class, 0xffffffff, Layer.IMAGEPLUS, ImagePlus.GRAY8);
158 } else {
159 // prepare enlarged roi: pad outwards by one tile width
160 final int padding = (int)la1.getDisplayables(Patch.class).get(0).getWidth();
161 final Rectangle roi2 = (Rectangle)roi.clone();
162 roi2.x -= padding;
163 roi2.y -= padding;
164 roi2.width += padding * 2;
165 roi2.height += padding * 2;
166 p("roi: " + roi);
167 p("padding: " + padding);
168 p("roi2: " + roi2);
170 // 1 - Make a subproject
171 Project sub = ls.getProject().createSubproject(roi, la1, la2);
172 LayerSet sub_ls = sub.getRootLayerSet();
173 // 2 - register all tiles freely and optimally
174 final Layer[] sub_la = new Layer[sub_ls.size()];
175 sub_ls.getLayers().toArray(sub_la);
176 final Thread task = Registration.registerTilesSIFT(sub_la, new boolean[]{true, false, false, false, false}, 512, 0.2499999f);
177 if (null != task) try { task.join(); } catch (Exception e) { e.printStackTrace(); }
178 // 3 - prepare roi for cropping
179 roi.x -= roi2.x;
180 roi.y -= roi2.y;
181 // 4 - grab microcube
182 ob = sub_ls.grab(0, sub_la.length-1, roi, scale, Patch.class, 0xffffffff, Layer.IMAGEPLUS, ImagePlus.GRAY8);
183 sub.destroy();
186 // Store
187 if (null != ob && ob instanceof ImagePlus) {
188 p("Ob is: " + ob);
189 ImagePlus imp = ((ImagePlus)ob);
190 //imp.show();
191 if (imp.getStackSize() > 1) {
192 p("Saving stack to " + arg[9]);
193 new FileSaver(imp).saveAsTiffStack(arg[9]);
194 p("Stack saved to " + arg[9]);
195 } else {
196 p("Saving image to " + arg[9]);
197 new FileSaver(imp).saveAsTiff(arg[9]);
198 p("Image saved to: " + arg[9]);
200 p("Microcube saved successfully to " + arg[9]);
201 } else {
202 p("Could not save microcube from " + ob);
203 return;
206 } catch (Throwable e) {
207 e.printStackTrace();
210 // done!
211 if (!was_open) {
212 //project.destroy();
213 System.exit(0);
217 static private final void p(final String msg) {
218 System.out.println(msg);