/** * Constructor * * @param image The intensity image to be labeled * @param scale the minimum distance between maxima of objects. Less technically, this should be * the diameter of the smallest object. * @param sigma1 the standard deviation for the larger smoothing. The difference between sigma1 * and sigma2 should be roughly the width of the desired edge in the DoG image. A larger * difference will obscure small, faint edges. * @param sigma2 the standard deviation for the smaller smoothing. This should be on the order of * the largest insignificant feature in the image. * @param names - an iterator that generates names of type L for the labels. The iterator will * waste the last name taken on the background label. You can use * AllConnectedComponents.getIntegerNames() as your name generator if you don't care about * names. */ public GradientWatershed( Image<T> input, double[] scale, double[] sigma1, double[] sigma2, Iterator<L> names) { this.input = input; this.scale = scale; this.sigma1 = sigma1; this.sigma2 = sigma2; structuringElement = AllConnectedComponents.getStructuringElement(input.getNumDimensions()); this.names = names; labelingFactory = new ImageFactory<LabelingType<L>>(new LabelingType<L>(), input.getContainerFactory()); }
/** * Return a difference of gaussian image that measures the gradient at a scale defined by the two * sigmas of the gaussians. * * @param image * @param sigma1 * @param sigma2 * @return */ public Image<FloatType> getGradientImage() { /* * Create the DoG kernel. */ double[][] kernels1d1 = new double[input.getNumDimensions()][]; double[][] kernels1d2 = new double[input.getNumDimensions()][]; int[] kernelDimensions = input.createPositionArray(); int[] offset = input.createPositionArray(); for (int i = 0; i < kernels1d1.length; i++) { kernels1d1[i] = Util.createGaussianKernel1DDouble(sigma1[i], true); kernels1d2[i] = Util.createGaussianKernel1DDouble(sigma2[i], true); kernelDimensions[i] = kernels1d1[i].length; offset[i] = (kernels1d1[i].length - kernels1d2[i].length) / 2; } Image<FloatType> kernel = getFloatFactory().createImage(kernelDimensions); LocalizableCursor<FloatType> kc = kernel.createLocalizableCursor(); int[] position = input.createPositionArray(); for (FloatType t : kc) { kc.getPosition(position); double value1 = 1; double value2 = 1; for (int i = 0; i < kernels1d1.length; i++) { value1 *= kernels1d1[i][position[i]]; int position2 = position[i] - offset[i]; if ((position2 >= 0) && (position2 < kernels1d2[i].length)) { value2 *= kernels1d2[i][position2]; } else { value2 = 0; } } t.setReal(value1 - value2); } kc.close(); /* * Apply the kernel to the image. */ FourierConvolution<FloatType, FloatType> convolution = new FourierConvolution<FloatType, FloatType>(getFloatImage(), kernel); if (!convolution.process()) return null; Image<FloatType> result = convolution.getResult(); /* * Quantize the image. */ ComputeMinMax<FloatType> computeMinMax = new ComputeMinMax<FloatType>(result); computeMinMax.process(); final float min = computeMinMax.getMin().get(); final float max = computeMinMax.getMax().get(); if (max == min) return result; ImageConverter<FloatType, FloatType> quantizer = new ImageConverter<FloatType, FloatType>( result, result.getImageFactory(), new Converter<FloatType, FloatType>() { @Override public void convert(FloatType input, FloatType output) { float value = (input.get() - min) / (max - min); value = Math.round(value * 100); output.set(value); } }); quantizer.process(); return quantizer.getResult(); }
protected boolean checkDimensions(double[] array) { return array.length == input.getNumDimensions(); }
@Override public int getNumDimensions() { return image.getNumDimensions(); }
/** * Fuse one slice/volume (one channel) * * @param output - same the type of the ImagePlus input * @param input - FloatType, because of Interpolation that needs to be done * @param transform - the transformation */ public static <T extends RealType<T>> void fuseChannel( final Image<T> output, final Image<FloatType> input, final float[] offset, final InvertibleCoordinateTransform transform, final InterpolatorFactory<FloatType> factory) { final int dims = output.getNumDimensions(); long imageSize = output.getDimension(0); for (int d = 1; d < output.getNumDimensions(); ++d) imageSize *= output.getDimension(d); // run multithreaded 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() { public void run() { // Thread ID final int myNumber = ai.getAndIncrement(); // get chunk of pixels to process final Chunk myChunk = threadChunks.get(myNumber); final long startPos = myChunk.getStartPosition(); final long loopSize = myChunk.getLoopSize(); final LocalizableCursor<T> out = output.createLocalizableCursor(); final Interpolator<FloatType> in = input.createInterpolator(factory); final float[] tmp = new float[input.getNumDimensions()]; try { // move to the starting position of the current thread out.fwd(startPos); // do as many pixels as wanted by this thread for (long j = 0; j < loopSize; ++j) { out.fwd(); for (int d = 0; d < dims; ++d) tmp[d] = out.getPosition(d) + offset[d]; transform.applyInverseInPlace(tmp); in.setPosition(tmp); out.getType().setReal(in.getType().get()); } } catch (NoninvertibleModelException e) { IJ.log("Cannot invert model, qutting."); return; } } }); SimpleMultiThreading.startAndJoin(threads); /* final LocalizableCursor<T> out = output.createLocalizableCursor(); final Interpolator<FloatType> in = input.createInterpolator( factory ); final float[] tmp = new float[ input.getNumDimensions() ]; try { while ( out.hasNext() ) { out.fwd(); for ( int d = 0; d < dims; ++d ) tmp[ d ] = out.getPosition( d ) + offset[ d ]; transform.applyInverseInPlace( tmp ); in.setPosition( tmp ); out.getType().setReal( in.getType().get() ); } } catch (NoninvertibleModelException e) { IJ.log( "Cannot invert model, qutting." ); return; } */ }