com.simiacryptus.mindseye.art.photo.SegmentUtil Maven / Gradle / Ivy
/*
* Copyright (c) 2019 by Andrew Charneski.
*
* The author licenses this file to you under the
* Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance
* with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package com.simiacryptus.mindseye.art.photo;
import com.simiacryptus.mindseye.art.photo.affinity.RasterAffinity;
import com.simiacryptus.mindseye.art.photo.cuda.RefUnaryOperator;
import com.simiacryptus.mindseye.art.photo.cuda.SparseMatrixFloat;
import com.simiacryptus.mindseye.art.photo.topology.RasterTopology;
import com.simiacryptus.mindseye.lang.CoreSettings;
import com.simiacryptus.mindseye.lang.Tensor;
import com.simiacryptus.mindseye.util.ImageUtil;
import com.simiacryptus.ref.lang.RefAware;
import com.simiacryptus.ref.lang.RefUtil;
import com.simiacryptus.ref.wrappers.RefArrays;
import com.simiacryptus.util.FastRandom;
import javax.annotation.Nonnull;
import java.awt.image.BufferedImage;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiPredicate;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
import static com.simiacryptus.mindseye.art.photo.affinity.RasterAffinity.adjust;
import static com.simiacryptus.mindseye.art.photo.affinity.RasterAffinity.degree;
import static java.util.stream.IntStream.range;
public class SegmentUtil {
@Nonnull
public static Tensor resize(@Nonnull Tensor tensor, int imageSize) {
final Tensor resized = Tensor.fromRGB(ImageUtil.resize(tensor.toImage(), imageSize, true));
tensor.freeRef();
return resized;
}
public static int[] valueCountArray(@Nonnull int[] ints) {
final Map countMap = valueCountMap(ints);
return range(0, RefArrays.stream(ints).max().getAsInt() + 1)
.map((int i) -> (int) (long) countMap.getOrDefault(i, 0l)).toArray();
}
public static Map valueCountMap(@Nonnull int[] ints) {
return Arrays.stream(ints).mapToObj(x -> x)
.collect(Collectors.groupingBy((Integer x) -> x, Collectors.counting()));
}
public static void printHistogram(@Nonnull int[] islands) {
RefArrays.stream(islands).mapToObj(x -> x).collect(Collectors.groupingBy(x -> x, Collectors.counting()))
.values().stream().collect(Collectors.groupingBy(x -> x, Collectors.counting())).entrySet().stream()
.sorted(Comparator.comparingDouble(entry -> {
long v = -entry.getValue() * entry.getKey();
RefUtil.freeRef(entry);
return v;
}))
.map(entry -> {
String msg = String.format("%d regions of size %s", entry.getValue(), entry.getKey());
RefUtil.freeRef(entry);
return msg;
})
.forEach(x1 -> System.out.println(x1));
}
@Nonnull
public static int[] markIslands(@Nonnull RasterTopology topology, @Nonnull Function extract, @Nonnull BiPredicate test,
int maxRecursion, int rows) {
int[] marks = new int[rows];
AtomicInteger islandNumber = new AtomicInteger(0);
int[] dimensions = topology.getDimensions();
IntStream stream = range(0, dimensions[0]);
if (!CoreSettings.INSTANCE().singleThreaded) stream = stream.parallel();
stream.mapToObj(x -> x).sorted(Comparator.comparingInt(x -> x.hashCode()))
.mapToInt(x -> x).forEach(x -> range(0, dimensions[1]).mapToObj(y -> y)
.sorted(Comparator.comparing(y -> y.hashCode())).mapToInt(y -> y).forEach(y -> {
int row = topology.getIndexFromCoords(x, y);
if (marks[row] == 0) {
final int thisIsland = islandNumber.incrementAndGet();
marks[row] = thisIsland;
_markIslands(topology.addRef(), extract, test, marks, maxRecursion, thisIsland, x, y);
}
}));
topology.freeRef();
return marks;
}
public static int[] removeTinyInclusions(@Nonnull int[] pixelMap, @Nonnull SparseMatrixFloat graph, int sizeThreshold) {
return removeTinyInclusions(pixelMap, graph, sizeThreshold, sizeThreshold);
}
public static int[] removeTinyInclusions(@Nonnull int[] pixelMap, @Nonnull SparseMatrixFloat graph, int smallSize, int largeSize) {
return removeTinyInclusions(valueCountMap(pixelMap), graph, smallSize, largeSize);
}
public static int[] removeTinyInclusions(@Nonnull Map islandSizes, @Nonnull SparseMatrixFloat graph,
int sizeThreshold) {
return removeTinyInclusions(islandSizes, graph, sizeThreshold, sizeThreshold);
}
public static int[] removeTinyInclusions(@Nonnull Map islandSizes, @Nonnull SparseMatrixFloat graph, int smallSize,
int largeSize) {
return range(0, graph.rows).map(row -> {
final int[] cols = graph.getCols(row);
if (islandSizes.getOrDefault(row, 0l) < smallSize) {
final int[] largeNeighbors = Arrays.stream(cols).filter(j -> {
return islandSizes.getOrDefault(j, 0l) >= largeSize;
}).toArray();
if (largeNeighbors.length == 1) {
return largeNeighbors[0];
}
}
return row;
}).toArray();
}
@Nonnull
public static BufferedImage flattenColors(@Nonnull Tensor content, RasterTopology topology, @RefAware @Nonnull RasterAffinity affinity, int n,
@Nonnull SmoothSolver solver) {
final RefUnaryOperator refUnaryOperator = solver.solve(topology,
affinity.wrap((graphEdges, innerResult) -> adjust(graphEdges, innerResult, degree(innerResult), 0.5)), 1e-4);
RefUtil.freeRef(affinity);
final Tensor tensor = refUnaryOperator.iterate(n, content);
refUnaryOperator.freeRef();
final BufferedImage image = tensor.toRgbImage();
tensor.freeRef();
return image;
}
@Nonnull
public static BufferedImage paintWithRandomColors(@Nonnull RasterTopology topology, int[] pixelMap, @Nonnull SparseMatrixFloat graph) {
return paint(topology, pixelMap, randomColors(graph, 0));
}
@Nonnull
public static BufferedImage paint(@Nonnull RasterTopology topology, int[] pixelMap, @Nonnull Map colors) {
Tensor blank = new Tensor(topology.getDimensions());
final Tensor tensor = blank.mapCoords(c -> {
final int[] coords = c.getCoords();
final int regionId = pixelMap[topology.getIndexFromCoords(coords[0], coords[1])];
final double[] color = colors.get(regionId);
return null == color ? 0 : color[coords[2]];
});
final BufferedImage image = tensor.toImage();
blank.freeRef();
tensor.freeRef();
topology.freeRef();
return image;
}
protected static void _markIslands(@Nonnull RasterTopology topology, @Nonnull Function extract, @Nonnull BiPredicate test,
int[] marks, int maxRecursion, int indexNumber, int... coords) {
final int row = topology.getIndexFromCoords(coords[0], coords[1]);
assert 0 < indexNumber;
final T rowColor = extract.apply(coords);
final List connectivity = topology.connectivity();
if (maxRecursion > 0) {
Arrays.stream(connectivity.get(row)).forEach(col -> {
if (0 == marks[col]) {
final int[] toCoords = topology.getCoordsFromIndex(col);
if (test.test(rowColor, extract.apply(toCoords))) {
if (0 == marks[col]) {
marks[col] = indexNumber;
_markIslands(topology.addRef(), extract, test, marks, maxRecursion - 1, indexNumber, toCoords);
}
}
}
});
}
topology.freeRef();
}
private static Map randomColors(@Nonnull SparseMatrixFloat graph, int iterations) {
return randomColors(graph,
x -> DoubleStream.generate(() -> FastRandom.INSTANCE.random() * 255).limit(3).toArray(),
new ConcurrentHashMap<>(), iterations);
}
private static Map randomColors(@Nonnull SparseMatrixFloat graph, @Nonnull Function seedColor,
@Nonnull Map colors, int n) {
if (n <= 0) {
if (colors.isEmpty()) {
return Arrays.stream(graph.activeRows()).mapToObj(x -> x).collect(Collectors.toMap(x -> x, seedColor));
} else {
return colors;
}
}
return randomColors(graph, seedColor, iterateColors(graph, seedColor, colors), n - 1);
}
private static Map iterateColors(@Nonnull SparseMatrixFloat graph, @Nonnull Function seedColor,
@Nonnull Map colors) {
IntStream stream = Arrays.stream(graph.activeRows());
if (!CoreSettings.INSTANCE().singleThreaded) stream = stream.parallel();
return stream.mapToObj(x -> x)
.collect(Collectors.toMap(key -> key, key -> {
final int[] cols = graph.getCols(key);
final float[] vals = graph.getVals(key);
final List neighborColors = range(0, cols.length)
.mapToObj(
ni -> Arrays.stream(colors.computeIfAbsent(cols[ni], seedColor)).map(x -> x / vals[ni]).toArray())
.collect(Collectors.toList());
if (neighborColors.isEmpty())
return colors.computeIfAbsent(key, seedColor);
final double[] average = range(0, 3)
.mapToDouble(i -> -neighborColors.stream().mapToDouble(j -> j[i] - 127).average().orElse(0.0)).toArray();
final double rms = Math.sqrt(Arrays.stream(average).map(x -> x * x).average().getAsDouble());
return Arrays.stream(average).map(x -> Math.min(Math.max(x / rms * 64 + 127, 0), 255)).toArray();
}));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy