
net.algart.matrices.morphology.BasicRankMorphology Maven / Gradle / Ivy
Show all versions of algart Show documentation
/*
* The MIT License (MIT)
*
* Copyright (c) 2007-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.matrices.morphology;
import net.algart.arrays.*;
import net.algart.math.functions.Func;
import net.algart.math.patterns.Pattern;
import net.algart.matrices.StreamingApertureProcessor;
import java.util.Objects;
/**
* Almost complete implementation of {@link RankMorphology} interface with an instantiation method
* of some complete implementation.
*
* This class fully implements all methods, declared in {@link RankMorphology} interface,
* according to the detailed specifications listed in the comments to that interface.
* The only methods, which stay abstract here, are the following 3 methods of {@link AbstractMorphology}
* superclass:
*
*
* - {@link #context(ArrayContext newContext)},
* - {@link #asDilationOrErosion(Matrix src, Pattern pattern, boolean isDilation)},
* - {@link
* #dilationOrErosion(Matrix dest, Matrix src, Pattern pattern, boolean isDilation, boolean disableMemoryAllocation)}.
*
*
*
* All other methods are implemented via the simple calls of
* {@link StreamingApertureProcessor#asProcessed(Class, Matrix, java.util.List, Pattern) asProcessed}
* and {@link StreamingApertureProcessor#process(Matrix, Matrix, java.util.List, Pattern) process}
* method of the corresponding streaming aperture processors, listed in {@link RankProcessors} class.
*
* This package provides some concrete complete inheritor, which implements also these 3 methods.
* This inheritor is instantiated by the following method:
*
*
* - {@link #getInstance(ArrayContext, double, CustomRankPrecision)}.
*
*
* But you can also inherit this class yourself and implement dilation and erosion (the basic operations
* of the mathematical morphology) in other way.
*
* @author Daniel Alievsky
*/
public abstract class BasicRankMorphology extends AbstractRankMorphology implements RankMorphology {
final boolean interpolated;
final int[] bitLevels; // we never change them, so the standard cloning scheme is suitable
BasicRankMorphology(ArrayContext context, boolean interpolated, int[] bitLevels) {
super(context);
Objects.requireNonNull(bitLevels, "Null bitLevels argument");
this.bitLevels = bitLevels.clone();
this.interpolated = interpolated;
RankProcessors.getPercentiler(null, interpolated, this.bitLevels); // checking bitLevels
}
/**
* Returns new instance of some inheritor of this class, implementing dilation and erosion operations
* via the percentiles. Namely, in the created object
* {@link #dilation(Matrix, Pattern) dilation}(m,pattern)
method is equivalent to
* {@link #percentile(Matrix, double, Pattern)
* percentile}(m,dilationLevel*N,pattern)
and
* {@link #erosion(Matrix, Pattern) erosion}(m,pattern)
method is equivalent to
* {@link #percentile(Matrix, double, Pattern)
* percentile}(m,dilationLevel*N,pattern.{@link Pattern#symmetric() symmetric()})
,
* where N=pattern.{@link Pattern#pointCount() pointCount()}-1
* and dilationLevel
is the argument of this method. This argument must be in range
* 0.0 ≤ dilationLevel ≤ 1.0
.
*
* More precisely, in the created object the methods
*
* - {@link #asDilationOrErosion(Matrix src, Pattern pattern, boolean isDilation)} and
* - {@link
* #dilationOrErosion(Matrix dest, Matrix src, Pattern pattern, boolean isDilation, boolean disableMemoryAllocation)}
*
*
* work in the following way.
*
*
Let the double value r be
* r=dilationLevel*(pattern.{@link Pattern#pointCount() pointCount()}-1)
* in a case of dilation (isDilation
argument is true
) or
* r=(1.0-dilationLevel)*(pattern.{@link Pattern#pointCount() pointCount()}-1)
* in a case of erosion (isDilation
argument is false
).
* Then, let index
be:
*
* - this double value
index
=r, if the element type of
* the source matrix src
is float
or double
or
* if we are using the precise histogram model
* (precision.{@link CustomRankPrecision#interpolated() interpolated()}
is true
);
* - or the rounded (
long
) integer value index=Math.round(
r)
,
* if the source matrix is fixed-point and we are using the simple histogram model
* (precision.{@link CustomRankPrecision#interpolated() interpolated()}
is false
).
*
*
* At last, let P be pattern
* in a case of dilation (isDilation
argument is true
)
* or pattern.{@link Pattern#symmetric() symmetric()}
* in a case of erosion (isDilation
argument is false
).
*
*
Then, in the returned object the 1st method
* {@link #asDilationOrErosion(Matrix, Pattern, boolean) asDilationOrErosion}
* is equivalent to
* {@link #asPercentile(Matrix, double, Pattern) asPercentile}(src,index,P)
* and the 2nd method {@link #dilationOrErosion(Matrix, Matrix, Pattern, boolean, boolean) dilationOrErosion}
* is equivalent to
* {@link #percentile(Matrix, Matrix, double, Pattern) percentile}(dest,src,index,P)
.
* (The disableMemoryAllocation
argument of
* {@link #dilationOrErosion(Matrix, Matrix, Pattern, boolean, boolean) dilationOrErosion} method
* is ignored by this implementation.)
*
*
Please note: using Math.round
for rounding the percentile index does not correspond
* to the standard behaviour of integer percentiles, which are rounded, in a case of the precise histogram model,
* according more complicated rules (see comments to
* {@link RankMorphology#percentile(Matrix, Matrix, Pattern)} and
* {@link Histogram#iPreciseValue(long[], double)} methods).
* So, this method does not try to round the real percentile index r in the precise histogram model.
* But in the simple histogram model, when the element type is fixed-point and some form of rounding is required
* in any case, this method calls Math.round
— it provides
* better "symmetry" between dilation and erosion operations.
*
*
The precise behaviour of all methods of {@link RankMorphology} interface in the returned object
* depends on precision
object: see comments to {@link CustomRankPrecision} and
* {@link RankMorphology}.
*
* @param context the {@link #context() context} that will be used by this object;
* can be {@code null}, then it will be ignored.
* @param dilationLevel the level: 1.0 means strict dilation and erosion as described in {@link Morphology}
* interface, 0.5 means median, 0.0 means that dilation works like erosion and erosion
* works like dilation (but with a {@link Pattern#symmetric() symmetric} pattern).
* @param precision precision characteristics of all rank operations, performed by the created object.
* @return new instance of this class.
* @throws NullPointerException if precision
argument is {@code null}
* @throws IllegalArgumentException if the dilationLevel
argument
* is out of 0.0..1.0
range
* of if bitLevels=precision.{@link CustomRankPrecision#bitLevels()
* bitLevels()}
are incorrect:
* bitLevels.length==0
, or if bitLevels.length>31
,
* or if some of the elements bitLevels
is not in 1..30 range, or if
* bitLevels
[k]>=bitLevels
[k+1]
* for some k.
*/
public static RankMorphology getInstance(
ArrayContext context,
double dilationLevel,
CustomRankPrecision precision) {
return new FixedPercentileRankMorphology(context, dilationLevel,
precision.interpolated(), precision.bitLevels());
}
@Override
public boolean isPseudoCyclic() {
return true;
}
@Override
protected abstract Matrix extends PArray> asDilationOrErosion(
Matrix extends PArray> src,
Pattern pattern,
boolean isDilation);
@Override
protected abstract Matrix extends UpdatablePArray> dilationOrErosion(
Matrix extends UpdatablePArray> dest,
Matrix extends PArray> src,
Pattern pattern,
boolean isDilation,
boolean disableMemoryAllocation);
@Override
public Matrix extends PArray> asPercentile(
Matrix extends PArray> src,
Matrix extends PArray> percentileIndexes,
Pattern pattern) {
StreamingApertureProcessor percentiler = RankProcessors.getPercentiler(context(), interpolated, bitLevels);
return percentiler.asProcessed(src.array().type(), src, percentileIndexes, pattern);
}
@Override
public void percentile(
Matrix extends UpdatablePArray> dest,
Matrix extends PArray> src,
Matrix extends PArray> percentileIndexes,
Pattern pattern) {
StreamingApertureProcessor percentiler = RankProcessors.getPercentiler(context(), interpolated, bitLevels);
percentiler.process(dest, src, percentileIndexes, pattern);
}
@Override
public Matrix asRank(
Class extends T> requiredType,
Matrix extends PArray> baseMatrix,
Matrix extends PArray> rankedMatrix,
Pattern pattern) {
StreamingApertureProcessor ranker = RankProcessors.getRanker(context(),
interpolated && PFloatingArray.class.isAssignableFrom(requiredType), bitLevels);
// if requiredType is not floating-point, interpolation leads to the same ranks
return ranker.asProcessed(requiredType, baseMatrix, rankedMatrix, pattern);
}
@Override
public void rank(
Matrix extends UpdatablePArray> dest,
Matrix extends PArray> baseMatrix,
Matrix extends PArray> rankedMatrix,
Pattern pattern) {
StreamingApertureProcessor ranker = RankProcessors.getRanker(context(),
interpolated && PFloatingArray.class.isAssignableFrom(dest.type()), bitLevels);
// if dest.type() is not floating-point, interpolation leads to the same ranks
ranker.process(dest, baseMatrix, rankedMatrix, pattern);
}
@Override
public Matrix extends PArray> asMeanBetweenPercentiles(
Matrix extends PArray> src,
Matrix extends PArray> fromPercentilesIndexes,
Matrix extends PArray> toPercentilesIndexes,
Pattern pattern,
double filler) {
StreamingApertureProcessor averager = RankProcessors.getAveragerBetweenPercentiles(context(), filler,
interpolated, bitLevels);
return averager.asProcessed(src.array().type(), src, fromPercentilesIndexes, toPercentilesIndexes, pattern);
}
@Override
public void meanBetweenPercentiles(
Matrix extends UpdatablePArray> dest,
Matrix extends PArray> src,
Matrix extends PArray> fromPercentilesIndexes,
Matrix extends PArray> toPercentilesIndexes,
Pattern pattern,
double filler) {
StreamingApertureProcessor averager = RankProcessors.getAveragerBetweenPercentiles(context(), filler,
interpolated, bitLevels);
averager.process(dest, src, fromPercentilesIndexes, toPercentilesIndexes, pattern);
}
@Override
public Matrix extends PArray> asMeanBetweenValues(
Matrix extends PArray> src,
Matrix extends PArray> minValues,
Matrix extends PArray> maxValues,
Pattern pattern,
double filler) {
StreamingApertureProcessor averager = RankProcessors.getAveragerBetweenValues(context(), filler,
interpolated, bitLevels);
return averager.asProcessed(src.array().type(), src, minValues, maxValues, pattern);
}
@Override
public void meanBetweenValues(
Matrix extends UpdatablePArray> dest,
Matrix extends PArray> src,
Matrix extends PArray> minValues,
Matrix extends PArray> maxValues,
Pattern pattern,
double filler) {
StreamingApertureProcessor averager = RankProcessors.getAveragerBetweenValues(context(), filler,
interpolated, bitLevels);
averager.process(dest, src, minValues, maxValues, pattern);
}
@Override
public Matrix extends PArray> asFunctionOfSum(
Matrix extends PArray> src,
Pattern pattern,
Func processingFunc) {
StreamingApertureProcessor averager = RankProcessors.getSummator(context(), processingFunc);
return averager.asProcessed(src.array().type(), src, pattern);
}
@Override
public void functionOfSum(
Matrix extends UpdatablePArray> dest,
Matrix extends PArray> src,
Pattern pattern,
Func processingFunc) {
StreamingApertureProcessor averager = RankProcessors.getSummator(context(), processingFunc);
averager.process(dest, src, pattern);
}
@Override
public Matrix extends PArray> asFunctionOfPercentilePair(
Matrix extends PArray> src,
Matrix extends PArray> percentilesIndexes1,
Matrix extends PArray> percentilesIndexes2,
Pattern pattern,
Func processingFunc) {
StreamingApertureProcessor contraster = RankProcessors.getPercentilePairProcessor(context(),
processingFunc, interpolated, bitLevels);
return contraster.asProcessed(src.array().type(),
src, src, percentilesIndexes1, percentilesIndexes2, pattern);
}
@Override
public void functionOfPercentilePair(
Matrix extends UpdatablePArray> dest, Matrix extends PArray> src,
Matrix extends PArray> percentilesIndexes1,
Matrix extends PArray> percentilesIndexes2,
Pattern pattern,
Func processingFunc) {
StreamingApertureProcessor contraster = RankProcessors.getPercentilePairProcessor(context(),
processingFunc, interpolated, bitLevels);
contraster.process(dest, src, src, percentilesIndexes1, percentilesIndexes2, pattern);
}
}