/* * ------------------------------------------------------------------------ * * Copyright (C) 2003 - 2013 * University of Konstanz, Germany and * 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. * --------------------------------------------------------------------- * * */ package org.knime.knip.base.nodes.view.segmentoverlay; import java.io.File; import java.io.IOException; import java.util.Arrays; import net.imglib2.type.numeric.RealType; import org.knime.core.data.DataCell; import org.knime.core.data.DataRow; import org.knime.core.data.DataTableSpec; import org.knime.core.data.DataType; import org.knime.core.data.RowIterator; import org.knime.core.data.def.DefaultRow; import org.knime.core.node.BufferedDataContainer; import org.knime.core.node.BufferedDataTable; import org.knime.core.node.BufferedDataTableHolder; import org.knime.core.node.CanceledExecutionException; import org.knime.core.node.ExecutionContext; import org.knime.core.node.ExecutionMonitor; import org.knime.core.node.InvalidSettingsException; import org.knime.core.node.NodeLogger; import org.knime.core.node.NodeModel; import org.knime.core.node.NodeSettingsRO; import org.knime.core.node.NodeSettingsWO; import org.knime.core.node.defaultnodesettings.SettingsModelBoolean; import org.knime.core.node.defaultnodesettings.SettingsModelString; import org.knime.core.node.port.PortType; import org.knime.core.node.tableview.TableContentModel; import org.knime.knip.base.data.img.ImgPlusCell; import org.knime.knip.base.data.img.ImgPlusValue; import org.knime.knip.base.data.labeling.LabelingCell; import org.knime.knip.base.data.labeling.LabelingValue; import org.knime.knip.base.node.NodeUtils; import org.knime.knip.core.util.StringTransformer; /** * * @author <a href="mailto:dietzc85@googlemail.com">Christian Dietz</a> * @author <a href="mailto:horn_martin@gmx.de">Martin Horn</a> * @author <a href="mailto:michael.zinsmaier@googlemail.com">Michael Zinsmaier</a> */ public class SegmentOverlayNodeModel<T extends RealType<T>, L extends Comparable<L>> extends NodeModel implements BufferedDataTableHolder { private class ClearableTableContentModel extends TableContentModel { /** */ private static final long serialVersionUID = 1L; // make clearCache accessible to allow clearing before the // content model is thrown away // in order to free large image resources from the java swing // caching strategy public void clearCacheBeforeClosing() { clearCache(); } } enum LabelTransformVariables { ImgName, ImgSource, Label, LabelingName, LabelingSource, RowID; }; public static final String CFG_ADJUST_VIRTUALLY = "cfg_adjust_virtually"; public static final String CFG_EXPRESSION = "cfg_expression"; public static final String CFG_IMG_COL = "cfg_img_col"; public static final String CFG_LABELING_COL = "cfg_seg_col"; public static final String CFG_SRC_IMG_ID_COL = "cfg_src_img_id_col"; public static final int COL_IDX_IMAGE = 0; public static final int COL_IDX_LABELING = 1; public static final int COL_IDX_SINGLE_LABELING = 0; /* * Logging */ private static final NodeLogger LOGGER = NodeLogger.getLogger(SegmentOverlayNodeModel.class); public static int PORT_IMG = 0; public static int PORT_SEG = 1; private final SettingsModelBoolean m_adjustVirtually = new SettingsModelBoolean(CFG_ADJUST_VIRTUALLY, true); private ClearableTableContentModel m_contentModel; private final SettingsModelString m_expression = new SettingsModelString(CFG_EXPRESSION, "$" + LabelTransformVariables.Label + "$"); private final SettingsModelString m_imgCol = new SettingsModelString(CFG_IMG_COL, ""); private BufferedDataTable m_imgTable; private boolean m_isDataSetToModel; private final SettingsModelString m_labelingCol = new SettingsModelString(CFG_LABELING_COL, ""); private BufferedDataTable m_segTable; protected SegmentOverlayNodeModel() { super(new PortType[]{new PortType(BufferedDataTable.class), new PortType(BufferedDataTable.class, true)}, new PortType[0]); m_contentModel = new ClearableTableContentModel(); m_isDataSetToModel = false; } /** * {@inheritDoc} */ @Override protected DataTableSpec[] configure(final DataTableSpec[] inSpecs) throws InvalidSettingsException { if (m_labelingCol.getStringValue() != null) { NodeUtils.autoColumnSelection(inSpecs[0], m_labelingCol, LabelingValue.class, this.getClass()); } if (m_imgCol.getStringValue() == null || m_imgCol.getStringValue().length() == 0) { NodeUtils.autoOptionalColumnSelection(inSpecs[0], m_imgCol, ImgPlusValue.class); } else { NodeUtils.silentOptionalAutoColumnSelection(inSpecs[0], m_imgCol, ImgPlusValue.class); } return new DataTableSpec[0]; } /** * {@inheritDoc} */ @Override protected BufferedDataTable[] execute(final BufferedDataTable[] inData, final ExecutionContext exec) throws Exception { assert (inData != null); assert (inData.length >= 1); m_imgTable = inData[PORT_IMG]; assert (m_imgTable != null); m_isDataSetToModel = true; m_imgTable = inData[PORT_IMG]; m_segTable = inData[PORT_SEG]; int imgColIdx = -1; if (m_imgCol.getStringValue() == null || m_imgCol.getStringValue().length() == 0) { imgColIdx = NodeUtils.autoOptionalColumnSelection(inData[0].getDataTableSpec(), m_imgCol, ImgPlusValue.class); } else { imgColIdx = NodeUtils.silentOptionalAutoColumnSelection(inData[0].getDataTableSpec(), m_imgCol, ImgPlusValue.class); } int labelingColIdx = -1; if (m_labelingCol.getStringValue() != null) { labelingColIdx = NodeUtils.autoColumnSelection(inData[0].getDataTableSpec(), m_labelingCol, LabelingValue.class, this.getClass()); } DataTableSpec spec; if (imgColIdx != -1) { spec = new DataTableSpec(DataTableSpec.createColumnSpecs(new String[]{"Image", "Labeling"}, new DataType[]{ImgPlusCell.TYPE, LabelingCell.TYPE})); } else { spec = new DataTableSpec(DataTableSpec.createColumnSpecs(new String[]{"Labeling"}, new DataType[]{LabelingCell.TYPE})); } final BufferedDataContainer con = exec.createDataContainer(spec); final RowIterator imgIt = m_imgTable.iterator(); DataRow row; if (m_imgTable.getRowCount() == 0) { return new BufferedDataTable[0]; } int rowCount = 0; while (imgIt.hasNext()) { row = imgIt.next(); // load final DataCell labCell = row.getCell(labelingColIdx); final DataCell imgCell = imgColIdx != -1 ? row.getCell(imgColIdx) : null; // test for missing cells if (labCell.isMissing() || ((imgColIdx != -1) && imgCell.isMissing())) { LOGGER.warn("Missing cell was ignored at row " + row.getKey()); } else { // process if (imgColIdx != -1) { // check compatibility long[] labelingDims = ((LabelingValue<L>)labCell).getDimensions(); long[] imageDims = ((ImgPlusValue<T>)imgCell).getDimensions(); if (labelingDims.length != imageDims.length && !m_adjustVirtually.getBooleanValue()) { setWarningMessage("The number of dimensions of some labelings and images. Rows have been skipped!"); LOGGER.warn("The dimensions are not compatible in row " + row.getKey()); continue; } if (!Arrays.equals(labelingDims, imageDims) && !m_adjustVirtually.getBooleanValue()) { setWarningMessage("Some labelings and images do not have compatible sizes and have been skipped! Use the 'Virtually extend'-option to overcome this problem."); LOGGER.warn("The dimension sizes are not compatible in row " + row.getKey()); continue; } con.addRowToTable(new DefaultRow(row.getKey(), imgCell, labCell)); } else { con.addRowToTable(new DefaultRow(row.getKey(), labCell)); } } exec.checkCanceled(); exec.setProgress((double)rowCount++ / m_imgTable.getRowCount()); } con.close(); m_contentModel.setDataTable(con.getTable()); return new BufferedDataTable[0]; } /** * {@inheritDoc} */ @Override public BufferedDataTable[] getInternalTables() { if (m_segTable != null) { return new BufferedDataTable[]{(BufferedDataTable)m_contentModel.getDataTable(), m_segTable}; } else { return new BufferedDataTable[]{(BufferedDataTable)m_contentModel.getDataTable()}; } } public TableContentModel getTableContentModel() { // temporary workaround since setDataTable blocks if (!m_isDataSetToModel) { m_contentModel.setDataTable(m_imgTable); m_isDataSetToModel = true; } return m_contentModel; } public StringTransformer getTransformer() { return new StringTransformer(m_expression.getStringValue(), "$"); } public boolean isTransformationActive() { // is not active == "$Label$" || "" final String expression = m_expression.getStringValue().trim(); return !((expression.equalsIgnoreCase("$" + LabelTransformVariables.Label + "$")) || expression.isEmpty()); } /** * {@inheritDoc} */ @Override protected void loadInternals(final File nodeInternDir, final ExecutionMonitor exec) throws IOException, CanceledExecutionException { // } /** * {@inheritDoc} */ @Override protected void loadValidatedSettingsFrom(final NodeSettingsRO settings) throws InvalidSettingsException { m_imgCol.loadSettingsFrom(settings); m_labelingCol.loadSettingsFrom(settings); m_adjustVirtually.loadSettingsFrom(settings); try { m_expression.loadSettingsFrom(settings); } catch (final Exception e) { // } } /** * {@inheritDoc} */ @Override protected void reset() { m_imgTable = null; m_segTable = null; m_contentModel.clearCacheBeforeClosing(); m_contentModel = new ClearableTableContentModel(); } /** * {@inheritDoc} */ @Override protected void saveInternals(final File nodeInternDir, final ExecutionMonitor exec) throws IOException, CanceledExecutionException { // } /** * {@inheritDoc} */ @Override protected void saveSettingsTo(final NodeSettingsWO settings) { m_imgCol.saveSettingsTo(settings); m_labelingCol.saveSettingsTo(settings); m_adjustVirtually.saveSettingsTo(settings); m_expression.saveSettingsTo(settings); } /** * {@inheritDoc} */ @Override public void setInternalTables(final BufferedDataTable[] tables) { if ((tables.length != 1) && (tables.length != 2)) { throw new IllegalArgumentException(); } // TODO: make workaround unnecessary // temporary workaround since setDataTable blocks // m_tableModel.setDataTable(tables[0]); m_imgTable = tables[0]; if (tables.length > 1) { m_segTable = tables[1]; } // HiLiteHandler inProp = getInHiLiteHandler(INPORT); // m_contModel.setHiLiteHandler(inProp); } /** * {@inheritDoc} */ @Override protected void validateSettings(final NodeSettingsRO settings) throws InvalidSettingsException { m_imgCol.validateSettings(settings); m_labelingCol.validateSettings(settings); m_adjustVirtually.validateSettings(settings); try { m_expression.validateSettings(settings); } catch (final Exception e) { // } } public boolean virtuallyAdjustImgs() { return m_adjustVirtually.getBooleanValue(); }; }