private void drawGraph( final double[] params, final double[] boxSizes, final double[] boxCountSums) { final int samples = 100; final float[] px = new float[samples]; final float[] py = new float[samples]; double[] a = Tools.getMinMax(boxSizes); final double xmin = a[0], xmax = a[1]; a = Tools.getMinMax(boxCountSums); double ymin = a[0], ymax = a[1]; final double inc = (xmax - xmin) / ((double) samples - 1); double tmp = xmin; for (int i = 0; i < samples; i++) { px[i] = (float) tmp; tmp += inc; } for (int i = 0; i < samples; i++) { py[i] = (float) CurveFitter.f(CurveFitter.STRAIGHT_LINE, params, px[i]); } a = Tools.getMinMax(py); ymin = Math.min(ymin, a[0]); ymax = Math.max(ymax, a[1]); final Plot plot = new Plot("Plot", "-log(box size)", "log(box count)", px, py); plot.setLimits(xmin, xmax * 0.9, ymin, ymax * 1.1); plot.draw(); plot.addPoints(boxSizes, boxCountSums, Plot.CIRCLE); plot.addPoints(px, py, Plot.LINE); final CurveFitter cf = new CurveFitter(boxSizes, boxCountSums); cf.doFit(CurveFitter.STRAIGHT_LINE); plot.addLabel( 0.1, 0.1, "Slope: " + IJ.d2s(params[1], 4) + "\n" + "R²: " + IJ.d2s(cf.getRSquared(), 4)); final ImagePlus plotImage = new ImagePlus("Plot", plot.getProcessor()); plotImage.show(); return; }
// TODO split run method into more sensible methods public void run(final String arg) { if (!ImageCheck.checkEnvironment()) return; final ImagePlus imp = IJ.getImage(); if (null == imp) { IJ.noImage(); return; } final ImageCheck ic = new ImageCheck(); if (!ImageCheck.isBinary(imp)) { IJ.showMessage("Fractal Count requires a binary image."); return; } if (noGo) return; final ImagePlus surfaceImp = findSurfaceVoxels(imp); try { // Fetch data final int width = imp.getWidth(); final int height = imp.getHeight(); final int depth = imp.getStackSize(); if (autoParam) { maxBox = Math.max(width, Math.max(height, depth)) / autoDiv; if (verboseOutput) { IJ.log("Automatic max box size " + maxBox + " selected"); } } // Create variables we need and set them long bestCount; // keep track of best count so far long count = 0; // current count final ArrayList<Double> xList = new ArrayList<Double>(); final ArrayList<Double> yList = new ArrayList<Double>(); int xPos, yPos, zPos, yPart; int xGrid, yGrid, zGrid; int xStart, yStart, zStart; int xEnd, yEnd, zEnd; // Start timer final long startTime = System.currentTimeMillis(); for (int boxSize = maxBox; boxSize >= minBox; boxSize /= divBox) { if (verboseOutput) { IJ.showStatus("Estimating dimension, box size: " + boxSize); } bestCount = Long.MAX_VALUE; // init count for this boxSize final int increment = Math.max(1, boxSize / numOffsets); for (int gridOffsetX = 0; (gridOffsetX < boxSize) && (gridOffsetX < width); gridOffsetX += increment) { for (int gridOffsetY = 0; (gridOffsetY < boxSize) && (gridOffsetY < height); gridOffsetY += increment) { for (int gridOffsetZ = 0; (gridOffsetZ < boxSize) && (gridOffsetZ < depth); gridOffsetZ += increment) { count = 0; final int iMax = width + gridOffsetX; final int jMax = height + gridOffsetY; final int kMax = depth + gridOffsetZ; // Iterate over box-grid for (int i = 0; i <= iMax; i += boxSize) { xGrid = -gridOffsetX + i; for (int j = 0; j <= jMax; j += boxSize) { yGrid = -gridOffsetY + j; for (int k = 0; k <= kMax; k += boxSize) { zGrid = -gridOffsetZ + k; xStart = 0; if (xGrid < 0) { xStart = -xGrid; } if ((boxSize + xGrid) >= width) { xEnd = Math.min(width, (width - xGrid)); } else { xEnd = boxSize; } yStart = 0; if (yGrid < 0) { yStart = -yGrid; } if ((boxSize + yGrid) >= height) { yEnd = Math.min(height, (height - yGrid)); } else { yEnd = boxSize; } zStart = 0; if (zGrid < 0) { zStart = -zGrid; } if ((boxSize + zGrid) >= depth) { zEnd = Math.min(depth, (depth - zGrid)); } else { zEnd = boxSize; } for (int x = xStart; x < xEnd; x++) { xPos = x + xGrid; for (int y = yStart; y < yEnd; y++) { yPos = (y + yGrid); yPart = yPos * width; for (int z = zStart; z < zEnd; z++) { zPos = z + zGrid; // If pixel inside region, // count it if ((0xff & ((byte[]) surfaceImp.getStack().getPixels(zPos + 1)) [xPos + yPart]) >= threshold) { count++; z = x = y = boxSize; // stops // things } } } } } } } if (count < bestCount) { bestCount = count; } } } } xList.add(new Double(boxSize)); yList.add(new Double(bestCount)); } if (verboseOutput) { IJ.log("\nDuration: " + (System.currentTimeMillis() - startTime) / 1000.0 + " seconds\n"); } final double[] boxSizes = new double[xList.size()]; final double[] boxCountSums = new double[yList.size()]; for (int i = 0; i < boxSizes.length; i++) { boxSizes[i] = -Math.log((xList.get(i)).doubleValue()); boxCountSums[i] = Math.log((yList.get(i)).doubleValue()); } if (verboseOutput) { IJ.log( "Used " + boxSizes.length + " different box sizes, from " + maxBox + " to " + minBox); IJ.log( "with a reduction rate of " + divBox + " and " + numOffsets + " translations of each box."); } if (boxSizes.length == 0) { IJ.error( "\nError: No boxes!\n" + "Make sure that starting and ending box size and" + "reduction rate allow for at least one" + " box size to exist!"); return; } final CurveFitter cf = new CurveFitter(boxSizes, boxCountSums); cf.doFit(CurveFitter.STRAIGHT_LINE); final double[] p = cf.getParams(); final double RSq = cf.getRSquared(); if (verboseOutput) { IJ.log( imp.getTitle() + ": Dimension estimate: " + IJ.d2s(p[1], 4) + ": R²: " + RSq + ": Settings: " + maxBox + ":" + minBox + ":" + divBox + ":" + numOffsets); } if (verboseOutput) { IJ.log("Box Size Box Count"); for (int i = 0; i < boxSizes.length; i++) { IJ.log(IJ.d2s(xList.get(i), 0) + " " + IJ.d2s(yList.get(i), 0)); } } final ResultInserter ri = ResultInserter.getInstance(); ri.setResultInRow(imp, "Fractal Dimension", p[1]); ri.setResultInRow(imp, "R²", RSq); ri.updateTable(); if (plotGraph && !Interpreter.isBatchMode()) { drawGraph(p, boxSizes, boxCountSums); } } catch (final Exception e) { e.printStackTrace(); } UsageReporter.reportEvent(this).send(); }