示例#1
0
 @Override
 public boolean checkInput() {
   if (!(source.numDimensions() == 2 || source.numDimensions() == 3)) {
     errorMessage = BASE_ERROR_MSG + "Only operates on 2D or 3D images.";
     return false;
   }
   return true;
 }
示例#2
0
  public Example6a1() throws ImgIOException {
    // open with ImgOpener as a FloatType
    Img<FloatType> image = new ImgOpener().openImg("DrosophilaWing.tif", new FloatType());

    // perform gaussian convolution with float precision
    double[] sigma = new double[image.numDimensions()];

    for (int d = 0; d < image.numDimensions(); ++d) sigma[d] = 8;

    // convolve & display
    ImageJFunctions.show(Gauss.toFloat(sigma, image));
  }
示例#3
0
  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();
  }
示例#4
0
  /**
   * 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;
  }
示例#5
0
 /** Test the Fourier transformation with a known frequency */
 @Test
 public void oneDimensional() throws IncompatibleTypeException {
   double[] values = {0, 1, 0, -1, 0};
   final Img<DoubleType> img = ArrayImgs.doubles(values, 5);
   final FourierTransform<DoubleType, ComplexDoubleType> fft =
       new FourierTransform<DoubleType, ComplexDoubleType>(img, new ComplexDoubleType());
   fft.process();
   Img<ComplexDoubleType> convolved = fft.getResult();
   assertEquals(convolved.numDimensions(), 1);
 }
示例#6
0
 void copyWithDestIteration(final Img<IntType> srcImg, final Img<IntType> dstImg) {
   final long[] pos = new long[dstImg.numDimensions()];
   final Cursor<IntType> dst = dstImg.localizingCursor();
   final RandomAccess<IntType> src = srcImg.randomAccess();
   while (dst.hasNext()) {
     dst.fwd();
     dst.localize(pos);
     src.setPosition(pos);
     dst.get().set(src.get());
   }
 }
示例#7
0
  public SubpixelLocalization(
      final Img<T> laPlacian, final List<DifferenceOfGaussianPeak<T>> peaks) {
    setNumThreads();
    this.laPlacian = laPlacian;
    this.peaks = peaks;
    this.allowedToMoveInDim = new boolean[laPlacian.numDimensions()];

    // principally one can move in any dimension
    for (int d = 0; d < allowedToMoveInDim.length; ++d) allowedToMoveInDim[d] = true;

    this.doubleArrayFactory = new ArrayImgFactory<DoubleType>();
  }
示例#8
0
 int[] getImgAsInts(final Img<IntType> img) {
   final RandomAccess<IntType> a = img.randomAccess();
   final int N = (int) img.size();
   final int[] data = new int[N];
   final long[] dim = new long[img.numDimensions()];
   final long[] pos = new long[img.numDimensions()];
   img.dimensions(dim);
   for (int i = 0; i < N; ++i) {
     IntervalIndexer.indexToPosition(i, dim, pos);
     a.setPosition(pos);
     data[i] = a.get().get();
   }
   return data;
 }
示例#9
0
  /** Makes sure input is okay and creates output image */
  @Override
  public boolean checkInput() {
    final Img inputImage = dataset.getImgPlus(); // TODO - raw type required
    // here

    inputDimensions = new long[inputImage.numDimensions()];

    inputImage.dimensions(inputDimensions);

    final long[] outputDimensions = flipper.calcOutputDimensions(inputDimensions);

    outputImage = inputImage.factory().create(outputDimensions, inputImage.firstElement());

    return true;
  }
  // change the return type to Img<FloatType> and adjust the code accordingly
  public <T extends RealType<T>> Img<T> gradient(Img<T> img) {
    // create a new ImgLib2 image of same dimensions, but FloatType
    // to to that, create a new PlanarImgFactory and instantiate a new
    // Img<FloatType>
    /**
     * ImgFactory<T> imgFactory = img.factory(); Img<T> gradientImg = imgFactory.create( img,
     * img.firstElement() );
     */

    // create a localizing cursor on the GradientImg, it will iterate all pixels
    // and is able to efficiently return its position at each pixel, at each
    // pixel we will compute the gradient
    /** Cursor<T> cursor = gradientImg.localizingCursor(); */

    // We extend the input image by a mirroring out of bounds strategy so
    // that we can access pixels outside of the image
    RandomAccessible<T> view = Views.extendMirrorSingle(img);

    // instantiate a RandomAccess on the extended view, it will be used to
    // compute the gradient locally at each pixel location
    RandomAccess<T> randomAccess = view.randomAccess();

    // iterate over all pixels
    while (cursor.hasNext()) {
      // move the cursor to the next pixel
      cursor.fwd();

      // compute gradient in each dimension
      double gradient = 0;

      for (int d = 0; d < img.numDimensions(); ++d) {
        // set the randomaccess to the location of the cursor
        randomAccess.setPosition(cursor);

        // move one pixel back in dimension d
        randomAccess.bck(d);

        // get the value
        double v1 = randomAccess.get().getRealDouble();

        // move twice forward in dimension d, i.e.
        // one pixel above the location of the cursor
        randomAccess.fwd(d);
        randomAccess.fwd(d);

        // get the value
        double v2 = randomAccess.get().getRealDouble();

        // add the square of the magnitude of the gradient
        gradient += ((v2 - v1) * (v2 - v1)) / 4;
      }

      // the square root of all quadratic sums yields
      // the magnitude of the gradient at this location,
      // set the pixel value of the gradient image

      // change the value to float, otherwise it will not be
      // compatible
      cursor.get().setReal(Math.sqrt(gradient));
    }

    return gradientImg;
  }
示例#11
0
  @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;
  }
示例#12
0
  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;
  }