/* * ------------------------------------------------------------------------ * Copyright by KNIME GmbH, Konstanz, Germany * Website: http://www.knime.org; Email: contact@knime.org * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, Version 3, as * published by the Free Software Foundation. * * 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>. * * Additional permission under GNU GPL version 3 section 7: * * KNIME interoperates with ECLIPSE solely via ECLIPSE's plug-in APIs. * Hence, KNIME and ECLIPSE are both independent programs and are not * derived from each other. Should, however, the interpretation of the * GNU GPL Version 3 ("License") under any applicable laws result in * KNIME and ECLIPSE being a combined program, KNIME GMBH herewith grants * you the additional permission to use and propagate KNIME together with * ECLIPSE with only the license terms in place for ECLIPSE applying to * ECLIPSE and the GNU GPL Version 3 applying for KNIME, provided the * license terms of ECLIPSE themselves allow for the respective use and * propagation of ECLIPSE together with KNIME. * * Additional permission relating to nodes for KNIME that extend the Node * Extension (and in particular that are based on subclasses of NodeModel, * NodeDialog, and NodeView) and that only interoperate with KNIME through * standard APIs ("Nodes"): * Nodes are deemed to be separate and independent programs and to not be * covered works. Notwithstanding anything to the contrary in the * License, the License does not apply to Nodes, you are not required to * license Nodes under the License, and you are granted a license to * prepare and propagate Nodes, in each case even if such Nodes are * propagated with or for interoperation with KNIME. The owner of a Node * may freely choose the license terms applicable to such Node, including * when such Node is propagated with or for interoperation with KNIME. * ------------------------------------------------------------------- * * History * 11.05.2006 (gabriel): created */ package org.knime.knip.base.nodes.proc.binner; import org.knime.core.node.InvalidSettingsException; import org.knime.core.node.NodeSettingsRO; import org.knime.core.node.NodeSettingsWO; import net.imglib2.type.logic.BitType; import net.imglib2.type.numeric.RealType; import net.imglib2.type.numeric.integer.ByteType; import net.imglib2.type.numeric.integer.IntType; import net.imglib2.type.numeric.integer.LongType; import net.imglib2.type.numeric.integer.ShortType; import net.imglib2.type.numeric.integer.UnsignedByteType; import net.imglib2.type.numeric.integer.UnsignedIntType; import net.imglib2.type.numeric.integer.UnsignedShortType; import net.imglib2.type.numeric.real.FloatType; /** * Helper class the represents pixel intensity bins, stores them, restores them etc. * */ class IntensityBins { /** Key for left value interval. */ private static final String LEFT_VALUE = "left_value"; /** Key for right value interval. */ private static final String RIGHT_VALUE = "right_value"; /** Key for left open interval. */ private static final String LEFT_OPEN = "left_open"; /** Key for right open interval. */ private static final String RIGHT_OPEN = "right_open"; /** Key for the bin's name. */ private static final String BIN_VALUE = "bin_value"; private final double[] m_binValues; private final boolean[] m_leftOpen; private final double[] m_leftValues; private final boolean[] m_rightOpen; private final double[] m_rightValues; private final int m_numBins; /** * @param numBins number of bins */ public IntensityBins(final int numBins) { m_numBins = numBins; m_binValues = new double[numBins]; m_leftOpen = new boolean[numBins]; m_leftValues = new double[numBins]; m_rightOpen = new boolean[numBins]; m_rightValues = new double[numBins]; } /** * * @return the pixel type */ @SuppressWarnings({"unchecked", "rawtypes"}) public <T extends RealType<T>> T getPixelType() { double min = Double.MAX_VALUE; double max = Double.MIN_VALUE; boolean floatingPoint = false; for (int i = 0; i < m_numBins; i++) { min = Math.min(m_binValues[i], min); max = Math.max(m_binValues[i], max); if (m_binValues[i] != Math.round(m_binValues[i])) { floatingPoint = true; } } if (floatingPoint) { return (T)new FloatType(); } else { RealType[] types = new RealType[]{new BitType(), new UnsignedByteType(), new ByteType(), new UnsignedShortType(), new ShortType(), new UnsignedIntType(), new IntType(), new LongType(), new FloatType()}; for (RealType t : types) { if (min >= t.getMinValue() && max <= t.getMaxValue()) { return (T)t; } } } //should actually never happen throw new IllegalStateException( "No pixel type can be found that is able to represent one of the given bin values."); } /** * * @param index the index of the bin, must be smaller than the given number of bins in the constructor ({@link #IntensityBins(int)}! * @param binValue * @param leftOpen * @param leftValue * @param rightOpen * @param rightValue */ public void setBinAtIndex(final int index, final double binValue, final boolean leftOpen, final double leftValue, final boolean rightOpen, final double rightValue) { m_binValues[index] = binValue; m_leftOpen[index] = leftOpen; m_leftValues[index] = leftValue; m_rightOpen[index] = rightOpen; m_rightValues[index] = rightValue; } /** * @return the number of bins */ public int getNumBins() { return m_numBins; } public double getBinValue(final int index) { return m_binValues[index]; } public boolean isLeftOpen(final int index) { return m_leftOpen[index]; } public double getLeftValue(final int index) { return m_leftValues[index]; } public boolean isRightOpen(final int index) { return m_rightOpen[index]; } public double getRightValue(final int index) { return m_rightValues[index]; } /** * @param source the pixel to check * @param target the pixel to be set in case the source is covered (should be of the type as given by * {@link #getPixelType()} * @return <code>true</code>, if interval covers the given value */ public <T1 extends RealType<T1>, T2 extends RealType<T2>> boolean covers(final T1 source, final T2 target) { double value = source.getRealDouble(); for (int i = 0; i < m_numBins; i++) { double l = m_leftValues[i]; double r = m_rightValues[i]; assert (l <= r); if (l < value && value < r) { target.setReal(m_binValues[i]); return true; } if (l == value && !m_leftOpen[i]) { target.setReal(m_binValues[i]); return true; } if (r == value && !m_rightOpen[i]) { target.setReal(m_binValues[i]); return true; } } return false; } public void saveToSettings(final NodeSettingsWO bins) { bins.addInt("num_bins", m_numBins); for (int i = 0; i < m_numBins; i++) { bins.addDouble(BIN_VALUE + "_" + i, m_binValues[i]); bins.addBoolean(LEFT_OPEN + "_" + i, m_leftOpen[i]); bins.addDouble(LEFT_VALUE + "_" + i, m_leftValues[i]); bins.addBoolean(RIGHT_OPEN + "_" + i, m_rightOpen[i]); bins.addDouble(RIGHT_VALUE + "_" + i, m_rightValues[i]); } } /** * Create pixel bins from NodeSettings. * * @param bin read settings from * @throws InvalidSettingsException if bins could not be read */ public IntensityBins(final NodeSettingsRO bins) throws InvalidSettingsException { this(bins.getInt("num_bins")); for (int i = 0; i < m_numBins; i++) { m_binValues[i] = bins.getDouble(BIN_VALUE + "_" + i); m_leftOpen[i] = bins.getBoolean(LEFT_OPEN + "_" + i); m_leftValues[i] = bins.getDouble(LEFT_VALUE + "_" + i); m_rightOpen[i] = bins.getBoolean(RIGHT_OPEN + "_" + i); m_rightValues[i] = bins.getDouble(RIGHT_VALUE + "_" + i); } } }