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

net.algart.maps.pyramids.io.api.sources.ExtendingPlanePyramidSource 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.maps.pyramids.io.api.sources;

import net.algart.arrays.*;
import net.algart.arrays.Arrays;
import net.algart.math.IPoint;
import net.algart.math.IRectangularArea;
import net.algart.maps.pyramids.io.api.AbstractPlanePyramidSource;
import net.algart.maps.pyramids.io.api.PlanePyramidSource;
import net.algart.maps.pyramids.io.api.PlanePyramidTools;

import java.awt.*;
import java.nio.channels.NotYetConnectedException;
import java.util.*;
import java.util.List;

public final class ExtendingPlanePyramidSource extends AbstractPlanePyramidSource implements PlanePyramidSource {
    private static final System.Logger LOG = System.getLogger(ExtendingPlanePyramidSource.class.getName());

    private final PlanePyramidSource parent;
    private final List dimensions;
    private final int compression;
    private final int bandCount;
    private final long extendedDimX;
    private final long extendedDimY;
    private final long positionXInExtendedMatrix;
    private final long positionYInExtendedMatrix;
    private final double[] backgroundColor;

    private int extendingBorderWidth = 0;
    private Color extendingBorderColor = Color.GRAY;

    private ExtendingPlanePyramidSource(
            PlanePyramidSource parent,
            long extendedDimX,
            long extendedDimY,
            long positionXInExtendedMatrix,
            long positionYInExtendedMatrix,
            double[] backgroundColor) {
        Objects.requireNonNull(parent, "Null parent");
        Objects.requireNonNull(backgroundColor, "Null backgroundColor");
        if (extendedDimX <= 0 || extendedDimY <= 0) {
            throw new IllegalArgumentException("Illegal extended dimensions " + extendedDimX + "x" + extendedDimY
                    + " (must be positive)");
        }
        if (backgroundColor.length == 0) {
            throw new IllegalArgumentException("Empty backgroundColor");
        }
        if (backgroundColor.length != 1 && backgroundColor.length != 3 && backgroundColor.length != 4) {
            throw new IllegalArgumentException("Illegal backgroundColor[" + backgroundColor.length
                    + "]: it must contain 1, 3 or 4 elements");
        }
        this.parent = parent;
        this.bandCount = Math.max(parent.bandCount(), backgroundColor.length);
        this.backgroundColor = new double[this.bandCount];
        for (int k = 0; k < this.bandCount; k++) {
            this.backgroundColor[k] = backgroundColor[Math.min(k, backgroundColor.length - 1)];
        }
        this.extendedDimX = extendedDimX;
        this.extendedDimY = extendedDimY;
        this.positionXInExtendedMatrix = positionXInExtendedMatrix;
        this.positionYInExtendedMatrix = positionYInExtendedMatrix;
        this.dimensions = new ArrayList();
        this.compression = parent.compression();
        long lastDimX = this.extendedDimX;
        long lastDimY = this.extendedDimY;
        this.dimensions.add(new long[]{this.bandCount, lastDimX, lastDimY});
        for (int k = 1, n = parent.numberOfResolutions(); k < n; k++) {
            lastDimX /= this.compression;
            lastDimY /= this.compression;
            this.dimensions.add(new long[]{this.bandCount, lastDimX, lastDimY});
        }
        LOG.log(System.Logger.Level.DEBUG, () -> String.format(Locale.US,
                "ExtendingPlanePyramidSource created on the base of %s: "
                        + "%dx%d, contains sub-image %dx%d at (%d,%d), %d bands, %d levels, compression in %d times",
                parent,
                extendedDimX, extendedDimY,
                parent.dim(0, DIM_WIDTH), parent.dim(0, DIM_HEIGHT),
                positionXInExtendedMatrix, positionYInExtendedMatrix,
                this.bandCount, this.dimensions.size(), this.compression));
    }

    public static ExtendingPlanePyramidSource newInstance(
            PlanePyramidSource parent,
            long extendedDimX,
            long extendedDimY,
            long positionXInExtendedMatrix,
            long positionYInExtendedMatrix,
            double[] backgroundColor) {
        return new ExtendingPlanePyramidSource(
                parent,
                extendedDimX, extendedDimY, positionXInExtendedMatrix, positionYInExtendedMatrix,
                backgroundColor);
    }

    public PlanePyramidSource parent() {
        return parent;
    }

    public long getExtendedDimX() {
        return extendedDimX;
    }

    public long getExtendedDimY() {
        return extendedDimY;
    }

    public long getPositionXInExtendedMatrix() {
        return positionXInExtendedMatrix;
    }

