net.algart.matrices.ApertureProcessor 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;
import net.algart.arrays.Matrix;
import net.algart.math.IRectangularArea;
import java.util.Map;
/**
* Abstract aperture matrix processor: an algorithm, processing a group of
* {@link Matrix n-dimensional matrices} and returning a group of resulting matrices,
* where the value of every element of every resulting matrix depends only on
* the elements of the source matrices in some aperture "around" the same position.
* This aperture should be a subset of some rectangular area ({@link IRectangularArea});
* this area (possibly different for different source matrices) is called a dependence aperture
* of the aperture processor. All matrices must have same dimensions.
* This interface is used, for example, together with {@link TiledApertureProcessorFactory} class.
*
* Below is more precise formal definition of the aperture matrix processors.
* All classes, implementing this interface, should comply this definition.
*
* The main method of this interface, {@link #process(Map, Map) process},
* works with some group of {@link Matrix AlgART matrices} Mi, i∈Q,
* called the arguments or source matrices,
* and a group of another AlgART matrices M'j, j∈R,
* called the results or resulting matrices.
* Here the indexes i and j (elements of the index sets Q and R)
* can be objects of any nature and should be represented by Java objects of the generic type K
—
* the generic argument of this interface (for example, Integer
or String
).
* The indexes of arguments and results are also called their keys.
* The group of the source arguments and the group of results are represented by java.util.Map
,
* more precisely, by a generic type java.util.Map<K, {@link Matrix}<?>>
:
*
*
* {@link #process(Map, Map)
* process}(java.util.Map<K, {@link Matrix}<?>> dest, java.util.Map<K, {@link Matrix}<?>> src)
*
*
* The group dest
of resulting matrices M'j can be passed to
* {@link #process(Map, Map) process} method, or can be formed by this method dynamically
* (an empty map dest
can be passed in this case),
* or it is possible that a part of resulting matrices is passed in dest
while calling the method
* and other resulting matrices are added to dest
map by the method itself.
*
* In all comments, the designations M'j
* and R means the group (dest
) and the index set
* (dest.keySet()
) of resulting matrices
* after the call of {@link #process(Map, Map) process} method.
*
* Some of the resulting matrices M'j (but not the source arguments),
* passed to {@link #process(Map, Map) process} method, can be {@code null} —
* it means that {@link #process(Map, Map) process} method must create them itself.
* (And it also may create some additional matrices M'j, not contained
* in dest
while calling this method.)
* All non-null resulting matrices M'j, passed to the method in dest
* argument, must be updatable, i.e. their {@link Matrix#array() built-in arrays}
* must implement {@link net.algart.arrays.UpdatableArray} interface.
* The dimensions of all source and non-null resulting matrices must be the same:
* {@link Matrix#dimEquals(Matrix)} method must return true
for any pair of them.
* The sets of indexes of arguments Q and results R, as well as the dimensions of matrices,
* may vary for different calls of {@link #process(Map, Map) process} method.
* (Even the number of dimensions n may vary, but it is a rarer situation.)
* Degenerated cases Q=∅ (no arguments) and R=∅ (no results) are allowed.
* If Q=∅ (no matrices are passed in src
map) and
* the dest
map, passed to {@link #process(Map, Map) process} method,
* either is empty or contains only {@code null} matrices,
* then {@link #process(Map, Map) process} method usually does nothing.
* See {@link #process(Map, Map) comments to that method} for additional details.
*
* For each source argument Mi this aperture processor defines
* the corresponding dependence aperture Ai: a rectangular set of
* integer points, represented by {@link IRectangularArea} class and returned by
* {@link #dependenceAperture(Object) dependenceAperture(i)} method.
* This aperture can depend only on the argument index i and cannot vary for different calls
* of {@link #dependenceAperture(Object) dependenceAperture} method for the same processor.
*
* The goal of the main {@link #process(Map, Map) process} method is
* to fill elements of the resulting matrices M'j
* (usually all elements of all M'j, j∈R) on the base
* of the elements of the source matrices Mi.
* There should be a guarantee, that every element of any resulting n-dimensional matrix
* M'j with coordinates
* x = (x0, x1, ..., xn−1)
* can depend only on elements of each source matrix Mi with coordinates
*
*
* x+a =
* x0+a0,
* x1+a1, ...,
* xn−1+an−1,
*
*
* where
* a = (a0, a1, ..., an−1)
* is one of points belonging to the corresponding dependence aperture
* Ai={@link #dependenceAperture(Object) dependenceAperture(i)}.
* Please draw attention that we use plus sing + in this definition, instead of more traditional minus sign −
* (used, for example, in specifications of {@link StreamingApertureProcessor}
* or {@link net.algart.matrices.morphology.RankMorphology}).
* In many cases, it is convenient to use {@link DependenceApertureBuilder} class to create
* dependence apertures for an aperture processor.
*
* If this rule is violated, for example, if the aperture, returned by
* {@link #dependenceAperture(Object) dependenceAperture} method, is too little (the results depend on elements
* outside this aperture), then {@link #process(Map, Map) process} method still works,
* but the results of processing can be incorrect.
* If the aperture, returned by {@link #dependenceAperture(Object) dependenceAperture} method, is too large
* (the results do not depend on most of elements in the aperture),
* then {@link #process(Map, Map) process} method still works and the results are correct,
* but excessively large aperture sizes can slow down the calculations.
*
* The {@link #process(Map, Map) process} method fills all resulting matrices.
* But if some of the resulting matrices M'j, passed via dest
map
* while calling this method, is {@code null}, then it must be automatically created.
* Moreover, if some of the resulting matrices M'j was not {@code null}
* while calling this method, the method still may create new matrix for this key (index) j
* and store it in dest
map instead of the original matrix.
* In addition, this method may create (and store in dest
map) some new resulting matrices
* with new keys (indexes), which were not contained in dest
while calling the method.
* But here must be the following guarantee: the set of indexes j of the resulting matrix
* M'j, created and stored in dest
map by this method in addition to
* existing key/value pairs in dest
map,
* cannot vary for different calls of {@link #process(Map, Map) process}
* method of the same instance of the processor.
*
* If {@link #process(Map, Map) process} method allocates some resulting matrix,
* then all these matrices must be created with the same dimensions as all the source and non-null resulting
* matrices, passed to the method.
* The type of elements of the newly created matrix is selected by some rules, depending on the implementation.
* Typical example — it is selected to be equal to the element type of some source matrices.
* But here must be the following guarantee: the element type of the resulting matrix M'j
* with the given index j cannot vary for different calls of {@link #process(Map, Map) process}
* method of the same processor, if the element types of all source matrices Mi
* do not vary while these calls. In other words, a concrete instance of the processor may
* select the element type of newly created matrices only on the base of the element types of the arguments,
* but not, for example, on the base of the matrix dimensions.
*
* @param the type of the keys.
* @author Daniel Alievsky
*/
public interface ApertureProcessor {
/**
* Main method of the aperture processor, that performs processing the source matrices src
* with saving results in the resulting matrices dest
.
* The source matrices
* Mi, i∈Q
* are passed in src
map: Mi=src.get(i)
.
* The resulting matrices
* M'j, j∈R,
* are passed or, maybe, dynamically created and stored in dest
map:
* M'j=dest.get(j)
.
* So, the sets of indexes Q and R are the Java sets
* src.keySet()
and dest.keySet()
(after the call of this method).
* See {@link ApertureProcessor comments to ApertureProcessor} for more details.
*
* Note 1: some of the source matrices may be references to the same object
* (src.get(i)==src.get(j)
) — such situations must be processed correctly by this method.
*
*
Note 2: this method must not modify src
map and the source matrices, contained in this map.
*
*
Note 3: this method must not remove key/value pairs from dest
map
* (but may add new resulting matrices).
*
*
Note 4: if some resulting matrix M'j, passed in dest
map
* as dest.get(j)
, is {@code null} while calling this method,
* it must be automatically created and stored back in dest
* map for the same key (index) j.
* (Besides this, this method may create and store in dest
another additional resulting matrices
* with another indexes.)
*
*
Note 5: if this method creates some resulting matrices itself, then dest
map should be mutable.
* The created resulting matrices are saved in this map by dest.put(K,...)
call.
*
*
Note 6: if this method stores some resulting matrices it dest
map, they must have the same
* dimensions as all matrices, passed in src
and dest
maps while calling the method.
* This method must not store {@code null} values in dest
map.
*
*
Note 7: resulting matrices, created by this method instead of {@code null} values in dest
map
* or in addition to the existing matrices in dest
map,
* may be not updatable, i.e. it is possible that their {@link Matrix#array() built-in arrays} will not
* implement {@link net.algart.arrays.UpdatableArray} interface.
* The simplest example: the algorithm may return, as a result (saved in dest
),
* some lazy view of one of the source matrices src.get(i)
* or even just a reference to a source matrix src.get(i)
.
* (But if this object is a tiled processors, returned by
* {@link TiledApertureProcessorFactory#tile(ApertureProcessor)} method, then it is impossible:
* in this case, all resulting matrices, created by this method and stored in
* dest
map, are always updatable, and their {@link Matrix#array() built-in arrays}
* implement {@link net.algart.arrays.UpdatableArray}.)
*
*
Note 8: unlike this, all non-null values, present in dest
map while calling this method,
* must be updatable — because this method may need to store results in them.
* It is true even if this method does not use the old non-null values
* M'j and replace them with newly created matrices —
* even in this case the method may check, that they are not updatable, and throw an exception if so.
*
*
Note 9: the sets of indexes of arguments Q and results R,
* as well as the dimensions of matrices, may vary for different calls of this method.
* (Even the number of dimensions may vary in some implementations, but it is a rarer situation.
* The tiled processors, returned by {@link TiledApertureProcessorFactory#tile(ApertureProcessor)} method,
* work only with a fixed number of dimensions, equal to {@link TiledApertureProcessorFactory#dimCount()}.)
*
*
Note 10: degenerated cases Q=∅ (src.isEmpty()
: no arguments) and
* R=∅ (dest.isEmpty()
: no results) are allowed.
* If Q=R=∅, as well as if Q=∅ and
* the dest
map, passed to this method,
* either is empty or contains only {@code null} matrices,
* then this method usually does nothing —
* it is really true for the tiled processors, returned by
* {@link TiledApertureProcessorFactory#tile(ApertureProcessor)} method,
* but it is not a strict requirement for other implementations.
*
* @param dest the resulting matrices: the result M'j
* will be the value dest.get(j)
of this map after calling this method.
* @param src the source arguments: the argument Mi is the value
* src.get(i)
of this map.
* @throws NullPointerException if dest
or src
argument
* is {@code null},
* or if one of values in src
map is {@code null}.
* @throws IllegalArgumentException if some values in dest
map are not {@code null}
* and, at the same time, their {@link Matrix#array()
* built-in arrays} are not
* {@link net.algart.arrays.UpdatableArray updatable};
* ClassCastException
can be also thrown in this case
* instead of this exception.
* @throws net.algart.arrays.SizeMismatchException if some matrices among the source arguments (values of
* src
map) or non-null resulting matrices
* (values of dest
map), passed while calling this
* method, have different dimensions. It is a typical run-time
* exception in this case, and it is really thrown by the tiled
* processors, created by
* {@link TiledApertureProcessorFactory#tile(ApertureProcessor)}
* method; but other implementations are permitted to throw
* another exceptions in this case.
*/
void process(Map> dest, Map> src);
/**
* Returns the dependence aperture Ai
* for the source matrix Mi with the given index
* i=srcMatrixKey
.
* See {@link ApertureProcessor comments to ApertureProcessor} for more details.
*
* This method must return correct result for any key, which can appear and can be processed in
* {@link #process(Map, Map) process} method as a key in its src
map.
* If srcMatrixKey
argument has some "impossible" value, the result is not specified.
*
*
This method should work quickly and must never throw exceptions.
*
* @param srcMatrixKey the index (key) of the source matrix.
* @return the dependence aperture of the processing algorithm for this source matrix.
*/
IRectangularArea dependenceAperture(K srcMatrixKey);
}