/** * Converts an {@link Img} into a matrix * * @param maxtrixImg - the input {@link Img} * @return a {@link Matrix} or null if the {@link Img} is not one or two-dimensional */ public static <S extends RealType<S>> Matrix getMatrix(final Img<S> maxtrixImg) { final int numDimensions = maxtrixImg.numDimensions(); if (numDimensions > 2) return null; final Matrix matrix; if (numDimensions == 1) { matrix = new Matrix((int) maxtrixImg.dimension(0), 1); final Cursor<S> cursor = maxtrixImg.localizingCursor(); while (cursor.hasNext()) { cursor.fwd(); matrix.set(cursor.getIntPosition(0), 0, cursor.get().getRealDouble()); } } else { matrix = new Matrix((int) maxtrixImg.dimension(0), (int) maxtrixImg.dimension(1)); final Cursor<S> cursor = maxtrixImg.localizingCursor(); while (cursor.hasNext()) { cursor.fwd(); matrix.set( cursor.getIntPosition(0), cursor.getIntPosition(1), cursor.get().getRealDouble()); } } return matrix; }
private static final <R extends RealType<R>> Img<R> processReal( final Img<R> img, final float[] m, final Mode mode, final OutOfBoundsFactory<R, Img<R>> oobf) throws Exception { final InterpolatorFactory<R, RandomAccessible<R>> inter; switch (mode) { case LINEAR: inter = new NLinearInterpolatorFactory<R>(); break; case NEAREST_NEIGHBOR: inter = new NearestNeighborInterpolatorFactory<R>(); break; default: throw new IllegalArgumentException("Scale: don't know how to scale with mode " + mode); } final ImageTransform<R> transform; final ExtendedRandomAccessibleInterval<R, Img<R>> imgExt = Views.extend(img, oobf); if (2 == img.numDimensions()) { // Transform the single-plane image in 2D AffineModel2D aff = new AffineModel2D(); aff.set(m[0], m[4], m[1], m[5], m[3], m[7]); transform = new ImageTransform<R>(imgExt, aff, (InterpolatorFactory) inter); } else if (3 == img.numDimensions()) { // Transform the image in 3D, or each plane in 2D if (m.length < 12) { throw new IllegalArgumentException( "Affine transform in 3D requires a matrix array of 12 elements."); } AffineModel3D aff = new AffineModel3D(); aff.set(m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8], m[9], m[10], m[11]); transform = new ImageTransform<R>(imgExt, aff, (InterpolatorFactory) inter); // Ensure Z dimension is not altered if scaleZ is 1: if (Math.abs(m[10] - 1.0f) < 0.000001 && 0 == m[8] && 0 == m[9]) { long[] d = transform.getNewImageSize(); d[2] = img.dimension(2); // 0-based: '2' is the third dimension transform.setNewImageSize(d); } } else { throw new Exception("Affine transform: only 2D and 3D images are supported."); } if (!transform.checkInput() || !transform.process()) { throw new Exception("Could not affine transform the image: " + transform.getErrorMessage()); } return transform.getResult(); }
@Override public boolean process() { final int numDimensions = img.numDimensions(); final long integralSize[] = new long[numDimensions]; // the size of the first dimension is changed for (int d = 0; d < numDimensions; ++d) integralSize[d] = img.dimension(d) + 1; final Img<T> integral = imgFactory.create(integralSize, type); // not enough RAM or disc space if (integral == null) return false; this.integral = integral; if (numDimensions > 1) { final long[] fakeSize = new long[numDimensions - 1]; // the size of dimension 0 final long size = integralSize[0]; for (int d = 1; d < numDimensions; ++d) fakeSize[d - 1] = integralSize[d]; final long imageSize = getNumPixels(fakeSize); final AtomicInteger ai = new AtomicInteger(0); final Thread[] threads = SimpleMultiThreading.newThreads(); final Vector<Chunk> threadChunks = SimpleMultiThreading.divideIntoChunks(imageSize, threads.length); for (int ithread = 0; ithread < threads.length; ++ithread) threads[ithread] = new Thread( new Runnable() { @Override public void run() { // Thread ID final int myNumber = ai.getAndIncrement(); // get chunk of pixels to process final Chunk myChunk = threadChunks.get(myNumber); final long loopSize = myChunk.getLoopSize(); final LocalizingZeroMinIntervalIterator cursorDim = new LocalizingZeroMinIntervalIterator(fakeSize); // location for the input location final long[] tmpIn = new long[numDimensions]; // location for the integral location final long[] tmpOut = new long[numDimensions]; final long[] tmp = new long[numDimensions - 1]; final RandomAccess<R> cursorIn = img.randomAccess(); final RandomAccess<T> cursorOut = integral.randomAccess(); final T tmpVar = integral.firstElement().createVariable(); final T sum = integral.firstElement().createVariable(); cursorDim.jumpFwd(myChunk.getStartPosition()); // iterate over all dimensions except the one we are computing the integral in, // which is dim=0 here main: for (long j = 0; j < loopSize; ++j) { cursorDim.fwd(); // get all dimensions except the one we are currently doing the integral on cursorDim.localize(tmp); tmpIn[0] = 0; tmpOut[0] = 1; for (int d = 1; d < numDimensions; ++d) { tmpIn[d] = tmp[d - 1] - 1; tmpOut[d] = tmp[d - 1]; // all entries of position 0 are 0 if (tmpOut[d] == 0) continue main; } // set the cursor to the beginning of the correct line cursorIn.setPosition(tmpIn); // set the cursor in the integral image to the right position cursorOut.setPosition(tmpOut); // integrate over the line integrateLineDim0(converter, cursorIn, cursorOut, sum, tmpVar, size); } } }); SimpleMultiThreading.startAndJoin(threads); } else { final T tmpVar = integral.firstElement().createVariable(); final T sum = integral.firstElement().createVariable(); // the size of dimension 0 final long size = integralSize[0]; final RandomAccess<R> cursorIn = img.randomAccess(); final RandomAccess<T> cursorOut = integral.randomAccess(); cursorIn.setPosition(0, 0); cursorOut.setPosition(1, 0); // compute the first pixel converter.convert(cursorIn.get(), sum); cursorOut.get().set(sum); for (long i = 2; i < size; ++i) { cursorIn.fwd(0); cursorOut.fwd(0); converter.convert(cursorIn.get(), tmpVar); sum.add(tmpVar); cursorOut.get().set(sum); } return true; } for (int d = 1; d < numDimensions; ++d) { final int dim = d; final long[] fakeSize = new long[numDimensions - 1]; // the size of dimension d final long size = integralSize[d]; // get all dimensions except the one we are currently doing the integral on int countDim = 0; for (int e = 0; e < numDimensions; ++e) if (e != d) fakeSize[countDim++] = integralSize[e]; final long imageSize = getNumPixels(fakeSize); final AtomicInteger ai = new AtomicInteger(0); final Thread[] threads = SimpleMultiThreading.newThreads(); final Vector<Chunk> threadChunks = SimpleMultiThreading.divideIntoChunks(imageSize, threads.length); for (int ithread = 0; ithread < threads.length; ++ithread) threads[ithread] = new Thread( new Runnable() { @Override public void run() { // Thread ID final int myNumber = ai.getAndIncrement(); // get chunk of pixels to process final Chunk myChunk = threadChunks.get(myNumber); final long loopSize = myChunk.getLoopSize(); final LocalizingZeroMinIntervalIterator cursorDim = new LocalizingZeroMinIntervalIterator(fakeSize); // local instances final long[] tmp2 = new long[numDimensions - 1]; final long[] tmp = new long[numDimensions]; final RandomAccess<T> cursor = integral.randomAccess(); final T sum = integral.firstElement().createVariable(); cursorDim.jumpFwd(myChunk.getStartPosition()); for (long j = 0; j < loopSize; ++j) { cursorDim.fwd(); // get all dimensions except the one we are currently doing the integral on cursorDim.localize(tmp2); tmp[dim] = 1; int countDim = 0; for (int e = 0; e < numDimensions; ++e) if (e != dim) tmp[e] = tmp2[countDim++]; // update the cursor in the input image to the current dimension position cursor.setPosition(tmp); // sum up line integrateLine(dim, cursor, sum, size); } } }); SimpleMultiThreading.startAndJoin(threads); } return true; }
public boolean analyzePeak(final DifferenceOfGaussianPeak<T> peak) { final int numDimensions = laPlacian.numDimensions(); // the subpixel values final double[] subpixelLocation = new double[numDimensions]; // the current position for the quadratic fit final long[] currentPosition = new long[numDimensions]; peak.localize(currentPosition); // the cursor for the computation (one that cannot move out of Img) final RandomAccess<T> cursor; if (canMoveOutside) cursor = Views.extendPeriodic(laPlacian).randomAccess(); else cursor = laPlacian.randomAccess(); // the current hessian matrix and derivative vector Img<DoubleType> hessianMatrix = doubleArrayFactory.create( new int[] {cursor.numDimensions(), cursor.numDimensions()}, new DoubleType()); Img<DoubleType> derivativeVector = doubleArrayFactory.create(new int[] {cursor.numDimensions()}, new DoubleType()); // the inverse hessian matrix Matrix A, B, X; // the current value of the center T value = peak.value.createVariable(); boolean foundStableMaxima = true, pointsValid = false; int numMoves = 0; // fit n-dimensional quadratic function to the extremum and // if the extremum is shifted more than 0.5 in one or more // directions we test wheather it is better there // until we // - converge (find a stable extremum) // - move out of the Img // - achieved the maximal number of moves do { ++numMoves; // move the cursor to the current positon cursor.setPosition(currentPosition); // store the center value value.set(cursor.get()); // compute the n-dimensional hessian matrix [numDimensions][numDimensions] // containing all second derivatives, e.g. for 3d: // // xx xy xz // yx yy yz // zx zy zz hessianMatrix = getHessianMatrix(cursor, hessianMatrix); // compute the inverse of the hessian matrix A = invertMatrix(hessianMatrix); if (A == null) return handleFailure(peak, "Cannot invert hessian matrix"); // compute the n-dimensional derivative vector derivativeVector = getDerivativeVector(cursor, derivativeVector); B = getMatrix(derivativeVector); if (B == null) return handleFailure(peak, "Cannot compute derivative vector"); // compute the extremum of the n-dimensinal quadratic fit X = (A.uminus()).times(B); for (int d = 0; d < numDimensions; ++d) subpixelLocation[d] = X.get(d, 0); // test all dimensions for their change // if the absolute value of the subpixel location // is bigger than 0.5 we move into that direction foundStableMaxima = true; for (int d = 0; d < numDimensions; ++d) { // Normally, above an offset of 0.5 the base position // has to be changed, e.g. a subpixel location of 4.7 // would mean that the new base location is 5 with an offset of -0.3 // // If we allow an increasing maxima tolerance we will // not change the base position that easily. Sometimes // it simply jumps from left to right and back, because // it is 4.51 (i.e. goto 5), then 4.49 (i.e. goto 4) // Then we say, ok, lets keep the base position even if // the subpixel location is 0.6... final double threshold = allowMaximaTolerance ? 0.5 + numMoves * maximaTolerance : 0.5; if (Math.abs(subpixelLocation[d]) > threshold) { if (allowedToMoveInDim[d]) { // move to another base location currentPosition[d] += Math.signum(subpixelLocation[d]); foundStableMaxima = false; } else { // set it to the position that is maximally away when keeping the current base position // e.g. if (0.7) do 4 -> 4.5 (although it should be 4.7, i.e. a new base position of 5) // or if (-0.9) do 4 -> 3.5 (although it should be 3.1, i.e. a new base position of 3) subpixelLocation[d] = Math.signum(subpixelLocation[d]) * 0.5; } } } // check validity of the new location if there is a need to move pointsValid = true; if (!canMoveOutside) if (!foundStableMaxima) for (int d = 0; d < numDimensions; ++d) if (currentPosition[d] <= 0 || currentPosition[d] >= laPlacian.dimension(d) - 1) pointsValid = false; } while (numMoves <= maxNumMoves && !foundStableMaxima && pointsValid); if (!foundStableMaxima) return handleFailure(peak, "No stable extremum found."); if (!pointsValid) return handleFailure(peak, "Moved outside of the Img."); // compute the function value (intensity) of the fit double quadrFuncValue = 0; for (int d = 0; d < numDimensions; ++d) quadrFuncValue += X.get(d, 0) * B.get(d, 0); quadrFuncValue /= 2.0; // set the results if everything went well // subpixel location for (int d = 0; d < numDimensions; ++d) peak.setSubPixelLocationOffset((float) subpixelLocation[d], d); // pixel location peak.setPixelLocation(currentPosition); // quadratic fit value final T quadraticFit = peak.getImgValue().createVariable(); quadraticFit.setReal(quadrFuncValue); peak.setFitValue(quadraticFit); // normal value peak.setImgValue(value); return true; }
@Override public boolean process() { /* 0. Instantiate tensor holder, and initialize cursors. */ long[] tensorDims = new long[input.numDimensions() + 1]; for (int i = 0; i < input.numDimensions(); i++) { tensorDims[i] = input.dimension(i); } tensorDims[input.numDimensions()] = 3; try { D = input.factory().imgFactory(new FloatType()).create(tensorDims, new FloatType()); } catch (IncompatibleTypeException e) { errorMessage = BASE_ERROR_MESSAGE + "Failed to create tensor holder:\n" + e.getMessage(); return false; } /* 1. Create a smoothed version of the input. */ Img<FloatType> smoothed = Gauss.toFloat(new double[] {sigma, sigma}, input); /* 2. Compute the gradient of the smoothed input, but only algon X & Y */ boolean[] doDimension = new boolean[input.numDimensions()]; doDimension[0] = true; doDimension[1] = true; Gradient<FloatType> gradientCalculator = new Gradient<FloatType>(smoothed, doDimension); gradientCalculator.process(); final Img<FloatType> gradient = gradientCalculator.getResult(); /* 3. Compute the structure tensor. */ final Img<FloatType> J = D.factory().create(D, new FloatType()); final int newDim = input.numDimensions(); final Vector<Chunk> chunks = SimpleMultiThreading.divideIntoChunks(input.size(), numThreads); Thread[] threads = SimpleMultiThreading.newThreads(numThreads); for (int i = 0; i < threads.length; i++) { final Chunk chunk = chunks.get(i); threads[i] = new Thread("" + BASE_ERROR_MESSAGE + "thread " + i) { @Override public void run() { float ux, uy; Cursor<T> cursor = input.localizingCursor(); RandomAccess<FloatType> grad_ra = gradient.randomAccess(); RandomAccess<FloatType> J_ra = J.randomAccess(); cursor.jumpFwd(chunk.getStartPosition()); for (long k = 0; k < chunk.getLoopSize(); k++) { cursor.fwd(); for (int i = 0; i < input.numDimensions(); i++) { grad_ra.setPosition(cursor.getLongPosition(i), i); J_ra.setPosition(cursor.getLongPosition(i), i); } grad_ra.setPosition(0, newDim); ux = grad_ra.get().get(); grad_ra.fwd(newDim); uy = grad_ra.get().get(); J_ra.setPosition(0, newDim); J_ra.get().set(ux * ux); J_ra.fwd(newDim); J_ra.get().set(ux * uy); J_ra.fwd(newDim); J_ra.get().set(uy * uy); } } }; } SimpleMultiThreading.startAndJoin(threads); /* 3.5 Smoooth the structure tensor. */ Gauss.inFloat(new double[] {rho, rho, 0}, J); /* 4. Construct Diffusion tensor. */ for (int i = 0; i < threads.length; i++) { final Chunk chunk = chunks.get(i); threads[i] = new Thread("" + BASE_ERROR_MESSAGE + "thread " + i) { @Override public void run() { Cursor<T> cursor = input.localizingCursor(); RandomAccess<FloatType> J_ra = J.randomAccess(); RandomAccess<FloatType> D_ra = D.randomAccess(); float Jxx, Jxy, Jyy; double tmp, v1x, v1y, v2x, v2y, mag, mu1, mu2, lambda1, lambda2, di; double newLambda1, newLambda2, Dxx, Dxy, Dyy; double scale; cursor.jumpFwd(chunk.getStartPosition()); for (long k = 0; k < chunk.getLoopSize(); k++) { cursor.fwd(); for (int j = 0; j < input.numDimensions(); j++) { D_ra.setPosition(cursor.getLongPosition(j), j); J_ra.setPosition(cursor.getLongPosition(j), j); } // Compute eigenvalues J_ra.setPosition(0, newDim); Jxx = J_ra.get().get(); J_ra.fwd(newDim); Jxy = J_ra.get().get(); J_ra.fwd(newDim); Jyy = J_ra.get().get(); tmp = Math.sqrt((Jxx - Jyy) * (Jxx - Jyy) + 4 * Jxy * Jxy); v2x = 2 * Jxy; v2y = Jyy - Jxx + tmp; mag = Math.sqrt(v2x * v2x + v2y * v2y); v2x /= mag; v2y /= mag; v1x = -v2y; v1y = v2x; mu1 = 0.5 * (Jxx + Jyy + tmp); mu2 = 0.5 * (Jxx + Jyy - tmp); // Large one in abs values must be the 2nd if (Math.abs(mu2) > Math.abs(mu1)) { lambda1 = mu1; lambda2 = mu2; } else { lambda1 = mu2; lambda2 = mu1; } di = lambda2 - lambda1; scale = Util.pow(di * di, m); newLambda1 = alpha + (1 - alpha) * Math.exp(-C / scale); newLambda2 = alpha; Dxx = newLambda1 * v1x * v1x + newLambda2 * v2x * v2x; Dxy = newLambda1 * v1x * v1y + newLambda2 * v2x * v2x; Dyy = newLambda1 * v1y * v1y + newLambda2 * v2y * v2y; D_ra.setPosition(0, 2); D_ra.get().setReal(Dxx); D_ra.fwd(2); D_ra.get().setReal(Dxy); D_ra.fwd(2); D_ra.get().setReal(Dyy); } } }; } SimpleMultiThreading.startAndJoin(threads); return true; }