All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.algart.executors.modules.maps.tiff.ReadTiff Maven / Gradle / Ivy

Go to download

Open-source libraries, providing the base functions for SciChains product in area of computer vision.

There is a newer version: 4.4.9
Show newest version
/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2017-2024 Daniel Alievsky, AlgART Laboratory (http://algart.net)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package net.algart.executors.modules.maps.tiff;

import net.algart.arrays.Matrix;
import net.algart.arrays.PArray;
import net.algart.executors.api.ReadOnlyExecutionInput;
import net.algart.executors.api.data.SMat;
import net.algart.executors.modules.maps.LongTimeOpeningMode;
import net.algart.math.IRectangularArea;
import net.algart.matrices.tiff.CachingTiffReader;
import net.algart.matrices.tiff.TiffReader;
import net.algart.matrices.tiff.tiles.TiffMap;
import net.algart.multimatrix.MultiMatrix;
import net.algart.multimatrix.MultiMatrix2D;

import java.io.FileNotFoundException;
import java.io.IOError;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Objects;

public final class ReadTiff extends AbstractTiffOperation implements ReadOnlyExecutionInput {
    public static final String OUTPUT_DIM_X = "dim_x";
    public static final String OUTPUT_DIM_Y = "dim_y";
    public static final String OUTPUT_RECTANGLE = "rectangle";

    private LongTimeOpeningMode openingMode = LongTimeOpeningMode.OPEN_AND_CLOSE;
    // - note: default value in this CLASS (not in the executor model) SHOULD be very simple,
    // because this class may be used without full setup of all parameter, for example, in InputReadTiff model
    private boolean requireFileExistence = true;
    private boolean requireValidTiff = true;
    private int ifdIndex = 0;
    private boolean wholeImage = true;
    private int x = 0;
    private int y = 0;
    private int sizeX = 1;
    private int sizeY = 1;
    private boolean cropToImage = true;
    // - necessary when we do not know level sizes before 1st call of this function,
    // for example, if we need to read large image fragment-per-fragment
    private boolean caching = false;
    private boolean autoUnpackBitsToBytes = false;
    private boolean autoScaleWhenIncreasingBitDepth = true;
    private boolean autoCorrectInvertedBrightness = false;
    private boolean cropTilesToImageBoundaries = true;
    private int numberOfChannels = 0;

    private volatile TiffReader reader = null;

    public ReadTiff() {
        useVisibleResultParameter();
        addInputMat(DEFAULT_INPUT_PORT);
        addInputScalar(INPUT_CLOSE_FILE);
        addOutputMat(DEFAULT_OUTPUT_PORT);
        addOutputScalar(OUTPUT_DIM_X);
        addOutputScalar(OUTPUT_DIM_Y);
        addOutputScalar(OUTPUT_VALID);
        addOutputScalar(OUTPUT_IFD_INDEX);
        addOutputScalar(OUTPUT_NUMBER_OF_IMAGES);
        addOutputScalar(OUTPUT_IMAGE_DIM_X);
        addOutputScalar(OUTPUT_IMAGE_DIM_Y);
        addOutputNumbers(OUTPUT_RECTANGLE);
        addOutputScalar(OUTPUT_IFD);
        addOutputScalar(OUTPUT_PRETTY_IFD);
        addOutputScalar(OUTPUT_FILE_SIZE);
        addOutputScalar(OUTPUT_CLOSED);
    }

    public static ReadTiff getInstance() {
        return new ReadTiff();
    }

    // Used in InputReadTiff model
    public static ReadTiff getSecureInstance() {
        final ReadTiff result = new ReadTiff();
        result.setSecure(true);
        return result;
    }

    @Override
    public ReadTiff setFile(String file) {
        super.setFile(file);
        return this;
    }

    public LongTimeOpeningMode getOpeningMode() {
        return openingMode;
    }

    public ReadTiff setOpeningMode(LongTimeOpeningMode openingMode) {
        this.openingMode = nonNull(openingMode);
        return this;
    }

    public boolean isRequireFileExistence() {
        return requireFileExistence;
    }

    public ReadTiff setRequireFileExistence(boolean requireFileExistence) {
        this.requireFileExistence = requireFileExistence;
        return this;
    }

    public boolean isRequireValidTiff() {
        return requireValidTiff;
    }

    public ReadTiff setRequireValidTiff(boolean requireValidTiff) {
        this.requireValidTiff = requireValidTiff;
        return this;
    }

    public int getIfdIndex() {
        return ifdIndex;
    }

    public ReadTiff setIfdIndex(int ifdIndex) {
        this.ifdIndex = nonNegative(ifdIndex);
        return this;
    }

    public boolean isWholeImage() {
        return wholeImage;
    }

    public ReadTiff setWholeImage(boolean wholeImage) {
        this.wholeImage = wholeImage;
        return this;
    }

    public int getX() {
        return x;
    }

    public ReadTiff setX(int x) {
        this.x = x;
        return this;
    }

    public int getY() {
        return y;
    }

    public ReadTiff setY(int y) {
        this.y = y;
        return this;
    }

    public int getSizeX() {
        return sizeX;
    }

    public ReadTiff setSizeX(int sizeX) {
        this.sizeX = nonNegative(sizeX);
        return this;
    }

    public int getSizeY() {
        return sizeY;
    }

    public ReadTiff setSizeY(int sizeY) {
        this.sizeY = nonNegative(sizeY);
        return this;
    }

    public boolean isCropToImage() {
        return cropToImage;
    }

    public ReadTiff setCropToImage(boolean cropToImage) {
        this.cropToImage = cropToImage;
        return this;
    }

    public boolean isCaching() {
        return caching;
    }

    public ReadTiff setCaching(boolean caching) {
        this.caching = caching;
        return this;
    }

    public boolean isAutoUnpackBitsToBytes() {
        return autoUnpackBitsToBytes;
    }

    public ReadTiff setAutoUnpackBitsToBytes(boolean autoUnpackBitsToBytes) {
        this.autoUnpackBitsToBytes = autoUnpackBitsToBytes;
        return this;
    }

    public boolean isAutoScaleWhenIncreasingBitDepth() {
        return autoScaleWhenIncreasingBitDepth;
    }

    public ReadTiff setAutoScaleWhenIncreasingBitDepth(boolean autoScaleWhenIncreasingBitDepth) {
        this.autoScaleWhenIncreasingBitDepth = autoScaleWhenIncreasingBitDepth;
        return this;
    }

    public boolean isAutoCorrectInvertedBrightness() {
        return autoCorrectInvertedBrightness;
    }

    public ReadTiff setAutoCorrectInvertedBrightness(boolean autoCorrectInvertedBrightness) {
        this.autoCorrectInvertedBrightness = autoCorrectInvertedBrightness;
        return this;
    }

    public boolean isCropTilesToImageBoundaries() {
        return cropTilesToImageBoundaries;
    }

    public ReadTiff setCropTilesToImageBoundaries(boolean cropTilesToImageBoundaries) {
        this.cropTilesToImageBoundaries = cropTilesToImageBoundaries;
        return this;
    }

    public int getNumberOfChannels() {
        return numberOfChannels;
    }

    public ReadTiff setNumberOfChannels(int numberOfChannels) {
        this.numberOfChannels = nonNegative(numberOfChannels);
        return this;
    }

    @Override
    public void initialize() {
        if (openingMode.isClosePreviousOnReset()) {
            closeReader();
        }
    }

    @Override
    public void process() {
        SMat input = getInputMat(defaultInputPortName(), true);
        if (input.isInitialized()) {
            logDebug(() -> "Copying " + input);
            getMat().setTo(input);
        } else {
            MultiMatrix multiMatrix = readTiff(completeFilePath(), isOutputNecessary(DEFAULT_OUTPUT_PORT));
            if (multiMatrix != null) {
                getMat().setTo(multiMatrix);
            } else {
                getMat().remove();
            }
        }
    }

    public MultiMatrix readTiff(Path path) {
        return readTiff(path, true);
    }

    public MultiMatrix readTiff(Path path, boolean doActualReading) {
        Objects.requireNonNull(path, "Null path");
        try {
            getScalar(OUTPUT_VALID).setTo(false);
            getScalar(OUTPUT_DIM_X).remove();
            getScalar(OUTPUT_DIM_Y).remove();
            getNumbers(OUTPUT_RECTANGLE).remove();
            if (!Files.isRegularFile(path)) {
                if (requireFileExistence) {
                    throw new FileNotFoundException("File not found: " + path);
                } else {
                    return null;
                }
            }
            final TiffReader reader = openFile(path);
            fillReadingOutputInformation(this, reader, ifdIndex);
            if (!reader.isValid()) {
                closeReader();
                return null;
            }
            final MultiMatrix2D multiMatrix = doActualReading ?
                    readMultiMatrix(reader) :
                    null;
            final boolean close = needToClose(this, openingMode);
            if (close) {
                closeReader();
            }
            return multiMatrix;
        } catch (IOException e) {
            closeReader();
            // - closing can be important to allow the user to fix the problem;
            // moreover, in a case the error it is better to free all possible connected resources
            throw new IOError(e);
        } finally {
            getScalar(OUTPUT_CLOSED).setTo(reader == null);
        }
    }

    @Override
    public void close() {
        super.close();
        closeReader();
    }


    public TiffReader openFile(Path path) throws IOException {
        Objects.requireNonNull(path, "Null path");
        logDebug(() -> "Reading " + path);
        TiffReader reader = this.reader;
        if (reader == null) {
            reader = caching ?
                    new CachingTiffReader(path, requireValidTiff) :
                    new TiffReader(path, requireValidTiff);
            reader.setAutoUnpackBitsToBytes(autoUnpackBitsToBytes);
            reader.setAutoScaleWhenIncreasingBitDepth(autoScaleWhenIncreasingBitDepth);
            reader.setAutoCorrectInvertedBrightness(autoCorrectInvertedBrightness);
            reader.setCropTilesToImageBoundaries(cropTilesToImageBoundaries);
            this.reader = reader;
            // - note: the assignments sequence guarantees that this method will not return null
        }
        fillOutputFileInformation(path);
        // - note: we need to fill output ports here, even if the file was already opened
        return reader;
    }

    private MultiMatrix2D readMultiMatrix(TiffReader reader) throws IOException {
        final TiffMap map = reader.map(ifdIndex);
        int fromX = this.x;
        int fromY = this.y;
        int toX = fromX + this.sizeX;
        int toY = fromY + this.sizeY;
        if (wholeImage) {
            fromX = 0;
            fromY = 0;
            toX = map.dimX();
            toY = map.dimY();
        } else if (cropToImage) {
            fromX = Math.max(fromX, 0);
            fromY = Math.max(fromY, 0);
            toX = Math.min(toX, map.dimX());
            toY = Math.min(toY, map.dimY());
            if (fromX >= toX || fromY >= toY) {
                return null;
            }
        }
        final Matrix m = reader.readMatrix(map, fromX, fromY, toX, toY);
        MultiMatrix2D result = MultiMatrix.valueOf2DMerged(m);
        if (numberOfChannels != 0) {
            result = result.asOtherNumberOfChannels(numberOfChannels);
        }
        getScalar(OUTPUT_DIM_X).setTo(toX - fromX);
        getScalar(OUTPUT_DIM_Y).setTo(toY - fromY);
        getNumbers(OUTPUT_RECTANGLE).setTo(IRectangularArea.valueOf(fromX, fromY, toX - 1, toY - 1));
        return result;
    }

    private void closeReader() {
        TiffReader reader = this.reader;
        if (reader != null) {
            this.reader = null;
            logDebug(() -> "Closing " + reader);
            try {
                reader.close();
            } catch (IOException e) {
                throw new IOError(e);
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy