org.dawnsci.conversion.ui.pages.AlignImagesConversionPage.java Source code

Java tutorial

Introduction

Here is the source code for org.dawnsci.conversion.ui.pages.AlignImagesConversionPage.java

Source

/*-
 * Copyright (c) 2015 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.conversion.ui.pages;

import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.dawb.common.ui.alignment.AlignProgressJob;
import org.dawb.common.ui.wizard.ResourceChoosePage;
import org.dawb.common.util.io.FileUtils;
import org.dawnsci.conversion.converters.AlignImagesConverter.ConversionAlignBean;
import org.dawnsci.conversion.ui.IConversionWizardPage;
import org.dawnsci.conversion.ui.LoaderServiceHolder;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.dawnsci.analysis.api.conversion.IConversionContext;
import org.eclipse.dawnsci.analysis.api.dataset.IDataset;
import org.eclipse.dawnsci.analysis.api.dataset.ILazyDataset;
import org.eclipse.dawnsci.analysis.api.dataset.Slice;
import org.eclipse.dawnsci.analysis.api.io.IDataHolder;
import org.eclipse.dawnsci.analysis.api.monitor.IMonitor;
import org.eclipse.dawnsci.analysis.dataset.roi.PerimeterBoxROI;
import org.eclipse.dawnsci.analysis.dataset.roi.RectangularROI;
import org.eclipse.dawnsci.plotting.api.IPlottingSystem;
import org.eclipse.dawnsci.plotting.api.PlotType;
import org.eclipse.dawnsci.plotting.api.PlottingFactory;
import org.eclipse.dawnsci.plotting.api.region.IRegion;
import org.eclipse.dawnsci.plotting.api.region.IRegion.RegionType;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Scale;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import uk.ac.diamond.scisoft.analysis.image.AlignMethod;
import uk.ac.diamond.scisoft.analysis.io.Utils;

/**
 * 
 * 
 * @author wqk87977
 *
 */
public class AlignImagesConversionPage extends ResourceChoosePage implements IConversionWizardPage {

    private static final Logger logger = LoggerFactory.getLogger(AlignImagesConversionPage.class);

    private IConversionContext context;
    private IPlottingSystem<Composite> plotSystem;
    private Combo alignMethodCombo;

    private IDataset firstImage;
    private List<IDataset> data;
    private List<IDataset> aligned;

    private AlignMethod alignState = AlignMethod.WITH_ROI;
    private static final String ROI_NAME = "align ROI";

    private AlignProgressJob alignProgressJob;
    private Button align;

    private List<Button> radioButtons;
    private int mode = 4; // Should be 2 or four

    private Scale scaleProgress;
    private int currentPosition;
    private List<Button> sliderButtons;
    private boolean showCorrected = false;

    private Job loadDataJob;

    public AlignImagesConversionPage() {
        super("Convert image directory", null, null);
        setDirectory(true);
        setFileLabel("Aligned images output folder");
        setNewFile(true);
        setOverwriteVisible(true);
        setPathEditable(true);
        setDescription("Returns an aligned stack of images given a stack of images");
    }

    @Override
    public IConversionContext getContext() {
        if (context == null)
            return null;
        context.setOutputPath(getAbsoluteFilePath());
        final File dir = new File(getSourcePath(context)).getParentFile();
        context.setWorkSize(dir.list().length);

        final ConversionAlignBean bean = new ConversionAlignBean();

        String[] filePaths = getSelectedPaths();
        if (data == null)
            data = loadData(filePaths);
        if (filePaths.length > 1) // if a list of image files has been selected or a directory
            context.setDatasetNames(filePaths);
        else { // if a single file (a nexus stack for example)
            List<String> names = new ArrayList<String>(data.size());
            for (int i = 0; i < data.size(); i++) {
                names.add("image_" + i);
            }
            context.setDatasetNames(names);
        }

        bean.setAligned(aligned);

        context.setUserObject(bean);

        return context;
    }