    public long getPositionYInExtendedMatrix() {
        return positionYInExtendedMatrix;
    }

    public int getExtendingBorderWidth() {
        if (extendingBorderWidth < 0) {
            throw new IllegalArgumentException("Negative extendingBorderWidth");
        }
        return extendingBorderWidth;
    }

    public void setExtendingBorderWidth(int extendingBorderWidth) {
        this.extendingBorderWidth = extendingBorderWidth;
    }

    public Color getExtendingBorderColor() {
        return extendingBorderColor;
    }

    public void setExtendingBorderColor(Color extendingBorderColor) {
        if (extendingBorderColor == null) {
            throw new NullPointerException("Null extendingBorderColor");
        }
        this.extendingBorderColor = extendingBorderColor;
    }

    @Override
    public int numberOfResolutions() {
        return this.dimensions.size();
    }

    @Override
    public int compression() {
        return compression;
    }

    @Override
    public int bandCount() {
        return this.bandCount;
    }

    @Override
    public boolean isResolutionLevelAvailable(int resolutionLevel) {
        return parent.isResolutionLevelAvailable(resolutionLevel);
    }

    @Override
    public boolean[] getResolutionLevelsAvailability() {
        return parent.getResolutionLevelsAvailability();
    }

    @Override
    public long[] dimensions(int resolutionLevel) {
        return dimensions.get(resolutionLevel).clone();
    }

    @Override
    public long dim(int resolutionLevel, int index) {
        return dimensions.get(resolutionLevel)[index];
    }

    @Override
    public boolean isElementTypeSupported() {
        return parent.isElementTypeSupported();
    }

    @Override
    public Class elementType() throws UnsupportedOperationException {
        return parent.elementType();
    }

    @Override
    public OptionalDouble pixelSizeInMicrons() {
        return parent.pixelSizeInMicrons();
    }

    @Override
    public OptionalDouble magnification() {
        return parent.magnification();
    }

    @Override
    public List zeroLevelActualRectangles() {
        List parentRectangles = parent.zeroLevelActualRectangles();
        if (parentRectangles == null) {
            parentRectangles = defaultZeroLevelActualRectangles(parent);
            if (parentRectangles == null) {
                return null;
            }
            final var pr = parentRectangles;
            LOG.log(System.Logger.Level.DEBUG, () -> "Creating default zero-level actual rectangle: " + pr);
        }
        final IPoint shift = IPoint.valueOf(positionXInExtendedMatrix, positionYInExtendedMatrix);
        final List result = new ArrayList(parentRectangles.size());
        for (IRectangularArea parentRectangle : parentRectangles) {
            final IRectangularArea shiftedRectangle = parentRectangle.shift(shift);
            LOG.log(System.Logger.Level.DEBUG, () -> String.format(
                    "Shifting zero-level actual rectangle %s by (%d,%d)",
                    parentRectangle, positionXInExtendedMatrix, positionYInExtendedMatrix));
            result.add(shiftedRectangle);
        }
        return result;
    }

    @Override
    public List>> zeroLevelActualAreaBoundaries() {
        final List>> boundaries = parent.zeroLevelActualAreaBoundaries();
        if (boundaries == null) {
            return super.zeroLevelActualAreaBoundaries();
        }
        final IPoint shift = IPoint.valueOf(positionXInExtendedMatrix, positionYInExtendedMatrix);
        final List>> result = new ArrayList>>();
        for (List> area : boundaries) {
            final List> shiftedArea = new ArrayList>();
            for (List boundary : area) {
                final List shiftedBoundary = new ArrayList();
                for (IPoint p : boundary) {
                    shiftedBoundary.add(p.add(shift));
                }
                LOG.log(System.Logger.Level.DEBUG, () -> String.format(
                        "Shifting zero-level actual area boundary %s by (%d,%d)",
                        boundary, positionXInExtendedMatrix, positionYInExtendedMatrix));
                shiftedArea.add(shiftedBoundary);
            }
            result.add(shiftedArea);
        }
        return result;
    }

    @Override
    public boolean isSpecialMatrixSupported(SpecialImageKind kind) {
        return parent.isSpecialMatrixSupported(kind);
    }

    @Override
    public Optional> readSpecialMatrix(SpecialImageKind kind)
            throws NotYetConnectedException {
        return parent.readSpecialMatrix(kind);
    }

    public boolean isDataReady() {
        return parent.isDataReady();
    }

    @Override
    public Optional metadata() {
        return parent.metadata();
    }

    public void loadResources() {
        parent.loadResources();
        super.loadResources();
    }

    public void freeResources(FlushMode flushMode) {
        super.freeResources(flushMode);
        parent.freeResources(flushMode);
    }

