/* * ------------------------------------------------------------------------ * * 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.io.nodes.imgreader; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import org.knime.core.data.DataCell; import org.knime.core.data.DataRow; import org.knime.core.data.DataTableSpec; import org.knime.core.data.StringValue; 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.NodeModel; import org.knime.core.node.NodeSettingsRO; import org.knime.core.node.NodeSettingsWO; import org.knime.core.node.defaultnodesettings.SettingsModel; import org.knime.core.node.defaultnodesettings.SettingsModelBoolean; import org.knime.core.node.defaultnodesettings.SettingsModelIntegerBounded; import org.knime.core.node.defaultnodesettings.SettingsModelString; import org.knime.core.node.defaultnodesettings.SettingsModelStringArray; import org.knime.core.node.port.PortType; import org.knime.core.node.port.PortTypeRegistry; import org.knime.knip.base.node.nodesettings.SettingsModelSubsetSelection; import net.imglib2.img.ImgFactory; import net.imglib2.img.array.ArrayImgFactory; import net.imglib2.img.cell.CellImgFactory; import net.imglib2.img.planar.PlanarImgFactory; import net.imglib2.type.NativeType; import net.imglib2.type.numeric.RealType; /** * This Node reads images. * * @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 ImgReaderNodeModel<T extends RealType<T> & NativeType<T>> extends NodeModel implements BufferedDataTableHolder { public static class CombinedIterable<E> implements Iterable<E> { private final Iterable<E>[] m_iterables; @SafeVarargs public CombinedIterable(final Iterable<E>... iterables) { m_iterables = iterables; } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") @Override public Iterator<E> iterator() { return new Iterator<E>() { private int m_currentIt; private final Iterator<E>[] m_iterators; { m_currentIt = 0; m_iterators = new Iterator[m_iterables.length]; for (int i = 0; i < m_iterators.length; i++) { m_iterators[i] = m_iterables[i].iterator(); } } @Override public boolean hasNext() { if (m_iterators[m_currentIt].hasNext()) { return true; } else if ((m_currentIt + 1) == m_iterators.length) { return false; } else { m_currentIt++; return hasNext(); } } @Override public E next() { return m_iterators[m_currentIt].next(); } @Override public void remove() { throw new UnsupportedOperationException(); } }; } } /** * Key to store the check file format option. */ public static final String CFG_CHECK_FILE_FORMAT = "check_file_format"; /** * Key to store if the complete path should be used as row key */ public static final String CFG_COMPLETE_PATH_ROWKEY = "complete_path_rowkey"; /** * Key to store the directory history. */ public static final String CFG_DIR_HISTORY = "imagereader_dirhistory"; /** * Key for the settings holding the file list. */ public static final String CFG_FILE_LIST = "file_list"; /** * Key to store the selected column in the optional input table */ public static final String CFG_FILENAME_COLUMN = "filename_column"; /** * Key for the settings holding information if group files modus is wanted */ public static final String CFG_GROUP_FILES = "group_files"; /** * Key to store the OME_XML-metadata column option. */ public static final String CFG_OME_XML_METADATA_COLUMN = "xmlcolumns"; /** * Key to store the factory used to create the images */ public static final String CFG_IMG_FACTORY = "img_factory"; /** * Key to store whether all series should be read */ public static final String CFG_READ_ALL_SERIES = "read_all_series"; /** * Key to store the selected series */ public static final String CFG_SERIES_SELECTION = "series_selection"; /* * Settings */ /** * Key for the settings holding selected image planes. */ public static final String CFG_PLANE_SLECTION = "plane_selection"; /** * The image out port of the Node. */ public static final int IMAGEOUTPORT = 0; /** * The meta data out port of the Node. */ public static final int METADATAOUTPORT = 1; /** * The available factory types available for selection. */ public static final String[] IMG_FACTORIES = new String[] { "Array Image Factory", "Planar Image Factory", "Cell Image Factory" }; private final SettingsModelBoolean m_checkFileFormat = new SettingsModelBoolean(CFG_CHECK_FILE_FORMAT, true); private final SettingsModelBoolean m_completePathRowKey = new SettingsModelBoolean(CFG_COMPLETE_PATH_ROWKEY, false); /* data table for the table cell viewer */ private BufferedDataTable m_data; private final SettingsModelString m_filenameCol = new SettingsModelString(CFG_FILENAME_COLUMN, ""); /* * Collection of all settings. */ private final SettingsModelStringArray m_files = new SettingsModelStringArray(CFG_FILE_LIST, new String[] {}); // New in 1.0.2 private final SettingsModelBoolean m_isGroupFiles = new SettingsModelBoolean(CFG_GROUP_FILES, true); private final SettingsModelBoolean m_omexmlCol = new SettingsModelBoolean(CFG_OME_XML_METADATA_COLUMN, false); private final SettingsModelSubsetSelection m_planeSelect = new SettingsModelSubsetSelection(CFG_PLANE_SLECTION); // new in 1.1 private final SettingsModelString m_imgFactory = new SettingsModelString(CFG_IMG_FACTORY, IMG_FACTORIES[0]); private SettingsModelBoolean m_readAllSeries = new SettingsModelBoolean(CFG_READ_ALL_SERIES, true); private final SettingsModelIntegerBounded m_seriesSelection = new SettingsModelIntegerBounded(CFG_SERIES_SELECTION, 0, 0, 1000); private final Collection<SettingsModel> m_settingsCollection; /** * Initializes the ImageReader */ public ImgReaderNodeModel() { super(new PortType[] { PortTypeRegistry.getInstance().getPortType(BufferedDataTable.class, true) }, new PortType[] { BufferedDataTable.TYPE }); m_settingsCollection = new ArrayList<SettingsModel>(); m_settingsCollection.add(m_files); m_settingsCollection.add(m_planeSelect); m_settingsCollection.add(m_omexmlCol); m_settingsCollection.add(m_filenameCol); m_settingsCollection.add(m_completePathRowKey); m_settingsCollection.add(m_checkFileFormat); // FIXME: should actually not be necessary to disable an dialog // component, when the node is added the first time? right? m_seriesSelection.setEnabled(false); } /** * {@inheritDoc} */ @Override protected DataTableSpec[] configure(final DataTableSpec[] inSpecs) throws InvalidSettingsException { final ReadFileImgTable<T> tab = new ReadFileImgTable<T>(m_omexmlCol.getBooleanValue()); // tab.setDimLabelProperty(m_planeSelect.getDimLabelsAsString()); return new DataTableSpec[] { tab.getDataTableSpec() }; } /** * {@inheritDoc} */ @Override protected BufferedDataTable[] execute(final BufferedDataTable[] inData, final ExecutionContext exec) throws Exception { final String[] fnames = m_files.getStringArrayValue(); // String[] metaDataColumns = // m_metadatakeys.getStringArrayValue(); // table with images from the dialog Iterable<String> tableImgList = null; Iterable<String> dialogImgList = null; if (inData[0] != null) { final int colIdx = inData[0].getDataTableSpec().findColumnIndex(m_filenameCol.getStringValue()); if (colIdx >= 0) { tableImgList = new Iterable<String>() { @Override public Iterator<String> iterator() { final Iterator<DataRow> rowIt = inData[0].iterator(); return new Iterator<String>() { @Override public boolean hasNext() { return rowIt.hasNext(); } @Override public String next() { DataCell cell = rowIt.next().getCell(colIdx); if (cell.isMissing()) { return "missing cell"; } return ((StringValue) cell).getStringValue(); } @Override public void remove() { throw new UnsupportedOperationException(); } }; } }; } } if (fnames.length > 0) { dialogImgList = Arrays.asList(fnames); } Iterable<String> imgIt = null; long numImages; if ((tableImgList != null) && (dialogImgList != null)) { imgIt = new CombinedIterable<String>(dialogImgList, tableImgList); numImages = fnames.length + inData[0].size(); } else if (tableImgList != null) { imgIt = tableImgList; numImages = inData[0].size(); } else { imgIt = dialogImgList; numImages = fnames.length; } // create ImgFactory ImgFactory<T> imgFac; if (m_imgFactory.getStringValue().equals(IMG_FACTORIES[1])) { imgFac = new PlanarImgFactory<T>(); } else if (m_imgFactory.getStringValue().equals(IMG_FACTORIES[2])) { // TODO: what is the appropriate cell size? imgFac = new CellImgFactory<T>(); } else { imgFac = new ArrayImgFactory<T>(); } // series selection int seriesSelection; if (m_readAllSeries.getBooleanValue()) { seriesSelection = -1; } else { seriesSelection = m_seriesSelection.getIntValue(); } // create data table final ReadFileImgTable<T> dt = new ReadFileImgTable<T>(exec, imgIt, numImages, m_planeSelect, m_omexmlCol.getBooleanValue(), m_checkFileFormat.getBooleanValue(), m_completePathRowKey.getBooleanValue(), m_isGroupFiles.getBooleanValue(), seriesSelection, imgFac); // dt.setDimLabelProperty(m_planeSelect.getDimLabelsAsString()); final BufferedDataTable[] out = new BufferedDataTable[] { exec.createBufferedDataTable(dt, exec) }; if (dt.hasAnErrorOccured()) { setWarningMessage("Some errors occured opening images or image planes! See console log for details."); } else if (!dt.usedDifferentReaders() && m_checkFileFormat.getBooleanValue() && out[0].size() > 1) { // used only one reader and had more than one image setWarningMessage( "All read files had the same format. To reduce read time uncheck \"Additional Options -> Check file format for each file\"."); } // data table for the table cell viewer m_data = out[0]; return out; } /** * {@inheritDoc} */ @Override public BufferedDataTable[] getInternalTables() { return new BufferedDataTable[] { m_data }; } /** * {@inheritDoc} */ @Override protected void loadInternals(final File nodeInternDir, final ExecutionMonitor exec) throws IOException, CanceledExecutionException { // } /** * {@inheritDoc} */ @Override public void reset() { m_data = null; // m_filenameCol.setStringValue(null); } /** * {@inheritDoc} */ @Override protected void saveInternals(final File nodeInternDir, final ExecutionMonitor exec) throws IOException, CanceledExecutionException { // } /** * {@inheritDoc} */ @Override protected void loadValidatedSettingsFrom(final NodeSettingsRO settings) throws InvalidSettingsException { try { for (final SettingsModel sm : m_settingsCollection) { sm.loadSettingsFrom(settings); } // group file setting new in 1.0.2 m_isGroupFiles.loadSettingsFrom(settings); // factory selection new in 1.1 m_imgFactory.loadSettingsFrom(settings); m_readAllSeries.loadSettingsFrom(settings); m_seriesSelection.loadSettingsFrom(settings); } catch (final Exception e) { // nothing to handle } } /** * {@inheritDoc} */ @Override protected void saveSettingsTo(final NodeSettingsWO settings) { for (final SettingsModel sm : m_settingsCollection) { sm.saveSettingsTo(settings); } // group file setting new in 1.0.2 m_isGroupFiles.saveSettingsTo(settings); // factory selection new in 1.1 m_imgFactory.saveSettingsTo(settings); m_readAllSeries.saveSettingsTo(settings); m_seriesSelection.saveSettingsTo(settings); } /** * {@inheritDoc} */ @Override protected void validateSettings(final NodeSettingsRO settings) throws InvalidSettingsException { try { for (final SettingsModel sm : m_settingsCollection) { sm.validateSettings(settings); } // group file setting new in 1.0.2 m_isGroupFiles.validateSettings(settings); // factory selection new in 1.1 m_imgFactory.validateSettings(settings); m_readAllSeries.validateSettings(settings); m_seriesSelection.validateSettings(settings); } catch (final Exception e) { // nothing to handle } } // // Methods for the table cell view //// /** * {@inheritDoc} */ @Override public void setInternalTables(final BufferedDataTable[] tables) { m_data = tables[0]; } }