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

com.simiacryptus.mindseye.art.photo.topology.SearchRadiusTopology 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.topology;

import com.simiacryptus.mindseye.lang.Tensor;

import javax.annotation.Nonnull;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.DoubleStream;
import java.util.stream.IntStream;

public class SearchRadiusTopology extends ContentTopology {
  private int neighborhoodSize = 4;
  private double maxChromaDist = 0.2;
  private double maxSpatialDist = 8;
  private boolean verbose = true;
  private int initialRadius = 1;
  private boolean spatialPriority = false;
  private boolean selfRef = false;

  public SearchRadiusTopology(@Nonnull Tensor content) {
    super(content);
  }

  public int getInitialRadius() {
    return initialRadius;
  }

  public void setInitialRadius(int initialRadius) {
    this.initialRadius = initialRadius;
  }

  public double getMaxChromaDist() {
    return maxChromaDist;
  }

  public void setMaxChromaDist(double maxChromaDist) {
    this.maxChromaDist = maxChromaDist;
  }

  public double getMaxSpatialDist() {
    return maxSpatialDist;
  }

  public void setMaxSpatialDist(double maxSpatialDist) {
    this.maxSpatialDist = maxSpatialDist;
  }

  public int getNeighborhoodSize() {
    return neighborhoodSize;
  }

  public void setNeighborhoodSize(int neighborhoodSize) {
    this.neighborhoodSize = neighborhoodSize;
  }

  public boolean isSelfRef() {
    return selfRef;
  }

  public void setSelfRef(boolean selfRef) {
    this.selfRef = selfRef;
  }

  public boolean isSpatialPriority() {
    return spatialPriority;
  }

  public void setSpatialPriority(boolean spatialPriority) {
    this.spatialPriority = spatialPriority;
  }

  public boolean isVerbose() {
    return verbose;
  }

  public void setVerbose(boolean verbose) {
    this.verbose = verbose;
  }

  @Nonnull
  public static Tensor medianFilter(@Nonnull Tensor content) {
    return medianFilter(content, 1);
  }

  @Nonnull
  public static Tensor medianFilter(@Nonnull Tensor content, int window) {
    final int[] dimensions = content.getDimensions();
    Tensor mapCoords = content.mapCoords(c -> {
      final int[] pos = c.getCoords();
      final double[] neighbors = IntStream
          .range(Math.max(0, pos[0] - window), Math.min(dimensions[0], pos[0] + window + 1))
          .mapToObj(x -> IntStream.range(Math.max(0, pos[1] - window), Math.min(dimensions[1], pos[1] + window + 1))
              .mapToDouble(y -> content.get(x, y, pos[2])).toArray())
          .flatMapToDouble(x -> Arrays.stream(x)).sorted().toArray();
      return neighbors[neighbors.length / 2];
    });
    content.freeRef();
    return mapCoords;
  }

  private static double median(@Nonnull DoubleStream doubleStream) {
    return doubleStream.average().getAsDouble();
  }

  @Override
  public List connectivity() {
    final double growth = Math.sqrt(2);
    final List symmetric = dual(
        IntStream.range(0, dimensions[0] * dimensions[1]).parallel().mapToObj(i -> {
          final int[] pos = getCoordsFromIndex(i);
          final double[] pixel = pixel(i);
          final ArrayList neighbors = new ArrayList<>();
          AtomicInteger window = new AtomicInteger(getInitialRadius());
          while (neighbors.stream().mapToInt(x -> x.length).sum() < getNeighborhoodSize()
              && window.get() < (maxSpatialDist > 0 ? Math.min(dimensions[0], maxSpatialDist) : dimensions[0])) {
            final int windowSize = window.get();
            final int windowMin = windowSize > 2 ? windowSize / 2 : -1;
            final int[] matchingGlobal = IntStream
                .range(Math.max(0, pos[0] - windowSize), Math.min(dimensions[0], pos[0] + windowSize + 1))
                .mapToObj(x -> IntStream
                    .range(Math.max(0, pos[1] - windowSize), Math.min(dimensions[1], pos[1] + windowSize + 1))
                    .filter(y -> {
                      final int index = getIndexFromCoords(x, y);
                      if (!isSelfRef() && index == i)
                        return false;
                      final int dx = pos[0] - x;
                      final int dy = pos[1] - y;
                      if (Math.abs(dy) <= windowMin && Math.abs(dx) <= windowMin) {
                        return false;
                      }
                      if (maxSpatialDist > 0) {
                        if (dx * dx + dy * dy > maxSpatialDist * maxSpatialDist) {
                          return false;
                        }
                      }
                      final double dist = chromaDistance(pixel, pixel(index));
                      return dist <= getMaxChromaDist();
                    }).map(y -> getIndexFromCoords(x, y)).toArray())
                .flatMapToInt(x -> Arrays.stream(x)).toArray();
            if (isSpatialPriority()) {
              collectSpacialNeighbors(pos, neighbors, matchingGlobal);
            } else {
              collectChromaNeighbors(pixel, neighbors, matchingGlobal);
            }
            window.set(Math.max((int) (windowSize * growth), windowSize + 1));
          }
          return neighbors.stream().flatMapToInt(data -> Arrays.stream(data)).toArray();
        }).collect(Collectors.toList()));
    if (isVerbose())
      log(symmetric);
    return symmetric;
  }

  public @SuppressWarnings("unused")
  void _free() {
    super._free();
  }

  @Nonnull
  public @Override
  @SuppressWarnings("unused")
  SearchRadiusTopology addRef() {
    return (SearchRadiusTopology) super.addRef();
  }

  private void collectSpacialNeighbors(int[] pos, @Nonnull ArrayList neighbors, @Nonnull int[] matchingGlobal) {
    final Map> collect = Arrays.stream(matchingGlobal).mapToObj(x -> x)
        .collect(Collectors.groupingBy((Integer x) -> {
          final int[] coords = getCoordsFromIndex(x);
          int dx = coords[0] - pos[0];
          int dy = coords[1] - pos[1];
          return (long) dx * dx + (long) dy * dy;
        }, Collectors.toList()));
    final long[] globalRadii = collect.keySet().stream().mapToLong(x -> x).sorted().toArray();
    for (int ring = 0; ring < globalRadii.length; ring++) {
      if (neighbors.stream().flatMapToInt(data -> Arrays.stream(data)).distinct().count() >= getNeighborhoodSize())
        break;
      neighbors.add(collect.get(globalRadii[ring]).stream().mapToInt(x -> x).toArray());
    }
  }

  private void collectChromaNeighbors(double[] fromPixel, @Nonnull ArrayList neighbors, @Nonnull int[] matchingGlobal) {
    neighbors.add(Arrays.stream(matchingGlobal).mapToObj(x -> x)
        .sorted(Comparator.comparingDouble(x -> chromaDistance(pixel(x), fromPixel)))
        .mapToInt(x -> x).distinct()
        .limit(getNeighborhoodSize() - neighbors.stream().mapToInt(x -> x.length).sum()).toArray());
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy