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

net.algart.multimatrix.MultiMatrix Maven / Gradle / Ivy

Go to download

Open-source libraries, providing the base core functionality for SciChains product.

There is a newer version: 4.4.7
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.multimatrix;

import net.algart.arrays.Arrays;
import net.algart.arrays.*;
import net.algart.math.Range;
import net.algart.math.functions.AbstractFunc;
import net.algart.math.functions.Func;
import net.algart.math.functions.LinearFunc;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

// All channels has the same dimensions and element type.
public interface MultiMatrix extends Cloneable {
    int MAX_NUMBER_OF_CHANNELS = 512; // - the same limit as in OpenCV

    double INTENSITY_R_WEIGHT = 0.299;
    double INTENSITY_B_WEIGHT = 0.114;
    double INTENSITY_G_WEIGHT = 1.0 - (INTENSITY_R_WEIGHT + INTENSITY_B_WEIGHT); // ~0.587

    int DEFAULT_R_CHANNEL = 0;
    int DEFAULT_G_CHANNEL = 1;
    int DEFAULT_B_CHANNEL = 2;
    int DEFAULT_ALPHA_CHANNEL = 3;

    List> SUPPORTED_ELEMENT_TYPES = List.of(
            byte.class, short.class, int.class, long.class, float.class, double.class, boolean.class, char.class);

    List> allChannels();

    Class elementType();

    int numberOfChannels();

    Matrix channel(int channelIndex);

    default PArray channelArray(int channelIndex) {
        return channel(channelIndex).array();
    }

    default Matrix mergeChannels() {
        return Matrices.mergeLayers(Arrays.SMM, allChannels());
    }

    default long[] dimensions() {
        return channel(0).dimensions();
    }

    default int dimCount() {
        return channel(0).dimCount();
    }

    default long dim(int n) {
        return channel(0).dim(n);
    }

    default Class arrayType() {
        return channel(0).type(PArray.class);
    }

    default double maxPossibleValue() {
        return Arrays.maxPossibleValue(arrayType(), 1.0);
    }

    default long size() {
        return channel(0).size();
    }

    default boolean isUnsigned() {
        return Arrays.isUnsignedElementType(elementType());
    }

    default boolean isFloatingPoint() {
        return Arrays.isFloatingPointElementType(elementType());
    }

    default int bitsPerElement() {
        return (int) channel(0).array().bitsPerElement();
    }

    long indexInArray(long... coordinates);

    /**
     * Returns equivalent {@link MultiMatrix2D} if this matrix is actually 2-dimensional
     * or throws an exception in another case.
     *
     * @return equivalent {@link MultiMatrix2D}.
     * @throws IllegalStateException if {@link #dimCount()}!=2.
     */
    default MultiMatrix2D asMultiMatrix2D() {
        if (this instanceof MultiMatrix2D) {
            return (MultiMatrix2D) this;
        }
        final List> allChannels = allChannels();
        if (allChannels.get(0).dimCount() == 2) {
            return valueOf2D(allChannels);
        } else {
            throw new IllegalStateException("This matrix is not actually 2-dimensional: " + this);
        }
    }

    default PixelValue getPixel(long indexInArray) {
        return getPixel(indexInArray, null);
    }

    default PixelValue getPixel(long indexInArray, PixelValue result) {
        if (result == null) {
            result = PixelValue.newZeroPixelValue(elementType(), numberOfChannels());
        }
        result.read(this, indexInArray);
        return result;
    }

    default void setPixel(long indexInArray, PixelValue pixelValue) {
        pixelValue.write(this, indexInArray);
    }

    default double getPixelChannel(int channelIndex, long indexInArray) {
        return channelArray(channelIndex).getDouble(indexInArray);
    }

    default void setPixelChannel(int channelIndex, long indexInArray, double value) {
        ((UpdatablePArray) channelArray(channelIndex)).setDouble(indexInArray, value);
    }

    MultiMatrix asPrecision(Class newElementType);

    MultiMatrix toPrecisionIfNot(Class newElementType);

    default MultiMatrix asFloatingPoint() {
        return isFloatingPoint() ? this : asPrecision(float.class);
    }

    default MultiMatrix asFloat() {
        return elementType() == float.class ? this : asPrecision(float.class);
    }

    default MultiMatrix toFloatIfNot() {
        return elementType() == float.class ? this : toPrecisionIfNot(float.class);
    }

    default boolean isMono() {
        return numberOfChannels() == 1;
    }

