package ij.plugin; import ij.*; import ij.gui.*; import ij.process.*; import ij.measure.Calibration; import java.awt.*; /** This plugin implements the Edit/Selection/Straighten command. */ public class Straightener implements PlugIn { static boolean processStack; public void run(String arg) { ImagePlus imp = IJ.getImage(); Roi roi = imp.getRoi(); if (roi==null || !roi.isLine()) { IJ.error("Straightener", "Line selection required"); return; } int width = (int)Math.round(roi.getStrokeWidth()); int originalWidth = width; boolean isMacro = IJ.macroRunning() && Macro.getOptions()!=null; int stackSize = imp.getStackSize(); if (stackSize==1) processStack = false; if (width==1 || isMacro || stackSize>1) { if (width==1) width = 20; GenericDialog gd = new GenericDialog("Straightener"); gd.addNumericField("Line Width:", width, 0, 3, "pixels"); if (stackSize>1) gd.addCheckbox("Process Entire Stack", processStack); gd.showDialog(); if (gd.wasCanceled()) return; width = (int)gd.getNextNumber(); Line.setWidth(width); if (stackSize>1) processStack = gd.getNextBoolean(); } roi = (Roi)imp.getRoi().clone(); int type = roi.getType(); if (type==Roi.FREELINE) IJ.run(imp, "Fit Spline", ""); ImageProcessor ip2 = null; ImagePlus imp2 = null; if (processStack) { ImageStack stack2 = straightenStack(imp, roi, width); imp2 = new ImagePlus(WindowManager.getUniqueName(imp.getTitle()), stack2); } else { ip2 = straighten(imp, roi, width); imp2 = new ImagePlus(WindowManager.getUniqueName(imp.getTitle()), ip2); } if (imp2==null) return; Calibration cal = imp.getCalibration(); if (cal.pixelWidth==cal.pixelHeight) imp2.setCalibration(cal); imp2.show(); //imp.setRoi(roi); //if (type==Roi.POLYLINE&& !((PolygonRoi)roi).isSplineFit()) { // ((PolygonRoi)roi).fitSpline(); // imp.draw(); //} if (isMacro) Line.setWidth(originalWidth); } public ImageProcessor straighten(ImagePlus imp, Roi roi, int width) { ImageProcessor ip2; if (imp.getBitDepth()==24 && roi.getType()!=Roi.LINE) ip2 = straightenRGB(imp, width); else if (imp.isComposite() && ((CompositeImage)imp).getMode()==CompositeImage.COMPOSITE) ip2 = straightenComposite(imp, width); else if (roi.getType()==Roi.LINE) ip2 = rotateLine(imp, width); else ip2 = straightenLine(imp, width); return ip2; } public ImageStack straightenStack(ImagePlus imp, Roi roi, int width) { int current = imp.getCurrentSlice(); int n = imp.getStackSize(); ImageStack stack2 = null; for (int i=1; i<=n; i++) { IJ.showProgress(i, n); imp.setSlice(i); ImageProcessor ip2 = straighten(imp, roi, width); if (stack2==null) stack2 = new ImageStack(ip2.getWidth(), ip2.getHeight()); stack2.addSlice(null, ip2); } imp.setSlice(current); return stack2; } public ImageProcessor straightenLine(ImagePlus imp, int width) { PolygonRoi roi = (PolygonRoi)imp.getRoi(); if (roi==null) return null; if (roi.getState()==Roi.CONSTRUCTING) roi.exitConstructingMode(); boolean isSpline = roi.isSplineFit(); int type = roi.getType(); int n = roi.getNCoordinates(); double len = roi.getLength(); if (!(isSpline && Math.abs(1.0-roi.getLength()/n)<0.5)) roi.fitSplineForStraightening(); if (roi.getNCoordinates()<2) return null; FloatPolygon p = roi.getFloatPolygon(); n = p.npoints; ImageProcessor ip = imp.getProcessor(); ImageProcessor ip2 = new FloatProcessor(n, width); ImageProcessor distances = null; if (IJ.debugMode) distances = new FloatProcessor(n, 1); float[] pixels = (float[])ip2.getPixels(); double x1, y1; double x2=p.xpoints[0]-(p.xpoints[1]-p.xpoints[0]); double y2=p.ypoints[0]-(p.ypoints[1]-p.ypoints[0]); if (width==1) ip2.putPixelValue(0, 0, ip.getInterpolatedValue(x2, y2)); for (int i=0; i<n; i++) { if (!processStack&&(i%10)==0) IJ.showProgress(i, n); x1=x2; y1=y2; x2=p.xpoints[i]; y2=p.ypoints[i]; if (distances!=null) distances.putPixelValue(i, 0, (float)Math.sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1))); if (width==1) { ip2.putPixelValue(i, 0, ip.getInterpolatedValue(x2, y2)); continue; } double dx = x2-x1; double dy = y1-y2; double length = (float)Math.sqrt(dx*dx+dy*dy); dx /= length; dy /= length; //IJ.log(i+" "+x2+" "+dy+" "+(dy*width/2f)+" "+y2+" "+dx+" "+(dx*width/2f)); double x = x2-dy*width/2.0; double y = y2-dx*width/2.0; int j = 0; int n2 = width; do { ip2.putPixelValue(i, j++, ip.getInterpolatedValue(x, y));; //ip.drawDot((int)x, (int)y); x += dy; y += dx; } while (--n2>0); } if (!processStack) IJ.showProgress(n, n); //imp.updateAndDraw(); if (!isSpline) { if (type==Roi.FREELINE) roi.removeSplineFit(); else imp.draw(); } if (imp.getBitDepth()!=24) { ip2.setColorModel(ip.getColorModel()); ip2.resetMinAndMax(); } if (distances!=null) { distances.resetMinAndMax(); (new ImagePlus("Distances", distances)).show(); } return ip2; } public ImageProcessor rotateLine(ImagePlus imp, int width) { Roi roi = imp.getRoi(); float saveStrokeWidth = roi.getStrokeWidth(); roi.setStrokeWidth(1f); Polygon p = roi.getPolygon(); roi.setStrokeWidth(saveStrokeWidth); imp.setRoi(new PolygonRoi(p.xpoints, p.ypoints, 2, Roi.POLYLINE)); ImageProcessor ip2 = imp.getBitDepth()==24?straightenRGB(imp, width):straightenLine(imp, width); imp.setRoi(roi); return ip2; } ImageProcessor straightenRGB(ImagePlus imp, int width) { int w=imp.getWidth(), h=imp.getHeight(); int size = w*h; byte[] r = new byte[size]; byte[] g = new byte[size]; byte[] b = new byte[size]; ColorProcessor cp = (ColorProcessor)imp.getProcessor(); cp.getRGB(r, g, b); ImagePlus imp2 = new ImagePlus("red", new ByteProcessor(w, h, r, null)); imp2.setRoi((Roi)imp.getRoi().clone()); ImageProcessor red = straightenLine(imp2, width); if (red==null) return null; imp2 = new ImagePlus("green", new ByteProcessor(w, h, g, null)); imp2.setRoi((Roi)imp.getRoi().clone()); ImageProcessor green = straightenLine(imp2, width); if (green==null) return null; imp2 = new ImagePlus("blue", new ByteProcessor(w, h, b, null)); imp2.setRoi((Roi)imp.getRoi().clone()); ImageProcessor blue = straightenLine(imp2, width); if (blue==null) return null; ColorProcessor cp2 = new ColorProcessor(red.getWidth(), red.getHeight()); red = red.convertToByte(false); green = green.convertToByte(false); blue = blue.convertToByte(false); cp2.setRGB((byte[])red.getPixels(), (byte[])green.getPixels(), (byte[])blue.getPixels()); imp.setRoi(imp2.getRoi()); return cp2; } ImageProcessor straightenComposite(ImagePlus imp, int width) { Image img = imp.getImage(); ImagePlus imp2 = new ImagePlus("temp", new ColorProcessor(img)); imp2.setRoi(imp.getRoi()); ImageProcessor ip2 = straightenRGB(imp2, width); imp.setRoi(imp2.getRoi()); return ip2; } }