Hi,
I'm using the ParticleAnalyzer class to find particles and I need to record the irregular ROIs that define each particle. I did this simply by overriding the drawParticle method to record each ROI as it drew them. So now my program displays an image of all the filled particles and records the ROIs, which I use elsewhere. My problem is that I run though this procedure many times and if I leave all those images open I run out of memory really quickly. So I've been trying to look for a way to close the images that the ParticleAnalyzer makes automatically. I'm working directly with classes so i'd rather not have to go to ImageJ's window manager and try to find each image, and instead get the ImagePlus object used directly from the ParticleAnalyzer class. Unfortunately, as far as I can see, this is the line that creates that window: new ImagePlus <file:///E:/documents/apps/imagej/imagej%20source/html-source/ij/ImagePl us.java.html> (prefix+title, outlines).show(); The ParticleAnalyzer thus has no way of keeping track of that window once it is created. Furthermore, I cannot override the method (at least, my IDE tells me I can't) since "it is private to a different package". Does anyone know of a way I can get around this? At the moment I do not directly use the ImageJ program and deal with the classes directly, thus, ImageJ doesn't recognise the windows I open as existing. TIA, dave |
Hi. If you want to wade through some code, FracLac does just what you describe (see attached).
Audrey "Ludgate, David" <[hidden email]> wrote: Hi, I'm using the ParticleAnalyzer class to find particles and I need to record the irregular ROIs that define each particle. I did this simply by overriding the drawParticle method to record each ROI as it drew them. So now my program displays an image of all the filled particles and records the ROIs, which I use elsewhere. My problem is that I run though this procedure many times and if I leave all those images open I run out of memory really quickly. So I've been trying to look for a way to close the images that the ParticleAnalyzer makes automatically. I'm working directly with classes so i'd rather not have to go to ImageJ's window manager and try to find each image, and instead get the ImagePlus object used directly from the ParticleAnalyzer class. Unfortunately, as far as I can see, this is the line that creates that window: new ImagePlus us.java.html> (prefix+title, outlines).show(); The ParticleAnalyzer thus has no way of keeping track of that window once it is created. Furthermore, I cannot override the method (at least, my IDE tells me I can't) since "it is private to a different package". Does anyone know of a way I can get around this? At the moment I do not directly use the ImageJ program and deal with the classes directly, thus, ImageJ doesn't recognise the windows I open as existing. TIA, dave |
In reply to this post by Ludgate, David
Hello.
Below is some code that you can cut and paste and compile as a new plugin that analyzes an image with the particle analyzer then writes the coordinates for the rois and optionally colours over them in a new image. Its ripped out of another plugin so is rather bulky and hacky as is but I think it is relevant to your question. Audrey import ij.*; import ij.IJ; import ij.ImagePlus; import ij.WindowManager; import ij.gui.*; import ij.gui.GenericDialog; import ij.gui.ImageWindow; import ij.gui.PolygonRoi; import ij.gui.Roi; import ij.gui.Toolbar; import ij.gui.Wand; import ij.measure.ResultsTable; import ij.plugin.*; import ij.plugin.filter.Analyzer; import ij.plugin.filter.ParticleAnalyzer; import ij.process.ImageProcessor; import ij.process.ImageStatistics; import java.awt.*; import java.awt.Graphics2D; import java.awt.Image; import java.awt.image.*; /* * UsePARois.java * * Created on August 3, 2005, 4:34 PM * * Code ripped out of FracLac and quickly hacked into a demo of pa rois. *It analyzes an image and writes out the x,y and width and height of the rois, *and optionally draws them back on the original image. It is just a quick hack that *will likely be prunable to something elegant. */ /** * * @author Audrey_2 Charles Sturt University, Neuroscience */ public class Use_PA_Rois implements PlugIn{ /** Creates a new instance of UsePARois */ public Use_PA_Rois() { } public void run(String args){ MakeImage=IJ.showMessageWithCancel("Make Image?", "Choose OK to draw the rois or cancel to make no new image"); Go();} int minparticle=30, maxparticle=99999; boolean MakeImage=false; public void Go(){ String imgtitle=null; String x="", y=""; ImageWindow wm = WindowManager.getCurrentWindow(); if(wm==null) { IJ.noImage(); return;} else { ImagePlus workingcopy=wm.getImagePlus(); if(workingcopy==null) { IJ.noImage(); return;} else { workingcopy.setSlice(1); {int lastslice=workingcopy.getStackSize(); for (int thisslice = 1; thisslice <= lastslice; ++thisslice) {//for each slice in the stack workingcopy.setSlice(thisslice); workingcopy.setRoi(0, 0, workingcopy.getWidth(), workingcopy.getHeight()); if(!ScanParticles(workingcopy, thisslice, lastslice))return; } }}}} /** * Copies the current ROI to the clipboard. The entire * image is copied if there is no ROI. * (Aug 2, 2005)@param cut boolean always false FIXME * @param imp ImagePlus to analyze * @param title String for image's title * @return ImagePlus copy of the roi selected */ public ImagePlus copy(boolean cut, ImagePlus imp, String title ) { Roi roi = imp.getRoi(); String msg = (cut)?"Cut":"Copy"; IJ.showStatus(msg+ "ing..."); ImageProcessor ip = imp.getProcessor(); ImageProcessor ip2 = ip.crop(); ImagePlus clipboard = new ImagePlus(title, ip2); if (roi!=null && roi.getType()!=Roi.RECTANGLE) clipboard.setRoi((Roi)roi.clone()); // if (cut) { // ip.snapshot(); // ip.setColor(F.V.background==F.V.WHITE?Color.white:Color.black); // ip.fill(); // if (roi!=null && roi.getType()!=Roi.RECTANGLE) // ip.reset(imp.getMask()); // imp.setColor( F.V.foreground==F.V.WHITE?Color.white:Color.black); // Undo.setup(Undo.FILTER, imp); // imp.updateAndDraw(); // } int bytesPerPixel = 1; switch (clipboard.getType()) { case ImagePlus.GRAY16: bytesPerPixel = 2; break; case ImagePlus.GRAY32: case ImagePlus.COLOR_RGB: bytesPerPixel = 4; } IJ.showStatus(msg + ": " + (clipboard.getWidth()* clipboard.getHeight()*bytesPerPixel)/1024 + "k"); return paste(clipboard, title); } /** * Pastes the passed image. I am trying to paste an roi without * (Aug 2, 2005)collecting the surrounding rectangle. FIXME * @return ImagePlus pasted * @param s needs fixing * @param clipboard ImagePlus copied */ public ImagePlus paste(ImagePlus clipboard, String s) { ImagePlus imp = new ImagePlus("pasted"+s, clipboard.getImage()); imp.setRoi(0, 0, imp.getWidth(), imp.getHeight()); imp.setColor(Color.white);//F.V.background==F.V.WHITE?Color.white:Color.black); imp.getProcessor().fill(); if (clipboard==null) return imp; int cType = clipboard.getType(); int iType = imp.getType(); boolean sameType = false; if ((cType==ImagePlus.GRAY8|cType==ImagePlus.COLOR_256)&& (iType==ImagePlus.GRAY8|iType==ImagePlus.COLOR_256)) sameType = true; else if ((cType==ImagePlus.COLOR_RGB|cType==ImagePlus.GRAY8|cType ==ImagePlus.COLOR_256)&&iType==ImagePlus.COLOR_RGB) sameType = true; else if (cType==ImagePlus.GRAY16&&iType==ImagePlus.GRAY16) sameType = true; else if (cType==ImagePlus.GRAY32&&iType==ImagePlus.GRAY32) sameType = true; if (!sameType) { IJ.error("Images must be the same type to paste."); return imp; } int w = clipboard.getWidth(); int h = clipboard.getHeight(); if (w>imp.getWidth() || h>imp.getHeight()) { IJ.error("Image is too large to paste."); return imp; } Roi roi = imp.getRoi(); Rectangle r = null; if (roi!=null) r = roi.getBounds(); if (true) { //non-interactive paste int pasteMode = Roi.getCurrentPasteMode(); boolean nonRect = roi.getType()!=Roi.RECTANGLE; ImageProcessor ip = imp.getProcessor(); if (nonRect) ip.snapshot(); r = roi.getBounds(); ip.copyBits(clipboard.getProcessor(), r.x, r.y, pasteMode); if (nonRect) ip.reset(imp.getMask()); imp.updateAndDraw(); imp.killRoi(); } else {} return imp; } /** * Gets the rois for all particles in a passed ImagePlus, * then calls a method to scan them, returning nothing. * Invokes the ImageJ ParticleAnalyzer to segment * the image, then passes the image and the ResultsTable * containing roi coordinates to the * {@link #ScanTheProcessorFromEachParticlesRoi scanning method} to * scan each particle. The data from the scans are * accessed through the * ImageJ Results window eventually; * refer to the scanning method for the next * step. * @param thisslice int for the current slice or 1 if this is not a stack * @param lastslice int for the number of slices in this stack or 1 if this is not a stack * @param img ImagePlus to scan * @return boolean true if not cancelled; false if the call to the{@link #setupPA particle anlayzer} * set up is cancelled */ public boolean ScanParticles(ImagePlus img, int thisslice, int lastslice) { boolean quit=false; boolean goOn=true; //store information String title = img.getTitle(); ImageProcessor ip = img.getProcessor(); Roi roi = img.getRoi(); if(roi==null) { img.setRoi(0,0, img.getWidth(), img.getHeight()); roi=img.getRoi(); }//get user's options for the particle analyzer goOn=true; if(thisslice==1){ goOn=setupPA();}//quit if cancelled here if (!goOn) return false; else//if not cancelled, go on { ImageStatistics stats = img.getStatistics(); if ((stats.histogram[0]+stats.histogram[255])!=stats.pixelCount) { IJ.showMessage("Binary (thresholded) image required."); quit=true; }//quit if the image is not binary else if (!quit)//or else go on { // if (F.V.background==F.V.BLACK) { // img.killRoi(); // invert(ip); // img.updateAndRepaintWindow(); // } //get the rois img.setRoi(roi); Analyzer ana=new Analyzer(); int measurements = Analyzer.getMeasurements(); ResultsTable rt = ana.getResultsTable(); int options= (ParticleAnalyzer.EXCLUDE_EDGE_PARTICLES| ParticleAnalyzer.RECORD_STARTS |ParticleAnalyzer.SHOW_NONE); measurements = (ana.PERIMETER|ana.AREA); ParticleAnalyzer pa=new ParticleAnalyzer (options, measurements, rt, minparticle, maxparticle); Analyzer.resetCounter(); img.setRoi(roi); pa.analyze(img); ip.setRoi(roi); ScanTheProcessorFromEachParticlesRoi(rt, img, thisslice, lastslice, title); } }return true; } public void ScanTheProcessorFromEachParticlesRoi (ResultsTable rt, ImagePlus img, int thisslice, int lastslice, String title) { ImageWindow win = img.getWindow(); int nResults = rt.getCounter(); ImageProcessor ip = img.getProcessor(); Image StoredCopyOfOriginal = ip.createImage(); float[] wandx=new float[nResults]; float []wandy=new float [nResults]; Roi [] RoisFromParticleAnalyzer=new PolygonRoi [nResults]; float [][] ParticleRectangleXY=new float[2][nResults]; for (int i=0; i<nResults; i++) { wandx[i] =(float) rt.getValue("XStart", i); wandy[i] =(float) rt.getValue("YStart", i); Wand w = new Wand(img.getProcessor()); w.autoOutline( (int)wandx[i],(int)wandy[i]); RoisFromParticleAnalyzer[i]= new PolygonRoi(w.xpoints, w.ypoints, w.npoints, Roi.TRACED_ROI); {ParticleRectangleXY[0][i]= (int)RoisFromParticleAnalyzer[i].getBounds().getBounds2D().getX(); ParticleRectangleXY[1][i]= (int)RoisFromParticleAnalyzer[i].getBounds().getBounds2D().getY();} } for (int i=0; i<nResults; i++) { WindowManager.setCurrentWindow(win);//img=win.getImagePlus(); img.setRoi(RoisFromParticleAnalyzer[i]); ImagePlus copied=copy(false, img, title); copied.killRoi(); IJ.write(ParticleRectangleXY[0][i]+","+ ParticleRectangleXY[1][i]+","+ (int)RoisFromParticleAnalyzer[i].getBounds().getBounds2D().getWidth()+","+ (int)RoisFromParticleAnalyzer[i].getBounds().getBounds2D().getHeight()); } if (MakeImage) { img.setSlice(thisslice); img.updateAndRepaintWindow(); BufferedImage bi = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_ARGB); Graphics2D g = (Graphics2D)bi.getGraphics(); g.drawImage(StoredCopyOfOriginal, 0, 0, null); ColourRois (ip, 1,//TYPE, g, RoisFromParticleAnalyzer, 190, 5, 0 ); ImagePlus imgg = new ImagePlus("new"+title, bi); if(thisslice==1) { ColouredRoisStack = new ImageStack(img.getWidth(), img.getHeight()); ColouredRoisStack.addSlice(title, imgg.getProcessor()); } else ColouredRoisStack.addSlice(title, imgg.getProcessor()); if(thisslice==lastslice) { StackWindow iww = new StackWindow(new ImagePlus(img.getTitle() + thisslice, ColouredRoisStack)); iww.setVisible(true); } } } ImageStack ColouredRoisStack =null; /**Sets up the ij.plugin.filter.ParticleAnalyzer using * a ij.gui.GenericDialog to get values for the minimum and * maximum particle size to find, and the background and * foreground arrangement (i.e., which is white and which is black). * Changes these variables in the {@link FLAnalyzer.FLVars variable class}. * @return boolean false if cancelled or else true */ public boolean setupPA() { GenericDialog gd = new GenericDialog("Particle Options", IJ.getInstance()); String [] C = {"Black", "White"}; String ch = C[1]; gd.addChoice("Background Colour", C, ch); gd.addNumericField("Minimum particle", minparticle, 0); gd.addNumericField("Maximum particle", maxparticle, 0); gd.showDialog(); if (gd.wasCanceled()) { return false;} else { Color bg=Color.white; int t=gd.getNextChoiceIndex(); if (t==0)bg=Color.black; Toolbar.setBackgroundColor(bg); minparticle=(int)gd.getNextNumber(); maxparticle=(int)gd.getNextNumber(); } return true; } public static void ColourRois (ImageProcessor ip, int TYPE, Graphics2D g2, Roi [] rois, //float[] colours, //int ColourCode, float LocalAlpha, int comp, int foreground //boolean makescaleimage, //float [] DFS ) { Color gcolour= new Color (210, 240, 240); Color basecolour= new Color (210, 240, 240); for (int i = 0; i <rois.length; i++) { g2.setColor (gcolour); AlphaComposite acc = AlphaComposite.getInstance (AlphaComposite.SRC_OVER, newAlpha(LocalAlpha, 1.5f)); g2.setComposite (acc); //colour the roi using the appropriate rule { g2.fillPolygon (rois[i].getPolygon ()); } //else if (TYPE==DRAW_IRREGULAR_ROI_NO_STRING) { g2.drawPolygon (rois[i].getPolygon ()); } ReplaceForegroundPixelsInIrregularRoi(rois[i], g2, ip, foreground); } } /** * Colours only the pixels that are the foreground * colour and in the passed roi on the passed image. * * @param roi Roi to colour * @param g2 Graphics2D on which to colour the roi * @param ip ImageProcessor to colour * @param foreground int for the foreground colour */ public static void ReplaceForegroundPixelsInIrregularRoi( Roi roi, Graphics2D g2, ImageProcessor ip, int foreground) { int startx = (int)(roi.getBoundingRect().getX()); int starty = (int)(roi.getBoundingRect().getY()); for (int xx = startx; xx< startx+(int)(roi.getBoundingRect().getWidth()); xx++) { for (int yy = starty; yy< starty+(int)(roi.getBoundingRect().getHeight()); yy++) { ip.setRoi (xx, yy, 1, 1); //Roi smallroi = ip.getRoi (); int [] histogram=ip.getHistogram (); if (histogram[foreground]>0) { if(roi.contains(xx, yy)) g2.fillRect (xx, yy, 1, 1); } } } } static float newAlpha(float LocalAlpha, float colour) { float floatLocalAlpha=(float)LocalAlpha/255.0f; floatLocalAlpha=floatLocalAlpha>1.0f?1.0f:floatLocalAlpha; floatLocalAlpha=floatLocalAlpha<0.0f?0.0f:floatLocalAlpha; return floatLocalAlpha; } } "Ludgate, David" <[hidden email]> wrote: Hi, I'm using the ParticleAnalyzer class to find particles and I need to record the irregular ROIs that define each particle. I did this simply by overriding the drawParticle method to record each ROI as it drew them. So now my program displays an image of all the filled particles and records the ROIs, which I use elsewhere. My problem is that I run though this procedure many times and if I leave all those images open I run out of memory really quickly. So I've been trying to look for a way to close the images that the ParticleAnalyzer makes automatically. I'm working directly with classes so i'd rather not have to go to ImageJ's window manager and try to find each image, and instead get the ImagePlus object used directly from the ParticleAnalyzer class. Unfortunately, as far as I can see, this is the line that creates that window: new ImagePlus us.java.html> (prefix+title, outlines).show(); The ParticleAnalyzer thus has no way of keeping track of that window once it is created. Furthermore, I cannot override the method (at least, my IDE tells me I can't) since "it is private to a different package". Does anyone know of a way I can get around this? At the moment I do not directly use the ImageJ program and deal with the classes directly, thus, ImageJ doesn't recognise the windows I open as existing. TIA, dave |
In reply to this post by Ludgate, David
Hi,
After your first email pointing out FracLac, I duplicated what seemed the relevent code, and it seems to work - most of the time. One of my processing steps is to go through the ROIs and dialate them (by taking the mask, then dilating, then gettign the ROI for that mask). However, after 713 successful dilations, one invariably causes an ArrayIndexOutOfBoundsException in ByteProcessor (line 446). My inclination is to put in an if statement to simply ignore this particle - but I can't find any obvious difference between it and the rest. Have you encountered similar errors, or am I missing some code? Here is what I do: -----Original Message----- From: ImageJ Interest Group [mailto:[hidden email]] On Behalf Of audrey karperien Sent: Wednesday, August 03, 2005 6:35 PM To: [hidden email] Subject: Re: Issue with ParticleAnalyzer output Hello. Below is some code that you can cut and paste and compile as a new plugin that analyzes an image with the particle analyzer then writes the coordinates for the rois and optionally colours over them in a new image. Its ripped out of another plugin so is rather bulky and hacky as is but I think it is relevant to your question. Audrey import ij.*; import ij.IJ; import ij.ImagePlus; import ij.WindowManager; import ij.gui.*; import ij.gui.GenericDialog; import ij.gui.ImageWindow; import ij.gui.PolygonRoi; import ij.gui.Roi; import ij.gui.Toolbar; import ij.gui.Wand; import ij.measure.ResultsTable; import ij.plugin.*; import ij.plugin.filter.Analyzer; import ij.plugin.filter.ParticleAnalyzer; import ij.process.ImageProcessor; import ij.process.ImageStatistics; import java.awt.*; import java.awt.Graphics2D; import java.awt.Image; import java.awt.image.*; /* * UsePARois.java * * Created on August 3, 2005, 4:34 PM * * Code ripped out of FracLac and quickly hacked into a demo of pa rois. *It analyzes an image and writes out the x,y and width and height of the rois, *and optionally draws them back on the original image. It is just a quick hack that *will likely be prunable to something elegant. */ /** * * @author Audrey_2 Charles Sturt University, Neuroscience */ public class Use_PA_Rois implements PlugIn{ /** Creates a new instance of UsePARois */ public Use_PA_Rois() { } public void run(String args){ MakeImage=IJ.showMessageWithCancel("Make Image?", "Choose OK to draw the rois or cancel to make no new image"); Go();} int minparticle=30, maxparticle=99999; boolean MakeImage=false; public void Go(){ String imgtitle=null; String x="", y=""; ImageWindow wm = WindowManager.getCurrentWindow(); if(wm==null) { IJ.noImage(); return;} else { ImagePlus workingcopy=wm.getImagePlus(); if(workingcopy==null) { IJ.noImage(); return;} else { workingcopy.setSlice(1); {int lastslice=workingcopy.getStackSize(); for (int thisslice = 1; thisslice <= lastslice; ++thisslice) {//for each slice in the stack workingcopy.setSlice(thisslice); workingcopy.setRoi(0, 0, workingcopy.getWidth(), workingcopy.getHeight()); if(!ScanParticles(workingcopy, thisslice, lastslice))return; } }}}} /** * Copies the current ROI to the clipboard. The entire * image is copied if there is no ROI. * (Aug 2, 2005)@param cut boolean always false FIXME * @param imp ImagePlus to analyze * @param title String for image's title * @return ImagePlus copy of the roi selected */ public ImagePlus copy(boolean cut, ImagePlus imp, String title ) { Roi roi = imp.getRoi(); String msg = (cut)?"Cut":"Copy"; IJ.showStatus(msg+ "ing..."); ImageProcessor ip = imp.getProcessor(); ImageProcessor ip2 = ip.crop(); ImagePlus clipboard = new ImagePlus(title, ip2); if (roi!=null && roi.getType()!=Roi.RECTANGLE) clipboard.setRoi((Roi)roi.clone()); // if (cut) { // ip.snapshot(); // ip.setColor(F.V.background==F.V.WHITE?Color.white:Color.black); // ip.fill(); // if (roi!=null && roi.getType()!=Roi.RECTANGLE) // ip.reset(imp.getMask()); // imp.setColor( F.V.foreground==F.V.WHITE?Color.white:Color.black); // Undo.setup(Undo.FILTER, imp); // imp.updateAndDraw(); // } int bytesPerPixel = 1; switch (clipboard.getType()) { case ImagePlus.GRAY16: bytesPerPixel = 2; break; case ImagePlus.GRAY32: case ImagePlus.COLOR_RGB: bytesPerPixel = 4; } IJ.showStatus(msg + ": " + (clipboard.getWidth()* clipboard.getHeight()*bytesPerPixel)/1024 + "k"); return paste(clipboard, title); } /** * Pastes the passed image. I am trying to paste an roi without * (Aug 2, 2005)collecting the surrounding rectangle. FIXME * @return ImagePlus pasted * @param s needs fixing * @param clipboard ImagePlus copied */ public ImagePlus paste(ImagePlus clipboard, String s) { ImagePlus imp = new ImagePlus("pasted"+s, clipboard.getImage()); imp.setRoi(0, 0, imp.getWidth(), imp.getHeight()); imp.setColor(Color.white);//F.V.background==F.V.WHITE?Color.white:Color. black); imp.getProcessor().fill(); if (clipboard==null) return imp; int cType = clipboard.getType(); int iType = imp.getType(); boolean sameType = false; if ((cType==ImagePlus.GRAY8|cType==ImagePlus.COLOR_256)&& (iType==ImagePlus.GRAY8|iType==ImagePlus.COLOR_256)) sameType = true; else if ((cType==ImagePlus.COLOR_RGB|cType==ImagePlus.GRAY8|cType ==ImagePlus.COLOR_256)&&iType==ImagePlus.COLOR_RGB) sameType = true; else if (cType==ImagePlus.GRAY16&&iType==ImagePlus.GRAY16) sameType = true; else if (cType==ImagePlus.GRAY32&&iType==ImagePlus.GRAY32) sameType = true; if (!sameType) { IJ.error("Images must be the same type to paste."); return imp; } int w = clipboard.getWidth(); int h = clipboard.getHeight(); if (w>imp.getWidth() || h>imp.getHeight()) { IJ.error("Image is too large to paste."); return imp; } Roi roi = imp.getRoi(); Rectangle r = null; if (roi!=null) r = roi.getBounds(); if (true) { //non-interactive paste int pasteMode = Roi.getCurrentPasteMode(); boolean nonRect = roi.getType()!=Roi.RECTANGLE; ImageProcessor ip = imp.getProcessor(); if (nonRect) ip.snapshot(); r = roi.getBounds(); ip.copyBits(clipboard.getProcessor(), r.x, r.y, pasteMode); if (nonRect) ip.reset(imp.getMask()); imp.updateAndDraw(); imp.killRoi(); } else {} return imp; } /** * Gets the rois for all particles in a passed ImagePlus, * then calls a method to scan them, returning nothing. * Invokes the ImageJ ParticleAnalyzer to segment * the image, then passes the image and the ResultsTable * containing roi coordinates to the * {@link #ScanTheProcessorFromEachParticlesRoi scanning method} to * scan each particle. The data from the scans are * accessed through the * ImageJ Results window eventually; * refer to the scanning method for the next * step. * @param thisslice int for the current slice or 1 if this is not a stack * @param lastslice int for the number of slices in this stack or 1 if this is not a stack * @param img ImagePlus to scan * @return boolean true if not cancelled; false if the call to the{@link #setupPA particle anlayzer} * set up is cancelled */ public boolean ScanParticles(ImagePlus img, int thisslice, int lastslice) { boolean quit=false; boolean goOn=true; //store information String title = img.getTitle(); ImageProcessor ip = img.getProcessor(); Roi roi = img.getRoi(); if(roi==null) { img.setRoi(0,0, img.getWidth(), img.getHeight()); roi=img.getRoi(); }//get user's options for the particle analyzer goOn=true; if(thisslice==1){ goOn=setupPA();}//quit if cancelled here if (!goOn) return false; else//if not cancelled, go on { ImageStatistics stats = img.getStatistics(); if ((stats.histogram[0]+stats.histogram[255])!=stats.pixelCount) { IJ.showMessage("Binary (thresholded) image required."); quit=true; }//quit if the image is not binary else if (!quit)//or else go on { // if (F.V.background==F.V.BLACK) { // img.killRoi(); // invert(ip); // img.updateAndRepaintWindow(); // } //get the rois img.setRoi(roi); Analyzer ana=new Analyzer(); int measurements = Analyzer.getMeasurements(); ResultsTable rt = ana.getResultsTable(); int options= (ParticleAnalyzer.EXCLUDE_EDGE_PARTICLES| ParticleAnalyzer.RECORD_STARTS |ParticleAnalyzer.SHOW_NONE); measurements = (ana.PERIMETER|ana.AREA); ParticleAnalyzer pa=new ParticleAnalyzer (options, measurements, rt, minparticle, maxparticle); Analyzer.resetCounter(); img.setRoi(roi); pa.analyze(img); ip.setRoi(roi); ScanTheProcessorFromEachParticlesRoi(rt, img, thisslice, lastslice, title); } }return true; } public void ScanTheProcessorFromEachParticlesRoi (ResultsTable rt, ImagePlus img, int thisslice, int lastslice, String title) { ImageWindow win = img.getWindow(); int nResults = rt.getCounter(); ImageProcessor ip = img.getProcessor(); Image StoredCopyOfOriginal = ip.createImage(); float[] wandx=new float[nResults]; float []wandy=new float [nResults]; Roi [] RoisFromParticleAnalyzer=new PolygonRoi [nResults]; float [][] ParticleRectangleXY=new float[2][nResults]; for (int i=0; i<nResults; i++) { wandx[i] =(float) rt.getValue("XStart", i); wandy[i] =(float) rt.getValue("YStart", i); Wand w = new Wand(img.getProcessor()); w.autoOutline( (int)wandx[i],(int)wandy[i]); RoisFromParticleAnalyzer[i]= new PolygonRoi(w.xpoints, w.ypoints, w.npoints, Roi.TRACED_ROI); {ParticleRectangleXY[0][i]= (int)RoisFromParticleAnalyzer[i].getBounds().getBounds2D().getX(); ParticleRectangleXY[1][i]= (int)RoisFromParticleAnalyzer[i].getBounds().getBounds2D().getY();} } for (int i=0; i<nResults; i++) { WindowManager.setCurrentWindow(win);//img=win.getImagePlus(); img.setRoi(RoisFromParticleAnalyzer[i]); ImagePlus copied=copy(false, img, title); copied.killRoi(); IJ.write(ParticleRectangleXY[0][i]+","+ ParticleRectangleXY[1][i]+","+ (int)RoisFromParticleAnalyzer[i].getBounds().getBounds2D().getWidth()+", "+ (int)RoisFromParticleAnalyzer[i].getBounds().getBounds2D().getHeight()); } if (MakeImage) { img.setSlice(thisslice); img.updateAndRepaintWindow(); BufferedImage bi = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_ARGB); Graphics2D g = (Graphics2D)bi.getGraphics(); g.drawImage(StoredCopyOfOriginal, 0, 0, null); ColourRois (ip, 1,//TYPE, g, RoisFromParticleAnalyzer, 190, 5, 0 ); ImagePlus imgg = new ImagePlus("new"+title, bi); if(thisslice==1) { ColouredRoisStack = new ImageStack(img.getWidth(), img.getHeight()); ColouredRoisStack.addSlice(title, imgg.getProcessor()); } else ColouredRoisStack.addSlice(title, imgg.getProcessor()); if(thisslice==lastslice) { StackWindow iww = new StackWindow(new ImagePlus(img.getTitle() + thisslice, ColouredRoisStack)); iww.setVisible(true); } } } ImageStack ColouredRoisStack =null; /**Sets up the ij.plugin.filter.ParticleAnalyzer using * a ij.gui.GenericDialog to get values for the minimum and * maximum particle size to find, and the background and * foreground arrangement (i.e., which is white and which is black). * Changes these variables in the {@link FLAnalyzer.FLVars variable class}. * @return boolean false if cancelled or else true */ public boolean setupPA() { GenericDialog gd = new GenericDialog("Particle Options", IJ.getInstance()); String [] C = {"Black", "White"}; String ch = C[1]; gd.addChoice("Background Colour", C, ch); gd.addNumericField("Minimum particle", minparticle, 0); gd.addNumericField("Maximum particle", maxparticle, 0); gd.showDialog(); if (gd.wasCanceled()) { return false;} else { Color bg=Color.white; int t=gd.getNextChoiceIndex(); if (t==0)bg=Color.black; Toolbar.setBackgroundColor(bg); minparticle=(int)gd.getNextNumber(); maxparticle=(int)gd.getNextNumber(); } return true; } public static void ColourRois (ImageProcessor ip, int TYPE, Graphics2D g2, Roi [] rois, //float[] colours, //int ColourCode, float LocalAlpha, int comp, int foreground //boolean makescaleimage, //float [] DFS ) { Color gcolour= new Color (210, 240, 240); Color basecolour= new Color (210, 240, 240); for (int i = 0; i <rois.length; i++) { g2.setColor (gcolour); AlphaComposite acc = AlphaComposite.getInstance (AlphaComposite.SRC_OVER, newAlpha(LocalAlpha, 1.5f)); g2.setComposite (acc); //colour the roi using the appropriate rule { g2.fillPolygon (rois[i].getPolygon ()); } //else if (TYPE==DRAW_IRREGULAR_ROI_NO_STRING) { g2.drawPolygon (rois[i].getPolygon ()); } ReplaceForegroundPixelsInIrregularRoi(rois[i], g2, ip, foreground); } } /** * Colours only the pixels that are the foreground * colour and in the passed roi on the passed image. * * @param roi Roi to colour * @param g2 Graphics2D on which to colour the roi * @param ip ImageProcessor to colour * @param foreground int for the foreground colour */ public static void ReplaceForegroundPixelsInIrregularRoi( Roi roi, Graphics2D g2, ImageProcessor ip, int foreground) { int startx = (int)(roi.getBoundingRect().getX()); int starty = (int)(roi.getBoundingRect().getY()); for (int xx = startx; xx< startx+(int)(roi.getBoundingRect().getWidth()); xx++) { for (int yy = starty; yy< starty+(int)(roi.getBoundingRect().getHeight()); yy++) { ip.setRoi (xx, yy, 1, 1); //Roi smallroi = ip.getRoi (); int [] histogram=ip.getHistogram (); if (histogram[foreground]>0) { if(roi.contains(xx, yy)) g2.fillRect (xx, yy, 1, 1); } } } } static float newAlpha(float LocalAlpha, float colour) { float floatLocalAlpha=(float)LocalAlpha/255.0f; floatLocalAlpha=floatLocalAlpha>1.0f?1.0f:floatLocalAlpha; floatLocalAlpha=floatLocalAlpha<0.0f?0.0f:floatLocalAlpha; return floatLocalAlpha; } } "Ludgate, David" <[hidden email]> wrote: Hi, I'm using the ParticleAnalyzer class to find particles and I need to record the irregular ROIs that define each particle. I did this simply by overriding the drawParticle method to record each ROI as it drew them. So now my program displays an image of all the filled particles and records the ROIs, which I use elsewhere. My problem is that I run though this procedure many times and if I leave all those images open I run out of memory really quickly. So I've been trying to look for a way to close the images that the ParticleAnalyzer makes automatically. I'm working directly with classes so i'd rather not have to go to ImageJ's window manager and try to find each image, and instead get the ImagePlus object used directly from the ParticleAnalyzer class. Unfortunately, as far as I can see, this is the line that creates that window: new ImagePlus us.java.html> (prefix+title, outlines).show(); The ParticleAnalyzer thus has no way of keeping track of that window once it is created. Furthermore, I cannot override the method (at least, my IDE tells me I can't) since "it is private to a different package". Does anyone know of a way I can get around this? At the moment I do not directly use the ImageJ program and deal with the classes directly, thus, ImageJ doesn't recognise the windows I open as existing. TIA, dave |
In reply to this post by Ludgate, David
Sorry for the incompleted email just now. I'm adding in the code to the
bottom for this version. Hi, After your first email pointing out FracLac, I duplicated what seemed the relevent code, and it seems to work - most of the time. One of my processing steps is to go through the ROIs and dialate them (by taking the mask, then dilating, then gettign the ROI for that mask). However, after 713 successful dilations, one invariably causes an ArrayIndexOutOfBoundsException in ByteProcessor (line 446). My inclination is to put in an if statement to simply ignore this particle - but I can't find any obvious difference between it and the rest. Have you encountered similar errors, or am I missing some code? Here is what I do: for(int i = 0; i < rt.getCounter(); i++){ Wand w = new Wand(ip); w.autoOutline((int)rt.getValue(6, i),(int)rt.getValue(7, i)); Roi r = new PolygonRoi(w.xpoints, w.ypoints, w.npoints, Roi.TRACED_ROI); procList.add(r.getMask()); roiList.add(r); } -----Original Message----- From: ImageJ Interest Group [mailto:[hidden email]] On Behalf Of audrey karperien Sent: Wednesday, August 03, 2005 6:35 PM To: [hidden email] Subject: Re: Issue with ParticleAnalyzer output Hello. Below is some code that you can cut and paste and compile as a new plugin that analyzes an image with the particle analyzer then writes the coordinates for the rois and optionally colours over them in a new image. Its ripped out of another plugin so is rather bulky and hacky as is but I think it is relevant to your question. Audrey import ij.*; import ij.IJ; import ij.ImagePlus; import ij.WindowManager; import ij.gui.*; import ij.gui.GenericDialog; import ij.gui.ImageWindow; import ij.gui.PolygonRoi; import ij.gui.Roi; import ij.gui.Toolbar; import ij.gui.Wand; import ij.measure.ResultsTable; import ij.plugin.*; import ij.plugin.filter.Analyzer; import ij.plugin.filter.ParticleAnalyzer; import ij.process.ImageProcessor; import ij.process.ImageStatistics; import java.awt.*; import java.awt.Graphics2D; import java.awt.Image; import java.awt.image.*; /* * UsePARois.java * * Created on August 3, 2005, 4:34 PM * * Code ripped out of FracLac and quickly hacked into a demo of pa rois. *It analyzes an image and writes out the x,y and width and height of the rois, *and optionally draws them back on the original image. It is just a quick hack that *will likely be prunable to something elegant. */ /** * * @author Audrey_2 Charles Sturt University, Neuroscience */ public class Use_PA_Rois implements PlugIn{ /** Creates a new instance of UsePARois */ public Use_PA_Rois() { } public void run(String args){ MakeImage=IJ.showMessageWithCancel("Make Image?", "Choose OK to draw the rois or cancel to make no new image"); Go();} int minparticle=30, maxparticle=99999; boolean MakeImage=false; public void Go(){ String imgtitle=null; String x="", y=""; ImageWindow wm = WindowManager.getCurrentWindow(); if(wm==null) { IJ.noImage(); return;} else { ImagePlus workingcopy=wm.getImagePlus(); if(workingcopy==null) { IJ.noImage(); return;} else { workingcopy.setSlice(1); {int lastslice=workingcopy.getStackSize(); for (int thisslice = 1; thisslice <= lastslice; ++thisslice) {//for each slice in the stack workingcopy.setSlice(thisslice); workingcopy.setRoi(0, 0, workingcopy.getWidth(), workingcopy.getHeight()); if(!ScanParticles(workingcopy, thisslice, lastslice))return; } }}}} /** * Copies the current ROI to the clipboard. The entire * image is copied if there is no ROI. * (Aug 2, 2005)@param cut boolean always false FIXME * @param imp ImagePlus to analyze * @param title String for image's title * @return ImagePlus copy of the roi selected */ public ImagePlus copy(boolean cut, ImagePlus imp, String title ) { Roi roi = imp.getRoi(); String msg = (cut)?"Cut":"Copy"; IJ.showStatus(msg+ "ing..."); ImageProcessor ip = imp.getProcessor(); ImageProcessor ip2 = ip.crop(); ImagePlus clipboard = new ImagePlus(title, ip2); if (roi!=null && roi.getType()!=Roi.RECTANGLE) clipboard.setRoi((Roi)roi.clone()); // if (cut) { // ip.snapshot(); // ip.setColor(F.V.background==F.V.WHITE?Color.white:Color.black); // ip.fill(); // if (roi!=null && roi.getType()!=Roi.RECTANGLE) // ip.reset(imp.getMask()); // imp.setColor( F.V.foreground==F.V.WHITE?Color.white:Color.black); // Undo.setup(Undo.FILTER, imp); // imp.updateAndDraw(); // } int bytesPerPixel = 1; switch (clipboard.getType()) { case ImagePlus.GRAY16: bytesPerPixel = 2; break; case ImagePlus.GRAY32: case ImagePlus.COLOR_RGB: bytesPerPixel = 4; } IJ.showStatus(msg + ": " + (clipboard.getWidth()* clipboard.getHeight()*bytesPerPixel)/1024 + "k"); return paste(clipboard, title); } /** * Pastes the passed image. I am trying to paste an roi without * (Aug 2, 2005)collecting the surrounding rectangle. FIXME * @return ImagePlus pasted * @param s needs fixing * @param clipboard ImagePlus copied */ public ImagePlus paste(ImagePlus clipboard, String s) { ImagePlus imp = new ImagePlus("pasted"+s, clipboard.getImage()); imp.setRoi(0, 0, imp.getWidth(), imp.getHeight()); imp.setColor(Color.white);//F.V.background==F.V.WHITE?Color.white:Color. black); imp.getProcessor().fill(); if (clipboard==null) return imp; int cType = clipboard.getType(); int iType = imp.getType(); boolean sameType = false; if ((cType==ImagePlus.GRAY8|cType==ImagePlus.COLOR_256)&& (iType==ImagePlus.GRAY8|iType==ImagePlus.COLOR_256)) sameType = true; else if ((cType==ImagePlus.COLOR_RGB|cType==ImagePlus.GRAY8|cType ==ImagePlus.COLOR_256)&&iType==ImagePlus.COLOR_RGB) sameType = true; else if (cType==ImagePlus.GRAY16&&iType==ImagePlus.GRAY16) sameType = true; else if (cType==ImagePlus.GRAY32&&iType==ImagePlus.GRAY32) sameType = true; if (!sameType) { IJ.error("Images must be the same type to paste."); return imp; } int w = clipboard.getWidth(); int h = clipboard.getHeight(); if (w>imp.getWidth() || h>imp.getHeight()) { IJ.error("Image is too large to paste."); return imp; } Roi roi = imp.getRoi(); Rectangle r = null; if (roi!=null) r = roi.getBounds(); if (true) { //non-interactive paste int pasteMode = Roi.getCurrentPasteMode(); boolean nonRect = roi.getType()!=Roi.RECTANGLE; ImageProcessor ip = imp.getProcessor(); if (nonRect) ip.snapshot(); r = roi.getBounds(); ip.copyBits(clipboard.getProcessor(), r.x, r.y, pasteMode); if (nonRect) ip.reset(imp.getMask()); imp.updateAndDraw(); imp.killRoi(); } else {} return imp; } /** * Gets the rois for all particles in a passed ImagePlus, * then calls a method to scan them, returning nothing. * Invokes the ImageJ ParticleAnalyzer to segment * the image, then passes the image and the ResultsTable * containing roi coordinates to the * {@link #ScanTheProcessorFromEachParticlesRoi scanning method} to * scan each particle. The data from the scans are * accessed through the * ImageJ Results window eventually; * refer to the scanning method for the next * step. * @param thisslice int for the current slice or 1 if this is not a stack * @param lastslice int for the number of slices in this stack or 1 if this is not a stack * @param img ImagePlus to scan * @return boolean true if not cancelled; false if the call to the{@link #setupPA particle anlayzer} * set up is cancelled */ public boolean ScanParticles(ImagePlus img, int thisslice, int lastslice) { boolean quit=false; boolean goOn=true; //store information String title = img.getTitle(); ImageProcessor ip = img.getProcessor(); Roi roi = img.getRoi(); if(roi==null) { img.setRoi(0,0, img.getWidth(), img.getHeight()); roi=img.getRoi(); }//get user's options for the particle analyzer goOn=true; if(thisslice==1){ goOn=setupPA();}//quit if cancelled here if (!goOn) return false; else//if not cancelled, go on { ImageStatistics stats = img.getStatistics(); if ((stats.histogram[0]+stats.histogram[255])!=stats.pixelCount) { IJ.showMessage("Binary (thresholded) image required."); quit=true; }//quit if the image is not binary else if (!quit)//or else go on { // if (F.V.background==F.V.BLACK) { // img.killRoi(); // invert(ip); // img.updateAndRepaintWindow(); // } //get the rois img.setRoi(roi); Analyzer ana=new Analyzer(); int measurements = Analyzer.getMeasurements(); ResultsTable rt = ana.getResultsTable(); int options= (ParticleAnalyzer.EXCLUDE_EDGE_PARTICLES| ParticleAnalyzer.RECORD_STARTS |ParticleAnalyzer.SHOW_NONE); measurements = (ana.PERIMETER|ana.AREA); ParticleAnalyzer pa=new ParticleAnalyzer (options, measurements, rt, minparticle, maxparticle); Analyzer.resetCounter(); img.setRoi(roi); pa.analyze(img); ip.setRoi(roi); ScanTheProcessorFromEachParticlesRoi(rt, img, thisslice, lastslice, title); } }return true; } public void ScanTheProcessorFromEachParticlesRoi (ResultsTable rt, ImagePlus img, int thisslice, int lastslice, String title) { ImageWindow win = img.getWindow(); int nResults = rt.getCounter(); ImageProcessor ip = img.getProcessor(); Image StoredCopyOfOriginal = ip.createImage(); float[] wandx=new float[nResults]; float []wandy=new float [nResults]; Roi [] RoisFromParticleAnalyzer=new PolygonRoi [nResults]; float [][] ParticleRectangleXY=new float[2][nResults]; for (int i=0; i<nResults; i++) { wandx[i] =(float) rt.getValue("XStart", i); wandy[i] =(float) rt.getValue("YStart", i); Wand w = new Wand(img.getProcessor()); w.autoOutline( (int)wandx[i],(int)wandy[i]); RoisFromParticleAnalyzer[i]= new PolygonRoi(w.xpoints, w.ypoints, w.npoints, Roi.TRACED_ROI); {ParticleRectangleXY[0][i]= (int)RoisFromParticleAnalyzer[i].getBounds().getBounds2D().getX(); ParticleRectangleXY[1][i]= (int)RoisFromParticleAnalyzer[i].getBounds().getBounds2D().getY();} } for (int i=0; i<nResults; i++) { WindowManager.setCurrentWindow(win);//img=win.getImagePlus(); img.setRoi(RoisFromParticleAnalyzer[i]); ImagePlus copied=copy(false, img, title); copied.killRoi(); IJ.write(ParticleRectangleXY[0][i]+","+ ParticleRectangleXY[1][i]+","+ (int)RoisFromParticleAnalyzer[i].getBounds().getBounds2D().getWidth()+", "+ (int)RoisFromParticleAnalyzer[i].getBounds().getBounds2D().getHeight()); } if (MakeImage) { img.setSlice(thisslice); img.updateAndRepaintWindow(); BufferedImage bi = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_ARGB); Graphics2D g = (Graphics2D)bi.getGraphics(); g.drawImage(StoredCopyOfOriginal, 0, 0, null); ColourRois (ip, 1,//TYPE, g, RoisFromParticleAnalyzer, 190, 5, 0 ); ImagePlus imgg = new ImagePlus("new"+title, bi); if(thisslice==1) { ColouredRoisStack = new ImageStack(img.getWidth(), img.getHeight()); ColouredRoisStack.addSlice(title, imgg.getProcessor()); } else ColouredRoisStack.addSlice(title, imgg.getProcessor()); if(thisslice==lastslice) { StackWindow iww = new StackWindow(new ImagePlus(img.getTitle() + thisslice, ColouredRoisStack)); iww.setVisible(true); } } } ImageStack ColouredRoisStack =null; /**Sets up the ij.plugin.filter.ParticleAnalyzer using * a ij.gui.GenericDialog to get values for the minimum and * maximum particle size to find, and the background and * foreground arrangement (i.e., which is white and which is black). * Changes these variables in the {@link FLAnalyzer.FLVars variable class}. * @return boolean false if cancelled or else true */ public boolean setupPA() { GenericDialog gd = new GenericDialog("Particle Options", IJ.getInstance()); String [] C = {"Black", "White"}; String ch = C[1]; gd.addChoice("Background Colour", C, ch); gd.addNumericField("Minimum particle", minparticle, 0); gd.addNumericField("Maximum particle", maxparticle, 0); gd.showDialog(); if (gd.wasCanceled()) { return false;} else { Color bg=Color.white; int t=gd.getNextChoiceIndex(); if (t==0)bg=Color.black; Toolbar.setBackgroundColor(bg); minparticle=(int)gd.getNextNumber(); maxparticle=(int)gd.getNextNumber(); } return true; } public static void ColourRois (ImageProcessor ip, int TYPE, Graphics2D g2, Roi [] rois, //float[] colours, //int ColourCode, float LocalAlpha, int comp, int foreground //boolean makescaleimage, //float [] DFS ) { Color gcolour= new Color (210, 240, 240); Color basecolour= new Color (210, 240, 240); for (int i = 0; i <rois.length; i++) { g2.setColor (gcolour); AlphaComposite acc = AlphaComposite.getInstance (AlphaComposite.SRC_OVER, newAlpha(LocalAlpha, 1.5f)); g2.setComposite (acc); //colour the roi using the appropriate rule { g2.fillPolygon (rois[i].getPolygon ()); } //else if (TYPE==DRAW_IRREGULAR_ROI_NO_STRING) { g2.drawPolygon (rois[i].getPolygon ()); } ReplaceForegroundPixelsInIrregularRoi(rois[i], g2, ip, foreground); } } /** * Colours only the pixels that are the foreground * colour and in the passed roi on the passed image. * * @param roi Roi to colour * @param g2 Graphics2D on which to colour the roi * @param ip ImageProcessor to colour * @param foreground int for the foreground colour */ public static void ReplaceForegroundPixelsInIrregularRoi( Roi roi, Graphics2D g2, ImageProcessor ip, int foreground) { int startx = (int)(roi.getBoundingRect().getX()); int starty = (int)(roi.getBoundingRect().getY()); for (int xx = startx; xx< startx+(int)(roi.getBoundingRect().getWidth()); xx++) { for (int yy = starty; yy< starty+(int)(roi.getBoundingRect().getHeight()); yy++) { ip.setRoi (xx, yy, 1, 1); //Roi smallroi = ip.getRoi (); int [] histogram=ip.getHistogram (); if (histogram[foreground]>0) { if(roi.contains(xx, yy)) g2.fillRect (xx, yy, 1, 1); } } } } static float newAlpha(float LocalAlpha, float colour) { float floatLocalAlpha=(float)LocalAlpha/255.0f; floatLocalAlpha=floatLocalAlpha>1.0f?1.0f:floatLocalAlpha; floatLocalAlpha=floatLocalAlpha<0.0f?0.0f:floatLocalAlpha; return floatLocalAlpha; } } "Ludgate, David" <[hidden email]> wrote: Hi, I'm using the ParticleAnalyzer class to find particles and I need to record the irregular ROIs that define each particle. I did this simply by overriding the drawParticle method to record each ROI as it drew them. So now my program displays an image of all the filled particles and records the ROIs, which I use elsewhere. My problem is that I run though this procedure many times and if I leave all those images open I run out of memory really quickly. So I've been trying to look for a way to close the images that the ParticleAnalyzer makes automatically. I'm working directly with classes so i'd rather not have to go to ImageJ's window manager and try to find each image, and instead get the ImagePlus object used directly from the ParticleAnalyzer class. Unfortunately, as far as I can see, this is the line that creates that window: new ImagePlus us.java.html> (prefix+title, outlines).show(); The ParticleAnalyzer thus has no way of keeping track of that window once it is created. Furthermore, I cannot override the method (at least, my IDE tells me I can't) since "it is private to a different package". Does anyone know of a way I can get around this? At the moment I do not directly use the ImageJ program and deal with the classes directly, thus, ImageJ doesn't recognise the windows I open as existing. TIA, dave |
Free forum by Nabble | Edit this page |