    default boolean isColor() {
        final int n = numberOfChannels();
        return n == 3 || n == 4;
    }

    default boolean isSimpleMemoryModel() {
        return allChannels().stream().allMatch(channel -> SimpleMemoryModel.isSimpleArray(channel.array()));
    }

    MultiMatrix asMono();

    default MultiMatrix toMonoIfNot() {
        return isMono() ? this : asMono().clone();
    }

    MultiMatrix asOtherNumberOfChannels(int numberOfChannels);

    /**
     * Returns an exact updatable clone of this multi-matrix.
     *
     * @return exact updatable clone of this multi-matrix.
     */
    MultiMatrix clone();

    /**
     * Returns an exact clone of this multi-matrix for every channel, which is not created by
     * {@link SimpleMemoryModel}.
     *
     * 

Note: this operation can optimize access to this matrix in many times, if it consists of lazy-calculated * channels! It performs cloning with maximal speed via multithreading optimization. We recommend to call * it after lazy calculations.

* * @return exact clone of this multi-matrix. */ MultiMatrix actualizeLazy(); default MultiMatrix nonZeroPixels(boolean checkOnlyRGBChannels) { return valueOfMono(nonZeroPixelsMatrix(checkOnlyRGBChannels)); } default MultiMatrix zeroPixels(boolean checkOnlyRGBChannels) { return valueOfMono(zeroPixelsMatrix(checkOnlyRGBChannels)); } default MultiMatrix nonZeroAnyChannel() { return valueOfMono(nonZeroAnyChannelMatrix()); } default MultiMatrix zeroAllChannels() { return valueOfMono(zeroAllChannelsMatrix()); } default MultiMatrix nonZeroRGB() { return MultiMatrix.valueOfMono(nonZeroRGBMatrix()); } default MultiMatrix zeroRGB() { return MultiMatrix.valueOfMono(zeroRGBMatrix()); } default Matrix nonZeroAnyChannelMatrix() { return nonZeroPixelsMatrix(false); } default Matrix nonZeroRGBMatrix() { return nonZeroPixelsMatrix(true); } default Matrix nonZeroPixelsMatrix(boolean checkOnlyRGBChannels) { if (numberOfChannels() == 1 && elementType() == boolean.class) { // - avoid extra cloning return channel(0).cast(BitArray.class); } final List> nonZeroPixelsInChannels = new ArrayList<>(); for (Matrix m : allChannels()) { if (checkOnlyRGBChannels && nonZeroPixelsInChannels.size() >= 3) { break; } nonZeroPixelsInChannels.add(MultiMatrix.nonZeroPixels(m)); } return nonZeroPixelsInChannels.size() == 1 ? nonZeroPixelsInChannels.get(0) : Matrices.asFuncMatrix(Func.MAX, BitArray.class, nonZeroPixelsInChannels) .cast(BitArray.class).clone(); } default Matrix zeroAllChannelsMatrix() { return zeroPixelsMatrix(false); } default Matrix zeroRGBMatrix() { return zeroPixelsMatrix(true); } default Matrix zeroPixelsMatrix(boolean checkOnlyRGBChannels) { return Matrices.asFuncMatrix(Func.REVERSE, BitArray.class, nonZeroPixelsMatrix(checkOnlyRGBChannels)) .cast(BitArray.class).clone(); } default MultiMatrix min(MultiMatrix other) { return asFunc(Func.MIN, other); } default MultiMatrix max(MultiMatrix other) { return asFunc(Func.MAX, other); } default MultiMatrix asFunc(Func funcOfOneArgument) { return asFunc(funcOfOneArgument, this.arrayType()); } default MultiMatrix asFunc(Func funcOfOneArgument, Class requiredType) { final int n = this.numberOfChannels(); final List> channels = new ArrayList<>(); for (int k = 0; k < n; k++) { channels.add(Matrices.asFuncMatrix(funcOfOneArgument, requiredType, channel(k))); } return MultiMatrix.valueOf(channels); } default MultiMatrix asFunc(Func funcOfTwoArguments, MultiMatrix other) { return asFunc(funcOfTwoArguments, other, this.arrayType()); } default MultiMatrix asFunc(Func funcOfTwoArguments, MultiMatrix other, Class requiredType) { Objects.requireNonNull(other, "Null other multi-matrix"); final int n = this.numberOfChannels(); other = other.asOtherNumberOfChannels(n).asPrecision(this.elementType()); assert other.numberOfChannels() == n : "Invalid asOtherNumberOfChannels implementation"; final List> channels = new ArrayList<>(); for (int k = 0; k < n; k++) { channels.add(Matrices.asFuncMatrix(funcOfTwoArguments, requiredType, channel(k), other.channel(k))); } return MultiMatrix.valueOf(channels); } default MultiMatrix mapChannels(Function, Matrix> function) { return MultiMatrix.valueOf(allChannels().stream().map(function::apply).collect(Collectors.toList())); } default List> allChannelsInRGBAOrder() { List> channels = allChannels(); SimpleMultiMatrix.checkNumberOfChannels(channels, true); return channels; } default List> allChannelsInBGRAOrder() { List> channels = allChannels(); SimpleMultiMatrix.checkNumberOfChannels(channels, true); return SimpleMultiMatrix.flipRB(channels); } /** * Returns the only channel if {@link #numberOfChannels()}==1, in another case returns * lazy intensity matrix created from R, G, B channels. * * @return intensity grays-scale matrix equivalent to this multi-matrix. */ default Matrix intensityChannel() { final Matrix result = intensityChannelOrNull(); if (result == null) { throw new IllegalStateException("Cannot convert " + numberOfChannels() + "-channel multichannel matrix into monochrome (intensity) one: " + "automatic conversion to monochrome " + "is possible only for 3- or 4-channel matrix (RGB or RGBA)"); } return result; } default Matrix intensityChannelOrNull() { final int n = numberOfChannels(); if (n == 1) { return channel(0); } if (n != 3 && n != 4) { return null; } final Matrix g = channel(DEFAULT_G_CHANNEL); final Matrix r = channel(DEFAULT_R_CHANNEL); final Matrix b = channel(DEFAULT_B_CHANNEL); final Class resultType = g.type(PArray.class); return Matrices.asFuncMatrix( LinearFunc.getInstance( isFloatingPoint() ? 0.0 : 0.5, INTENSITY_R_WEIGHT, INTENSITY_G_WEIGHT, INTENSITY_B_WEIGHT), resultType, r, g, b); // - for integer matrices, we prefer rounding (by adding 0.5) } default Range rangeOfIntensityOrNull() { final Matrix intensityChannel = intensityChannelOrNull(); return intensityChannel == null ? null : Arrays.rangeOf(intensityChannel.array()); } default Range nonZeroRangeOf(int channelIndex) { return nonZeroRangeOf(null, channel(channelIndex).array()); } default Matrix constantMatrix(double value) { return Matrices.constantMatrix(value, arrayType(), dimensions()).clone(); } default MultiMatrix contrast() { final Range range = rangeOfIntensityOrNull(); if (range == null) { return this; } return contrast(range, false); } default MultiMatrix contrast(Range sourceRangeToContrast, boolean requireMonochromeOrColor) { final Range destRange = Range.valueOf(0.0, maxPossibleValue()); final LinearFunc function = sourceRangeToContrast == null || sourceRangeToContrast.size() == 0 ? null : LinearFunc.getInstance(destRange, sourceRangeToContrast); return correctIntensity(function, requireMonochromeOrColor); } default MultiMatrix correctIntensity( LinearFunc intensityCorrectingFunctionOfOneArgument, boolean requireMonochromeOrColor) { final int n = numberOfChannels(); if (n != 1 && n != 3 && n != 4) { if (requireMonochromeOrColor) { throw new IllegalStateException("Cannot correct intensity of " + n + "-channel multichannel matrix: " + "it is possible only for 1-, 3- or 4-channel matrix (monochrome, RGB or RGBA)"); } else { return this; } } if (intensityCorrectingFunctionOfOneArgument == null) { return this; } if (n == 1) { return MultiMatrix.valueOfMono(Matrices.asFuncMatrix( intensityCorrectingFunctionOfOneArgument, channel(0).type(PArray.class), channel(0))); } final Matrix i = intensityChannel(); final List> channels = new ArrayList<>(allChannels()); final double a = intensityCorrectingFunctionOfOneArgument.a(0); final double b = intensityCorrectingFunctionOfOneArgument.b(); for (int k = 0; k < 3; k++) { // - Important: not "k < n"! We MUST NOT attempt to "contrast" alpha-channel, // it is senseless and can lead to invisible results final Matrix m = channels.get(k); final Range destRange = Range.valueOf(0.0, m.array().maxPossibleValue(1.0)); Func f = new AbstractFunc() { public double get(double... x) { // result = x * ic / i, where ic = ai+b; so result = x * (a+b/i) return x[1] == 0.0 ? 0.0 : destRange.cut(x[0] * (a + b / x[1])); } @Override public double get(double x0, double x1) { return x1 == 0.0 ? 0.0 : destRange.cut(x0 * (a + b / x1)); } }; Matrix result = Matrices.asFuncMatrix(f, m.type(PArray.class), m, i); channels.set(k, result); } return MultiMatrix.valueOf(channels); } default boolean dimEquals(MultiMatrix other) { Objects.requireNonNull(other, "Null multi-matrix"); int dimCount = dimCount(); if (other.dimCount() != dimCount) { return false; } for (int k = 0; k < dimCount; k++) { if (other.dim(k) != dim(k)) { return false; } } return true; } default void checkDimensionEquality(MultiMatrix other, String thisMatrixName, String otherMatrixName) throws SizeMismatchException { if (other == null) { return; } if (!dimEquals(other)) { throw new SizeMismatchException("The " + thisMatrixName + " and " + otherMatrixName + " multi-matrix dimensions mismatch: " + this + " and " + other); } } default void freeResources() { for (Matrix m : allChannels()) { m.freeResources(); } } static MultiMatrix valueOf(List> channels) { return new SimpleMultiMatrix(channels); } static MultiMatrix valueOfRGBA(List> channels) { SimpleMultiMatrix.checkNumberOfChannels(channels, false); return new SimpleMultiMatrix(channels); } static MultiMatrix valueOfBGRA(List> channels) { SimpleMultiMatrix.checkNumberOfChannels(channels, false); return new SimpleMultiMatrix(SimpleMultiMatrix.flipRB(channels)); } static MultiMatrix valueOfMono(Matrix singleChannel) { Objects.requireNonNull(singleChannel, "Null single-channel matrix"); return new SimpleMultiMatrix(Collections.singletonList(singleChannel)); } static MultiMatrix valueOfMerged(Matrix mergedChannels) { Objects.requireNonNull(mergedChannels, "Null mergedChannels"); return new SimpleMultiMatrix(Matrices.asLayers(mergedChannels, MAX_NUMBER_OF_CHANNELS)); } static MultiMatrix2D newMultiMatrix2D(Class elementType, int numberOfChannels, long dimX, long dimY) { return newMultiMatrix2D(elementType, numberOfChannels, dimX, dimY, true); } static MultiMatrix2D zeroConstant2D(Class elementType, int numberOfChannels, long dimX, long dimY) { return newMultiMatrix2D(elementType, numberOfChannels, dimX, dimY, false); } static MultiMatrix2D newMultiMatrix2D( Class elementType, int numberOfChannels, long dimX, long dimY, boolean updatable) { Objects.requireNonNull(elementType, "Null elementType"); if (numberOfChannels <= 0) { throw new IllegalArgumentException("Zero or negative numberOfChannels=" + numberOfChannels); } final List> channels = new ArrayList<>(); for (int k = 0; k < numberOfChannels; k++) { channels.add(updatable ? Arrays.SMM.newMatrix(UpdatablePArray.class, elementType, dimX, dimY) : Matrices.constantMatrix(0.0, Arrays.type(PArray.class, elementType), dimX, dimY)); } return new SimpleMultiMatrix2D(channels); } static MultiMatrix2D valueOf2D(List> channels) { return new SimpleMultiMatrix2D(channels); } static MultiMatrix2D valueOf2DRGBA(List> channels) { SimpleMultiMatrix.checkNumberOfChannels(channels, false); return new SimpleMultiMatrix2D(channels); } static MultiMatrix2D valueOf2DBGRA(List> channels) { SimpleMultiMatrix.checkNumberOfChannels(channels, false); return new SimpleMultiMatrix2D(SimpleMultiMatrix.flipRB(channels)); } static MultiMatrix2D valueOf2DMono(Matrix singleChannel) { Objects.requireNonNull(singleChannel, "Null single-channel matrix"); return new SimpleMultiMatrix2D(Collections.singletonList(singleChannel)); } static MultiMatrix2D valueOf2DMerged(Matrix packedChannels) { Objects.requireNonNull(packedChannels, "Null packedChannels"); return new SimpleMultiMatrix2D(Matrices.asLayers(packedChannels, MAX_NUMBER_OF_CHANNELS)); } static Matrix nonZeroPixels(Matrix matrix) { if (matrix.elementType() == boolean.class) { return matrix.cast(BitArray.class); } return Matrices.asFuncMatrix(Func.IDENTITY, BitArray.class, matrix); } static Matrix zeroPixels(Matrix matrix) { return Matrices.asFuncMatrix(Func.REVERSE, BitArray.class, nonZeroPixels(matrix)); } static List> cloneMatrices(List> channels) { final List> result = new ArrayList<>(); for (Matrix c : channels) { result.add(Matrices.clone(c)); } return result; } static List> actualizeLazyMatrices(List> channels) { final List> result = new ArrayList<>(); for (Matrix c : channels) { result.add(SimpleMemoryModel.isSimpleArray(c.array()) ? c : Matrices.clone(c)); } return result; } static List asMultiMatrices2D(Collection matrices) { final List result = new ArrayList<>(); for (MultiMatrix m : matrices) { result.add(m == null ? null : m.asMultiMatrix2D()); } return result; } // Note: returns null if there are no non-zero values static Range nonZeroRangeOf(PArray array) { return nonZeroRangeOf(null, array); } // Note: returns null if there are no non-zero values static Range nonZeroRangeOf(ArrayContext context, PArray array) { final NonZeroRangeCalculator calculator = new NonZeroRangeCalculator(context, array); calculator.process(); return calculator.resultRange; } /** * Pixel value: primitive Java array of a channel + hashCode/equals. * Note that this class is partially mutable: it allows modifying elements of channels. * If you modify them, multithreaded usage requires external synchronization. */ abstract class PixelValue { private int hashCode = -1; boolean hashCodeCalculated = false; private PixelValue() { } public static PixelValue valueOf(Object channelsJavaArray) { return valueOf(channelsJavaArray, true); } public static PixelValue newZeroPixelValue(Class elementType, int numberOfChannels) { return valueOf(java.lang.reflect.Array.newInstance(elementType, numberOfChannels), false); } static PixelValue valueOf(Object channelsJavaArray, boolean doClone) { Objects.requireNonNull(channelsJavaArray, "Null channels java array"); if (channelsJavaArray instanceof boolean[] channels) { return new Bit(doClone ? channels.clone() : channels); } else if (channelsJavaArray instanceof char[] channels) { return new Char(doClone ? channels.clone() : channels); } else if (channelsJavaArray instanceof byte[] channels) { return new Byte(doClone ? channels.clone() : channels); } else if (channelsJavaArray instanceof short[] channels) { return new Short(doClone ? channels.clone() : channels); } else if (channelsJavaArray instanceof int[] channels) { return new Int(doClone ? channels.clone() : channels); } else if (channelsJavaArray instanceof long[] channels) { return new Long(doClone ? channels.clone() : channels); } else if (channelsJavaArray instanceof float[] channels) { return new Float(doClone ? channels.clone() : channels); } else if (channelsJavaArray instanceof double[] channels) { return new Double(doClone ? channels.clone() : channels); } else { throw new IllegalArgumentException("The passed java-array argument is not boolean[], char[], " + "byte[], short[], int[], long[], float[] or double[] (it is " + channelsJavaArray.getClass().getSimpleName() + ")"); } } @Override public final int hashCode() { if (!hashCodeCalculated) { hashCode = hashCodeImpl(); hashCodeCalculated = true; } return hashCode; } @Override public abstract boolean equals(Object obj); public abstract int numberOfChannels(); public abstract Class elementType(); public Object getChannels() { return getChannels(null); } public Object getChannels(Object result) { if (result == null) { result = java.lang.reflect.Array.newInstance(elementType(), numberOfChannels()); } System.arraycopy(channelsRef(), 0, result, 0, numberOfChannels()); return result; } public void setChannels(Object channels) { hashCodeCalculated = false; System.arraycopy(channels, 0, channelsRef(), 0, numberOfChannels()); } public abstract double getChannel(int channelIndex); public abstract void setChannel(int channelIndex, double value); /** * Reads this pixel value from the specified multi-matrix at the given array position. * * @param multiMatrix some multi-matrix * @param indexInArray in the built-in matrix array. * @throws IndexOutOfBoundsException if this {@link #numberOfChannels()} is less than * number of channels in the multi-matrix. * @throws ClassCastException if this {@link #elementType()} differs from * the element type of the multi-matrix. */ public abstract void read(MultiMatrix multiMatrix, long indexInArray); /** * Stores this pixel value in the specified multi-matrix at the given array position. * All channels in the multi-matrix must be updatable ({@code Matrix}). * * @param multiMatrix some multi-matrix * @param indexInArray in the built-in matrix array. * @throws IndexOutOfBoundsException if this {@link #numberOfChannels()} is less than * number of channels in the multi-matrix. * @throws ClassCastException if this {@link #elementType()} differs from * the element type of the multi-matrix * or if some channels of the multi-matrix are not updatable. */ public abstract void write(MultiMatrix multiMatrix, long indexInArray); public double[] getDoubleChannels() { return getDoubleChannels(null); } public double[] getDoubleChannels(double[] result) { if (result == null) { result = new double[numberOfChannels()]; } for (int k = 0; k < result.length; k++) { result[k] = getChannel(k); } return result; } public float[] getFloatChannels() { return getFloatChannels(null); } public float[] getFloatChannels(float[] result) { if (result == null) { result = new float[numberOfChannels()]; } for (int k = 0; k < result.length; k++) { result[k] = (float) getChannel(k); } return result; } public int[] getIntChannels() { return getIntChannels(null); } public int[] getIntChannels(int[] result) { if (result == null) { result = new int[numberOfChannels()]; } for (int k = 0; k < result.length; k++) { result[k] = (int) getChannel(k); } return result; } abstract Object channelsRef(); abstract int hashCodeImpl(); public static class Bit extends PixelValue { private final boolean[] channels; Bit(boolean[] channels) { this.channels = Objects.requireNonNull(channels, "Null channels"); } @Override public int numberOfChannels() { return channels.length; } @Override public Class elementType() { return boolean.class; } @Override public double getChannel(int channelIndex) { return channels[channelIndex] ? 1.0 : 0.0; } @Override public void setChannel(int channelIndex, double value) { hashCodeCalculated = false; channels[channelIndex] = value != 0.0; } @Override public void read(MultiMatrix multiMatrix, long indexInArray) { hashCodeCalculated = false; for (int k = 0, n = multiMatrix.numberOfChannels(); k < n; k++) { channels[k] = ((BitArray) multiMatrix.channelArray(k)).getBit(indexInArray); } } @Override public void write(MultiMatrix multiMatrix, long indexInArray) { for (int k = 0, n = multiMatrix.numberOfChannels(); k < n; k++) { ((UpdatableBitArray) multiMatrix.channelArray(k)).setBit(indexInArray, channels[k]); } } @Override public String toString() { StringBuilder sb = new StringBuilder("bit pixel ["); for (int k = 0; k < channels.length; k++) { if (k > 0) { sb.append(", "); } sb.append(channels[k] ? '1' : '0'); } return sb.append("]").toString(); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Bit aBit = (Bit) o; return java.util.Arrays.equals(channels, aBit.channels); } @Override Object channelsRef() { return channels; } @Override int hashCodeImpl() { return java.util.Arrays.hashCode(channels); } } public static class Char extends PixelValue { private final char[] channels; Char(char[] channels) { this.channels = Objects.requireNonNull(channels, "Null channels"); } @Override public int numberOfChannels() { return channels.length; } @Override public Class elementType() { return char.class; } @Override public char[] channelsRef() { return channels; } @Override public double getChannel(int channelIndex) { return channels[channelIndex]; } @Override public void setChannel(int channelIndex, double value) { hashCodeCalculated = false; channels[channelIndex] = (char) value; } @Override public void read(MultiMatrix multiMatrix, long indexInArray) { hashCodeCalculated = false; for (int k = 0, n = multiMatrix.numberOfChannels(); k < n; k++) { channels[k] = ((CharArray) multiMatrix.channelArray(k)).getChar(indexInArray); } } @Override public void write(MultiMatrix multiMatrix, long indexInArray) { for (int k = 0, n = multiMatrix.numberOfChannels(); k < n; k++) { ((UpdatableCharArray) multiMatrix.channelArray(k)).setChar(indexInArray, channels[k]); } } @Override public String toString() { StringBuilder sb = new StringBuilder("char pixel ["); for (int k = 0; k < channels.length; k++) { if (k > 0) { sb.append(", "); } sb.append(String.format(Locale.US, "'\\u%04X'", (int) channels[k])); } return sb.append("]").toString(); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Char aChar = (Char) o; return java.util.Arrays.equals(channels, aChar.channels); } @Override int hashCodeImpl() { return java.util.Arrays.hashCode(channels); } } public static class Byte extends PixelValue { private final byte[] channels; Byte(byte[] channels) { this.channels = Objects.requireNonNull(channels, "Null channels"); } @Override public int numberOfChannels() { return channels.length; } @Override public Class elementType() { return byte.class; } @Override public double getChannel(int channelIndex) { return channels[channelIndex] & 0xFF; } @Override public void setChannel(int channelIndex, double value) { hashCodeCalculated = false; channels[channelIndex] = (byte) value; } @Override public void read(MultiMatrix multiMatrix, long indexInArray) { hashCodeCalculated = false; for (int k = 0, n = multiMatrix.numberOfChannels(); k < n; k++) { channels[k] = (byte) ((ByteArray) multiMatrix.channelArray(k)).getByte(indexInArray); } } @Override public void write(MultiMatrix multiMatrix, long indexInArray) { for (int k = 0, n = multiMatrix.numberOfChannels(); k < n; k++) { ((UpdatableByteArray) multiMatrix.channelArray(k)).setByte(indexInArray, channels[k]); } } @Override public String toString() { StringBuilder sb = new StringBuilder("byte pixel ["); for (int k = 0; k < channels.length; k++) { if (k > 0) { sb.append(", "); } sb.append(String.format(Locale.US, "0x%02X", channels[k] & 0xFF)); } return sb.append("]").toString(); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Byte aByte = (Byte) o; return java.util.Arrays.equals(channels, aByte.channels); } @Override Object channelsRef() { return channels; } @Override int hashCodeImpl() { return java.util.Arrays.hashCode(channels); } } public static class Short extends PixelValue { private final short[] channels; Short(short[] channels) { this.channels = Objects.requireNonNull(channels, "Null channels"); } @Override public int numberOfChannels() { return channels.length; } @Override public Class elementType() { return short.class; } @Override public double getChannel(int channelIndex) { return channels[channelIndex] & 0xFFFF; } @Override public void setChannel(int channelIndex, double value) { hashCodeCalculated = false; channels[channelIndex] = (short) value; } @Override public void read(MultiMatrix multiMatrix, long indexInArray) { hashCodeCalculated = false; for (int k = 0, n = multiMatrix.numberOfChannels(); k < n; k++) { channels[k] = (short) ((ShortArray) multiMatrix.channelArray(k)).getShort(indexInArray); } } @Override public void write(MultiMatrix multiMatrix, long indexInArray) { for (int k = 0, n = multiMatrix.numberOfChannels(); k < n; k++) { ((UpdatableShortArray) multiMatrix.channelArray(k)).setShort(indexInArray, channels[k]); } } @Override public String toString() { StringBuilder sb = new StringBuilder("short pixel ["); for (int k = 0; k < channels.length; k++) { if (k > 0) { sb.append(", "); } sb.append(String.format(Locale.US, "0x%04X", channels[k] & 0xFFFF)); } return sb.append("]").toString(); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Short aShort = (Short) o; return java.util.Arrays.equals(channels, aShort.channels); } @Override Object channelsRef() { return channels; } @Override int hashCodeImpl() { return java.util.Arrays.hashCode(channels); } } public static class Int extends PixelValue { private final int[] channels; Int(int[] channels) { this.channels = Objects.requireNonNull(channels, "Null channels"); } @Override public int numberOfChannels() { return channels.length; } @Override public Class elementType() { return int.class; } @Override public double getChannel(int channelIndex) { return channels[channelIndex]; } @Override public void setChannel(int channelIndex, double value) { hashCodeCalculated = false; channels[channelIndex] = (int) value; } @Override public void read(MultiMatrix multiMatrix, long indexInArray) { hashCodeCalculated = false; for (int k = 0, n = multiMatrix.numberOfChannels(); k < n; k++) { channels[k] = ((IntArray) multiMatrix.channelArray(k)).getInt(indexInArray); } } @Override public void write(MultiMatrix multiMatrix, long indexInArray) { for (int k = 0, n = multiMatrix.numberOfChannels(); k < n; k++) { ((UpdatableIntArray) multiMatrix.channelArray(k)).setInt(indexInArray, channels[k]); } } @Override public String toString() { StringBuilder sb = new StringBuilder("int pixel ["); for (int k = 0; k < channels.length; k++) { if (k > 0) { sb.append(", "); } if (channels[k] == 0) { sb.append('0'); } else { sb.append("0x").append(Integer.toHexString(channels[k]).toUpperCase()); } } return sb.append("]").toString(); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Int aInt = (Int) o; return java.util.Arrays.equals(channels, aInt.channels); } @Override Object channelsRef() { return channels; } @Override int hashCodeImpl() { return java.util.Arrays.hashCode(channels); } } public static class Long extends PixelValue { private final long[] channels; Long(long[] channels) { this.channels = Objects.requireNonNull(channels, "Null channels"); } @Override public int numberOfChannels() { return channels.length; } @Override public Class elementType() { return long.class; } @Override public double getChannel(int channelIndex) { return channels[channelIndex]; } @Override public void setChannel(int channelIndex, double value) { hashCodeCalculated = false; channels[channelIndex] = (long) value; } @Override public void read(MultiMatrix multiMatrix, long indexInArray) { hashCodeCalculated = false; for (int k = 0, n = multiMatrix.numberOfChannels(); k < n; k++) { channels[k] = ((LongArray) multiMatrix.channelArray(k)).getLong(indexInArray); } } @Override public void write(MultiMatrix multiMatrix, long indexInArray) { for (int k = 0, n = multiMatrix.numberOfChannels(); k < n; k++) { ((UpdatableLongArray) multiMatrix.channelArray(k)).setLong(indexInArray, channels[k]); } } @Override public String toString() { StringBuilder sb = new StringBuilder("long pixel ["); for (int k = 0; k < channels.length; k++) { if (k > 0) { sb.append(", "); } if (channels[k] == 0) { sb.append('0'); } else { sb.append("0x").append(java.lang.Long.toHexString(channels[k]).toUpperCase()); } } return sb.append("]").toString(); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Long aLong = (Long) o; return java.util.Arrays.equals(channels, aLong.channels); } @Override Object channelsRef() { return channels; } @Override int hashCodeImpl() { return java.util.Arrays.hashCode(channels); } } public static class Float extends PixelValue { private final float[] channels; Float(float[] channels) { this.channels = Objects.requireNonNull(channels, "Null channels"); } @Override public int numberOfChannels() { return channels.length; } @Override public Class elementType() { return float.class; } @Override public double getChannel(int channelIndex) { return channels[channelIndex]; } @Override public void setChannel(int channelIndex, double value) { hashCodeCalculated = false; channels[channelIndex] = (float) value; } @Override public void read(MultiMatrix multiMatrix, long indexInArray) { hashCodeCalculated = false; for (int k = 0, n = multiMatrix.numberOfChannels(); k < n; k++) { channels[k] = ((FloatArray) multiMatrix.channelArray(k)).getFloat(indexInArray); } } @Override public void write(MultiMatrix multiMatrix, long indexInArray) { for (int k = 0, n = multiMatrix.numberOfChannels(); k < n; k++) { ((UpdatableFloatArray) multiMatrix.channelArray(k)).setFloat(indexInArray, channels[k]); } } @Override public String toString() { return "float pixel " + java.util.Arrays.toString(channels); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Float aFloat = (Float) o; return java.util.Arrays.equals(channels, aFloat.channels); } @Override Object channelsRef() { return channels; } @Override int hashCodeImpl() { return java.util.Arrays.hashCode(channels); } } public static class Double extends PixelValue { private final double[] channels; Double(double[] channels) { this.channels = Objects.requireNonNull(channels, "Null channels"); } @Override public int numberOfChannels() { return channels.length; } @Override public Class elementType() { return double.class; } @Override public double getChannel(int channelIndex) { return channels[channelIndex]; } @Override public void setChannel(int channelIndex, double value) { hashCodeCalculated = false; channels[channelIndex] = value; } @Override public void read(MultiMatrix multiMatrix, long indexInArray) { hashCodeCalculated = false; for (int k = 0, n = multiMatrix.numberOfChannels(); k < n; k++) { channels[k] = ((DoubleArray) multiMatrix.channelArray(k)).getDouble(indexInArray); } } @Override public void write(MultiMatrix multiMatrix, long indexInArray) { for (int k = 0, n = multiMatrix.numberOfChannels(); k < n; k++) { ((UpdatableDoubleArray) multiMatrix.channelArray(k)).setDouble(indexInArray, channels[k]); } } @Override public String toString() { return "double pixel " + java.util.Arrays.toString(channels); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Double aDouble = (Double) o; return java.util.Arrays.equals(channels, aDouble.channels); } @Override Object channelsRef() { return channels; } @Override int hashCodeImpl() { return java.util.Arrays.hashCode(channels); } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy