
org.scijava.ops.image.morphology.Outline Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of scijava-ops-image Show documentation
Show all versions of scijava-ops-image Show documentation
Image processing operations for SciJava Ops.
The newest version!
/*-
* #%L
* Image processing operations for SciJava Ops.
* %%
* Copyright (C) 2014 - 2024 SciJava developers.
* %%
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
* #L%
*/
package org.scijava.ops.image.morphology;
import java.util.Arrays;
import net.imglib2.Cursor;
import net.imglib2.RandomAccess;
import net.imglib2.RandomAccessibleInterval;
import net.imglib2.outofbounds.OutOfBounds;
import net.imglib2.roi.labeling.BoundingBox;
import net.imglib2.type.BooleanType;
import net.imglib2.type.logic.BitType;
import net.imglib2.util.Intervals;
import net.imglib2.util.Util;
import net.imglib2.view.ExtendedRandomAccessibleInterval;
import net.imglib2.view.IntervalView;
import net.imglib2.view.Views;
import org.scijava.function.Computers;
/**
* The Op creates an output interval where the objects are hollow versions from
* the input. Rectangles become outlines, solid cubes become surfaces etc.
*
* @author Richard Domander (Royal Veterinary College, London)
* @implNote op names='morphology.outline'
*/
public class Outline> implements
Computers.Arity2, Boolean, RandomAccessibleInterval>
{
/**
* Copies the outlines of the objects in the input interval into the output
*
* @param input a binary interval
* @param excludeEdges are elements on stack edges outline or not
*
* For example, a 2D square:
* 0 0 0 0
* 1 1 1 0
* E 1 1 0
* 1 1 1 0
* 0 0 0 0
* Element E is removed if parameter true, kept if false
*
* @param output outlines of the objects in interval
*/
/**
* TODO
*
* @param input
* @param excludeEdges
* @param output
*/
@Override
public void compute(final RandomAccessibleInterval input,
final Boolean excludeEdges, final RandomAccessibleInterval output)
{
if (!Intervals.equalDimensions(input, output))
throw new IllegalArgumentException(
"input and output must have equal dimensions!");
final Cursor inputCursor = Views.iterable(input).localizingCursor();
final long[] coordinates = new long[input.numDimensions()];
final ExtendedRandomAccessibleInterval> extendedInput =
extendInterval(input, excludeEdges);
final RandomAccess outputAccess = output.randomAccess();
while (inputCursor.hasNext()) {
inputCursor.fwd();
inputCursor.localize(coordinates);
if (isOutline(extendedInput, coordinates)) {
outputAccess.setPosition(coordinates);
outputAccess.get().set(inputCursor.get().get());
}
}
}
// region -- Helper methods --
private ExtendedRandomAccessibleInterval>
extendInterval(RandomAccessibleInterval interval, boolean excludeEdges)
{
final B type = Util.getTypeFromInterval(interval).createVariable();
type.set(excludeEdges);
return Views.extendValue(interval, type);
}
/**
* Creates a view that spans from (x-1, y-1, ... i-1) to (x+1, y+1, ... i+1)
* around the given coordinates
*
* @param interval the space of the coordinates
* @param coordinates coordinates (x, y, ... i)
* @return a view of a neighbourhood in the space
*/
private IntervalView neighbourhoodInterval(
final ExtendedRandomAccessibleInterval> interval,
final long[] coordinates)
{
final int dimensions = interval.numDimensions();
final BoundingBox box = new BoundingBox(dimensions);
final long[] minBounds = Arrays.stream(coordinates).map(c -> c - 1)
.toArray();
final long[] maxBounds = Arrays.stream(coordinates).map(c -> c + 1)
.toArray();
box.update(minBounds);
box.update(maxBounds);
return Views.offsetInterval(interval, box);
}
/** Checks if any element in the neighbourhood is background */
private boolean isAnyBackground(final IntervalView neighbourhood) {
final Cursor cursor = neighbourhood.cursor();
while (cursor.hasNext()) {
cursor.fwd();
if (!cursor.get().get()) {
return true;
}
}
return false;
}
/**
* Checks if an element is part of the outline of an object
*
* @param source the location of the element
* @param coordinates coordinates of the element
* @return true if element is foreground and has at least one background
* neighbour
*/
private boolean isOutline(
final ExtendedRandomAccessibleInterval> source,
final long[] coordinates)
{
final OutOfBounds access = source.randomAccess();
access.setPosition(coordinates);
if (!access.get().get()) {
return false;
}
final IntervalView neighbourhood = neighbourhoodInterval(source,
coordinates);
return isAnyBackground(neighbourhood);
}
// endregion
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy