/* LICENCE Copyright (c) 2013, University of Nottingham All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the University of Nottingham nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ import ij.*; import ij.process.*; import ij.gui.*; import java.awt.*; import ij.plugin.filter.*; import ij.gui.*; import java.awt.event.*; import javax.vecmath.*; import java.util.Vector; import java.util.Iterator; import java.util.Hashtable; import delaunay.DelaunayTriangulation; //http://fiji.sc/javadoc/delaunay/DelaunayTriangulation.html import delaunay.Pnt; import delaunay.Simplex; import ij.process.*; import ij.io.Opener; import java.util.concurrent.atomic.AtomicInteger; public class SurfaceProject3D_ implements PlugInFilter, MouseListener, KeyListener { ImagePlus imp; ImageCanvas canvas; ColorProcessor triImage; //Vector points; //int nPoints=0; DelaunayTriangulation delaunay; int inf = 50000; int array[] = new int[1024*1024]; ImagePlus duplicate; ImagePlus triOutput; ColorProcessor triImageoriginal; public int setup(String arg, ImagePlus imp) { this.imp = imp; return DOES_RGB+STACK_REQUIRED; //forceRGB? } public void run(ImageProcessor ip) { //duplicate = imp.duplicate(); //duplicate.setTitle("Duplicate data"); //duplicate.show(); ImageWindow win = imp.getWindow(); //was imp. instead of duplicate. canvas = win.getCanvas(); canvas.addMouseListener(this); triImageoriginal = (ColorProcessor)imp.getStack().getProcessor(imp.getStack().getSize()/2).duplicate(); ColorProcessor triImage = (ColorProcessor)imp.getStack().getProcessor(imp.getStack().getSize()/2).duplicate(); //triImage.snapshot(); triOutput = new ImagePlus("Triangles", triImage); ImageWindow triOutputw = new ImageWindow(triOutput); //duplicate.getWindow().minimize(); win.toFront(); //points = new Vector(50,10); Simplex initial = new Simplex(new Pnt[] { new Pnt(-inf, -inf), new Pnt(-inf, 5 * inf), new Pnt(5 * inf, -inf)}); delaunay = new DelaunayTriangulation(initial); } public double interpolate(double x, double y) { //IJ.write("In Interpolate..."); boolean edge = false; Simplex enclosingTri = delaunay.locate(new Pnt(x, y)); Iterator iter2 = enclosingTri.iterator(); Pnt a = (Pnt)iter2.next(); Pnt b = (Pnt)iter2.next(); Pnt c = (Pnt)iter2.next(); //IJ.write("a"); if (Math.abs(a.coord(0)) >= inf || Math.abs(b.coord(0)) >= inf || Math.abs(c.coord(0)) >= inf) edge = true; else edge = false; //IJ.write("b"); double z1=0; double z2=0; double z3=0; //try{ if (!edge){ z1 = array[(int)a.coord(0)*imp.getHeight()+(int)a.coord(1)]; z2 = array[(int)b.coord(0)*imp.getHeight()+(int)b.coord(1)]; z3 = array[(int)c.coord(0)*imp.getHeight()+(int)c.coord(1)]; } else { // at edges, leave inifnite vertices set at z=0. They are so far away they shouldn't affect calc much if (Math.abs(a.coord(0)) < inf) z1 = array[(int)a.coord(0)*imp.getHeight()+(int)a.coord(1)]; if (Math.abs(b.coord(0)) < inf) z2 = array[(int)b.coord(0)*imp.getHeight()+(int)b.coord(1)]; if (Math.abs(c.coord(0)) < inf) z3 = array[(int)c.coord(0)*imp.getHeight()+(int)c.coord(1)]; } // DO INTERPOLATION double interpolated = 0; //IJ.write("Calculating Lambda..."); double lambda1 = (b.coord(1) - c.coord(1))*(x - c.coord(0)) + (c.coord(0)-b.coord(0))*(y-c.coord(1)); lambda1 /= (b.coord(1) - c.coord(1))*(a.coord(0) - c.coord(0)) + (c.coord(0)-b.coord(0))*(a.coord(1)-c.coord(1)); double lambda2 = (c.coord(1) - a.coord(1))*(x - c.coord(0)) + (a.coord(0)-c.coord(0))*(y-c.coord(1)); lambda2 /= (b.coord(1) - c.coord(1))*(a.coord(0) - c.coord(0)) + (c.coord(0)-b.coord(0))*(a.coord(1)-c.coord(1)); double lambda3 = 1 - lambda1 - lambda2; interpolated = z1*lambda1 + z2*lambda2 + z3 * lambda3; return interpolated; } private void showDelaunay(ColorProcessor cp){ for (Iterator iter = delaunay.iterator(); iter.hasNext(); ) { Simplex triangle = (Simplex)iter.next(); Iterator iter2 = triangle.iterator(); Pnt a = (Pnt)iter2.next(); Pnt b = (Pnt)iter2.next(); Pnt c = (Pnt)iter2.next(); if (Math.abs(a.coord(0)) >= inf || Math.abs(b.coord(0)) >= inf || Math.abs(c.coord(0)) >= inf) cp.setColor(java.awt.Color.red); else cp.setColor(java.awt.Color.white); cp.drawLine((int)a.coord(0), (int)a.coord(1), (int)b.coord(0), (int)b.coord(1)); cp.drawLine((int)c.coord(0), (int)c.coord(1), (int)b.coord(0), (int)b.coord(1)); cp.drawLine((int)a.coord(0), (int)a.coord(1), (int)c.coord(0), (int)c.coord(1)); } //duplicate.repaintWindow(); } public void mouseClicked(MouseEvent e) { int x = e.getX(); int y = e.getY(); int offscreenX = canvas.offScreenX(x); int offscreenY = canvas.offScreenY(y); if (e.getButton()==MouseEvent.BUTTON1){ //left button int slice = imp.getCurrentSlice(); Point3d p3d = new Point3d(offscreenX, offscreenY, slice); //points.add(p3d); //nPoints++; Pnt p = new Pnt(p3d.x, p3d.y); delaunay.delaunayPlace(p); //h.put((int)(x*imp.getHeight()+y), new Integer(slice)); array[offscreenX*imp.getHeight()+offscreenY] = slice; IJ.write("Got point: "+p3d.x+","+p3d.y+", "+p3d.z); //show delaunay triOutput.setProcessor(triImageoriginal.duplicate()); showDelaunay((ColorProcessor)triOutput.getProcessor()); triOutput.repaintWindow(); } else { //middle button IJ.write("MIDDLEBUTTONPRESSED"); boolean multithreaded = true; duplicate = imp.duplicate(); duplicate.setTitle("Duplicate data"); duplicate.show(); IJ.write("DATADUPLICATED"); if (multithreaded) { final ColorProcessor ip2 = new ColorProcessor(imp.getWidth(), imp.getHeight()); final ImagePlus output = new ImagePlus("Depth map", ip2); final ImageWindow outputw = new ImageWindow(output); final ColorProcessor ip3 = new ColorProcessor(imp.getWidth(), imp.getHeight()); final ImagePlus output3 = new ImagePlus("SurfaceProject output", ip3); final ImageWindow outputw3 = new ImageWindow(output3); final Thread[] threads = newThreadArray(); final AtomicInteger ai = new AtomicInteger(0); IJ.write("*MULTITHREADED - "+threads.length+" threads*"); for (int ithread = 0; ithread < threads.length; ithread++) { threads[ithread] = new Thread() { { setPriority(Thread.NORM_PRIORITY); } double sizeOfXchunk = ((double)imp.getWidth()/threads.length); int xStartIndex = (int)(sizeOfXchunk*(double)ai.getAndIncrement()); double i; int col; int col2; int col3; int r1, g1, b1, r2, g2, b2, r3, g3, b3; public void run() { for (int xx= xStartIndex; xx> 24) & 0xff; r1 = (col >> 16) & 0xff; g1 = (col >> 8) & 0xff; b1 = col & 0xff; //a2 = (col2 >> 24) & 0xff; r2 = (col2 >> 16) & 0xff; g2 = (col2 >> 8) & 0xff; b2 = col2 & 0xff; r3 = (int)((double)r1*(1.0-weight) + (double)r2*weight); g3 = (int)((double)g1*(1.0-weight) + (double)g2*weight); b3 = (int)((double)b1*(1.0-weight) + (double)b2*weight); int col3 = 0xff000000 | r3 <<16 | g3 << 8 | b3; /*int col2 = (int)col&0x00ff0000 >> 16; col2 = 255; int col3 = 0xff000000 | col2 <<16 | col2 << 8 | col2;*/ int ii = (int)(i*(254.0/(imp.getStack().getSize()))); int col4 = 0xff000000 | ii <<16 | ii << 8 | ii; ip3.set(xx, yy, col3); ip2.set(xx, yy, col4); duplicate.getStack().setVoxel(xx, yy, (int)i, 128); } } showDelaunay(ip2); output.repaintWindow(); output3.repaintWindow(); } }; } startAndJoin(threads); } else {//not multithreaded IJ.write("Not multithreaded. Might need updating."); ByteProcessor ip2 = new ByteProcessor(imp.getWidth(), imp.getHeight()); ImagePlus output = new ImagePlus("Depth map", ip2); ImageWindow outputw = new ImageWindow(output); ColorProcessor ip3 = new ColorProcessor(imp.getWidth(), imp.getHeight()); ImagePlus output3 = new ImagePlus("Spline-o-matic output", ip3); ImageWindow outputw3 = new ImageWindow(output3); double i; double col; for (int xx=0; xx> 16; col2 = 255; int col3 = 0xff000000 | col2 <<16 | col2 << 8 | col2;*/ ip3.set(xx, yy, (int)col); ip2.set(xx, yy, (int)(i*(254.0/(imp.getStack().getSize())))); duplicate.getStack().setVoxel(xx, yy, (int)i, 128); } } output.repaintWindow(); output3.repaintWindow(); } } } /** Create a Thread[] array as large as the number of processors available. * From Stephan Preibisch's Multithreading.java class. See: * http://repo.or.cz/w/trakem2.git?a=blob;f=mpi/fruitfly/general/MultiThreading.java;hb=HEAD */ private Thread[] newThreadArray() { int n_cpus = Runtime.getRuntime().availableProcessors(); return new Thread[n_cpus]; } /** Start all given threads and wait on each of them until all are done. * From Stephan Preibisch's Multithreading.java class. See: * http://repo.or.cz/w/trakem2.git?a=blob;f=mpi/fruitfly/general/MultiThreading.java;hb=HEAD */ public static void startAndJoin(Thread[] threads) { for (int ithread = 0; ithread < threads.length; ++ithread) { threads[ithread].setPriority(Thread.NORM_PRIORITY); threads[ithread].start(); } try { for (int ithread = 0; ithread < threads.length; ++ithread) threads[ithread].join(); } catch (InterruptedException ie) { throw new RuntimeException(ie); } } public void mousePressed(MouseEvent e) {} public void mouseReleased(MouseEvent e) {} public void mouseEntered(MouseEvent e) {} public void mouseExited(MouseEvent e) {} public void keyPressed(KeyEvent e) {} public void keyReleased(KeyEvent e) {} public void keyTyped(KeyEvent e) {} }