/* * Copyright 2010-2015 Institut Pasteur. * * This file is part of Icy. * * Icy 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. * * Icy 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 Icy. If not, see <http://www.gnu.org/licenses/>. */ package icy.roi; import icy.canvas.IcyCanvas; import icy.type.point.Point5D; import icy.type.rectangle.Rectangle3D; import icy.type.rectangle.Rectangle4D; import icy.type.rectangle.Rectangle5D; import java.util.ArrayList; import java.util.List; /** * 5D ROI base class. */ public abstract class ROI5D extends ROI { /** * @deprecated Use {@link ROI5D#getROI5DList(List)} instead. */ @Deprecated public static ArrayList<ROI5D> getROI5DList(ArrayList<ROI> rois) { final ArrayList<ROI5D> result = new ArrayList<ROI5D>(); for (ROI roi : rois) if (roi instanceof ROI5D) result.add((ROI5D) roi); return result; } /** * Return all 5D ROI from the ROI list */ public static List<ROI5D> getROI5DList(List<ROI> rois) { final List<ROI5D> result = new ArrayList<ROI5D>(); for (ROI roi : rois) if (roi instanceof ROI5D) result.add((ROI5D) roi); return result; } public ROI5D() { super(); } @Override public String getDefaultName() { return "ROI5D"; } @Override final public int getDimension() { return 5; } @Override public boolean isActiveFor(IcyCanvas canvas) { return true; } /** * Returns an integer {@link Rectangle5D} that completely encloses the <code>ROI</code>. Note * that there is no guarantee that the returned <code>Rectangle5D</code> is the smallest * bounding box that encloses the <code>ROI</code>, only that the <code>ROI</code> lies entirely * within the indicated <code>Rectangle5D</code>. The returned <code>Rectangle5D</code> might * also fail to completely enclose the <code>ROI</code> if the <code>ROI</code> overflows the * limited range of the integer data type. The <code>getBounds5D</code> method generally returns * a tighter bounding box due to its greater flexibility in representation. * * @return an integer <code>Rectangle5D</code> that completely encloses the <code>ROI</code>. */ public Rectangle5D.Integer getBounds() { return getBounds5D().toInteger(); } /** * Returns the integer ROI position which normally correspond to the <i>minimum</i> point of the * ROI bounds. * * @see #getBounds() */ public Point5D.Integer getPosition() { final Rectangle5D.Integer bounds = getBounds(); return new Point5D.Integer(bounds.x, bounds.y, bounds.z, bounds.t, bounds.c); } @Override public boolean canSetBounds() { // default return false; } @Override public void setBounds5D(Rectangle5D bounds) { // do nothing by default (not supported) } @Override public boolean canSetPosition() { // default implementation use translation if available return canTranslate(); } @Override public void setPosition5D(Point5D position) { // use translation operation by default if supported if (canTranslate()) { final Point5D oldPos = getPosition5D(); translate(position.getX() - oldPos.getX(), position.getY() - oldPos.getY(), position.getZ() - oldPos.getZ(), position.getT() - oldPos.getT(), position.getC() - oldPos.getC()); } } /** * Returns <code>true</code> if the ROI support translate operation. * * @see #translate(double, double, double, double, double) */ public boolean canTranslate() { // by default return false; } /** * Translate the ROI position by the specified delta X/Y/Z/T.<br> * Note that not all ROI support this operation so you should test it by calling {@link #canTranslate()} first. * * @param dx * translation value to apply on X dimension * @param dy * translation value to apply on Y dimension * @param dz * translation value to apply on Z dimension * @param dt * translation value to apply on T dimension * @param dc * translation value to apply on C dimension * @see #canTranslate() * @see #setPosition5D(Point5D) */ public void translate(double dx, double dy, double dz, double dt, double dc) { } /* * Generic implementation using the BooleanMask which is not accurate and slow. * Override this for specific ROI type. */ @Override public boolean contains(ROI roi) { if (roi instanceof ROI5D) { final ROI5D roi5d = (ROI5D) roi; // special case of ROI Point if (roi5d.isEmpty()) return contains(roi5d.getPosition5D()); BooleanMask5D mask; BooleanMask5D roiMask; // take content first mask = getBooleanMask(false); roiMask = roi5d.getBooleanMask(false); // test first only on content if (!mask.contains(roiMask)) return false; // take content and edge mask = getBooleanMask(true); roiMask = roi5d.getBooleanMask(true); // then test on content and edge if (!mask.contains(roiMask)) return false; // contained return true; } // use default implementation return super.contains(roi); } /* * Generic implementation using the BooleanMask which is not accurate and slow. * Override this for specific ROI type. */ @Override public boolean intersects(ROI roi) { if (roi instanceof ROI5D) return getBooleanMask(true).intersects(((ROI5D) roi).getBooleanMask(true)); // use default implementation return super.intersects(roi); } @Override public BooleanMask2D getBooleanMask2D(int z, int t, int c, boolean inclusive) { final BooleanMask2D result = super.getBooleanMask2D(z, t, c, inclusive); // optimized bounds to optimize memory usage for this specific Z, T, C slice mask result.optimizeBounds(); return result; } /** * Returns the {@link BooleanMask3D} object representing the XYZ volume content at specified Z, * T, C position.<br> * It contains the 3D rectangle mask bounds and the associated boolean array mask.<br> * * @param z * Z position we want to retrieve the boolean mask or -1 to retrieve the whole Z * dimension * @param t * T position we want to retrieve the boolean mask. * @param c * C position we want to retrieve the boolean mask. * @param inclusive * If true then all partially contained (intersected) pixels are included in the mask. */ public BooleanMask3D getBooleanMask3D(int z, int t, int c, boolean inclusive) { // whole Z dimension if (z == -1) return getBooleanMask3D(t, c, inclusive); // define bounds final Rectangle3D.Integer bounds = getBounds5D().toRectangle3D().toInteger(); bounds.setZ(z); bounds.setSizeZ(1); return new BooleanMask3D(bounds, new BooleanMask2D[] {getBooleanMask2D(z, t, c, inclusive)}); } /** * Returns the {@link BooleanMask3D} object representing the XYZ volume content at specified T, * C position.<br> * It contains the 3D rectangle mask bounds and the associated boolean array mask.<br> * * @param inclusive * If true then all partially contained (intersected) pixels are included in the mask. */ public BooleanMask3D getBooleanMask3D(int t, int c, boolean inclusive) { final Rectangle3D.Integer bounds = getBounds5D().toRectangle3D().toInteger(); final BooleanMask2D masks[] = new BooleanMask2D[bounds.sizeZ]; for (int z = 0; z < masks.length; z++) masks[z] = getBooleanMask2D(bounds.z + z, t, c, inclusive); return new BooleanMask3D(bounds, masks); } /** * Returns the {@link BooleanMask4D} object representing the XYZT space content at specified Z * and C position. * * @param z * Z position we want to retrieve the boolean mask or -1 to retrieve the whole Z * dimension * @param t * T position we want to retrieve the boolean mask or -1 to retrieve the whole T * dimension * @param c * C position we want to retrieve the boolean mask. * @param inclusive * If true then all partially contained (intersected) pixels are included in the mask. */ public BooleanMask4D getBooleanMask4D(int z, int t, int c, boolean inclusive) { // whole Z dimension if (z == -1) { // whole Z and T dimension if (t == -1) return getBooleanMask4D(c, inclusive); // define bounds final Rectangle4D.Integer bounds = getBounds5D().toRectangle4D().toInteger(); bounds.setT(t); bounds.setSizeT(1); // whole Z dimension but specific T return new BooleanMask4D(bounds, new BooleanMask3D[] {getBooleanMask3D(t, c, inclusive)}); } final Rectangle4D.Integer bounds4d = getBounds5D().toRectangle4D().toInteger(); // specific Z bounds4d.setZ(z); bounds4d.setSizeZ(1); // specific T dimension ? if (t != -1) { bounds4d.setT(t); bounds4d.setSizeT(1); } final Rectangle3D.Integer bounds3d = (Rectangle3D.Integer) bounds4d.toRectangle3D(); final BooleanMask3D masks[] = new BooleanMask3D[bounds4d.sizeT]; for (int i = 0; i < bounds4d.sizeT; i++) masks[i] = new BooleanMask3D((Rectangle3D.Integer) bounds3d.clone(), new BooleanMask2D[] {getBooleanMask2D( z, bounds4d.t + i, c, inclusive)}); return new BooleanMask4D(bounds4d, masks); } /** * Get the {@link BooleanMask4D} object representing the roi for specified C position.<br> * It contains the 4D rectangle mask bounds and the associated boolean array mask.<br> * * @param inclusive * If true then all partially contained (intersected) pixels are included in the mask. */ public BooleanMask4D getBooleanMask4D(int c, boolean inclusive) { final Rectangle4D.Integer bounds = getBounds5D().toRectangle4D().toInteger(); final BooleanMask3D masks[] = new BooleanMask3D[bounds.sizeT]; for (int t = 0; t < masks.length; t++) masks[t] = getBooleanMask3D(bounds.t + t, c, inclusive); return new BooleanMask4D(bounds, masks); } /** * Returns the {@link BooleanMask5D} object representing the XYZTC space content at specified Z, * T, C position. * * @param z * Z position we want to retrieve the boolean mask or -1 to retrieve the whole Z * dimension * @param t * T position we want to retrieve the boolean mask or -1 to retrieve the whole T * dimension * @param c * C position we want to retrieve the boolean mask or -1 to retrieve the whole C * dimension * @param inclusive * If true then all partially contained (intersected) pixels are included in the mask. */ public BooleanMask5D getBooleanMask5D(int z, int t, int c, boolean inclusive) { // whole Z dimension if (z == -1) { // whole Z and T dimension if (t == -1) { // whole Z, T and C dimension if (c == -1) return getBooleanMask(inclusive); // define bounds final Rectangle5D.Integer bounds = getBounds5D().toInteger(); bounds.setC(c); bounds.setSizeC(1); // whole Z and T dimension but specific C return new BooleanMask5D(bounds, new BooleanMask4D[] {getBooleanMask4D(c, inclusive)}); } final Rectangle5D.Integer bounds5d = getBounds(); // specific T bounds5d.setT(t); bounds5d.setSizeT(1); // specific C dimension ? if (c != -1) { bounds5d.setC(c); bounds5d.setSizeC(1); } final Rectangle4D.Integer bounds4d = (Rectangle4D.Integer) bounds5d.toRectangle4D(); final BooleanMask4D masks[] = new BooleanMask4D[bounds5d.sizeC]; for (int i = 0; i < bounds5d.sizeC; i++) masks[i] = new BooleanMask4D((Rectangle4D.Integer) bounds4d.clone(), new BooleanMask3D[] {getBooleanMask3D(t, bounds5d.c + i, inclusive)}); return new BooleanMask5D(bounds5d, masks); } final Rectangle5D.Integer bounds5d = getBounds(); // specific Z bounds5d.setZ(z); bounds5d.setSizeZ(1); // specific T dimension ? if (t != -1) { bounds5d.setT(t); bounds5d.setSizeT(1); } // specific C dimension ? if (c != -1) { bounds5d.setC(c); bounds5d.setSizeC(1); } final Rectangle4D.Integer bounds4d = (Rectangle4D.Integer) bounds5d.toRectangle4D(); final Rectangle3D.Integer bounds3d = (Rectangle3D.Integer) bounds4d.toRectangle3D(); final BooleanMask4D masks[] = new BooleanMask4D[bounds5d.sizeC]; for (int i = 0; i < bounds5d.sizeC; i++) { final BooleanMask3D masks3d[] = new BooleanMask3D[bounds4d.sizeT]; for (int j = 0; j < bounds5d.sizeT; j++) masks3d[i] = new BooleanMask3D((Rectangle3D.Integer) bounds3d.clone(), new BooleanMask2D[] {getBooleanMask2D(z, bounds5d.t + j, bounds5d.c + i, inclusive)}); masks[i] = new BooleanMask4D((Rectangle4D.Integer) bounds4d.clone(), masks3d); } return new BooleanMask5D(bounds5d, masks); } /** * Get the {@link BooleanMask5D} object representing the roi.<br> * It contains the 5D rectangle mask bounds and the associated boolean array mask.<br> * * @param inclusive * If true then all partially contained (intersected) pixels are included in the mask. */ public BooleanMask5D getBooleanMask(boolean inclusive) { final Rectangle5D.Integer bounds = getBounds(); final BooleanMask4D masks[] = new BooleanMask4D[bounds.sizeC]; for (int c = 0; c < masks.length; c++) masks[c] = getBooleanMask4D(bounds.c + c, inclusive); return new BooleanMask5D(bounds, masks); } /* * Generic implementation for ROI5D using the BooleanMask object so * the result is just an approximation. * Override to optimize for specific ROI. */ @Override public double computeNumberOfContourPoints() { // approximation by using number of point of the edge of boolean mask return getBooleanMask(true).getContourPointsAsIntArray().length / getDimension(); } /* * Generic implementation for ROI5D using the BooleanMask object so * the result is just an approximation. * Override to optimize for specific ROI. */ @Override public double computeNumberOfPoints() { double numPoints = 0; // approximation by using number of point of boolean mask with and without border numPoints += getBooleanMask(true).getNumberOfPoints(); numPoints += getBooleanMask(false).getNumberOfPoints(); numPoints /= 2d; return numPoints; } }