Java tutorial
/* * Copyright (c) 2012 Diamond Light Source Ltd. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ package org.dawnsci.boofcv.internal; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.dawnsci.boofcv.converter.ConvertIDataset; import org.dawnsci.boofcv.registration.ImageHessianRegistration; import org.eclipse.dawnsci.analysis.api.dataset.IDataset; import org.eclipse.dawnsci.analysis.api.dataset.ILazyDataset; import org.eclipse.dawnsci.analysis.api.dataset.ILazyWriteableDataset; import org.eclipse.dawnsci.analysis.api.dataset.Slice; import org.eclipse.dawnsci.analysis.api.dataset.SliceND; import org.eclipse.dawnsci.analysis.api.image.IImageTransform; import org.eclipse.dawnsci.analysis.api.io.ScanFileHolderException; import org.eclipse.dawnsci.analysis.api.monitor.IMonitor; import org.eclipse.dawnsci.analysis.dataset.impl.AbstractDataset; import org.eclipse.dawnsci.hdf5.HDF5Utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import boofcv.alg.distort.DistortImageOps; import boofcv.alg.interpolate.TypeInterpolate; import boofcv.struct.feature.TupleDesc; import boofcv.struct.image.ImageFloat32; import boofcv.struct.image.ImageSingleBand; import org.apache.commons.math3.util.Pair; /** * Implementation of IImageTransform<br> * * This class is internal and not supposed to be used out of this bundle. * * @author wqk87977 * */ public class BoofCVImageTransformImpl<T extends ImageSingleBand<?>, TD extends TupleDesc<?>> implements IImageTransform { static { System.out.println("Starting BoofCV image transform service."); } private Logger logger = LoggerFactory.getLogger(BoofCVImageFilterImpl.class); public BoofCVImageTransformImpl() { // Important do nothing here, OSGI may start the service more than once. } @Override public IDataset rotate(IDataset data, double angle) throws Exception { return rotate(data, angle, true); } @Override public IDataset rotate(IDataset data, double angle, boolean keepShape) throws Exception { if (data.getShape().length != 2) throw new Exception("Data shape is not 2D"); ImageFloat32 image = ConvertIDataset.convertFrom(data, ImageFloat32.class, 1); int width = 0, height = 0; if (keepShape) { width = image.width; height = image.height; } else { // calculate resulting bounding box double cos = Math.abs(Math.cos(Math.toRadians(angle))); double sin = Math.abs(Math.sin(Math.toRadians(angle))); width = (int) (image.width * cos + image.height * sin); height = (int) (image.height * cos + image.width * sin); } ImageFloat32 rotated = new ImageFloat32(width, height); DistortImageOps.rotate(image, rotated, TypeInterpolate.BILINEAR, (float) Math.toRadians(angle)); return ConvertIDataset.convertTo(rotated, true); } @Override public List<IDataset> align(List<IDataset> images, IMonitor monitor) throws Exception { List<IDataset> alignedList = new ArrayList<IDataset>(); ImageFloat32 imageA = ConvertIDataset.convertFrom(images.get(0), ImageFloat32.class, 1); alignedList.add(images.get(0)); if (images.get(0).getShape().length != 2) throw new Exception("Data shape is not 2D"); for (int i = 1; i < images.size(); i++) { if (images.get(i).getShape().length != 2) throw new Exception("Data shape is not 2D"); ImageFloat32 imageB = ConvertIDataset.convertFrom(images.get(i), ImageFloat32.class, 1); ImageSingleBand<?> aligned = ImageHessianRegistration.registerHessian(imageA, imageB); IDataset alignedData = ConvertIDataset.convertTo(aligned, true); alignedData.setName(images.get(i).getName()); alignedList.add(alignedData); if (monitor != null) { if (monitor.isCancelled()) return alignedList; monitor.worked(1); } } return alignedList; } @Override public ILazyDataset align(ILazyDataset lazydata, IMonitor monitor) throws Exception { if (lazydata.getShape().length != 3) throw new Exception("Supported Lazy data is 3D, please provide a 3D dataset"); // save on a temp file String file = System.getProperty("java.io.tmpdir") + File.separator + "tmp_aligned.h5"; String path = "/entry/data/"; String name = "aligned"; File tmpFile = new File(file); if (tmpFile.exists()) tmpFile.delete(); IDataset firstSlice = lazydata.getSlice(new Slice(0, lazydata.getShape()[1], lazydata.getShape()[2])) .squeeze(); ILazyWriteableDataset lazy = HDF5Utils.createLazyDataset(file, path, name, lazydata.getShape(), null, lazydata.getShape(), AbstractDataset.FLOAT32, null, false); //convert to boofcv data ImageFloat32 imageA = ConvertIDataset.convertFrom(firstSlice, ImageFloat32.class, 1); // add first image appendDataset(lazy, firstSlice, 0, monitor); int[] shape = lazydata.getShape(); if (firstSlice.getShape().length != 2) throw new Exception("Data shape is not 2D"); for (int i = 1; i < shape[0]; i++) { IDataset slice = lazydata.getSlice(new Slice(i, lazydata.getShape()[1], lazydata.getShape()[2])) .squeeze(); if (slice.getShape().length != 2) throw new Exception("Data shape is not 2D"); ImageFloat32 imageB = ConvertIDataset.convertFrom(slice, ImageFloat32.class, 1); ImageSingleBand<?> aligned = ImageHessianRegistration.registerHessian(imageA, imageB); IDataset alignedData = ConvertIDataset.convertTo(aligned, true); alignedData.setName(slice.getName()); // add data to lazy file appendDataset(lazy, alignedData, i, monitor); if (monitor != null) { if (monitor.isCancelled()) { // reload file lazy.setName("Aligned"); return getLazyData(file, path + name); } monitor.worked(1); } } // reload file lazy.setName("Aligned"); return getLazyData(file, path + name); } @Override public IDataset affineTransform(IDataset data, double a11, double a12, double a21, double a22, double dx, double dy) throws Exception { return affineTransform(data, a11, a12, a21, a22, dx, dy, false); } @Override public IDataset affineTransform(IDataset data, double a11, double a12, double a21, double a22, double dx, double dy, boolean keepShape) throws Exception { if (data.getShape().length != 2) throw new Exception("Data shape is not 2D"); ImageFloat32 image = ConvertIDataset.convertFrom(data, ImageFloat32.class, 1); // IDataset uses row major ordering, but ImageFloat32 uses column major // this is why the the affine transform parameters will be exchanged in what follows next... int width = 0, height = 0; if (keepShape) { width = image.width; height = image.height; } else { // calculate resulting bounding box Pair<Double, Double> coords00 = affineTransformation(0, 0, a22, a21, a12, a11, dy, dx); Pair<Double, Double> coords10 = affineTransformation(0, image.height, a22, a21, a12, a11, dy, dx); Pair<Double, Double> coords01 = affineTransformation(image.width, 0, a22, a21, a12, a11, dy, dx); Pair<Double, Double> coords11 = affineTransformation(image.width, image.height, a22, a21, a12, a11, dy, dx); List<Double> coordsx = Arrays.asList(coords00.getFirst(), coords10.getFirst(), coords01.getFirst(), coords11.getFirst()); List<Double> coordsy = Arrays.asList(coords00.getSecond(), coords10.getSecond(), coords01.getSecond(), coords11.getSecond()); double maxx = Collections.max(coordsx); double maxy = Collections.max(coordsy); height = (int) (maxy); width = (int) (maxx); } ImageFloat32 transformed = new ImageFloat32(width, height); DistortImageOps.affine(image, transformed, TypeInterpolate.BILINEAR, a22, a21, a12, a11, dy, dx); return ConvertIDataset.convertTo(transformed, true); } private ILazyDataset getLazyData(String filename, String node) { ILazyDataset shifted = null; try { shifted = HDF5Utils.loadDataset(filename, node); } catch (ScanFileHolderException e) { logger.error("Could not reload the temp h5 file:", e); } return shifted; } private static void appendDataset(ILazyWriteableDataset lazy, IDataset data, int idx, IMonitor monitor) throws Exception { SliceND ndSlice = new SliceND(lazy.getShape(), new int[] { idx, 0, 0 }, new int[] { (idx + 1), data.getShape()[0], data.getShape()[1] }, null); lazy.setSlice(monitor, data, ndSlice); } private static Pair<Double, Double> affineTransformation(double x, double y, double a11, double a12, double a21, double a22, double dx, double dy) { return new Pair<Double, Double>(x * a11 + y * a12 + dx, x * a21 + y * a22 + dy); } }