/* * ------------------------------------------------------------------------ * * 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.imgwriter2; import java.io.IOException; import java.util.Collection; import java.util.HashMap; import org.knime.core.node.NodeLogger; import org.knime.knip.io.ScifioGateway; import org.scijava.Context; import io.scif.DependencyException; import io.scif.Format; import io.scif.FormatException; import io.scif.MissingLibraryException; import io.scif.SCIFIO; import io.scif.Writer; import io.scif.common.DataTools; import io.scif.config.SCIFIOConfig; import io.scif.img.ImgIOException; import io.scif.img.ImgSaver; import io.scif.services.ServiceException; import net.imagej.ImgPlus; import net.imglib2.exception.IncompatibleTypeException; import net.imglib2.img.Img; import net.imglib2.img.ImgView; import net.imglib2.type.numeric.RealType; import net.imglib2.view.Views; /** * Provides the functionality to write {@link Img}s using the * <a href = "http://loci.wisc.edu/software/scifio">scifio</a>-library. * * * @author <a href="mailto:horn_martin@gmx.de">Martin Horn</a> * @author <a href="mailto:gabriel.einsdorf@uni.kn">Gabriel Einsdorf</a> */ public class ImgWriter2 { private static final NodeLogger LOGGER = NodeLogger .getLogger(ImgWriter2.class); /* * List of the names of the available writers associated with specific * formats. */ private String[] m_writers; /* map from the writer names to the actual writers */ private HashMap<String, Format> m_mapFormats = null; /* Frame rate to use when writing in frames per second, if applicable. */ private int m_fps = 10; /* Handles the writing on disk */ private ImgSaver m_saver; /* * Provides access to SCifio methods */ private final SCIFIO SCIFIO = ScifioGateway.getSCIFIO(); private boolean m_writeSequentially = true; /** * Creates an new writer. * */ public ImgWriter2() { } /* helper to get the list of supported writers */ private void retrieveSupportedWriters() { if (m_mapFormats == null) { final Context con = SCIFIO.getContext(); m_saver = new ImgSaver(con); final Collection<Format> outFormats = SCIFIO.format() .getOutputFormats(); m_writers = new String[outFormats.size()]; m_mapFormats = new HashMap<String, Format>(); int i = 0; for (Format f : outFormats) { m_writers[i] = f.getClass().getSimpleName().replace("Format", "") + " (" + f.getSuffixes()[0] + ")"; m_mapFormats.put(m_writers[i], f); i++; } } } /** * Returns the list of the possible compression types of the specific * writer. * * @param format * the name of the writer * @return the list of possible compressions, <code>null</code> if there are * no compression types */ public String[] getCompressionTypes(final String format) { retrieveSupportedWriters(); Writer w = null; try { w = m_mapFormats.get(format).createWriter(); } catch (final FormatException e) { e.printStackTrace(); } if (w == null) { return null; } return w.getCompressionTypes(); } /** * @return the available writers as strings */ public String[] getWriters() { retrieveSupportedWriters(); return m_writers; } /** * Gets one suffix normally used to identify the format associated with the * specific writer. * * @param format * the writer * @return the suffix, e.g. '.tif' * @throws FormatException */ public String getSuffix(final String format) throws FormatException { retrieveSupportedWriters(); Writer w = null; try { w = m_mapFormats.get(format).createWriter(); } catch (final io.scif.FormatException e) { throw new FormatException( "Could not create writer for selected format " + format + "\n" + e.getMessage()); } catch (final NullPointerException e) { throw new FormatException( "No writer for selected format " + format); } if (w == null) { return null; } return w.getFormat().getSuffixes()[0]; } /** * Writes the image plane stack to the given file. The resulting image * format is determined by the given writer. * * @param img * the image to be written * @param <T> * the image type * * @param outfile * the absolute path of the file to write in * @param writer * the writer * @param compressionType * the compression type, if available, can be <code>null</code>. * @param dimMapping * mapping of the image dimensions (without X and Y) to the * dimension order ZCT (array must be of length 3), If * <code>null</code> Z=dim2, C=dim3, T=dim4. If a mapping is -1 , * that particular dimensions is assumed to be not existent. * @throws IOException * @throws FormatException * @throws MissingLibraryException * @throws ServiceException * @throws DependencyException */ public <T extends RealType<T>> void writeImage(final Img<T> img, final String outfile, final String format, final String compressionType, final int[] dimMapping) throws FormatException, IOException { retrieveSupportedWriters(); // only wrap into img view if necessary, as writing is slower for ImgViews Img<T> out = img; for (int d = 0; d < img.numDimensions(); d++) { if (img.min(d) != 0) { out = ImgView.wrap(Views.zeroMin(img), img.factory()); break; } } writeImage(out, outfile, m_mapFormats.get(format), compressionType, dimMapping); } /** * Writes the image plane stack to the given file. The resulting image * format is determined by the given writer. * * @param img * the image to be written * @param <T> * the image type * * @param outfile * the absolute path of the file to write in * @param writer * the writer * @param compressionType * the compression type, if available, can be <code>null</code>. * @param dimMapping * mapping of the image dimensions (without X and Y) to the * dimension order ZCT (array must be of length 3), If * <code>null</code> Z=dim2, C=dim3, T=dim4. If a mapping is -1 , * that particular dimensions is assumed to be not existent. * @throws IOException * @throws FormatException * @throws MissingLibraryException * @throws ServiceException * @throws DependencyException */ public <T extends RealType<T>> void writeImage(final Img<T> img, final String outfile, final Format format, final String compressionType, final int[] dimMapping) throws FormatException, IOException { LOGGER.debug("File: " + outfile + " \n Type:" + img.firstElement().getClass().getSimpleName() + "format: " + format.getFormatName()); int[] map; if ((dimMapping == null) || (dimMapping.length != 3)) { map = new int[] { 2, 3, 4 }; } else { map = dimMapping.clone(); } // get the number of channels int sizeC = (img.numDimensions() > map[1]) && (map[1] != -1) ? (int) img.dimension(2 + map[1]) : 1; if (sizeC > 3) { LOGGER.warn( "Image has more than 3 channels. These channels might be " + "ignored my some formats."); } final int sizeT = (img.numDimensions() > map[2]) && (map[2] != -1) ? (int) img.dimension(2 + map[2]) : 1; final int sizeZ = ((img.numDimensions() > map[0]) && (map[0] != -1)) ? (int) img.dimension(2 + map[0]) : 1; if (img.numDimensions() > 5) { LOGGER.warn("Image has more than five dimension. " + "These dimensions will be ignored."); } Writer tempWriter = null; try { tempWriter = format.createWriter(); } catch (final FormatException e) { LOGGER.error(e.getMessage()); } final boolean doStack = tempWriter.canDoStacks(); if (!doStack && ((sizeT > 1) || (sizeZ > 1))) { throw new FormatException( "Selected format doesn't support image stacks."); } final ImgPlus<T> imp = ImgPlus.wrap(img); imp.setCompositeChannelCount(sizeC); try { tempWriter.isSupportedType( SCIFIO.imgUtil().makeType(imp.firstElement()), compressionType); final int pixeltype = SCIFIO.imgUtil().makeType(imp.firstElement()); if (!DataTools.containsValue( tempWriter.getPixelTypes(compressionType), pixeltype)) { throw new ImgIOException(compressionType); } } catch (final ImgIOException e1) { throw new FormatException( "Pixeltype " + imp.firstElement().getClass().getSimpleName() + " can't be written in the selected Format."); } // create writer configuration final SCIFIOConfig config = new SCIFIOConfig() .writerSetSequential(m_writeSequentially) .writerSetFramesPerSecond(m_fps); if ((compressionType != null) && (tempWriter.getCompressionTypes() != null)) { config.writerSetCompression(compressionType); } try { m_saver.saveImg(outfile, imp, config); } catch (final ImgIOException e) { LOGGER.error("Skipped image: " + imp.getName() + " " + e.getMessage().replace("io.scif.FormatException:", "")); } catch (final IncompatibleTypeException e) { LOGGER.error(e.getMessage()); } } /** * Sets the FPS for the writer. * * @param fps * the frames per second * @return the writer (for chaining) */ public ImgWriter2 setFramesPerSecond(final int fps) { m_fps = fps; return this; } /** * Sets if the writer writes files sequentially . * * @param writeSequentially * if the image is to be written sequentially * @return the writer (for chaining) */ public ImgWriter2 setWriteSequantially(final boolean writeSequentially) { m_writeSequentially = writeSequentially; return this; } }