import java.awt.*; import java.awt.event.*; import java.awt.geom.*; import java.util.*; import ij.*; import ij.gui.*; import ij.measure.*; import ij.plugin.filter.*; import ij.process.*; /** * * @author (C)Dimiter Prodanov * IMEC * * @date 28 April 2009 * * @acknowledgments Many thanks to Jerome Mutterer for the code contributions and testing. * Thanks to Wayne Raspband for the code that properly handles the image magnification. * * * @version 1.2 28 April 2009 * - added support for arrow keys * - fixed a bug in the cross position calculation * - added FocusListener behavior * - added support for magnification factors * 1.1.6 31 March 2009 * - added AdjustmentListener behavior thanks to Jerome Mutterer * - improved pane visualization * - added window rearrangement behavior. Initial code suggested by Jerome Mutterer * - bug fixes by Wayne Raspband * 1.1 24 March 2009 * - improved projection image resizing * - added ImageListener behaviors * - added check-ups * - improved pane updating * 1.0.5 23 March 2009 * - fixed pane updating issue * 1.0 21 March 2009 * * @contents This plugin projects dynamically orthogonal XZ and YZ views of a stack. * The user should provide a point selection in the active image window. * The output images are calibrated, which allows measurements to be performed more easily. * Optionally the YZ image can be rotated at 90 deg. * Optionally the XY and YZ windows can be arrange around the main image. * Input * Aspect ratios (ax:ay:az) of the voxels sizes. * Default values ax=1.0; ay=pixelHeight/pixelWidth; az=pixelDepth/pixelWidth * The ratio of 1:1:1 assumes cubic voxel geometry. * User interaction: * q - quits the plugin * ? - displays help message * * @license This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ public class StackSlicer_ implements PlugInFilter, MouseListener, MouseMotionListener, KeyListener, ActionListener, ImageListener, WindowListener, AdjustmentListener, MouseWheelListener, FocusListener { private boolean isProcessibleRoi=false; private Roi roi; // private Thread bgThread; //thread for plotting (in the background) // private boolean doUpdate; //tells the background thread to update // threading may be introduced in the later versions private ImageWindow win; private ImagePlus imp; private ImageCanvas canvas; private static final int H_ROI=0, H_ZOOM=1; private static final String version="1.2"; private ImagePlus xz_image=new ImagePlus(), yz_image=new ImagePlus(); private ImageProcessor fp1, fp2; private static final String AX="AX", AY="AY", AZ="AZ", YROT="YROT", SPANELS="STICKY_PANELS"; private static float ax=(float)Prefs. getDouble(AX,1.0); private static float ay=(float)Prefs. getDouble(AY,1.0); private static float az=(float)Prefs. getDouble(AZ,1.0); private static boolean rotate=(boolean)Prefs. getBoolean(YROT,false); private static boolean sticky=(boolean)Prefs. getBoolean(SPANELS,false); private int xyImX = 0; private int xyImY = 0; private Calibration cal=null, cal_xz=new Calibration(), cal_yz=new Calibration(); double magnification=1.0; Color color = Color.red; /* (non-Javadoc) * @see ij.plugin.filter.PlugInFilter#setup(java.lang.String, ij.ImagePlus) */ //@Override public int setup(String arg, ImagePlus imp) { this.imp=imp; cal=this.imp.getCalibration(); //Log("cal info: "+cal.pixelDepth); if (cal!=null) { double calx=cal.pixelWidth; double caly=cal.pixelHeight; double calz=cal.pixelDepth; ax=1.0f; ay=(float)(caly/calx); az=(float)(calz/calx); } if (arg.equals("about")){ showAbout(); return DONE; } try { if (imp!=null) { win = imp.getWindow(); //win.setResizable(false); win.running = true; isProcessibleRoi=processibleRoi(imp); canvas = win.getCanvas(); addListeners(canvas); magnification= canvas.getMagnification(); if (!isProcessibleRoi) { //showHelp(H_ROI); //dispose(); //return DONE; imp.setRoi(new PointRoi(imp.getWidth()/2,imp.getHeight()/2)); Toolbar.getInstance().setTool(Toolbar.POINT); } } else { return DONE; } } catch (NullPointerException ex) { return DONE; } if(IJ.versionLessThan("1.40)") || !showDialog(imp)) { return DONE; } else { return DOES_8G+DOES_16+NO_CHANGES+DOES_32+NO_UNDO +STACK_REQUIRED; } } // /** * @param canvass */ private void addListeners(ImageCanvas canvass) { canvas.addMouseListener(this); canvas.addMouseMotionListener(this); canvas.addKeyListener(this); win.addWindowListener ((WindowListener) this); win.addMouseWheelListener((MouseWheelListener) this); win.addFocusListener(this); Component[] c = win.getComponents(); //IJ.log(c[1].toString()); ((java.awt.Scrollbar) c[1]).addAdjustmentListener ((AdjustmentListener) this); ImagePlus.addImageListener(this); } /* (non-Javadoc) * @see ij.plugin.filter.PlugInFilter#run(ij.process.ImageProcessor) */ //@Override public void run(ImageProcessor ip) { ImageStack is=imp.getStack(); //Log("cal info: "+cal.pixelDepth); //cal=imp.getCalibration(); calibrate(); imp.unlock(); if (createProcessors(is)) { exec(); arrangeWindows(sticky); } else dispose(); } /** * */ private void calibrate() { double arat=az/ax; double brat=az/ay; if (cal!=null) { String unit=cal.getUnit(); double o_depth=cal.pixelDepth; double o_height=cal.pixelHeight; double o_width=cal.pixelWidth; cal_xz.setUnit(unit); if (rotate) { cal_xz.pixelHeight=o_depth/arat; cal_xz.pixelWidth=o_width*ax; } else { cal_xz.pixelHeight=o_width*ax;//o_depth/arat; cal_xz.pixelWidth=o_depth/arat; } xz_image.setCalibration(cal_xz); cal_yz.setUnit(unit); cal_yz.pixelWidth=o_height*ay; cal_yz.pixelHeight=o_depth/brat; yz_image.setCalibration(cal_yz); } } /** * @param cal * @param p * @return */ public String getCoordString(Calibration cal, Point p) { if (cal!=null) { return "("+p.x +" ; "+p.y+") ("+ IJ.d2s(cal.getX(p.x),2)+" ; "+ IJ.d2s(cal.getX(p.y),2)+")"; } else { return "("+p.x +" ; "+p.y+")"; } } /** * @param magnification */ /* private void updateMagnification(double magnification){ if (magnification!=1.0) { ImageCanvas xz_canvas=xz_image.getCanvas(); xz_canvas.setMagnification(magnification); Graphics g =xz_canvas.getGraphics(); xz_canvas.update(g); xz_image.show(); //xz_image.updateAndDraw(); //xz_image.updateAndRepaintWindow(); ImageCanvas yz_canvas=yz_image.getCanvas(); yz_canvas.setMagnification(magnification); g =yz_canvas.getGraphics(); yz_canvas.update(g); yz_image.show(); //yz_image.updateAndRepaintWindow(); //yz_image.updateAndDraw(); g.dispose(); } } */ private void updateMagnification(double magnification) { ImageWindow win = xz_image.getWindow(); ImageCanvas ic = win.getCanvas(); ic.setMagnification(magnification); double w = xz_image.getWidth()*magnification; double h = xz_image.getHeight()*magnification; Dimension screen = IJ.getScreenSize(); if (w>screen.width-20) w = screen.width - 20; // does it fit? if (h>screen.height-50) h = screen.height - 50; // ic.setSourceRect(new Rectangle(0, 0, (int)(w/magnification), (int)(h/magnification))); ic.setDrawingSize((int)w, (int)h); win.pack(); ic.repaint(); win = yz_image.getWindow(); ic = win.getCanvas(); ic.setMagnification(magnification); w = yz_image.getWidth()*magnification; h = yz_image.getHeight()*magnification; if (w>screen.width-20) w = screen.width - 20; // does it fit? if (h>screen.height-50) h = screen.height - 50; // ic.setSourceRect(new Rectangle(0, 0, (int)(w/magnification), (int)(h/magnification))); ic.setDrawingSize((int)w, (int)h); win.pack(); ic.repaint(); } /** * @param p * @param is */ public void doProjections(Point p, ImageStack is) { if (fp1==null) return; doXZprojection(p,is); float arat=az/ax; if (arat!=1.0f) { fp1.setInterpolate(true); ImageProcessor sfp1=fp1.resize((int)(fp1.getWidth()*ax), (int)(fp1.getHeight()*arat)); sfp1.resetMinAndMax(); xz_image.setProcessor("XZ-"+getCoordString(cal, p), sfp1); } else { fp1.resetMinAndMax(); xz_image.setProcessor("XZ-"+getCoordString(cal, p), fp1); } //xz_image.show(); /**********************************************************/ if (rotate) doYZprojection(p,is); else doZYprojection(p,is); arat=az/ay; if (arat!=1.0f) { fp2.setInterpolate(true); //fp2.scale(1.0f, arat); if (rotate) { ImageProcessor sfp2=fp2.resize( (int)(fp2.getWidth()*ay), (int)(fp2.getHeight()*arat)); sfp2.resetMinAndMax(); yz_image.setProcessor("ZY-"+getCoordString(cal, p), sfp2); } else { ImageProcessor sfp2=fp2.resize( (int)(fp2.getWidth()*arat), (int)(fp2.getHeight()*ay)); sfp2.resetMinAndMax(); yz_image.setProcessor("YZ-"+getCoordString(cal, p), sfp2); } //IJ.log(" "+ is.getSize()*arat); } else { fp2.resetMinAndMax(); if (rotate) { yz_image.setProcessor("YZ-"+getCoordString(cal, p), fp2); } else { yz_image.setProcessor("ZY-"+getCoordString(cal, p), fp2); } } calibrate(); xz_image.show(); yz_image.show(); } /** * @param sticky */ public void arrangeWindows(boolean sticky) { magnification= win.getCanvas().getMagnification(); //win.getInitialMagnification(); Log("mag info: "+magnification); updateMagnification(magnification); if (!sticky) return; if ((xyImX != imp.getWindow().getLocation().x)||(xyImY != imp.getWindow().getLocation().y)) { xyImX = imp.getWindow().getLocation().x; xyImY = imp.getWindow().getLocation().y; ImageWindow win1 = xz_image.getWindow(); win1.setLocation(xyImX,xyImY +imp.getWindow().getHeight()); ImageWindow win2 = yz_image.getWindow(); win2.setLocation(xyImX+imp.getWindow().getWidth(),xyImY); } } /** * @param is - used to get the dimensions of the new ImageProcessors * @return */ public boolean createProcessors(ImageStack is) { //ImageStack is=imp.getStack(); ImageProcessor ip=is.getProcessor(1); int width= is.getWidth(); int height=is.getHeight(); int ds=is.getSize(); float arat=1.0f;//az/ax; float brat=1.0f;//az/ay; // float arat=az/ax; // float brat=az/ay; int za=(int)(ds*arat); int zb=(int)(ds*brat); IJ.log("za: "+za +" zb: "+zb); if (ip instanceof FloatProcessor) { fp1=new FloatProcessor(width,za); if (rotate) fp2=new FloatProcessor(height,zb); else fp2=new FloatProcessor(zb,height); return true; } if (ip instanceof ByteProcessor) { fp1=new ByteProcessor(width,za); if (rotate) fp2=new ByteProcessor(height,zb); else fp2=new ByteProcessor(zb,height); return true; } if (ip instanceof ShortProcessor) { fp1=new ShortProcessor(width,za); if (rotate) fp2=new ShortProcessor(height,zb); else fp2=new ShortProcessor(zb,height); return true; } if (ip instanceof ColorProcessor) { fp1=new ColorProcessor(width,za); if (rotate) fp2=new ColorProcessor(height,zb); else fp2=new ColorProcessor(zb,height); return true; } return false; } /** * @param p * @param is */ public void doXZprojection(Point p, ImageStack is) { int width= is.getWidth(); int ds=is.getSize(); ImageProcessor ip=is.getProcessor(1); int y=p.y; try { // XZ if (ip instanceof FloatProcessor) { float[] newpix=new float[width*ds]; for (int i=0;i=0; i--) { for (int i=0;i=0; i--){ for (int i=0;i=0; i--) { for (int i=0;i=0; i--) { for (int i=0;i=height) y=height-1; if (x>=width) x=width-1; if (x<0) x=0; if (y<0) y=0; Point p=new Point (x,y); IJ.log("width: "+width +" height: "+ height +" "+ getCoordString(null, p)); doProjections(p, is); if (canvas==null) return; else { GeneralPath path = new GeneralPath(); drawCross(imp, p, path); canvas.setDisplayList(path, color, new BasicStroke(Toolbar.getBrushSize())); //canvas.setDisplayList(path, color, new BasicStroke(1)); } updateCrosses(x, y, arat, brat); } magnification= win.getCanvas().getMagnification(); updateMagnification(magnification); } /** * @param x * @param y * @param arat * @param brat */ private void updateCrosses(int x, int y, double arat, double brat) { Point p; int z=imp.getNSlices(); int zlice=imp.getCurrentSlice()-1; //offset int zcoord=(int)(arat*(z-zlice)); //IJ.log ("xz cord "+zcoord); p=new Point (x, zcoord); ImageCanvas xz_canvas=xz_image.getCanvas(); //xz_canvas.setMagnification(magnification); if (xz_canvas==null) return; else { GeneralPath path = new GeneralPath(); drawCross(xz_image, p, path); xz_canvas.setDisplayList(path, color, new BasicStroke(Toolbar.getBrushSize())); //canvas.setDisplayList(path, color, new BasicStroke(1)); } zcoord=(int)(brat*(z-zlice)); //IJ.log ("yz cord "+zcoord); if (rotate) p=new Point (y, zcoord); else { zcoord=(int)(arat*zlice); p=new Point (zcoord, y); } ImageCanvas yz_canvas=yz_image.getCanvas(); //yz_canvas.setMagnification(magnification); if (yz_canvas==null) return; else { GeneralPath path = new GeneralPath(); drawCross(yz_image, p, path); yz_canvas.setDisplayList(path, color, new BasicStroke(Toolbar.getBrushSize())); //canvas.setDisplayList(path, color, new BasicStroke(1)); } } //@Override public void mouseDragged(MouseEvent e) { exec(); arrangeWindows(sticky); } //@Override public void mouseMoved(MouseEvent e) { // TODO Auto-generated method stub } //@Override public void keyPressed(KeyEvent e) { int key = e.getKeyCode(); switch (key) { case KeyEvent.VK_LEFT: ; case KeyEvent.VK_RIGHT: ; case KeyEvent.VK_UP: ; case KeyEvent.VK_DOWN: { exec(); } } e.consume(); } //@Override public void keyReleased(KeyEvent e) { // TODO Auto-generated method stub } //@Override public void keyTyped(KeyEvent e) { char c=e.getKeyChar(); //int code=e.getKeyCode(); //int modext=e.getModifiersEx(); //String modexts=e.getModifiersExText(modext); switch (c) { case 'q':{ dispose(); break;} case '?':{ showHelp(H_ZOOM); break;} } e.consume(); //IJ.log("modext "+modext+ " " + modexts+" key "+ c); // 64 } //@Override public void actionPerformed(ActionEvent ev) { String cmd = ev.getActionCommand(); if (cmd.equals("q")) dispose(); } //@Override public void imageClosed(ImagePlus imp) { dispose(); } //@Override public void imageOpened(ImagePlus imp) { // TODO Auto-generated method stub } //@Override public void imageUpdated(ImagePlus imp) { // TODO Auto-generated method stub exec(); } //@Override public void windowActivated(WindowEvent e) { // TODO Auto-generated method stub arrangeWindows(sticky); } //@Override public void windowClosed(WindowEvent e) { // TODO Auto-generated method stub } //@Override public void windowClosing(WindowEvent e) { // TODO Auto-generated method stub dispose(); } //@Override public void windowDeactivated(WindowEvent e) { // TODO Auto-generated method stub //arrangeWindows(sticky); } //@Override public void windowDeiconified(WindowEvent e) { // TODO Auto-generated method stub arrangeWindows(sticky); } //@Override public void windowIconified(WindowEvent e) { // TODO Auto-generated method stub } //@Override public void windowOpened(WindowEvent e) { // TODO Auto-generated method stub } //@Override public void adjustmentValueChanged(AdjustmentEvent e) { exec(); } //@Override public void mouseWheelMoved(MouseWheelEvent e) { exec(); } //@Override public void focusGained(FocusEvent e) { // TODO Auto-generated method stub arrangeWindows(sticky); } //@Override public void focusLost(FocusEvent e) { // TODO Auto-generated method stub arrangeWindows(sticky); } }