    private List<IDataset> loadData(final String[] filePaths) {
        final List<IDataset> data = new ArrayList<IDataset>();
        if (loadDataJob == null) {
            loadDataJob = new Job("Loading image stack") {
                @Override
                protected IStatus run(IProgressMonitor monitor) {
                    try {
                        Utils.loadData(data, filePaths);
                    } catch (Exception e) {
                        logger.error("Failed to load dataset:" + e);
                        return Status.CANCEL_STATUS;
                    }
                    return Status.OK_STATUS;
                }
            };
            loadDataJob.addJobChangeListener(new JobChangeAdapter() {
                @Override
                public void done(IJobChangeEvent event) {
                    Display.getDefault().syncExec(new Runnable() {
                        @Override
                        public void run() {
                            if (scaleProgress != null) {
                                scaleProgress.setMaximum(data.size());
                                scaleProgress.redraw();
                            }
                        }
                    });
                }
            });
        }
        loadDataJob.schedule();
        return data;
    }

    @Override
    protected void createContentAfterFileChoose(Composite container) {
        Composite controlComp = new Composite(container, SWT.NONE);
        controlComp.setLayout(new GridLayout(2, false));
        controlComp.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false, 4, 1));

        // Check type of data and load first image
        if (getSelectedPaths() == null)
            return;
        String filePath = getSelectedPaths()[0];
        try {
            IDataHolder holder = LoaderServiceHolder.getLoaderService().getData(filePath, new IMonitor.Stub());
            ILazyDataset lazy = holder.getLazyDataset(0);
            int[] shape = lazy.getShape();
            if (lazy.getRank() == 2)
                firstImage = lazy.getSlice(new Slice());
            else
                firstImage = lazy.getSlice(new Slice(0, shape[0], shape[1])).squeeze();
        } catch (Exception e) {
            logger.error("Error loading file:" + e.getMessage());
        }
        Label methodLabel = new Label(controlComp, SWT.NONE);
        methodLabel.setText("Align method:");
        alignMethodCombo = new Combo(controlComp, SWT.READ_ONLY);
        alignMethodCombo.setItems(new String[] { "With ROI", "Affine transform" });
        alignMethodCombo.setToolTipText(
                "Choose the method of alignement: with a region of interest or without using an affine transformation");
        alignMethodCombo.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent event) {
                alignState = AlignMethod.getAlignMethod(alignMethodCombo.getSelectionIndex());
                IRegion region = getRegion(ROI_NAME);
                if (region != null) {
                    boolean withROI = alignState == AlignMethod.WITH_ROI;
                    region.setVisible(withROI);
                    radioButtons.get(0).setEnabled(withROI);
                    radioButtons.get(1).setEnabled(withROI);
                }
            }
        });
        // set default selection
        alignMethodCombo.select(AlignMethod.WITH_ROI.getIdx());

        Label modeLabel = new Label(controlComp, SWT.NONE);
        modeLabel.setText("Mode:");
        final Composite modeComp = new Composite(controlComp, SWT.NONE);
        modeComp.setLayout(new RowLayout());
        modeComp.setToolTipText("Number of columns used for image alignment with ROI");
        Button b;
        radioButtons = new ArrayList<Button>();
        b = new Button(modeComp, SWT.RADIO);
        b.setText("2");
        b.setToolTipText("Use 2 columns to implement image alignment with ROI");
        b.addSelectionListener(radioListener);
        radioButtons.add(b);
        b = new Button(modeComp, SWT.RADIO);
        b.setText("4");
        b.setToolTipText("Use 4 columns to implement image alignment with ROI");
        b.addSelectionListener(radioListener);
        radioButtons.add(b);
        b.setSelection(true);

        align = new Button(controlComp, SWT.NONE);
        align.setText("Align");
        align.setToolTipText("Run the alignment calculation with the corresponding alignment type chosen");
        GridData gd = new GridData(SWT.CENTER, SWT.CENTER, true, false, 2, 1);
        align.setLayoutData(gd);
        align.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                if (alignState == AlignMethod.WITH_ROI) {
                    // if there is a ROI on the plotting system we can perform an align calculation
                    IRegion region = getRegion(ROI_NAME);
                    if (region != null && region.getROI() instanceof RectangularROI) {
                        align((RectangularROI) region.getROI());
                    } else {
                        String[] dialogButtonLabel = { "OK" };
                        Image warning = new Image(Display.getCurrent(),
                                getClass().getResourceAsStream("/icons/warning_small.gif"));
                        MessageDialog messageDialog = new MessageDialog(Display.getCurrent().getActiveShell(),
                                "Error running image alignment", warning, "", MessageDialog.WARNING,
                                dialogButtonLabel, 0);
                        messageDialog.open();
                    }
                } else if (alignState == AlignMethod.AFFINE_TRANSFORM) {
                    align(null);
                }
                setPageComplete(true);
                getWizard().getContainer().updateButtons();
            }
        });

        Group sliderGroup = new Group(container, SWT.NONE);
        sliderGroup.setText("Image stack slicing");
        sliderGroup.setToolTipText(
                "Use the slider to browse through the original " + "loaded images or through the corrected images");
        sliderGroup.setLayout(new GridLayout(1, false));
        sliderGroup.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false, 4, 1));

        sliderButtons = new ArrayList<Button>();
        b = new Button(sliderGroup, SWT.RADIO);
        b.setText("Original Data");
        b.setToolTipText("Show original stack of images");
        b.addSelectionListener(sliderListener);
        sliderButtons.add(b);
        b.setSelection(true);
        b = new Button(sliderGroup, SWT.RADIO);
        b.setText("Aligned Data");
        b.setToolTipText("Show aligned stack of images");
        b.addSelectionListener(sliderListener);
        sliderButtons.add(b);
        sliderButtons.get(1).setEnabled(false);

        scaleProgress = new Scale(sliderGroup, SWT.HORIZONTAL);
        scaleProgress.setPageIncrement(1);
        scaleProgress.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                int p = scaleProgress.getSelection();
                if (p != currentPosition) {
                    currentPosition = p;
                    if (showCorrected) {
                        if (aligned != null && aligned.size() > 0 && p < aligned.size()) {
                            plotSystem.updatePlot2D(aligned.get(p), null, null);
                        }
                    } else {
                        if (data != null && data.size() > 0 && p < data.size())
                            plotSystem.updatePlot2D(data.get(p), null, null);
                    }
                }
            }
        });
        scaleProgress.setLayoutData(new GridData(SWT.FILL, SWT.LEFT, true, false));
        if (data != null) {
            scaleProgress.setMaximum(data.size());
            scaleProgress.redraw();
        }

        Composite plotComp = new Composite(container, SWT.NONE);
        plotComp.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 4, 1));
        plotComp.setLayout(new GridLayout(1, false));

        Composite subComp = new Composite(plotComp, SWT.NONE);
        subComp.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
        subComp.setLayout(new GridLayout(2, false));

        Label description = new Label(subComp, SWT.WRAP);
        description.setText("Press 'Align' to register the stack of images loaded then "
                + "press 'Finish' to save all aligned images in the output folder of your choice.");
        description.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

        try {
            plotSystem = PlottingFactory.createPlottingSystem();
            plotSystem.createPlotPart(plotComp, "Preprocess", null, PlotType.IMAGE, null);
            plotSystem.getPlotComposite().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
            if (firstImage != null && firstImage.getRank() == 2)
                plotSystem.createPlot2D(firstImage, null, null);
            plotSystem.setKeepAspect(true);
            createRegion(firstImage, ROI_NAME);
        } catch (Exception e) {
            logger.error("Error creating the plotting system:" + e.getMessage());
            e.printStackTrace();
        }
    }

    private SelectionListener radioListener = new SelectionAdapter() {
        @Override
        public void widgetSelected(SelectionEvent e) {
            Button btn = (Button) e.widget;
            if (!btn.getSelection()) {
                btn.setSelection(false);
            } else {
                mode = radioButtons.indexOf(btn) == 0 ? 2 : 4;
            }
        }
    };

    private SelectionListener sliderListener = new SelectionAdapter() {
        @Override
        public void widgetSelected(SelectionEvent e) {
            Button btn = (Button) e.widget;
            if (!btn.getSelection()) {
                btn.setSelection(false);
            } else {
                boolean showOriginal = sliderButtons.indexOf(btn) == 0;
                if (showOriginal && data != null && data.size() > 0) {
                    plotSystem.updatePlot2D(data.get(currentPosition), null, null);
                    showCorrected = false;
                } else if (!showOriginal && aligned != null && aligned.size() > 0) {
                    plotSystem.updatePlot2D(aligned.get(currentPosition), null, null);
                    showCorrected = true;
                } else if (!showOriginal && aligned == null) {
                    showCorrected = true;
                }
            }
        }
    };

    private void align(final RectangularROI roi) {
        if (alignProgressJob == null) {
            alignProgressJob = new AlignProgressJob();
        }
        alignProgressJob.setRectangularROI(roi);
        alignProgressJob.setMode(mode);
        alignProgressJob.setData(data);
        alignProgressJob.setAlignMethod(alignState);
        ProgressMonitorDialog alignProgress = new ProgressMonitorDialog(Display.getCurrent().getActiveShell());
        alignProgress.setCancelable(true);
        try {
            alignProgress.run(true, true, alignProgressJob);
        } catch (InvocationTargetException e1) {
            MessageDialog.openError(Display.getCurrent().getActiveShell(), "Alignment Error",
                    "An error occured during alignment: " + e1.getTargetException().getLocalizedMessage());
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }
        aligned = alignProgressJob.getShiftedImages();
        sliderButtons.get(1).setEnabled(aligned != null);
        sliderButtons.get(0).setSelection(false);
        sliderButtons.get(1).setSelection(true);
        showCorrected = true;
    }

    @Override
    public boolean isPageComplete() {
        return aligned != null;
    }

    private IRegion getRegion(String regionName) {
        Collection<IRegion> regions = plotSystem.getRegions();
        for (IRegion region : regions) {
            if (region.getName().equals(regionName))
                return region;
        }
        return null;
    }

    private void createRegion(IDataset data, String regionName) {
        try {
            Collection<IRegion> regions = plotSystem.getRegions();
            for (IRegion region : regions) {
                if (region.getName().equals(regionName))
                    return;
            }
            IRegion region = plotSystem.createRegion(regionName, RegionType.BOX);
            double width = plotSystem.getAxes().get(0).getUpper() / 2;
            double height = plotSystem.getAxes().get(1).getLower() / 2; // images by default have their ordinate up side down
            RectangularROI rroi = new PerimeterBoxROI(0, 0, width, height, 0);
            rroi.setName(regionName);
            region.setROI(rroi);
            plotSystem.addRegion(region);
        } catch (Exception e) {
            logger.error("Error creating Region Of Interest:", e);
        }
    }

    @Override
    public void setContext(IConversionContext context) {
        if (context != null && context.equals(this.context))
            return;

        this.context = context;
        setErrorMessage(null);
        if (context == null) { // new context being prepared.
            setPageComplete(false);
            return;
        }

        final File dir = new File(getSourcePath(context)).getParentFile();
        String uniqueDir = FileUtils.getUnique(dir, "Aligned_Images", null).getAbsolutePath();
        setPath(uniqueDir);

    }

    @Override
    public boolean isOpen() {
        return true;
    }

    public void pathChanged() {
        final String p = getAbsoluteFilePath();
        if (p == null || p.length() == 0) {
            setErrorMessage("Please select a folder to export to.");
            return;
        }
        final File path = new File(p);
        if (path.exists()) {
            if (overwrite != null && !overwrite.getSelection()) {
                setErrorMessage("The folder " + path.getName() + " already exists.");
                return;
            }

            if (!path.canWrite()) {
                setErrorMessage("Please choose another location to export to; this one is read only.");
                return;
            }
        }
        setErrorMessage(null);
    }
}