use of mpicbg.imagefeatures.Feature in project TrakEM2 by trakem2.
the class AlignTask method alignGraphs.
private static final boolean alignGraphs(final Align.Param p, final Layer layer1, final Layer layer2, final Iterable<Tile<?>> graph1, final Iterable<Tile<?>> graph2) {
final Align.Param cp = p.clone();
final Selection selection1 = new Selection(null);
for (final Tile<?> tile : graph1) selection1.add(((AbstractAffineTile2D<?>) tile).getPatch());
final Rectangle graph1Box = selection1.getBox();
final Selection selection2 = new Selection(null);
for (final Tile<?> tile : graph2) selection2.add(((AbstractAffineTile2D<?>) tile).getPatch());
final Rectangle graph2Box = selection2.getBox();
final int maxLength = Math.max(Math.max(Math.max(graph1Box.width, graph1Box.height), graph2Box.width), graph2Box.height);
// final double scale = ( double )cp.sift.maxOctaveSize / maxLength;
/* rather ad hoc but we cannot just scale this to maxOctaveSize */
cp.sift.maxOctaveSize = Math.min(maxLength, 2 * p.sift.maxOctaveSize);
/* make sure that, despite rounding issues from scale, it is >= image size */
final double scale = (double) (cp.sift.maxOctaveSize - 1) / maxLength;
// cp.maxEpsilon *= scale;
final FloatArray2DSIFT sift = new FloatArray2DSIFT(cp.sift);
final SIFT ijSIFT = new SIFT(sift);
final ArrayList<Feature> features1 = new ArrayList<Feature>();
final ArrayList<Feature> features2 = new ArrayList<Feature>();
final ArrayList<PointMatch> candidates = new ArrayList<PointMatch>();
final ArrayList<PointMatch> inliers = new ArrayList<PointMatch>();
long s = System.currentTimeMillis();
ijSIFT.extractFeatures(layer1.getProject().getLoader().getFlatImage(layer1, graph1Box, scale, 0xffffffff, ImagePlus.GRAY8, Patch.class, selection1.getSelected(Patch.class), false, Color.GRAY).getProcessor(), features1);
Utils.log(features1.size() + " features extracted for graphs in layer \"" + layer1.getTitle() + "\" (took " + (System.currentTimeMillis() - s) + " ms).");
ijSIFT.extractFeatures(layer2.getProject().getLoader().getFlatImage(layer2, graph2Box, scale, 0xffffffff, ImagePlus.GRAY8, Patch.class, selection2.getSelected(Patch.class), false, Color.GRAY).getProcessor(), features2);
Utils.log(features2.size() + " features extracted for graphs in layer \"" + layer1.getTitle() + "\" (took " + (System.currentTimeMillis() - s) + " ms).");
boolean modelFound = false;
if (features1.size() > 0 && features2.size() > 0) {
s = System.currentTimeMillis();
FeatureTransform.matchFeatures(features1, features2, candidates, cp.rod);
final AbstractAffineModel2D<?> model;
switch(cp.expectedModelIndex) {
case 0:
model = new TranslationModel2D();
break;
case 1:
model = new RigidModel2D();
break;
case 2:
model = new SimilarityModel2D();
break;
case 3:
model = new AffineModel2D();
break;
default:
return false;
}
boolean again = false;
try {
do {
again = false;
modelFound = model.filterRansac(candidates, inliers, 1000, cp.maxEpsilon, cp.minInlierRatio, cp.minNumInliers, 3);
if (modelFound && cp.rejectIdentity) {
final ArrayList<Point> points = new ArrayList<Point>();
PointMatch.sourcePoints(inliers, points);
if (Transforms.isIdentity(model, points, cp.identityTolerance)) {
IJ.log("Identity transform for " + inliers.size() + " matches rejected.");
candidates.removeAll(inliers);
inliers.clear();
again = true;
}
}
} while (again);
} catch (final NotEnoughDataPointsException e) {
modelFound = false;
}
if (modelFound) {
Utils.log("Model found for graphs in layer \"" + layer1.getTitle() + "\" and \"" + layer2.getTitle() + "\":\n correspondences " + inliers.size() + " of " + candidates.size() + "\n average residual error " + (model.getCost() / scale) + " px\n took " + (System.currentTimeMillis() - s) + " ms");
final AffineTransform b = new AffineTransform();
b.translate(graph2Box.x, graph2Box.y);
b.scale(1.0f / scale, 1.0f / scale);
b.concatenate(model.createAffine());
b.scale(scale, scale);
b.translate(-graph1Box.x, -graph1Box.y);
for (final Displayable d : selection1.getSelected(Patch.class)) d.getAffineTransform().preConcatenate(b);
/* assign patch affine transformation to the tile model */
for (final Tile<?> t : graph1) ((AbstractAffineTile2D<?>) t).initModel();
Display.repaint(layer1);
} else
IJ.log("No model found for graphs in layer \"" + layer1.getTitle() + "\" and \"" + layer2.getTitle() + "\":\n correspondence candidates " + candidates.size() + "\n took " + (System.currentTimeMillis() - s) + " ms");
}
return modelFound;
}
use of mpicbg.imagefeatures.Feature in project TrakEM2 by trakem2.
the class Align method fetchFeatures.
protected static final Collection<Feature> fetchFeatures(final Param p, final AbstractAffineTile2D<?> t) {
Collection<Feature> features = deserializeFeatures(p, t);
if (features == null) {
final FloatArray2DSIFT sift = new FloatArray2DSIFT(p.sift);
final SIFT ijSIFT = new SIFT(sift);
features = new ArrayList<Feature>();
final long s = System.currentTimeMillis();
ijSIFT.extractFeatures(t.createMaskedByteImage(), features);
Utils.log(features.size() + " features extracted in tile \"" + t.getPatch().getTitle() + "\" (took " + (System.currentTimeMillis() - s) + " ms).");
if (!serializeFeatures(p, t, features))
Utils.log("Saving features failed for tile: " + t.getPatch());
}
return features;
}
use of mpicbg.imagefeatures.Feature in project TrakEM2 by trakem2.
the class AlignLayersTask method alignLayersNonLinearlyJob.
public static final void alignLayersNonLinearlyJob(final LayerSet layerSet, final int first, final int last, final boolean propagateTransform, final Rectangle fov, final Filter<Patch> filter) {
// will reverse order if necessary
final List<Layer> layerRange = layerSet.getLayers(first, last);
final Align.Param p = Align.param.clone();
// Remove all empty layers
for (final Iterator<Layer> it = layerRange.iterator(); it.hasNext(); ) {
if (!it.next().contains(Patch.class, true)) {
it.remove();
}
}
if (0 == layerRange.size()) {
Utils.log("No layers in range show any images!");
return;
}
/* do not work if there is only one layer selected */
if (layerRange.size() < 2)
return;
final List<Patch> all = new ArrayList<Patch>();
for (final Layer la : layerRange) {
for (final Patch patch : la.getAll(Patch.class)) {
if (null != filter && !filter.accept(patch))
continue;
all.add(patch);
}
}
AlignTask.transformPatchesAndVectorData(all, new Runnable() {
@Override
public void run() {
// ///
final Loader loader = layerSet.getProject().getLoader();
// Not concurrent safe! So two copies, one per layer and Thread:
final SIFT ijSIFT1 = new SIFT(new FloatArray2DSIFT(p.sift));
final SIFT ijSIFT2 = new SIFT(new FloatArray2DSIFT(p.sift));
final Collection<Feature> features1 = new ArrayList<Feature>();
final Collection<Feature> features2 = new ArrayList<Feature>();
final List<PointMatch> candidates = new ArrayList<PointMatch>();
final List<PointMatch> inliers = new ArrayList<PointMatch>();
final int n_proc = Runtime.getRuntime().availableProcessors() > 1 ? 2 : 1;
final ExecutorService exec = Utils.newFixedThreadPool(n_proc, "alignLayersNonLinearly");
List<Patch> previousPatches = null;
int s = 0;
for (int i = 1; i < layerRange.size(); ++i) {
if (Thread.currentThread().isInterrupted())
break;
final Layer layer1 = layerRange.get(i - 1);
final Layer layer2 = layerRange.get(i);
final long t0 = System.currentTimeMillis();
features1.clear();
features2.clear();
final Rectangle box1 = null == fov ? layer1.getMinimalBoundingBox(Patch.class, true) : fov;
final Rectangle box2 = null == fov ? layer2.getMinimalBoundingBox(Patch.class, true) : fov;
/* calculate the common scale factor for both flat images */
final double scale = Math.min(1.0f, (double) p.sift.maxOctaveSize / (double) Math.max(box1.width, Math.max(box1.height, Math.max(box2.width, box2.height))));
final List<Patch> patches1;
if (null == previousPatches) {
patches1 = layer1.getAll(Patch.class);
if (null != filter) {
for (final Iterator<Patch> it = patches1.iterator(); it.hasNext(); ) {
if (!filter.accept(it.next()))
it.remove();
}
}
} else {
patches1 = previousPatches;
}
final List<Patch> patches2 = layer2.getAll(Patch.class);
if (null != filter) {
for (final Iterator<Patch> it = patches2.iterator(); it.hasNext(); ) {
if (!filter.accept(it.next()))
it.remove();
}
}
final Future<ImageProcessor> fu1 = exec.submit(new Callable<ImageProcessor>() {
@Override
public ImageProcessor call() {
final ImageProcessor ip1 = loader.getFlatImage(layer1, box1, scale, 0xffffffff, ImagePlus.GRAY8, Patch.class, patches1, true).getProcessor();
ijSIFT1.extractFeatures(ip1, features1);
Utils.log(features1.size() + " features extracted in layer \"" + layer1.getTitle() + "\" (took " + (System.currentTimeMillis() - t0) + " ms).");
return ip1;
}
});
final Future<ImageProcessor> fu2 = exec.submit(new Callable<ImageProcessor>() {
@Override
public ImageProcessor call() {
final ImageProcessor ip2 = loader.getFlatImage(layer2, box2, scale, 0xffffffff, ImagePlus.GRAY8, Patch.class, patches2, true).getProcessor();
ijSIFT2.extractFeatures(ip2, features2);
Utils.log(features2.size() + " features extracted in layer \"" + layer2.getTitle() + "\" (took " + (System.currentTimeMillis() - t0) + " ms).");
return ip2;
}
});
final ImageProcessor ip1, ip2;
try {
ip1 = fu1.get();
ip2 = fu2.get();
} catch (final Exception e) {
IJError.print(e);
return;
}
if (features1.size() > 0 && features2.size() > 0) {
final long t1 = System.currentTimeMillis();
candidates.clear();
FeatureTransform.matchFeatures(features2, features1, candidates, p.rod);
final AbstractAffineModel2D<?> model;
switch(p.expectedModelIndex) {
case 0:
model = new TranslationModel2D();
break;
case 1:
model = new RigidModel2D();
break;
case 2:
model = new SimilarityModel2D();
break;
case 3:
model = new AffineModel2D();
break;
default:
return;
}
boolean modelFound;
boolean again = false;
try {
do {
again = false;
modelFound = model.filterRansac(candidates, inliers, 1000, p.maxEpsilon, p.minInlierRatio, p.minNumInliers, 3);
if (modelFound && p.rejectIdentity) {
final ArrayList<Point> points = new ArrayList<Point>();
PointMatch.sourcePoints(inliers, points);
if (Transforms.isIdentity(model, points, p.identityTolerance)) {
IJ.log("Identity transform for " + inliers.size() + " matches rejected.");
candidates.removeAll(inliers);
inliers.clear();
again = true;
}
}
} while (again);
} catch (final NotEnoughDataPointsException e) {
modelFound = false;
}
if (modelFound) {
IJ.log("Model found for layer \"" + layer2.getTitle() + "\" and its predecessor:\n correspondences " + inliers.size() + " of " + candidates.size() + "\n average residual error " + (model.getCost() / scale) + " px\n took " + (System.currentTimeMillis() - t1) + " ms");
final ImagePlus imp1 = new ImagePlus("target", ip1);
final ImagePlus imp2 = new ImagePlus("source", ip2);
final List<Point> sourcePoints = new ArrayList<Point>();
final List<Point> targetPoints = new ArrayList<Point>();
PointMatch.sourcePoints(inliers, sourcePoints);
PointMatch.targetPoints(inliers, targetPoints);
imp2.setRoi(Util.pointsToPointRoi(sourcePoints));
imp1.setRoi(Util.pointsToPointRoi(targetPoints));
final ImageProcessor mask1 = ip1.duplicate();
mask1.threshold(1);
final ImageProcessor mask2 = ip2.duplicate();
mask2.threshold(1);
final Transformation warp = bUnwarpJ_.computeTransformationBatch(imp2, imp1, mask2, mask1, elasticParam);
final CubicBSplineTransform transf = new CubicBSplineTransform();
transf.set(warp.getIntervals(), warp.getDirectDeformationCoefficientsX(), warp.getDirectDeformationCoefficientsY(), imp2.getWidth(), imp2.getHeight());
final ArrayList<Future<?>> fus = new ArrayList<Future<?>>();
// Transform desired patches only
for (final Patch patch : patches2) {
try {
final Rectangle pbox = patch.getCoordinateTransformBoundingBox();
final AffineTransform at = patch.getAffineTransform();
final AffineTransform pat = new AffineTransform();
pat.scale(scale, scale);
pat.translate(-box2.x, -box2.y);
pat.concatenate(at);
pat.translate(-pbox.x, -pbox.y);
final mpicbg.trakem2.transform.AffineModel2D toWorld = new mpicbg.trakem2.transform.AffineModel2D();
toWorld.set(pat);
final CoordinateTransformList<CoordinateTransform> ctl = new CoordinateTransformList<CoordinateTransform>();
// move the patch into the global space where bUnwarpJ calculated the transformation
ctl.add(toWorld);
// Apply non-linear transformation
ctl.add(transf);
// move it back
ctl.add(toWorld.createInverse());
patch.appendCoordinateTransform(ctl);
fus.add(patch.updateMipMaps());
// Compensate for offset between boxes
final AffineTransform offset = new AffineTransform();
offset.translate(box1.x - box2.x, box1.y - box2.y);
offset.concatenate(at);
patch.setAffineTransform(offset);
} catch (final Exception e) {
e.printStackTrace();
}
}
// await regeneration of all mipmaps
Utils.wait(fus);
Display.repaint(layer2);
} else
IJ.log("No model found for layer \"" + layer2.getTitle() + "\" and its predecessor:\n correspondence candidates " + candidates.size() + "\n took " + (System.currentTimeMillis() - s) + " ms");
}
IJ.showProgress(++s, layerRange.size());
// for next iteration
previousPatches = patches2;
}
exec.shutdown();
if (propagateTransform)
Utils.log("Propagation not implemented yet for non-linear layer alignment.");
/* // CANNOT be done (at least not trivially:
* //an appropriate "scale" cannot be computed, and the box2 is part of the spline computation.
if ( propagateTransform && null != lastTransform )
{
for (final Layer la : l.getParent().getLayers(last > first ? last +1 : first -1, last > first ? l.getParent().size() -1 : 0)) {
// Transform visible patches only
final Rectangle box2 = la.getMinimalBoundingBox( Patch.class, true );
for ( final Displayable disp : la.getDisplayables( Patch.class, true ) )
{
// ...
}
}
}
*/
}
});
// end of transformPatchesAndVectorData
}
use of mpicbg.imagefeatures.Feature in project TrakEM2 by trakem2.
the class Util method serializeFeatures.
/**
* Save a {@link Collection} of {@link Feature Features} to the TrakEM2
* project folder. The saved file contains a key {@link Object} which
* may specify the properties of the {@link Feature} {@link Collection}.
*
* @param project
* @param key
* @param prefix
* @param id
* @param f
* @return
*/
public static final boolean serializeFeatures(final Project project, final Object key, final String prefix, final long id, final Collection<Feature> f) {
final ArrayList<Feature> list = new ArrayList<Feature>();
list.addAll(f);
final String name = prefix == null ? "features" : prefix + ".features";
final Loader loader = project.getLoader();
final Features fe = new Features(key, list);
return loader.serialize(fe, new StringBuilder(loader.getUNUIdFolder()).append("features.ser/").append(FSLoader.createIdPath(Long.toString(id), name, ".ser")).toString());
}
use of mpicbg.imagefeatures.Feature in project TrakEM2 by trakem2.
the class Distortion_Correction method extractSIFTFeaturesThreaded.
static List<Feature>[] extractSIFTFeaturesThreaded(final int numberOfImages, final String directory, final String[] names) {
// extract all SIFT Features
final List<Feature>[] siftFeatures = new ArrayList[numberOfImages];
final Thread[] threads = MultiThreading.newThreads();
// start at second slice
final AtomicInteger ai = new AtomicInteger(0);
IJ.showStatus("Extracting SIFT Features");
for (int ithread = 0; ithread < threads.length; ++ithread) {
threads[ithread] = new Thread() {
@Override
public void run() {
for (int i = ai.getAndIncrement(); i < numberOfImages; i = ai.getAndIncrement()) {
final ArrayList<Feature> fs = new ArrayList<Feature>();
final ImagePlus imps = new Opener().openImage(directory + names[i + sp.firstImageIndex]);
imps.setProcessor(imps.getTitle(), imps.getProcessor().convertToFloat());
final FloatArray2DSIFT sift = new FloatArray2DSIFT(sp.sift.clone());
final SIFT ijSIFT = new SIFT(sift);
ijSIFT.extractFeatures(imps.getProcessor(), fs);
Collections.sort(fs);
IJ.log("Extracting SIFT of image: " + i);
siftFeatures[i] = fs;
}
}
};
}
MultiThreading.startAndJoin(threads);
return siftFeatures;
}
Aggregations