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

com.simiacryptus.mindseye.art.util.Plasma 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.util;

import com.simiacryptus.mindseye.lang.Tensor;
import com.simiacryptus.mindseye.util.ImageUtil;
import com.simiacryptus.ref.wrappers.RefArrays;
import com.simiacryptus.util.data.DoubleStatistics;
import org.jetbrains.annotations.NotNull;

import javax.annotation.Nonnull;
import java.awt.image.BufferedImage;
import java.util.function.DoubleUnaryOperator;
import java.util.function.IntFunction;
import java.util.function.IntUnaryOperator;
import java.util.stream.IntStream;

public class Plasma {
  private int bands;
  private double[] noiseAmplitude;
  private double noisePower;

  public Plasma() {
    setNoisePower(0.5);
    setNoiseAmplitude(100);
    setBands(3);
  }

  public int getBands() {
    return bands;
  }

  @Nonnull
  public Plasma setBands(int bands) {
    this.bands = bands;
    return this;
  }

  public double[] getNoiseAmplitude() {
    return noiseAmplitude;
  }

  @Nonnull
  public Plasma setNoiseAmplitude(double... noiseAmplitude) {
    this.noiseAmplitude = noiseAmplitude;
    return this;
  }

  public double getNoisePower() {
    return noisePower;
  }

  @Nonnull
  public Plasma setNoisePower(double noisePower) {
    this.noisePower = noisePower;
    return this;
  }

  public static double[][] bandStats(Tensor image) {
    double[][] doubles = IntStream.range(0, image.getDimensions()[2]).mapToObj(band -> {
      Tensor selectBand = image.selectBand(band);
      DoubleStatistics doubleStatistics = selectBand.getDoubleStatistics();
      selectBand.freeRef();
      double min = doubleStatistics.getMin();
      double scale = 255 / (doubleStatistics.getMax() - min);
      return new double[]{min, scale};
    }).toArray(double[][]::new);
    image.freeRef();
    return doubles;
  }

  @NotNull
  public static BufferedImage toImage(@Nonnull Tensor image) {
    BufferedImage rgbImage = image.toRgbImage();
    image.freeRef();
    return rgbImage;
  }

  @NotNull
  public static Tensor resize(BufferedImage rgbImage, int width, int height) {
    return Tensor.fromRGB(ImageUtil.resize(rgbImage, width, height));
  }

  @Nonnull
  private static Tensor initSquare(final int bands) {
    Tensor tensor = new Tensor(1, 1, bands);
    tensor.setByCoord(c1 -> 100 + 200 * (Math.random() - 0.5));
    Tensor tensor1 = new Tensor(2, 2, bands);
    tensor1.setByCoord(c -> tensor.get(0, 0, c.getCoords()[2]));
    tensor.freeRef();
    return tensor1;
  }

  @Nonnull
  public Tensor paint(final int width, final int height) {
    Tensor image = initSquare(bands);
    while (image.getDimensions()[0] < Math.max(width, height)) {
      final double factor = Math.pow(image.getDimensions()[0], noisePower);
      Tensor newImage = expandPlasma(image.addRef(), RefArrays.stream(noiseAmplitude).map(v -> v / factor).toArray());
      if (image != newImage) {
        image.freeRef();
        image = newImage;
      }
    }
    return resize(toImage(renormBands(image)), width, height);
  }

  @NotNull
  public Tensor renormBands(Tensor image) {
    double[][] bandStats = bandStats(image.addRef());
    Tensor mapCoords = image.mapCoords(c -> {
      int band = c.getCoords()[2];
      return (image.get(c) - bandStats[band][0]) * bandStats[band][1];
    });
    image.freeRef();
    return mapCoords;
  }

  @Nonnull
  private Tensor expandPlasma(@Nonnull final Tensor seed, @Nonnull double... noise) {
    int bands = seed.getDimensions()[2];
    int width = seed.getDimensions()[0] * 2;
    int height = seed.getDimensions()[1] * 2;
    Tensor returnValue = new Tensor(width, height, bands);
    IntFunction fn1 = b -> x -> clamp(x + noise[b % noise.length] * (Math.random() - 0.5));
    IntFunction fn2 = b -> x -> clamp(x + Math.sqrt(2) * noise[b % noise.length] * (Math.random() - 0.5));
    IntUnaryOperator addrX = x -> {
      while (x >= width)
        x -= width;
      while (x < 0)
        x += width;
      return x;
    };
    IntUnaryOperator addrY = x -> {
      while (x >= height)
        x -= height;
      while (x < 0)
        x += height;
      return x;
    };
    for (int band = 0; band < bands; band++) {
      for (int x = 0; x < width; x += 2) {
        for (int y = 0; y < height; y += 2) {
          double value = seed.get(x / 2, y / 2, band);
          returnValue.set(x, y, band, value);
        }
      }
      final DoubleUnaryOperator f2_band = fn2.apply(band);
      for (int x = 1; x < width; x += 2) {
        for (int y = 1; y < height; y += 2) {
          double value = returnValue.get(addrX.applyAsInt(x - 1), addrY.applyAsInt(y - 1), band)
              + returnValue.get(addrX.applyAsInt(x - 1), addrY.applyAsInt(y + 1), band)
              + returnValue.get(addrX.applyAsInt(x + 1), addrY.applyAsInt(y - 1), band)
              + returnValue.get(addrX.applyAsInt(x + 1), addrY.applyAsInt(y + 1), band);
          value = f2_band.applyAsDouble(value / 4);
          returnValue.set(x, y, band, value);
        }
      }
      final DoubleUnaryOperator f1_band = fn1.apply(band);
      for (int x = 0; x < width; x += 2) {
        for (int y = 1; y < height; y += 2) {
          double value = returnValue.get(addrX.applyAsInt(x - 1), addrY.applyAsInt(y), band)
              + returnValue.get(addrX.applyAsInt(x + 1), addrY.applyAsInt(y), band)
              + returnValue.get(addrX.applyAsInt(x), addrY.applyAsInt(y - 1), band)
              + returnValue.get(addrX.applyAsInt(x), addrY.applyAsInt(y + 1), band);
          value = f1_band.applyAsDouble(value / 4);
          returnValue.set(x, y, band, value);
        }
      }
      for (int x = 1; x < width; x += 2) {
        for (int y = 0; y < height; y += 2) {
          double value = returnValue.get(addrX.applyAsInt(x - 1), addrY.applyAsInt(y), band)
              + returnValue.get(addrX.applyAsInt(x + 1), addrY.applyAsInt(y), band)
              + returnValue.get(addrX.applyAsInt(x), addrY.applyAsInt(y - 1), band)
              + returnValue.get(addrX.applyAsInt(x), addrY.applyAsInt(y + 1), band);
          value = f1_band.applyAsDouble(value / 4);
          returnValue.set(x, y, band, value);
        }
      }
    }
    seed.freeRef();
    return returnValue;
  }

  private double clamp(double a) {
    return Math.max(Math.min(a, 255), 0);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy