1 package com.github.celldynamics.quimp.plugin.ecmm;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.util.ArrayList;
6
7 import java.util.Random;
8
9 import org.slf4j.Logger;
10 import org.slf4j.LoggerFactory;
11
12 import com.github.celldynamics.quimp.Nest;
13 import com.github.celldynamics.quimp.Outline;
14 import com.github.celldynamics.quimp.OutlineHandler;
15 import com.github.celldynamics.quimp.QParams;
16 import com.github.celldynamics.quimp.QParamsQconf;
17 import com.github.celldynamics.quimp.QuimP;
18 import com.github.celldynamics.quimp.QuimpException;
19 import com.github.celldynamics.quimp.QuimpException.MessageSinkTypes;
20 import com.github.celldynamics.quimp.SnakeHandler;
21 import com.github.celldynamics.quimp.Vert;
22 import com.github.celldynamics.quimp.filesystem.DataContainer;
23 import com.github.celldynamics.quimp.filesystem.FileExtensions;
24 import com.github.celldynamics.quimp.filesystem.OutlinesCollection;
25 import com.github.celldynamics.quimp.filesystem.QconfLoader;
26 import com.github.celldynamics.quimp.filesystem.converter.FormatConverter;
27 import com.github.celldynamics.quimp.geom.ExtendedVector2d;
28 import com.github.celldynamics.quimp.plugin.AbstractPluginQconf;
29 import com.github.celldynamics.quimp.plugin.QuimpPluginException;
30
31 import ij.IJ;
32 import ij.WindowManager;
33 import ij.gui.YesNoCancelDialog;
34 import ij.process.ImageProcessor;
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75 public class ECMM_Mapping extends AbstractPluginQconf {
76
77 private static String thisPluginName = "ECMM Mapping";
78
79 private File fileToLoad = null;
80
81
82
83 static final Logger LOGGER = LoggerFactory.getLogger(ECMM_Mapping.class.getName());
84 private OutlineHandler oh;
85 private OutlineHandler outputH;
86 private OutlinesCollection outputOutlineHandlers;
87
88
89
90
91 public static ECMplot plot;
92
93
94
95
96 public ECMM_Mapping() {
97 super(new EcmmOptions(), thisPluginName);
98 }
99
100
101
102
103
104
105 public ECMM_Mapping(int frames) {
106 this();
107 if (ECMp.plot) {
108 plot = new ECMplot(frames);
109 }
110 }
111
112
113
114
115
116
117
118
119 public ECMM_Mapping(String paramString) throws QuimpPluginException {
120 super(paramString, new EcmmOptions(), thisPluginName);
121 }
122
123
124
125
126
127
128
129
130 public ECMM_Mapping(File file) {
131 super(new EcmmOptions(file), thisPluginName);
132 apiCall = true;
133 }
134
135
136
137
138
139
140 @Override
141 public void run(String arg) {
142 super.run(arg);
143 }
144
145
146
147
148
149
150 @Override
151 protected void loadFile(String paramFile) throws QuimpException {
152 if (options.paramFile == null || options.paramFile.isEmpty()) {
153 fileToLoad = null;
154 } else {
155 fileToLoad = new File(options.paramFile);
156 }
157
158 qconfLoader = new QconfLoader(fileToLoad);
159 if (qconfLoader == null || qconfLoader.getQp() == null) {
160 return;
161 }
162 if (qconfLoader.isFileLoaded() == QParams.QUIMP_11) {
163 QParams qp;
164 runFromPaqp();
165
166 File[] otherPaFiles = qconfLoader.getQp().findParamFiles();
167 if (otherPaFiles.length > 0) {
168 YesNoCancelDialog yncd = new YesNoCancelDialog(IJ.getInstance(), "Batch Process?",
169 "\tBatch Process?\n\n" + "Process other " + FileExtensions.configFileExt
170 + " files in the same folder with ECMM?\n"
171 + "[Files already run through ECMM will be skipped!]");
172 if (yncd.yesPressed()) {
173 ArrayList<String> runOn = new ArrayList<String>(otherPaFiles.length);
174 ArrayList<String> skipped = new ArrayList<String>(otherPaFiles.length);
175
176 for (int j = 0; j < otherPaFiles.length; j++) {
177 plot.close();
178 qconfLoader = new QconfLoader(otherPaFiles[j]);
179 if (qconfLoader == null || qconfLoader.getQp() == null) {
180 return;
181 }
182 qp = qconfLoader.getQp();
183 if (!qp.isEcmmHasRun()) {
184 IJ.log("Running on " + otherPaFiles[j].getAbsolutePath());
185 runFromPaqp();
186 runOn.add(otherPaFiles[j].getName());
187 } else {
188 IJ.log("Skipped " + otherPaFiles[j].getAbsolutePath());
189 skipped.add(otherPaFiles[j].getName());
190 }
191
192 }
193 IJ.log("\n\nBatch - Successfully ran ECMM on:");
194 for (int i = 0; i < runOn.size(); i++) {
195 IJ.log(runOn.get(i));
196 }
197 IJ.log("\nSkipped:");
198 for (int i = 0; i < skipped.size(); i++) {
199 IJ.log(skipped.get(i));
200 }
201 } else {
202 return;
203 }
204 }
205 } else if (qconfLoader.isFileLoaded() == QParams.NEW_QUIMP) {
206
207 validate();
208 if (qconfLoader.isECMMPresent() && apiCall == false && errorSink == MessageSinkTypes.GUI) {
209 YesNoCancelDialog ync;
210 ync = new YesNoCancelDialog(IJ.getInstance(), "Overwrite",
211 "You are about to override previous ECMM results. Is it ok?");
212 if (!ync.yesPressed()) {
213 IJ.log("No changes done in input file.");
214 return;
215 }
216 }
217 runFromQconf();
218 IJ.log("The new data file " + qconfLoader.getQp().getParamFile().toString()
219 + " has been updated by results of ECMM analysis.");
220 } else {
221 throw new QuimpPluginException("QconfLoader returned unknown version of QuimP or error: "
222 + qconfLoader.isFileLoaded());
223 }
224 }
225
226
227
228
229
230
231 @Override
232 protected void executer() throws QuimpException {
233
234 if (apiCall == true) {
235 errorSink = MessageSinkTypes.CONSOLE;
236 }
237 super.executer();
238 }
239
240
241
242
243
244
245
246
247
248 public OutlineHandler./../../../com/github/celldynamics/quimp/OutlineHandler.html#OutlineHandler">OutlineHandler runByANA(OutlineHandler m, ImageProcessor ipr, double d) {
249 oh = m;
250 ECMp.image = ipr;
251 ECMp.setParams(oh.maxLength);
252 ECMp.startFrame = oh.getStartFrame();
253 ECMp.endFrame = oh.getEndFrame();
254 ECMp.plot = false;
255 ECMp.ANA = true;
256 ECMp.anaMigDist = d;
257 ECMp.migQ = 1.5E-5;
258 ECMp.tarQ = -1.5E-5;
259
260 if (ECMp.plot) {
261 plot = new ECMplot(oh.getSize() - 1);
262 }
263
264
265
266
267 ECMp.h = 0.9;
268 ECMp.chargeDensity = 4;
269 ECMp.d = 0.4;
270 ECMp.maxVertF = 0.7;
271
272 runPlugin();
273
274 return outputH;
275 }
276
277
278
279
280
281
282
283 private void runPlugin() {
284 long time = System.currentTimeMillis();
285 if (!ECMp.ANA) {
286 IJ.log("ECMM resolution: " + ECMp.markerRes + "(av. spacing)\n");
287 }
288
289 outputH = new OutlineHandler(oh);
290 ECMp.unSnapped = 0;
291
292
293
294 Mapping map1;
295 int f = ECMp.startFrame;
296 Outline o1 = oh.getStoredOutline(f);
297
298 if (!ECMp.ANA) {
299 if (Math.abs(ECMp.markerRes) > 0) {
300 o1.setResolution(Math.abs(ECMp.markerRes));
301 }
302 }
303
304
305 o1.resetAllCoords();
306 o1.clearFluores();
307 o1.calcCentroid();
308
309 outputH.save(o1, f);
310 Outline o2;
311
312 int stopAt = -1;
313
314 for (; f <= oh.getEndFrame() - 1; f++) {
315 if (f == stopAt) {
316 ECMp.plot = true;
317 }
318 if (o1.checkCoordErrors()) {
319 IJ.error("There was an error in tracking due to a bug (frame " + (f) + ")"
320 + "\nPlease try again");
321 break;
322 }
323
324 if (!ECMp.ANA) {
325 IJ.showStatus("Running ECMM");
326 IJ.showProgress(f, oh.getEndFrame());
327 IJ.log("Mapping " + f + " to " + (f + 1));
328 }
329
330 o2 = oh.getStoredOutline(f + 1);
331
332 if (!ECMp.ANA && ECMp.markerRes > 0) {
333 o2.setResolution(ECMp.markerRes);
334 }
335 o2.resetAllCoords();
336 o2.clearFluores();
337
338 if (!ECMp.ANA) {
339 this.nudgeOverlaps(o1, o2);
340 }
341
342 if (ECMp.plot) {
343 plot.setDrawingFrame(f);
344 o1.calcCentroid();
345
346 plot.centre = o1.getCentroid();
347
348 if (ECMp.drawInitialOutlines) {
349 plot.setColor(0d, 0d, 1d);
350 plot.drawOutline(o1);
351 plot.setColor(0d, 1d, 0d);
352 plot.drawOutline(o2);
353 plot.setSlice(f);
354 }
355 }
356
357
358
359
360 map1 = new Mapping(o1, o2);
361
362
363
364
365
366
367
368 o1 = map1.migrate();
369
370
371 if (!ECMp.ANA) {
372
373 if (!ECMp.disableDensityCorrections) {
374 if (o1.removeNanoEdges()) {
375
376 }
377 if (o1.cutSelfIntersects()) {
378 IJ.log(" result self intersected - fixed");
379 if (ECMp.plot) {
380 plot.writeText("Fixed self intersection");
381 }
382 }
383
384 if (ECMp.markerRes == 0) {
385 o1.correctDensity(2 * 1.6, 2 / 1.6);
386 } else {
387 o1.correctDensity(ECMp.markerRes * 1.6, ECMp.markerRes / 1.6);
388 }
389 }
390 if (ECMp.plot && ECMp.drawSolutionOutlines) {
391 plot.setColor(1d, 0d, 0d);
392 plot.drawOutline(o1);
393 }
394 }
395
396 if (ECMp.ANA && ECMp.plot) {
397 plot.setColor(0d, 0.7d, 0.7d);
398 plot.drawOutline(o1);
399 }
400
401
402
403
404 o1.coordReset();
405
406 outputH.save(o1, f + 1);
407 if (f == stopAt) {
408 break;
409 }
410 }
411
412
413 if (ECMp.plot) {
414 plot.repaint();
415 }
416
417 if (!ECMp.ANA) {
418 double timeSec = (System.currentTimeMillis() - time) / 1000d;
419 IJ.showStatus("ECMM finished");
420 IJ.log("ECMM finished in " + timeSec + " seconds.");
421 }
422 return;
423 }
424
425 private void nudgeOverlaps(Outlineref="../../../../../../com/github/celldynamics/quimp/Outline.html#Outline">Outline o1, Outline o2) {
426
427 int state;
428 double[] intersect = new double[2];
429 Random rg = new Random();
430
431 Vert na = o1.getHead();
432 Vert nb;
433 do {
434 nb = o2.getHead();
435 do {
436
437 if (nb.getX() == na.getX() && nb.getY() == na.getY()) {
438
439 na.setX(na.getX() + (rg.nextDouble() * 0.5) + 0.01);
440 na.setY(na.getY() + (rg.nextDouble() * 0.5) + 0.01);
441 }
442
443
444 state = ExtendedVector2d.segmentIntersection(na.getX(), na.getY(), na.getNext().getX(),
445 na.getNext().getY(), nb.getX(), nb.getY(), nb.getNext().getX(), nb.getNext().getY(),
446 intersect);
447 if (state == -1 || state == -2) {
448
449 na.setX(na.getX() + (rg.nextDouble() * 0.5) + 0.01);
450 na.setY(na.getY() + (rg.nextDouble() * 0.5) + 0.01);
451 }
452
453 nb = nb.getNext();
454 } while (!nb.isHead());
455 na = na.getNext();
456
457 } while (!na.isHead());
458
459 }
460
461
462
463
464
465
466 @Override
467 public String about() {
468 return "ECMM plugin.\n" + "Authors: Piotr Baniukiewicz\n"
469 + "mail: p.baniukiewicz@warwick.ac.uk\n" + "Richard Tyson";
470 }
471
472
473
474
475
476
477 @Override
478 protected void runFromQconf() throws QuimpException {
479 LOGGER.debug("Processing from new file format");
480 Nest nest = ((QParamsQconf) qconfLoader.getQp()).getNest();
481 outputOutlineHandlers = new OutlinesCollection(nest.size());
482 for (int i = 0; i < nest.size(); i++) {
483
484
485 ((QParamsQconf) qconfLoader.getQp()).setActiveHandler(i);
486 SnakeHandler sh = nest.getHandler(i);
487 if (sh == null) {
488 continue;
489 }
490 oh = new OutlineHandler(sh);
491 ECMp.setup(qconfLoader.getQp());
492 ECMp.setParams(oh.maxLength);
493
494 if (ECMp.plot) {
495 plot = new ECMplot(oh.getSize() - 1);
496 plot.imPlus.setTitle(WindowManager.makeUniqueName(ECMplot.ECMM_TITLE + "_" + "cell_" + i));
497 }
498 runPlugin();
499 outputOutlineHandlers.oHs.add(i, new OutlineHandler(outputH));
500 }
501
502 DataContainer dc = ((QParamsQconf) qconfLoader.getQp()).getLoadedDataContainer();
503 dc.ECMMState = outputOutlineHandlers;
504 try {
505 qconfLoader.getQp().writeParams();
506 } catch (IOException e) {
507 throw new QuimpPluginException(e);
508 }
509
510 if (QuimP.newFileFormat.get() == false) {
511 FormatConverterm/converter/FormatConverter.html#FormatConverter">FormatConverter foramtConvrter = new FormatConverter(qconfLoader);
512 foramtConvrter.doConversion();
513 }
514
515 }
516
517
518
519
520
521
522 @Override
523 protected void runFromPaqp() throws QuimpException {
524 oh = new OutlineHandler(qconfLoader.getQp());
525 if (!oh.readSuccess) {
526 throw new QuimpException("Could not read OutlineHandler");
527 }
528
529 ECMp.setup(qconfLoader.getQp());
530
531
532
533 ECMp.setParams(oh.maxLength);
534 if (ECMp.plot) {
535 plot = new ECMplot(oh.getSize() - 1);
536 }
537 runPlugin();
538
539 if (ECMp.saveTemp) {
540
541
542 File tempFile = new File(ECMp.OUTFILE.getAbsolutePath() + ".temp.txt");
543 outputH.writeOutlines(tempFile, true);
544 IJ.log("ECMM:137, saving to a temp file instead");
545 } else {
546 ECMp.INFILE.delete();
547 outputH.writeOutlines(ECMp.OUTFILE, true);
548 }
549
550 }
551
552
553
554
555
556
557 @Override
558 protected void validate() throws QuimpException {
559 super.validate();
560 boolean fail = false;
561 String ver = IJ.getVersion();
562 if (ver.indexOf("/") > -1) {
563 ver = ver.substring(ver.indexOf("/") + 1);
564 }
565 LOGGER.info(ver);
566
567 try {
568 if (Character.isLetter(ver.charAt(ver.length() - 1))) {
569 String vern = ver.substring(0, ver.length() - 1);
570 if (Double.parseDouble(vern) < 1.52) {
571 fail = true;
572 } else {
573 if (ver.charAt(ver.length() - 1) < 'n') {
574 fail = true;
575 }
576 }
577 } else {
578 if (Double.parseDouble(ver) < 1.52) {
579 fail = true;
580 }
581 }
582 if (fail) {
583 throw new QuimpException("IJ 1.52n version required (or above)");
584 }
585 } catch (NullPointerException | NumberFormatException e) {
586 LOGGER.warn("Cannot process IJ version.");
587 }
588 }
589
590
591
592
593
594
595 @Override
596 public void showUi(boolean val) throws Exception {
597
598 executer();
599
600
601 if (qconfLoader != null && qconfLoader.getQp() != null) {
602 options.paramFile = qconfLoader.getQp().getParamFile().getAbsolutePath();
603 }
604 }
605 }