    @Override
    protected Matrix readLittleSubMatrix(
            int resolutionLevel, long fromX, long fromY, long toX, long toY)
            throws NoSuchElementException, NotYetConnectedException {
        checkSubMatrixRanges(resolutionLevel, fromX, fromY, toX, toY, false);
        final long sizeX = toX - fromX;
        final long sizeY = toY - fromY;
        if (sizeX == 0 || sizeY == 0) {
            // Note: bandCount can be greater than in the parent!
            final Class elementType = parent.readSubMatrix(resolutionLevel, 0, 0, sizeX, sizeY).elementType();
            return Arrays.SMM.newMatrix(UpdatablePArray.class, elementType, bandCount, sizeX, sizeY);
        }
        long x = positionXInExtendedMatrix;
        long y = positionYInExtendedMatrix;
        for (int k = 0; k < resolutionLevel; k++) {
            x /= compression;
            y /= compression;
        }
        final long[] parentDimensions = parent.dimensions(resolutionLevel);
        final long aMinX = x;
        final long aMinY = y;
        final long aMaxX = x + parentDimensions[1] - 1;
        final long aMaxY = y + parentDimensions[2] - 1;
        assert aMinX <= aMaxX && aMinY <= aMaxY : "Illegal implementation of " + parent;
        if (aMinX <= fromX && aMinY <= fromY && aMaxX + 1 >= toX && aMaxY + 1 >= toY
                && bandCount == parent.bandCount()) {
            return parent.readSubMatrix(
                    resolutionLevel, fromX - x, fromY - y, toX - x, toY - y);
        }
        final long partFromX = Math.max(fromX, aMinX);
        final long partFromY = Math.max(fromY, aMinY);
        final long partToX = Math.min(toX, aMaxX + 1);
        final long partToY = Math.min(toY, aMaxY + 1);
        final boolean hasActualData = partFromX < partToX && partFromY < partToY;
        final Matrix actual = hasActualData ?
                parent.readSubMatrix(resolutionLevel, partFromX - x, partFromY - y, partToX - x, partToY - y) :
                parent.readSubMatrix(resolutionLevel, 0, 0, 0, 0); // used for retrieving elementType only
        final long aBandCount = actual.dim(0);

        final Matrix result = Arrays.SMM.newMatrix(
                UpdatablePArray.class, actual.elementType(), bandCount, sizeX, sizeY);
        PlanePyramidTools.fillMatrix(result, backgroundColor);
        if (hasActualData && bandCount == 4 && aBandCount < 4) { // the area under the actual data must be opaque
            PlanePyramidTools.fillMatrix(result,
                    partFromX - fromX, partFromY - fromY,
                    partToX - fromX, partToY - fromY,
                    new double[]{1.0, 1.0, 1.0, 1.0});
        }

        final long borderedMinX = aMinX - extendingBorderWidth;
        final long borderedMaxX = aMaxX + extendingBorderWidth;
        final long borderedMinY = aMinY - extendingBorderWidth;
        final long borderedMaxY = aMaxY + extendingBorderWidth;
        if (fromX > borderedMaxX || fromY > borderedMaxY || toX <= borderedMinX || toY <= borderedMinY) {
            return result; // out of bordered actual area
        }
        if (extendingBorderWidth > 0) {
            final long borderedPartFromX = Math.max(fromX, borderedMinX);
            final long borderedPartFromY = Math.max(fromY, borderedMinY);
            final long borderedPartToX = Math.min(toX, borderedMaxX + 1);
            final long borderedPartToY = Math.min(toY, borderedMaxY + 1);
            PlanePyramidTools.fillMatrix(result,
                    borderedPartFromX - fromX, borderedPartFromY - fromY,
                    borderedPartToX - fromX, borderedPartToY - fromY,
                    extendingBorderColor);
        }

        if (!hasActualData) {
            return result; // out of (non-bordered) actual area
        }
        result.subMatrix(0, partFromX - fromX, partFromY - fromY, aBandCount, partToX - fromX, partToY - fromY)
                .array().copy(actual.array());
        // this operator may be slow enough, if bandCount is greater than in the parent!
        if (aBandCount == 1 && bandCount >= 3) { // need to copy monochrome image also into G and B components
            result.subMatrix(1, partFromX - fromX, partFromY - fromY, 2, partToX - fromX, partToY - fromY)
                    .array().copy(actual.array());
            result.subMatrix(2, partFromX - fromX, partFromY - fromY, 3, partToX - fromX, partToY - fromY)
                    .array().copy(actual.array());
        }
        return result;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy