/*- * #%L * Fiji's plugin for colocalization analysis. * %% * Copyright (C) 2009 - 2017 Fiji developers. * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program. If not, see * <http://www.gnu.org/licenses/gpl-3.0.html>. * #L% */ package sc.fiji.coloc.gadgets; import java.util.Arrays; import net.imglib2.Cursor; import net.imglib2.RandomAccess; import net.imglib2.RandomAccessibleInterval; import net.imglib2.img.ImgFactory; import net.imglib2.img.array.ArrayImgFactory; import net.imglib2.type.logic.BitType; import net.imglib2.type.numeric.RealType; import net.imglib2.view.Views; import sc.fiji.coloc.algorithms.MissingPreconditionException; public class MaskFactory { public enum CombinationMode { AND, OR, NONE } /** * Create a new mask image without any specific content, but with * a defined size. */ public static RandomAccessibleInterval<BitType> createMask(long[] dim) { ImgFactory< BitType > imgFactory = new ArrayImgFactory< BitType >(); return imgFactory.create(dim, new BitType()); } /** * Create a new mask image with a defined size and preset content. */ public static RandomAccessibleInterval<BitType> createMask(long[] dim, boolean val) { RandomAccessibleInterval<BitType> mask = createMask(dim); for (BitType t : Views.iterable(mask)) t.set(val); return mask; } /** * Create a new mask image with a defined size and preset content. * @throws MissingPreconditionException */ public static RandomAccessibleInterval<BitType> createMask(long[] dim, long[] roiOffset, long[] roiDim) throws MissingPreconditionException { if (dim.length != roiOffset.length || dim.length != roiDim.length) { throw new MissingPreconditionException("The dimensions of the mask as well as the ROIs and his offset must be the same."); } final RandomAccessibleInterval<BitType> mask = createMask(dim); final int dims = mask.numDimensions(); final long[] pos = new long[dims]; // create an array with the max corner of the ROI final long[] roiOffsetMax = new long[dims]; for (int i=0; i<dims; ++i) roiOffsetMax[i] = roiOffset[i] + roiDim[i]; // go through the mask and mask points as valid that are in the ROI Cursor<BitType> cursor = Views.iterable(mask).localizingCursor(); while ( cursor.hasNext() ) { cursor.fwd(); cursor.localize(pos); boolean valid = true; // test if the current position is contained in the ROI for(int i=0; i<dims; ++i) valid &= pos[i] >= roiOffset[i] && pos[i] < roiOffsetMax[i]; cursor.get().set(valid); } return mask; } /** * Create a new mask based on a threshold condition for two images. */ public static<T extends RealType< T >> RandomAccessibleInterval<BitType> createMask( RandomAccessibleInterval<T> ch1, RandomAccessibleInterval<T> ch2, T threshold1, T threshold2, ThresholdMode tMode, CombinationMode cMode) { final long[] dims = new long[ ch1.numDimensions() ]; ch1.dimensions(dims); RandomAccessibleInterval<BitType> mask = createMask(dims); Cursor<T> cursor1 = Views.iterable(ch1).cursor(); Cursor<T> cursor2 = Views.iterable(ch2).cursor(); Cursor<BitType> maskCursor = Views.iterable(mask).cursor(); while (cursor1.hasNext() && cursor2.hasNext() && maskCursor.hasNext()) { cursor1.fwd(); cursor2.fwd(); maskCursor.fwd(); boolean ch1Valid, ch2Valid; T data1 = cursor1.get(); T data2 = cursor2.get(); // get relation to threshold if (tMode == ThresholdMode.Above) { ch1Valid = data1.compareTo(threshold1) > 0; ch2Valid = data2.compareTo(threshold2) > 0; } else if (tMode == ThresholdMode.Below) { ch1Valid = data1.compareTo(threshold1) < 0; ch2Valid = data2.compareTo(threshold2) < 0; } else { throw new UnsupportedOperationException(); } BitType maskData = maskCursor.get(); // combine the results into mask if (cMode == CombinationMode.AND) { maskData.set( ch1Valid && ch2Valid ); } else if (cMode == CombinationMode.OR) { maskData.set( ch1Valid || ch2Valid ); } else if (cMode == CombinationMode.NONE) { maskData.set( !(ch1Valid || ch2Valid) ); } else { throw new UnsupportedOperationException(); } } return mask; } /** * Creates a new mask of the given dimensions, based on the image data * in the passed image. If the requested dimensionality is higher than * what is available in the data, the data gets repeated in the higher * dimensions. * * @param dim The dimensions of the new mask image * @param origMask The image from which the mask should be created from */ public static<T extends RealType< T >> RandomAccessibleInterval<BitType> createMask( final long[] dim, final RandomAccessibleInterval<T> origMask) { final RandomAccessibleInterval<BitType> mask = createMask(dim); final long[] origDim = new long[ origMask.numDimensions() ]; origMask.dimensions(origDim); // test if original mask and new mask have same dimensions if (Arrays.equals(dim, origDim)) { // copy the input image to the mask output image Cursor<T> origCursor = Views.iterable(origMask).localizingCursor(); RandomAccess<BitType> maskCursor = mask.randomAccess(); while (origCursor.hasNext()) { origCursor.fwd(); maskCursor.setPosition(origCursor); boolean value = origCursor.get().getRealDouble() > 0.001; maskCursor.get().set(value); } } else if (dim.length > origDim.length) { // sanity check for (int i=0; i<origDim.length; i++) { if (origDim[i] != dim[i]) throw new UnsupportedOperationException("Masks with lower dimensionality than the image, " + " but a different extent are not yet supported."); } // mask and image have different dimensionality and maybe even a different extent Cursor<T> origCursor = Views.iterable(origMask).localizingCursor(); RandomAccess<BitType> maskCursor = mask.randomAccess(); final long[] pos = new long[ origMask.numDimensions() ]; // iterate over the original mask while (origCursor.hasNext()) { origCursor.fwd(); origCursor.localize(pos); boolean value = origCursor.get().getRealDouble() > 0.001; // set available (lower dimensional) position information for (int i=0; i<origDim.length; i++) // setPosition requires first the position and then the dimension maskCursor.setPosition(pos[i], i); // go through the missing dimensions and set the value for (int i=origDim.length; i<dim.length; i++) for (int j=0; j<dim[i]; j++) { // setPosition requires first the position and then the dimension maskCursor.setPosition(j, i); maskCursor.get().set(value); } } } else if (dim.length < origDim.length) { // mask has more dimensions than image throw new UnsupportedOperationException("Masks with more dimensions than the image are not supported, yet."); } else { // mask and image have a different extent, but are equal in dimensionality. Scale it? throw new UnsupportedOperationException("Masks with same dimensionality, but a different extent than the image are not supported, yet."); } return mask; } }