/*- * Copyright 2016 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 uk.ac.diamond.scisoft.analysis.processing.operations.mask; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import org.eclipse.dawnsci.analysis.api.metadata.IDiffractionMetadata; import org.eclipse.dawnsci.analysis.api.processing.Atomic; import org.eclipse.dawnsci.analysis.api.processing.OperationData; import org.eclipse.dawnsci.analysis.api.processing.OperationException; import org.eclipse.dawnsci.analysis.api.processing.OperationRank; import org.eclipse.dawnsci.analysis.api.processing.model.AbstractOperationModel; import org.eclipse.dawnsci.analysis.dataset.operations.AbstractOperation; import org.eclipse.january.IMonitor; import org.eclipse.january.MetadataException; import org.eclipse.january.dataset.BooleanDataset; import org.eclipse.january.dataset.Comparisons; import org.eclipse.january.dataset.Dataset; import org.eclipse.january.dataset.DatasetFactory; import org.eclipse.january.dataset.DatasetUtils; import org.eclipse.january.dataset.DoubleDataset; import org.eclipse.january.dataset.IDataset; import org.eclipse.january.metadata.MaskMetadata; import org.eclipse.january.metadata.MetadataFactory; import uk.ac.diamond.scisoft.analysis.dataset.function.MakeMask; import uk.ac.diamond.scisoft.analysis.diffraction.powder.PixelIntegrationUtils; import uk.ac.diamond.scisoft.analysis.io.DiffractionMetadata; /** * Mask a 2 dimensional Dataset based on the coordinates of the points */ @Atomic public class CoordinateMaskOperation extends AbstractOperation<CoordinateMaskModel, OperationData> { private volatile Dataset mask = null; private PropertyChangeListener listener; private IDiffractionMetadata metadata; @Override public String getId() { return "uk.ac.diamond.scisoft.analysis.processing.operations.CoordinateMaskOperation"; } @Override public OperationRank getInputRank() { return OperationRank.TWO; } @Override public OperationRank getOutputRank() { return OperationRank.TWO; } @Override protected OperationData process(IDataset input, IMonitor monitor) throws OperationException { if (model.getCoordinateRange() == null) return new OperationData(input); DiffractionMetadata meta = input.getFirstMetadata(DiffractionMetadata.class); if (meta == null && !model.getCoordinateType().equals(MaskAxis.PIXEL)) throw new OperationException(this, "Does not contain calibration information!"); if (metadata == null) { metadata = meta; mask = null; } else { boolean dee = metadata.getDiffractionCrystalEnvironment().equals(meta.getDiffractionCrystalEnvironment()); boolean dpe = metadata.getDetector2DProperties().equals(meta.getDetector2DProperties()); if (!dpe || !dee) { metadata = meta; mask = null; } } Dataset coordinateMask = getMask(input); // Get the input mask, and combine the two masks IDataset inputMask = DatasetUtils.convertToDataset(getFirstMask(input)); if (inputMask == null) { inputMask = coordinateMask; } else { inputMask = Comparisons.logicalAnd(inputMask, coordinateMask); } MaskMetadata maskMetadata; try { maskMetadata = MetadataFactory.createMetadata(MaskMetadata.class, inputMask); } catch (MetadataException e) { throw new OperationException(this, e); } input.setMetadata(maskMetadata); return new OperationData(input); } private Dataset getMask(IDataset input) { Dataset lmask = mask; if (lmask == null) { synchronized(this) { lmask = mask; if (lmask == null) { // Assume q MaskAxis theAxis = model.getCoordinateType(); double coordinateRange[] = model.getCoordinateRange(); //TODO: get data from an external data source, rather than the // diffraction calibration, if not present DiffractionMetadata diffractionMD = input.getFirstMetadata(DiffractionMetadata.class); // Generate the Dataset of the chosen coordinate Dataset coordinateArray; if (diffractionMD != null) { switch (theAxis) { case Q: coordinateArray = PixelIntegrationUtils.generateQArray(input.getShape(),diffractionMD); break; case ANGLE: // In degrees coordinateArray = PixelIntegrationUtils.generate2ThetaArrayRadians(input.getShape(),diffractionMD); coordinateArray.imultiply(180/Math.PI); break; case AZIMUTHAL_ANGLE: coordinateArray = PixelIntegrationUtils.generateAzimuthalArray(input.getShape(), diffractionMD, false); break; case PIXEL: double[] beamCentre = diffractionMD.getOriginalDetector2DProperties().getBeamCentreCoords(); coordinateArray = DatasetFactory.zeros(DoubleDataset.class, input.getShape()); for (int i = 0; i < input.getShape()[0]; i++) for (int j = 0; j < input.getShape()[1]; j++) // The elements of getBeamCentreCoords are transposed from those of getShape coordinateArray.set(Math.sqrt(square(i-beamCentre[1]) + square(j-beamCentre[0])), i, j); break; default: throw new OperationException(this, "This coordinate is not yet supported"); } } else { switch (theAxis) { case PIXEL: double[] beamCentre = new double[]{input.getShape()[0]*0.5, input.getShape()[1]*0.5}; coordinateArray = DatasetFactory.zeros(DoubleDataset.class, input.getShape()); for (int i = 0; i < input.getShape()[0]; i++) for (int j = 0; j < input.getShape()[1]; j++) coordinateArray.set(Math.sqrt(square(i-beamCentre[0]) + square(j-beamCentre[1])), i, j); break; case ANGLE: case AZIMUTHAL_ANGLE: case Q: default: throw new OperationException(this, "Dataset-based masking not yet supported"); } } MakeMask maskMaker = new MakeMask(coordinateRange[0], coordinateRange[1]); // MakeMask makes BooleanDatasets BooleanDataset coordinateMask = (BooleanDataset) maskMaker.value(coordinateArray).get(0); // Invert if required if (model.isMaskedInside()) coordinateMask.isubtract(true); mask = lmask = coordinateMask; } } } return lmask; } @Override public void setModel(CoordinateMaskModel model) { super.setModel(model); if (listener == null) { listener = new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { mask = null; } }; } else { ((AbstractOperationModel)this.model).removePropertyChangeListener(listener); } ((AbstractOperationModel)this.model).addPropertyChangeListener(listener); } public enum MaskAxis { ANGLE, PIXEL, Q, AZIMUTHAL_ANGLE } private double square(double x) { return x*x; } }